欲看图文版文档,请用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调用。