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. [转]Passing Managed Structures With Strings To Unmanaged Code Part 1

    1. Introduction. 1.1 Managed structures that contain strings are a common sight. The trouble is that ...

  2. 机器学习基石笔记:11 Linear Models for Classification、LC vs LinReg vs LogReg、OVA、OVO

    原文地址:https://www.jianshu.com/p/6f86290e70f9 一.二元分类的线性模型 线性回归后的参数值常用于PLA/PA/Logistic Regression的参数初始化 ...

  3. 设置label的文字,一行多种颜色

    调用 [self fuwenbenLabel:contentLabel FontNumber:[UIFont systemFontOfSize:] AndRange:NSMakeRange(, ) A ...

  4. NOIp 2018 货币系统 贪心

    题目描述 在网友的国度中共有 nnn 种不同面额的货币,第 iii 种货币的面额为 a[i]a[i]a[i],你可以假设每一种货币都有无穷多张.为了方便,我们把货币种数为 nnn.面额数组为 a[1. ...

  5. 【转】eclipse上的.properties文件中文编辑显示问题

    首先,解决.properties文件中的中文编辑问题,我们发现,在.properties文件中输入的中文变成了这个样子: 这是eclipse的.properties文件,默认的编码方式是iso-885 ...

  6. GUI JFrame窗体介绍:

    GUI JFrame窗体介绍: https://www.cnblogs.com/-ksz/p/3422074.html

  7. php 安装扩展库

    liunx系统 1. /usr/local/php/bin/php-config php 配置文件位置 [ php-config是一个脚本文件,用于获取所安装的php配置的信息 ] 在编译扩展时,如果 ...

  8. NYOJ144_小珂的苦恼_C++

    题目:http://acm.nyist.net/JudgeOnline/problem.php?pid=144 用扩展欧几里得定理判断是否有解即可,然后记得打上读入优化 扩展欧几里得算法:http:/ ...

  9. hdu1754 区间更新查询(单点更新+查询求区间最大值)

    I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  10. jupyter notebook初步使用

    Jupyter Notebook(此前被称为 IPython notebook)是一个交互式笔记本,支持运行 40 多种编程语言.在本文中,我们将介绍 Jupyter notebook 的主要特性,以 ...