CVI+MySQL编程入门之用户管理

欲看图文版文档,请用PC浏览器下载附件pdf

本文写给数据库的编程小白。参考文档有《一个硬件工程师眼中的MySQL数据库 20230202》,对数据库有个初步认识;还有《CVI+MySQL编程入门之用户管理 20230203》,简单介绍了一下基于DP_MySQL.dll对MySQL数据库的编程方法。

这次因为在硬件上加入了串口连接的指纹模块,软件上加入了支持用户通过指纹登录,故本文有更新。

一、 数据库的基础构成元素

这里,需要我们熟悉的Excel和陌生的Database进行了一次简单粗暴地类比,方便快速上车。请记住这个类比关系,这是我这样的笨小孩才会采用的笨办法,但真实有效。

1,一个excel文件,可视为一座数据库(database);

2, 一个excel文件中的不同页sheet1/2/3/...n,可视为一座数据库中的不同表单(table);

3,一个excel文件中某一sheet页面上的一行,可视为一座数据库中某一表单的一条记录(record);

4,一个excel文件中某一sheet页面上的每一列(Column),可视为一座数据库中某一表单的字段(Field);

5,一个excel文件中某一sheet页面上的某个单元格(Cell),可视为一座数据库中某一表单的字段值(Value)。

二、 创建一个数据库和一个表单

默认在本机上已经安装有MySQL数据库软件。额外还需要在本机安装一个数据库管理工具软件Navicat,下载最新版V17,请务必先阅读crack目录下的《本激活工具适合16、17全部版本.txt》,不然安装顺序搞反了就破解不了。或者安装DataGrip,这是开源且免费的。

欲用Navicat,连接到本机的mySQL服务器,需要新建一个连接。输入连接名和密码,其他保持默认值,就可以了。

这样,在左侧连接栏,就多了一个MySQLConnect的连接:

双击MySQLConnect的这个连接,会变绿,并拉出该连接包含的一众数据库,其中,myi2c这个数据库,是我曾经建好的一个私有数据库,其他都是安装完mySQL自动就有的。

现在准备新建一座数据库,取名mySFPPlusZR。点击"新建查询",在查询框内输入豆包给出的SQL语句"CREATE DATABASE MYSFPPluSZR"

然后点"运行",完了去左边的连接框内右键刷新,就应该可以看到此处确实新增了一个名为mySFPPlusZR的数据库:

当然,如果不习惯运行SQL指令,也可以全图形化地创建数据库。在连接名处点击鼠标右键,选"新建数据库...",然后在新窗口中,填入数据库名= mySFPPlusZR,选择字符集= utf8mb4-- UTF-8 Unicode,选择排序规则=utf8mb4 general ci,即可。

此刻双击mySFPPlusZR数据库,还是空的,没有表单。

那么这里首先就需要创建一个用户表单,取名user。

下面是用代码给mySFPPlusZR数据库创建一个名为User表单的SQL语句。其中,字段有唯一索引号键值id(int,且需要自动递增)、用户账号UserName(varchar字符串)、用户密码UserPassword(varchar字符串)、指纹编码 UserFingerprintID(int)和创建时间DATE_TIME(timestamp,默认值CURRENT_TIMESTAMP)。

CREATE TABLE user (
id int(11) NOT NULL AUTO_INCREMENT,
UserName varchar(50) NOT NULL,
UserPassword varchar(100) NOT NULL,
UserFingerprintID int(11) DEFAULT NULL,
DATE_TIME timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

PRIMARY KEY (id) USING BTREE

) ;

当然,如果不习惯运行SQL指令,也可以全图形化地创建数据库表单。在"表"上点鼠标右键,选"新建表"。

然后如下图,填入字段名、数据类型等。注意,id要勾选"不是null"并设置其属性是自动递增;DATE_TIME的类型是timestamp,其属性的默认值选CURRENT_TIMESTAMP,并勾选"根据当前时间戳更新"。

所有要用我们ATE软件的用户,都得先登录。要么输入正确的账号和密码,要么验明指纹再从数据库带出对应账号,才能继续操作ATE软件。登录前肯定得先注册,首先得输入账号和密码,注意本ATE不保存明文密码到数据库,只存MD5编码之后的密码;如果还想要更简单的指纹登录,就应该先注册一个账号和密码,然后再注册指纹,这样指纹才能绑定到一个有效账号+密码。下面是已经创建好了的并保存有三条记录的user表单,可见这里的id并不连续,一般说明该表单的id=3~5的历史记录有被删减过,而且,用户Ellen没有录入过指纹,只能靠账号密码登录。

三、 用户管理的CVI界面设计

让CVI自动生成一个带GUI的template文件包,注意其panelHandle在c文件中是带static修饰符的。但是我们会有新增C文件,也可能会用到panelHandle这个全局变量,就会用extern来申明。显然,在C语言中,static和extern是相互排斥的,不能同时使用。所以,要跨文件引用同名变量,就必须把static修饰符删掉。

