1.  抽象协议AbstractProtocol

抽象协议AbstractProtocol定义CommManager与协议之间的接口。AbstractProtocol中的一些属性(如enabled)用于设置是否能够处理数据。

processData是AbstractProtocol中定义的回调函数,当设备中有数据返回时,数据将会被交给该方法进行处理。

若该方法返回true,则表明当前处理数据的协议已经找到相应的数据并且不需要将数据再交给其它协议处理,CommManager便会停止向其它协议发送这些数据;

若该方法返回为false,表明当前处理数据的协议并未找到相应的数据,并且希望交由其它协议进行处理,此时Com会继续转发这些数据。

lastQueryFailed 是在查询失败时由协议实现类进行处理的回调函数。

AbstractProtocol(生产者)实例将会维护各自的命令队列,CommManager(消费者)在完成一次查询后拉取AbstractProtocol(生产者)生成的指令。因此CommManager会根据指令消耗的速度决定是否应该进行下一次查询。有关生产者和消费者的更多信息,可以查看 https://www.cnblogs.com/charlesblc/p/6045238.html

2.  通讯器构造工厂

CommFactory是通讯器的构造工厂类,要实例化某个AbstractComm的通讯器实现类,需要通过addComm方法将通讯器注册到CommFactory中,工厂会保存各个通讯器的实现类对象,在构造之后进行切换会尝试找出已经构造过的对象,避免重复构造通讯器对象。

CommManager中可用的串口实现类对象由CommFactory进行例化,通过createComm方法将参数传递给工厂类的实例化方法,获取可用的实现类对象指针。

Comm层作为库,暴露给其它代码使用的部分主要是CommManager,因此要在程序运行时改变串口工作模式或切换成网络接口,调用 CommManager::resetMode 方法即可。

若该模块内部已经有实现好的通讯器,则可以调用 CommManager::resetMode(const QString &type, const bool halfDuplex),根据类型和全/半双工模式选择特定的通讯器,若要使用扩展的自定义通讯器,可以使用 CommManager::resetMode(const QString &className) 传入具体的类名选择通讯器。

如果需要添加新的通讯器,创建一个继承于AbstractComm的子类(构造函数需要用Q_INVOKABLE修饰),通过addComm方法将元对象和描述信息添加到工厂类中,之后可以传递类名来构造其它的通讯器。

构造工厂的使用使得CommManager只用传递类名或通讯器类型即可获得通讯器对象,从而使CommManager的功能只需要对通讯器对象进行管理和收发数据,简化CommManager 的实现。

3.  虚拟串口示例(待完善)

虚拟串口VirtualCom是为了方便上位机单独进行调试而编写的AbstractCom的子类。以虚拟串口为例,介绍如何继承实现AbstractCom的子类,以设计新模式的串口。

虚拟串口VirtualCom的接口:

如图所示,VirtualCom的init(), close(), openComm, writeCmd, setCommProperty, query, onRead等方法都是继承自AbstractCom的接口(在方法声明后增加了 Q_DECL_OVERRIDE宏,即override 关键字)。

init()方法中进行成员变量的初始化(多线程中的QIODevice只能在创建其实例的线程中进行操作,否则在运行时会警告/报错),需要在该方法中可以实例化QTimer,QSerialPort或QTcpSocket等对象(这些对象不能在构造函数中实例化)。

虚拟串口中没有使用到串口对象,因此不需要实例化串口/QIODevice对象,这里的init方法中实例化timer对象并连接相应的信号和槽。

openComm方法用于打开设备(串口/Socket),由于虚拟串口中没有串口对象,这里只修改设备状态即可。打开虚拟串口后,timer开始计时。

打开串口后即可调用write写入命令:一般该方法只需要传递参数给query函数执行即可,但半双工模式下,需要保存指令,待线路空闲时发送。

若接受到命令后可以立即查询,那么需要通过QMetaObject::invokeMethod静态方法在子线程中运行(以致访问串口/Socket对象时不会出现错误)。虚拟串口中的该方法就是将cmd参数传递给query方法通过子线程执行。

要使用虚拟串口发送一些特定的数据,需要修改VirtualCom的实现,在实际使用过程中比较麻烦。目前可用的解决方法有两个:

  1. 把虚拟串口编译成动态链接库,在不同的程序中使用不同的动态库文件即可,但是这样做实际上在开发过程中变得非常麻烦。
  2. 自定义xml文件格式,从指定的xml文件中读取信息,根据信息来决定发送的数据内容,这样的话就能够使VirtualCom作为库文件与实际的信息回复实现分离。如果要这样实现,首先要定义xml文件的格式,然后编写相应的解析器,并将解析器解析的结果传递给VirtualCom对象,当接收到来自上层的指令后根据解析器的结果进行响应。这样做实际上是在模拟下位机收发数据的行为,看上去工作量较大,但比较实际的下位机应用应该是简单不少了。

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

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

    通讯模块Communicator 通讯模块是整个项目设计中位于最底层的模块,用于处理与串口或网络等设备的通讯,所有设备的通讯通过CommManager类完成,上层软件设计时需要根据comm模块(主要是 ...

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

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

  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. linux文件字符集转换(utf8-gb2312)

    一,命令行 在LINUX上进行编码转换时,可以利用iconv命令实现,这是针对文件的,即将指定文件从一种编码转换为另一种编码. iconv命令用法如下:iconv [选项...] [文件...] 1. ...

  2. c++滚动数组

    说来惭愧,我老早以前就学习了dp,可直到最近才知道滚动数组. 所以说,滚动数组是什么呢? 它是一种优化dp空间复杂度的思想. 在dp转移时,我们往往不需要之前推的所有的,而是只需要前一两个转移的. 我 ...

  3. yum(Fedora和RedHat以及SUSE中的Shell前端软件包管理器)命令详解

    yum官方网站:http://yum.baseurl.org/ Fedora对于yum的介绍:http://fedoraproject.org/wiki/Yum yum(全称为 Yellow dog ...

  4. D - Back and Forth(模拟)

    Problem Statement Dolphin resides in two-dimensional Cartesian plane, with the positive x-axis point ...

  5. 【转】 PHP 两个日期(时间段) 之间的日期数组

    在开发过程中会遇到这样一个需求:获取2018-11-02到2018-11-15之间的日期数组 希望得到如下数组: Array ( [] => -- [] => -- [] => -- ...

  6. go语言实战教程之 后台管理页面统计功能开发(1)

    本节内容我们将学习开发实现后台管理平台页面统计功能开发的功能接口,本章节内容将涉及到多种请求路由的方式. 功能介绍 后台管理平台不仅是功能管理平台,同时还是数据管理平台.从数据管理平台角度来说,在管理 ...

  7. LVS+OSPF 架构(转)

    http://blog.51cto.com/pmghong/1399385 LVS 和 LVS+keepalived 这两种架构在平时听得多了,最近才接触到另外一个架构LVS+OSPF.这个架构实际上 ...

  8. 1、OpenCV Python 图像加载和保存

    __author__ = "WSX" import cv2 as cv # 这里的文件是图片或者视频 def Save_File( image ): cv.imwrite(&quo ...

  9. 给花_Q

  10. 重装iTunes 错误代码42401 解决办法

    昨晚手贱点击从iTunes 11升级到iTunes12,之后发现iTunes 12各种卡,简直不能忍,然后直接拉iTunes 12到AppClear,然后安装iTunes 11,安装完成之后打开iTu ...