【持续更新】近期C++开发Modbus通讯接口小结

项目需求:对PLC上存储的数据进行读取,并转存到数据库

语言:C++、DDL

所需知识点:Socket通信、Modbus帧结构、C++中数据库的操作、多线程、Linux

项目进度拆解记录(不会做就是困难!!管它简不简单!)

1、Socket通信。

        由于之前一直采用串口通信,并未了解过其他通信机制,所以&*%¥%!@#(啥也不会)。打开网页、视频各种学习,发现是自己掌握的TCP通信机制,但自己只是理论掌握,不具备代码实现的能力,在CSDN、GitHub寻找各种资源后,终于写出了自己的Socket.cpp。这只是开始,耗时一周(严重浪费!!!)

        1.1、Socket通信连接问题

       在不打开服务器的情况下,程序会一直停在连接时(我用的是connect连接)。且打开服务器后,通信连接成功,但会报出一个错误(自己写的,取出connect返回值为-1)。这里理想的优化方案是连接服务器超时时,报出一个错误,然后再尝试继续连接。实现方式学习中……

2、Modbus帧结构。(这个我可以,最喜欢帧结构!)

        2.1、Socket通信连接问题

       耗时一个下午,各处查找资料后,学习到Modbus帧中的位所代表的信息。——这里安利两个小工具(Modbus Slave和Modbus Poll)下载地址(链接:https://pan.baidu.com/s/1YNz1wbb6PeRrVDhg2mrdgQ 提取码:z2a0 --来自百度网盘超级会员V4的分享)——下图可以即为帧结构,显然~~~所以~~~这里懂了。在代码实现的过程中,由于不可抗力的被要求加入CRC校验算法,然后打开GitHub一顿扒拉,读了很多之后,自己写了一段适合自己的,然后!发现CRC在TCP里不需要(内心:hhhh我就当学习了吧,rm -rf /*                  One week later!)

【持续更新】近期C++开发Modbus通讯接口小结_第1张图片

        2.2、忽略MODBUS通信机制造成的错误

        在modbus中,下发信息后,目标机会回复一条信息(即便是在下发修改指令)。此处,由于我在下发修改指令后,并未进行接收数据的操作,导致我其它读值部分读取的全是修改的返回数据。于是便在修改后加入一个recv接收socket中的缓冲区进行清空缓冲区(十分重要),用数组接受完后,记得释放数组,不然也会引发异常。

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语句实现。

--------------------------------------------------------------分割------------------------------------------------------------

         抱着重温Mysql知识的想法,去官网看了一下插入语言。突然发现对INSERT插入时,有一个关键字IGNORE可以忽视主键存在,进行重复插入。于是取消REPLACE方法。(此处有一个猜想,在调用REPLACE时,前、后端对数据库的异步操作,会不会对前端来说,在某次查询数据时,由于REPLACE引起的清空而导致表中数据为NULL然后报错?)有待验证~

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中的内容即时输出

——————————————————分割———————————————————————

        继昨天的printf无法输出的问题,找了各种资料(可能还是不够),试了cout、printf、cerr(这个早上还好用),都在最后调试的时候无法输出,看了资料介绍,cerr不需要缓冲区,是直接输出的。所以应该和缓冲区无关,毕竟我清空过,也设置过即时输出。开始考虑是否存在其他因素,导致我的Cpp无法输出信息?

——————————————————分割———————————————————————

         很搞笑的是,搞了我两三天的心态,我发现我的问题出现在:未关闭上次调试打开的命令行窗口,导致程序输出流的实现出现假死(不知道从哪里可以看到关于VS调试中,程序输出流的机制。好奇为什么会产生这种现象。是由于对命令行窗口的选择导致无法输出吗?)TIPS:每次调试结束看完输出信息,一定一定要关闭命令行窗口

        5.2、C++ 项目超出资源限制

           这是一个遗留问题!!!我也不知道为什么,同一个项目,有时候超出资源限制,有时候不会。但是超出资源限制好像并不影响我的接口继续实现功能?也可能是我太菜了,我不懂。。。难道是我的资源泄露嘛,可是我也几个变量啊,都是不断的复用。C++好蓝啊,我还是想回去写C#!

6、Linux

         6.1 库文件引用

         果然不出意外,对Linux的不熟悉,直接拖了我三天的时间。这里出现的问题在于:移植项目的过程中,其中涉及到对MYSQL数据库的库引用问题。感谢这个问题帮我学习了Linux下的部分指令!!!这里的库名是:libmysqlclient。设置好了包含目录、库目录和库依赖项。然后开始各种ld找不到库。下面是解决办法:

                 1、首先查找你的libmysqlclient。locate libmysqlclient 。没有的话记得yum install mysql-devel。

                  2、给libmysqlclient复制一份过去(方法1)。

                  3、在lib下创建软连接(推荐)。ln /usr/lib64/mysql/libmysqlclient.so /usr/lib/libmysqlclient.so

                  4、更改ld的搜索路径,可以通过更改预设路径(config文件内添加你的libmysqlclient的路径)或者更改环境变量export巴拉巴拉的。

                   5、如果你做完上面的步骤,还搜索不到!可能你就和我一样是半路出家C++的!令人头秃的是,在对VS的调试设置中,就是库依赖项那里,不要填写libmysqlclient.so。这里不得不吐槽这个检索和命名机制,lib+mysqlclient+.so。so是文件后缀的标识,类似lib,这个不加我就不说了,为什么lib也不加啊!!打开命令行一看,人家命令写了,-L(路径)-l(库名)。就这个-l代表的就是lib了,好家伙,文件名还要拆开才可以检索到,已无力吐槽。

         其余的移植过程,无非就是几个函数名称的替换(建议开发时,尽量使用C++标准函数,少用IDE封装出来的函数)、头文件的引用问题,暂无大碍。

有感而发,这个程序越写越感觉,就是得多线程并发工作,串行只是勉强能用!!!未完待续。。。

你可能感兴趣的:(c++,开发语言,后端,modbus)