1.   半双工模式实时检测串口

ComHalfDuplex类是为了解决上位机发送控制指令和下位机发送数据会在半双工RS485总线中产生冲突引起乱码而引入的(v0.010版本引入)。

解决冲突的原理主要是实时检测串口,若一段时间内下位机不发送数据,则认为此时串口是空闲的,可以向下位机发送数据。

若在等待过程中接收到下位机发送数的据,则重置超时定时器。实时检测串口采用阻塞式方法waitForReadyRead等待串口接收到数据,

顾名思义,该方法其实就是等到readyRead信号到来时停止阻塞并继续运行代码,理论上和ComFullDuplex的实际做法是一样的,而如果要ComFullDuplex类实现实时检测串口,需要添加一个读取超时定时器并对超时做一定处理(未做处理)。

1.1.  CommForWin (deprecated)

CommForWin类是利用Windows的C API进行串口的读取操作。主要原理及任务和ComHalfDuplex类似,用的同样是阻塞式方法读取串口,实现实时检测串口。

需要注意的是,程序运行时只会用到这三个类中的某一个类,其中CommForWin不推荐使用:

1、许多继承下来的方法只是一个空实现,需要用Windows API来实现代码;

2、这部分代码只能在Windows使用,不如使用Qt提供的API;

3. 需要将校验位、波特率等对应到Qt的校验位和波特率上,否则将会导致串口无法正确收发数据。

串口是线程独占的,无法在其它线程或进程中打开同一个串口(若跨线程调用方法将会出现警告)。

AbstractComm的这两个实现类ComHalfDuplex和CommForWin是为了解决无法实时检测串口的问题。因为串口收发数据时有16ms的间隔,所以半双工模式无法正常工作,关于FTR232的半双工工作模式问题可参考我的另一篇文章:https://www.cnblogs.com/brifuture/p/9113091.html

2. 控制器与设备的交互

  每个协议中都应该维护一个待查讯的指令队列,若当前指令尚未执行完毕(成功执行或超时都视为执行完毕),后续需要查询的指令需要排队。当一个指令执行完毕时,CommManager会通知该命令所属的协议,取出该协议的指令队列中的下一条指令,并执行该指令。在当前协议中的所有命令都处理完毕后,再从其它的协议中选择。每当有指令的入队和出队操作时,CommManager发出cmdCountChanged信号,通知其它部件当前队列的长度有变化。

队首指令从队列中弹出时,将进入到子线程中等待查询,为了避免多线程竞争访问资源,在设置指令时需要进行加锁操作,保证线程安全。同样的,当指令成功执行后,对指令进行清除(防止不必要的重复查询)也需要进行加锁操作,操作完成后释放锁。

控制器在主线程(UI线程)中执行,串口操作在子线程中执行,控制器的主要工作是将来自串口的信号转发给其它部件:

图  Com中信号和槽的连接

  对串口操作类的调用并没有用到信号和槽,而是直接使用了QMetaObject::invokeMethod方法进行调用,原理和信号槽机制是类似的,但不需要特意声明一个信号了,使用起来比较方便。

3. 网络接口

  网络接口CommNetwork实现了AbstractComm 的接口,作为网络通讯的实现类,其与串口通讯的代码大同小异。内部实际负责通讯的对象为QTcpSocket,与QSerialPort类似(两者都是QIODevice的子类,所以接口大部分也相同)。

4. CommandObject与DataObject

  CommandObject和DataObject是在CommManager和AbstractProtocol之间传递信息时用到的对象。使用CommandObject可以获取命令的内容和其它的信息,使用DataObject可以获取下位机发送的数据和其它信息。

  比起直接在CommManager和AbstractProtocol之间传递QByteArray来说,使用这两种数据对象可以方便程序扩展,以后需要增加传递其它信息时添加这两种数据对象的接口即可。

5. 总结

  最开始编写communicator库的目的是为了让已有的程序分层,communicator作为最底层负责处理的都是QByteArray字节,后来对代码进行优化之后程序的逻辑更加清楚,而且communicator层的代码基本上不需要改动,直接就将其编译成静态库文件,减少项目重新构建时所需的编译时间。

  将communicator作为静态库构建的过程中也出现不少问题,之前并没有构建过静态库/动态库,对这方面不太了解。在实际项目中遇到过这样的问题:

General库(构建为动态库)引用了Communicator库,并在general库中调用Comm命令空间的初始化方法,应用程序引用general动态库和Communicator静态库,运行Demo程序时,发现经常出现内存访问错误的问题,经过调试后发现general库中虽然对Communicator进行了初始化,但demo应用程序中Communicator却没有初始化,Comm::manager 指针始终为空指针。

  在程序中对Communicator库进行初始化操作后仍然不能解决问题,原因可能在于动态库和应用程序之间的全局变量不能共享。最后把general库改为静态链接库就可以解决这个问题。

另外关于Qt的应用程序构建还有很多值得学习的东西,在构建Communicator这个库时尝试使用.pri文件,它可以简化库文件的引用,

不过它更多的用处应该是可以把一个大的项目分成若干个小部分,方便项目的构建和测试。例如我在Communicator项目中添加一个Communicator.pri文件。

Communicator 的代码仓库:https://github.com/BriFuture/qt_components/tree/master/basic_communicator

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

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

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

  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. 转载-ActiveMQ通过JAAS实现的安全机制

    JAAS(Java Authentication and Authorization Service)也就是java认证/授权服务.这是两种不同的服务,下面对其做一些区别:验证(Authenticat ...

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

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

  3. [SinGuLaRiTy] NOIP 膜你赛-Day 2

    [SinGuLaRiTy-1031] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 对于所有题目: Time Limit: 1s | Mem ...

  4. [USACO10OCT]湖计数Lake Counting 联通块

    题目描述 Due to recent rains, water has pooled in various places in Farmer John's field, which is repres ...

  5. ie浏览器(Internet Explorer)不播放背景音乐

    这里就不说bgsound了,支持格式较少 一个网站要加背景音乐,好些年没加背景音乐了,用embed标签把背景音乐加上了,Mozilla Firefox,Google Chrome,Safari都正常, ...

  6. python 中文路径问题

    Python直接读取中文路径的文件时失败,可做如下处理: inpath = 'D:/work/yuanxx/在线导航/驾车导航/walk_log/20130619_172355.txt' uipath ...

  7. [ZJOI2018]历史(LCT)

    这篇还发了洛谷题解 [Luogu4338] [BZOJ5212] 题解 题意 给出一棵树,给定每一个点的 \(access\) 次数,计算轻重链切换次数的最大值,带修改. 先考虑不带修改怎么做 假设 ...

  8. Codeforces Round #347 (Div. 2) A

    Description Greatest common divisor GCD(a, b) of two positive integers a and b is equal to the bigge ...

  9. Java实现范围内随机数

    JDK1.7及以上 int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1); // 包含max,所以要加1 JDK1.7以前 ...

  10. IDEA中Java代码存入DB中为乱码

    有一种可能是编译后出现的乱码,可以在Setting的Java compiler中加如下 -encoding UTF-8