先来了一个common.c和common.h,把复杂的数据类型比如结构体等定义,和一众需要include的.h文件,还有用extern修饰的将被众多.c交叉引用的全局变量,都搞到common.h中去;把将被众多.c交叉引用的全局变量,定义在common.c中。这将极大简化其他.c文件的头,只需要一句#include "common.h"就完事儿。

inifile.c是读取ini文件所需,md5.c是md5加密所需,pwctrl.c是密码以星号显示所需,user.c是用户管理界面回调函数所在。mySQL_CVI_test.c是主业务和mian()入口函数所在。

将静态调用的DP_MySql.dll来访问MySQL数据库,所以项目文件树中把DP_MySql.h和DP_MySql.Lib都拎进来了。

主业务界面上有操作员,就是成功登录时的那个账户名:

用户登录面板控件名是PANELLOGIN,用户成功登陆之后,会切换到主业务界面,主业务界面上也有操作员名称,方便计算计件工资。用户亦可在该界面点击"注册"或"修改密码",进入各自的功能界面。点击"退出"是放弃登录,退出程序。右上新增了指纹登陆和退出指纹登录两个按键,左上是操作向导字符串TipWizard。

用户注册面板的控件名是PANELREG,点击"返回"是返回到登录界面。右上新增了指纹登陆和退出指纹登录两个按键,左上是操作向导字符串TipWizard。

用户更改密码面板的控件名是PANELCHANG,点击"返回"是返回到登录界面:

四、 CVI调用DP_MySQLDLL来访问mySFPPlusZR数据库的User表单

默认本机已安装CVISQL_ToolKit220,这是CVI 的访问数据库的引擎。那接下来开始CVI编程,把基于数据库的用户管理这部分给实现了。

main()一来就会调用on_mysql_connect()函数尝试连接数据库,这里CVI已经连上mySFPPlusZR数据库了,error=0未报错。请注意这个common结构体各成员的值。其中,common.IP=127.0.0.1表示是本机IP,如果要连MySQL服务器,就需要修改成MySQL服务器的IP地址。而连接MySQL服务器的客户端账号/密码是之前在安装MySQL软件到服务器时,就定好了的。最好不要用root这个管理员权限的账号,可以请管理员再分配一些权限低一点的账号拿来给客户端用为宜,不然哪天服务器连上外网不幸被黑客盯上中了勒索病毒,那我们的数据库就玩完了。

DLL函数用到了DP_sql_init():

复制代码
//1,构造数据库连接结构体  
strcpy(Common.IP, "127.0.0.1"); // IPstr=本机IP"127.0.0.1",或远程服务器                        
strcpy(Common.UserId, "read_write_user");  
strcpy(Common.Password, "User123");  
strcpy(Common.Database, DB);  
Common.Port = 3306;  
  
//2,尝试连接数据库  
if (0 > DP_sql_init(&mysql, Common))  
{  
    //SetCtrlVal(panelHandle, PANEL_LED_SearchAllWOSI, 0); //只有这里SetLED=0  
    MessagePopup("Error", "本地数据库连接失败,请检查数据库!");  
    return -2;  
}  

新用户需要先注册一个账号。账号在数据库中必须是唯一的,如果已经被注册了,就不能再次注册,除非数据库后台删除该账号才能被重新注册。

如果新用户的账号合法,则程序将向mySFPPlusZR数据库的user表单插入一条记录,包括自动加1的id、账号、密码、自动创建的时间戳等。由于数据库报错的密码并不是原始密码,而是MD5编码之后的字符串,所以如果用户自己忘了密码,数据库是没法恢复出原始密码的,只能删除用户再重新注册。

这里,允许用户在记得正确密码的前提下,更改自己的旧密码。注意这里是插入,所以先用DP_OpenTransaction()开启了一个事务,如果DP_GetExcute ()出错了,就再调用DP_Rollback()回滚指令,最终调用DP_Commit(mysql)执行指令。

DLL函数用到了DP_GetExcute (),SQL语法是insert或update。注意这里是更新,所以先用DP_OpenTransaction()开启了一个事务,如果DP_GetExcute ()出错了,就再调用DP_Rollback()回滚指令,最终调用DP_Commit(mysql)执行指令。

复制代码
//开启事务  
DP_OpenTransaction(mysql);  
//构造sql指令  
sprintf(sql, "UPDATE mySFPPlusZR.user SET UserFingerprintID = %d WHERE UserName = '%s' ", enroll_id, user_name);  
//执行sql指令  
if (0 > DP_GetExcute(mysql, gbk, sql, LogErr))  
{  
    //撤销事务    
    DP_Rollback(mysql); //指令回滚    
    //释放指针    
    if (NULL !=dataset)    
    {   DP_ArrayFree(dataset); dataset = NULL;      }    
    SetWaitCursor (0); return -14;  
}  
else  
{  
    //提交事务  
    DP_Commit(mysql); //指令确认需要执行  
    //释放指针  
    if (NULL !=dataset)    
    {   DP_ArrayFree(dataset); dataset = NULL;      }    
    MessagePopup("succeed", "指纹注册成功");  
}  

