通讯模块Communicator

通讯模块是整个项目设计中位于最底层的模块,用于处理与串口或网络等设备的通讯,所有设备的通讯通过CommManager类完成,上层软件设计时需要根据comm模块(主要是CommManager)提供的接口访问设备。通讯组件实现类的重要类是 QIODevice,所有通讯过程中发送/接收的字符都经过QIODevice发送到设备中。

通讯模块的测试和API 文档 基本完成,生成文档可使用 qdoc 工具,将代码中的注释转换成网页格式的文档,文档图如下:

Communicator层中提供了Comm 命名空间简化使用该库的过程,要使用该库文件,可以参考下列方式:

1.         通讯设备:串口或网络

磁罗盘通过USB-RS485转接线连接到PC上位机,从而实现的上下位机通讯,由于半双工的RS485无法同时收发数据,若在下位机发送数据时,上位机向下位机发送指令,将会导致RS485上电平异常,通讯失败,上位机接收数据时出现乱码(由于下位机程序设计的原因,不能同时收发数据,所以此时下位机一般不会接收数据)。为了找到下位机发送语句的间隔而找到合适的发送指令时机,通讯代码使用多线程+阻塞式的方法监听串口,在通讯接口空闲时上位机才发送数据(实际上RS485是通过FT232r芯片转接到USB上连接电脑,所以其实是通过USB端口监听RS485串口)。

若设备的连接方式支持全双工模式,可以设置串口的工作模式为全双工,全双工模式和半双工的模式区别在于:全双工在接收到管理器的查询指令后将会立即将指令写入到设备中;半双工则会监听设备,在线路空闲时发送指令。

为实现远程操控下位机,通讯设备中新增加了网络通讯模式。网络通讯模式与串口通讯模式差别不大,主要差别在于网络通讯要指定端口,而串口通讯要指定波特率停止位等。

2.         控制器CommManager

项目中串口控制器的类为CommManager,该类的作用有:

  • 聚合协议和串口;
  • 处理来自协议的查询请求;
  • 根据协议的部分参数进行请求;
  • 接收来自串口的数据并分发给各个协议;
  • 对串口/网络子线程进行控制。

关于CommManager的更多具体用法,可以参考API文档。

上位机串口通信若采用信号/槽类型的异步接收数据方式在主线程中接收并处理数据,不需要让通讯类在一个独立的子线程中运行,但半双工模式下需要通过阻塞方式实时监听串口,因此需要通过一个串口线程控制器来操作子线程的运行和停止。

接收、发送数据及字符串拼接的工作在子线程中执行。

图 1 CommManager的部分接口

CommManager的主要目的是隐藏具体的多线程实现,统一了编程接口,使得其它类可以直接调用控制器中相应的方法,而不需要通过连接多线程对象中的信号与槽(即信号槽的方式)调用通讯器的方法。因为Qt的多线程机制,在控制器中采用了反射或信号等方式,通过CommManager隐藏多线程的实现,简化其它部分的代码。

注:Qt的Thread多线程机制与其它编程语言的多线程稍有不同,不同之处在于QThread更确切的来说应该是一个线程控制器而非一个具体的线程。而将QObject子类对象通过moveToThread方法放置到子线程中后,若直接调用该对象的foo方法,则foo方法将会在主线程中执行,为了避免这种情况的发生,需要使用Qt的信号槽机制,利用信号来调用foo槽函数,使槽函数在所在的子线程中执行,即不会阻塞UI线程。

串口通讯需要有相应的协议来实现,通过CommManager对象的addProtocol接口添加协议,CommManager将会自动绑定信号和槽。接收到来自下位机的数据(以行为单位)时,调用接口AbstractProtocol实现类的processData回调函数对接收到的数据进行处理。接收到来自协议的请求时,从协议队列中选取一个协议并取出命令,将命令的内容发送到下位机。

CommManager计算指令的执行时间(从上位机Qt程序中发送到串口至下位机响应的时间),根据AbstractProtocol实现类的 cmdExecuteTime值决定命令的重发次数。管理器在接收到协议发送的信号后,会优先查询高优先级协议,并获取高优先级的AbstractProtocol类中的待查讯指令进行查询。默认情况下,最先注册到CommManager中的协议有最高的优先级,当该协议中的指令全部查询结束后才会查询次优先级协议中的指令。

3.         通讯接口及部分实现类

项目中所有的串口/网络通讯设备都继承自AbstractComm接口并实现AbstractComm中定义的纯虚方法。AbstractComm定义了与CommManager之间的接口。

图 2 AbstractCom 的部分接口

若要调用 AbstractCom 的实现类中的方法,应当采用信号槽机制或这QMetaObject::invokeMethod 方法来调用,这样才可以保证AbstractCom实现类中的槽函数全部运行在子线程中。对于AbstractCom实现类中的标志位和查询指令可以直接通过函数调用来修改(需要加锁保证线程安全)。

ComFullDuplex 通讯器实现类为工作于全双工模式下的串口,通过连接 QSerialPort 实例的 readyRead 信号和自身的onRead槽函数对串口发送的信息进行处理。使用 readyRead 信号进行响应,异步执行数据处理,尽管这个类的方法可以在主线程中执行,但为了统一控制器和串口之间的接口,仍将其放在子线程中执行。在这个实现类中上位机不会控制发送指令的时间(认为该类是工作于全双工模式,上位机和下位机可以同时发送信息,不会导致信道电平异常产生乱码),而是在接收到来自用户的操作后立即向下位机发送指令,但若在RS485串口中错误使用该类,将可能导致在半双工模式下的RS485总线中产生乱码无法正常收到回复。若单片机使用RS485半双工连接上位机时,建议不要选择该类发送串口数据(若上位机不需要发送指令,则不会导致乱码)。而单片机使用RS232全双工连接时可以使用该模式。

