项目需求:对PLC上存储的数据进行读取,并转存到数据库
语言:C++、DDL
所需知识点:Socket通信、Modbus帧结构、C++中数据库的操作、多线程、Linux
项目进度拆解记录(不会做就是困难!!管它简不简单!)
1、Socket通信。由于之前一直采用串口通信,并未了解过其他通信机制,所以&*%¥%!@#(啥也不会)。打开网页、视频各种学习,发现是自己掌握的TCP通信机制,但自己只是理论掌握,不具备代码实现的能力,在CSDN、GitHub寻找各种资源后,终于写出了自己的Socket.cpp。这只是开始,耗时一周(严重浪费!!!)
2、Modbus帧结构。(这个我可以,最喜欢帧结构!)耗时一个下午,各处查找资料后,学习到Modbus帧中的位所代表的信息。——这里安利两个小工具(Modbus Slave和Modbus Poll)下载地址(链接:https://pan.baidu.com/s/1YNz1wbb6PeRrVDhg2mrdgQ 提取码:z2a0 --来自百度网盘超级会员V4的分享)——下图可以即为帧结构,显然~~~所以~~~这里懂了。在代码实现的过程中,由于不可抗力的被要求加入CRC校验算法,然后打开GitHub一顿扒拉,读了很多之后,自己写了一段适合自己的,然后!发现CRC在TCP里不需要(内心:hhhh我就当学习了吧,rm -rf /* One week later!)
3、MySQL。之前只用过Access和SQLServers,众所周知,这俩都是图形化操作界面,真的是很友好了,首先,拿到外包提供的数据库不会链接到自己的库中,只好转txt然后Ctrl CV(神技)自己执行。这里感谢好朋友的帮助,提供给我Navicat,用起来感觉MySQL也就洒洒水啦,有了图形化操作界面我乱点都点的出来,嘿嘿嘿。。。。回归正题,之后用C++链接数据库,这里应该不算是一个难点了,无非是增删改查,各种资源一看就懂。
3.1 数据库写入时的中文乱码。本来在写连接的时候,有些函数设置了语句:
const char* query = "set names 'GBK'";
mysql_query(_mysql, query);
但是由于并未在我写入之前进行设置,所以我把这里的设置改到对数据库的连接中,连接上数据库后立刻设置,Nice!
3.2 重复写入机制的优化由于对对某一个表需要重复写入200条记录,每次的值都相当于是刷新替代旧的值。为了防止对数据库写入超行和写入数据重复,我在写入前优先使用查询语句进行查询内容是否存在,存在则选择UPDATE更新值,不存在的话使用INSERT插入值。
--------------------------------------------------------------分割------------------------------------------------------------
然后突然发现有一个REPLACE语句,可以直接更换,相当于对我的查询和插入做了一个集合。对接口来说,在整个写入过程中,时间减少了一半,且REPLACE写入机制刚好符合我的要求,果断选择更换到REPLACE语句实现。
4、关于整个接口的实现方式引发的不同版本。姑且称多线程为V1.0,单线程为V2.0。问题来了,在V1.0版本中需要多线程的知识(我真的超级喜欢多线程,顺带学习了C++的多线程实现,声明实现~)问题来了,在多线程中我肯定不能用同一个socket连接和数据库连接吧,于是大方地给了每个线程发了一个新的。这里就得加上互斥锁了!但,问题来了,数据库新建连接多了导致我写入超时(这里是因为我在每个线程后未断开数据库)。由于时间的影响,本来找到的解决办法:数据库的线程池操作,就被我pass掉了。于是Copy出副本,改V2.0,用同一个socket和数据库连接(但愿不会崩)一切就挺顺利,剩下的代码大同小异的查询PLC然后转存到数据库,祭出神技:Ctrl C(V)!! (One week later)等待有空了一定把那个多线程的给搞出来,多线程在逻辑上更符合多功能实现!
5、C++。按理说这个应该是首要问题,但是对其的学习贯穿了我整个开发过程,属于边学习边实现。不过对const char *、char *、char、string还是区别不清(有问题即时百度,虽然有点浪费时间)。
5.1、Printf假死C++中的printf竟然还会出现假死状态,我这里真的弄不懂为什么在连接socket和数据库的时候,我的printf不输出值,而且假死在数据库的连接中!!!为什么我的缓冲区就满了呀!我就写了几个字符而已啊!!!看了网上的子,清空缓冲区和设置为即时输出都不行!然后我就!直接注释掉我的prinf。神奇的是每一次读写中的printf跑的可快了,一点也看不出来是会在缓冲区中转圈圈的样子!
fflush(stdout); //清空缓冲区 setvbuf(stdout, NULL, _IONBF, 0); //设置stdout中的内容即时输出
暂时就是这样,还在继续实现中,后续还要转到Linux上,可恶啊!又是完全不会呢!