一旦用户拥有了合法的账号密码,就可以直接用账户+密码登陆,输入正确的账号密码后,不再有报警弹窗,而是直接跳转到主业务界面去。

DLL函数用到了DP_GetExcute (),SQL语法是select:

复制代码
//构造SQL指令  
sprintf(buf, "SELECT  UserName, UserPassword FROM mySFPPlusZR.user \  
    WHERE UserName = \'%s\'", user_name);  
//执行SQL指令  
dataset = DP_GetDataSet(mysql, gbk, buf, LogErr);  
if ((NULL == dataset) || (0 == dataset->cur_row))//一行数据库记录都没有获取到   
{  
    //释放指针    
    if (NULL !=dataset)    
    {   DP_ArrayFree(dataset); dataset = NULL;      }    
  
    MessagePopup("error", "用户名或密码错误");  
    SetWaitCursor(0); return -2;//执行数据库select失败  
}  
else //打印  
{  
    get_md5(user_password, md5_user_password);  
  
    if (!strcmp(user_name, dataset->Tables[0][0]) && !strcmp(md5_user_password, dataset->Tables[0][1]))  
    {  
        //释放指针    
        if (NULL !=dataset)    
        {   DP_ArrayFree(dataset); dataset = NULL;      }    
    }  
    else  
    {  
        //SetCtrlVal(PANEL, PANEL_STR_Operator, " ");  
        MessagePopup("error", "用户名或密码错误");  
        //释放指针  
        if (NULL !=dataset)    
        {   DP_ArrayFree(dataset); dataset = NULL;      }    
        SetWaitCursor(0);  
        return -3; // data not found in database!  
    }  
}  

一旦用户拥有了合法的账号密码,还可以继续注册指纹,将来可以直接用指纹登陆,然后跳转到主业务界面去。

注册指纹,仍然需要用户先输入合法的账号+密码,然后点击"指纹注册"按键,两次录入指纹后合并特征保存到指纹模块并生成指纹ID,完了会将指纹ID存入user表单对应账户的那一行记录中。

注册指纹按键的回调函数的源代码流程如下:

复制代码
1)从注册面板获取账户和密码,与数据库核对判断当前用户账号合法性,必须是有效账号+密码方式的注册账号,而且该账户应未注册过指纹;
2)通过访问Windows注册表获得所有有效串口号的总数(comNum)和串口号列表(strSerialList[]);
3)遍历所有串口号,尝试打开串口并发送PS_ReadIndexTable指令,判断哪个串口号能正确响应才是AS608指纹模块,并解析已录入指纹ID的总数IDAmount和序号IDList[]; 
4)从指纹模块已录入指纹ID的序号IDList[]中,搜索可分配指纹ID号最小值;
5)前面已确定有效账户名和可分配指纹ID号最小值,现在开始两次循环采集指纹(直到用户指纹被成功采集,或按Quit键退出循环),再合并指纹特征后绑定指纹ID号一并保存到指纹模块,最后关闭串口;
6)update数据库,绑定指纹ID号到有效账户名这条记录,完成指纹注册。

指纹登录的要求相对简单点,只需要点击"指纹登录"按键

指纹登录按键的回调函数的源代码流程如下:

复制代码
1)通过访问Windows注册表获得所有有效串口号的总数comNum和串口号列表strSerialList[];
2)遍历所有串口号,尝试打开串口并发送PS_ReadIndexTable指令,判断哪个串口号能正确响应才是AS608指纹模块,找到AS608指纹模块后,继续查询指纹模块已录入指纹ID的总数IDAmount和序号IDList[] ;
3)循环核验指纹,直到用户指纹匹配上指纹模块的指纹库并返回匹配指纹ID,或按Quit键退出循环,最后关闭串口;
4)select数据库,查出指纹ID对应的账户名;
5)关闭登录界面,显示主界面,并将账户名显示到主界面CVI调用。
相关推荐
正在走向自律2 小时前
电科金仓MySQL迁移实战:一个技术专家的深度踩坑与突围笔记
数据库·mysql·电科金仓·kfs·kdts
泯仲2 小时前
从零起步学习MySQL 第二章:DDL语句定义及常见用法示例
数据库·mysql
Leon-Ning Liu2 小时前
记录MySQL 主从架构切换双主(互为主从)操作步骤
数据库·mysql
前进的李工3 小时前
数据库视图:数据安全与权限管理利器
开发语言·数据库·mysql·navicat
程序员这么可爱3 小时前
MySQL分页踩坑实录:LIMIT分页出现重复数据,同一主键ID跨页重复完美解决
数据库·mysql·limit分页重复·sql分页优化·数据库踩坑·主键排序规范
熊文豪4 小时前
MySQL迁移的“隐形坑”与电科金仓的“零改造”破局之道
数据库·mysql
czlczl200209254 小时前
Mysql的多版本快照MVCC机制与Mysql四种隔离级别
数据库·mysql
有想法的py工程师4 小时前
PostgreSQL 事务隔离级别详解(以及与MySQL实现差异)
数据库·mysql·postgresql
chuxinweihui5 小时前
MySQL内外连接
数据库·mysql