4. 代码及文档

文档:https://brifuture.github.io/qt_components/basic_communicator/docs/

代码:https://github.com/BriFuture/qt_components/tree/master/basic_communicator

自定义Qt组件-通讯模块(P1)的更多相关文章

  1. 自定义Qt组件-通讯模块(P3)

    1.   半双工模式实时检测串口 ComHalfDuplex类是为了解决上位机发送控制指令和下位机发送数据会在半双工RS485总线中产生冲突引起乱码而引入的(v0.010版本引入). 解决冲突的原理主 ...

  2. 自定义Qt组件-通讯模块(P2)

    1.  抽象协议AbstractProtocol 抽象协议AbstractProtocol定义CommManager与协议之间的接口.AbstractProtocol中的一些属性(如enabled)用 ...

  3. C/C++ Qt TableDelegate 自定义代理组件

    TableDelegate 自定义代理组件的主要作用是对原有表格进行调整,例如默认情况下Table中的缺省代理就是一个编辑框,我们只能够在编辑框内输入数据,而有时我们想选择数据而不是输入,此时就需要重 ...

  4. SSIS自定义数据流组件开发(血路)

    由于特殊的原因(怎么特殊不解释),需要开发自定义数据流组件处理. 查了很多资料,用了不同的版本,发现各种各样的问题没有找到最终的解决方案. 遇到的问题如下: 用VS2015编译出来的插件,在SSDTB ...

  5. Android Studio开发基础之自定义View组件

    一般情况下,不直接使用View和ViewGroup类,而是使用使用其子类.例如要显示一张图片可以用View类的子类ImageView,开发自定义View组件可分为两个主要步骤: 一.创建一个继承自an ...

  6. [UE4]自定义MovementComponent组件

    自定义Movement组件 目的:实现自定义轨迹如抛物线,线性,定点等运动方式,作为组件控制绑定对象的运动. 基类:UMovementComponent 过程: 1.创建UCustomMovement ...

  7. Qt组件中的双缓冲无闪烁绘图

      双缓冲绘图在Qt4中,所有的窗口部件默认都使用双缓冲进行绘图.使用双缓冲,可以减轻绘制的闪烁感.在有些情况下,用户要关闭双缓冲,自己管理绘图.下面的语句设置了窗口部件的Qt::WA_PaintOn ...

  8. 【转】Android学习基础自定义Checkbox组件

    原文网址:http://forum.maiziedu.com/thread-515-1-1.html heckbox组件是一种可同时选中多项的基础控件,即复选框,在android学习中,Checkbo ...

  9. 自定义Qt按钮

    转自:http://blog.csdn.net/starcloud_zxt/article/details/5185556 Qt自带的PushButton样式比较单一,在开发的时候往往按钮的形状各异, ...

随机推荐

  1. sqlserver清楚文本中的换行符

    REPLACE(@string,CHAR(13)+CHAR(10),char(32))

  2. javascript 取掉空格自定义函数

    js  取掉空格自定义函数 //取掉左右空格: function trim(str){ return str.replace(/(^\s*)|(\s*$)/g, ""); } // ...

  3. Spring Boot 学习系列(02)—使用热部署,提升开发效

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 开发调试很简单 热部署的使用非常简单,但能极大的提高我们的开发效率,像传统的web应用,我们修改后需要重新编 ...

  4. Go:创建新进程(os.StartProcess源码解读)

    关于如何使用go语言实现新进程的创建和进程间通信,我在网上找了不少的资料,但是始终未能发现让自己满意的答案,因此我打算自己来分析这部分源代码,然后善加利用,并且分享给大家,期望大家能从中获得启发. 首 ...

  5. Java基础之8个基本类型及基本类型间转换

    8种基本数据类型 Java语言有8种基本数据类型, 分别用于存储整数.浮点数.字符数据和布尔类型数据.需要注意的是: 现在所介绍的仅仅是基本数据类型,后续还会介绍很多非基本数据类型.基本数据类型如图 ...

  6. 文件上传之Ajax篇

    AJAX上传文件 1.为什么要写这篇文章  楼主前几天去北京面试,聊起ajax上传文件, 面试官告之不能,遂讨论之,不得果,于是写下这篇文章,希望能和大家一起学习 2.正文 首先,要使用ajax上传文 ...

  7. Drop user 报ORA-00600 [KTSSDRP1]

    一客户删除一个数据库用户THH时报错: 说明在获取seg$时没有找到相应的条目,先来解释下这个600错误的参数含义: Arg [a] Tablespace number Arg [b] File nu ...

  8. Django 08 Django模型基础3(关系表的数据操作、表关联对象的访问、多表查询、聚合、分组、F、Q查询)

    Django 08 Django模型基础3(关系表的数据操作.表关联对象的访问.多表查询.聚合.分组.F.Q查询) 一.关系表的数据操作 #为了能方便学习,我们进入项目的idle中去执行我们的操作,通 ...

  9. 执行umount 的时候却提示:device is busy 的处理方法

    [root@web2-server yum.repos.d]# umount /mnt/cdrom/ umount: /mnt/cdrom: device is busy. (In some case ...

  10. Helvetic Coding Contest 2016 online mirror A1

    Description Tonight is brain dinner night and all zombies will gather together to scarf down some de ...