自定义Qt组件-通讯模块(P3)
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)的更多相关文章
- 自定义Qt组件-通讯模块(P1)
通讯模块Communicator 通讯模块是整个项目设计中位于最底层的模块,用于处理与串口或网络等设备的通讯,所有设备的通讯通过CommManager类完成,上层软件设计时需要根据comm模块(主要是 ...
- 自定义Qt组件-通讯模块(P2)
1. 抽象协议AbstractProtocol 抽象协议AbstractProtocol定义CommManager与协议之间的接口.AbstractProtocol中的一些属性(如enabled)用 ...
- C/C++ Qt TableDelegate 自定义代理组件
TableDelegate 自定义代理组件的主要作用是对原有表格进行调整,例如默认情况下Table中的缺省代理就是一个编辑框,我们只能够在编辑框内输入数据,而有时我们想选择数据而不是输入,此时就需要重 ...
- SSIS自定义数据流组件开发(血路)
由于特殊的原因(怎么特殊不解释),需要开发自定义数据流组件处理. 查了很多资料,用了不同的版本,发现各种各样的问题没有找到最终的解决方案. 遇到的问题如下: 用VS2015编译出来的插件,在SSDTB ...
- Android Studio开发基础之自定义View组件
一般情况下,不直接使用View和ViewGroup类,而是使用使用其子类.例如要显示一张图片可以用View类的子类ImageView,开发自定义View组件可分为两个主要步骤: 一.创建一个继承自an ...
- [UE4]自定义MovementComponent组件
自定义Movement组件 目的:实现自定义轨迹如抛物线,线性,定点等运动方式,作为组件控制绑定对象的运动. 基类:UMovementComponent 过程: 1.创建UCustomMovement ...
- Qt组件中的双缓冲无闪烁绘图
双缓冲绘图在Qt4中,所有的窗口部件默认都使用双缓冲进行绘图.使用双缓冲,可以减轻绘制的闪烁感.在有些情况下,用户要关闭双缓冲,自己管理绘图.下面的语句设置了窗口部件的Qt::WA_PaintOn ...
- 【转】Android学习基础自定义Checkbox组件
原文网址:http://forum.maiziedu.com/thread-515-1-1.html heckbox组件是一种可同时选中多项的基础控件,即复选框,在android学习中,Checkbo ...
- 自定义Qt按钮
转自:http://blog.csdn.net/starcloud_zxt/article/details/5185556 Qt自带的PushButton样式比较单一,在开发的时候往往按钮的形状各异, ...
随机推荐
- NSNull空值
1.前言 作为占据空间的一个空值,如用在数组或字典中占据一个没有任何值的空间. 1.1 NULL & nil 的区别: nil 是 OC 的,空对象,地址指向空的对象,指针地址指向的是 NUL ...
- AtCoder Grand Contest 011D(思维,规律,异或)
#include<bits/stdc++.h>using namespace std;char s[200007];int ans[200007];int main(){ int n ...
- C#静态类 转载:(原文:http://www.cnblogs.com/chenlulouis/ )
静态类是不能实例化的,我们直接使用它的属性与方法,静态类最大的特点就是共享. 探究 public static class StaticTestClass{ public static int ...
- luogu2948 滑雪课
题解里面全是dp的大神本蒟蒻瑟瑟发抖奉上一篇记忆化搜索... 其实嘛,记忆化搜索还是很安全透彻清真人品的,一般递推不好实现dp可以用记忆化搜索 然后本题先预处理一个mint[i]代表当前能力值为i,参 ...
- 本地化ASP.NET core模型绑定错误消息
默认错误消息: MissingBindRequiredValueAccessor A value for the '{0}' property was not provided. MissingKey ...
- VisualSVN的安装使用
1.什么是VisualSVN VisualSVN Server是集成了Subversion和Apache的一种版本管理工具,它简化了手工配置Subversion的繁琐步骤,安装的时候SVN Serve ...
- JS执行顺序-函数声明提升、匿名函数、函数表达式
大方向上: JS 是按照 代码块 进行 编译.执行 的. 学习至: 1.变量声明提升 2.新唐的博客 3.js中匿名函数的创建与调用方法分析 4.前端圣经 - <高程三> 5.深入理解变量 ...
- [Groovy]Parse properties file in Groovy
def props = new Properties() new File("foo.properties").withInputStream { s -> props.lo ...
- Nginx 基本 安装..
ubuntu 下 Nginx是高度自由化的Web服务器,它的功能是由许多模块来支持.如果使用了某个模块,这个模块使用了一些类似zlib或OpenSSL等的第三方库,那么就必须先安装这些软件.Ubunt ...
- Python的主要应用领域及应用场景
参考链接:https://www.cnblogs.com/kaid/p/9016673.html 正文: Python简介 Python(英国发音:/ˈpaɪθən/美国发音:/ˈpaɪθɑːn/), ...