通讯模块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. Algorithms - Bucket sort

    印象 图1 将元素分布在桶中 图2 元素在每个桶中排序 思想 桶排序将数组分到有限数量的桶子里.每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序). 分析 时间复杂度: ...

  2. .NET 一般处理程序使用Session

    .ashx中引用 session必须 using System.Web.SessionState ,继承IReadOnlySessionState/IRequiresSessionState IRea ...

  3. 判断本地是否存在Jquery文件,如果不存在则使用CDN加速的Jquery文件

    <script>//判断是否成功将Jquery库引入,如果没有成功引入则引入本地Jquery库if (typeof jQuery == 'undefined') {document.wri ...

  4. Insus Paging Utility Version 2

    Insus.NET对GridView或是DataList分页,都是使用自己的分页组件:http://www.cnblogs.com/insus/archive/2009/03/19/1417102.h ...

  5. Appium的常用定位方法

    使用uiautomatorviewer.bat工具来找到属性定位元素,在SDK的tools目录下找到该工具,双击打开.左边框内展示app的界面元素,右上角框内展示元素的层级关系,右下角框内展示元素的属 ...

  6. UIColor

    UIColor.CIColor 和 CGColor 出现在不同的类库里面,其实就是颜色存储方式不同而已,比如 999 可以用 10 进制.2 进制.16 进制等存储.三者之间都是能够方便转换的,特别是 ...

  7. 在eclipse中使用hadoop插件

    我的配置环境看我的上篇博文. 配置过程: (1)把插件放到eclipse/plugins目录下.(我的版本上一篇也有) )重启eclipse,配置Hadoop installation directo ...

  8. cenos php执行pdf2swf 配置环境

    1.第一步:安装xpdf语言包 1.mkdir –p /usr/share/xpdf 2.cd /usr/share/xpdf/ 3.下载中文支持及字体库wget ftp://ftp.foolabs. ...

  9. Nginx01---简单使用

    基于腾讯云--ubuntu系统 1.安装nginx sudo apt-get install nginx 2.启动,停止nginx nginx -c /usr/local/nginx/conf/ngi ...

  10. 11. 变量提升 && 执行上下文

    /* 变量升级 预处理 */ /* js引擎在代码正式执行之前会做一个预处理的工作: 1.收集变量 2.收集函数 依据: var 将var后边的变量定义但不赋值 var username=undefi ...