一、前言

在上一章中已经介绍了协议设计和封包设计。那么怎样把这些设计优美的落实为代码的形式呢。使用几个函数就可以实现,但是不够优美和实用,因此本章主要介绍一个协议层构架。使用构架的优点如下:

  • 所有协议命令整合到一个类中方便管理和修改
  • 构架可以方便协议的扩展
  • 构架实现了协议层和逻辑层的解耦
  • 构架实现了通信层和协议层的解耦
  • 构架实现了通信线程和UI线程的分割

二、通信构架的需求分析(框架具有的功能)

前言只是简单介绍了构架的优点,这根据编程中遇到的实际问题来列举出对协议框架的需求。需求如下:

  • 完成基本数据通信,即能发送数据,能接收数据
  • 要求可以确认数据是否被下位机正确收到,即数据收到验证机制(依靠协议设计)
  • 要求有失败重传机制,多次重传仍然失败报错机制(一般重试3次)
  • 要求支持通信超时检测和通信断开检测机制
  • 要求与UI逻辑层使用不同线程,要求不能占用UI线程来发送数据,接收数据也不用UI线程
  • 要求和通信层解耦,从而保证构架支持多种通信协议,比如,串口,网络UDP,网络Tcp,蓝牙等
  • 要求支持下位机主动反馈机制(依靠协议设计支持,定义下位机主动给上位机发送指令的指令类型)
  • 要求系统的鲁棒性高,即基础数据层出现数据意外丢失一部分的情况下,不会造成协议处理构架崩溃,而是能检测出来这个意外,把不完整的数据包丢掉的功能。
  • 要求有报错回调函数,在解析封包的时候,遇到任何问题都可以通过回调函数反馈给逻辑层

三、对一般通信过程简单描述

一般上位机与下位机通信协议示意图:

四、正式开始构架设计

  1. 框架示意图:
  2. 框架中类的的简单功能说明
  • CRC
    用CRC校验的类,包括生成RCR码和校验CRC码。

  • Command
    用于储存协议定制的命令,以及完成封包的组装。整个软件用到的命令都储存在这个类中形成独特的命令层。本类以函数的形式向对功能逻辑层提供调用接口。方便后期随时对命令做出修改,并能保证不影响现有的功能逻辑代码。

  • Worker
    “工人类”也可以叫做“任务类”,主要用于储存待发送的命令。类中包括待发送的实际16进制格式指令、命令重发次数、和当前指令的反馈指令识别函数。

  • WorkerManager
    主要由Worker为子项的List列表组成。对外提供对list列表操作的一些函数。主要功能是用于管理待发送指令的的列表。此设计的主要目的是实现UI线程和发送线程的分割。避免使用UI线程直接调用通信层的发送函数发送指令而导致UI线程的卡顿。

  • ProtocolManager
    此类以单实例模式设计。是实现协议框架的主要处理类。此类中集合了通信层的实例、一个线程用于循环从待发送指令列表里提取指令并发送、一个定时器用于判断指令发送以及等待反馈指令是否超时、完成对接收封包的拆解和校验工作。

  1. 框架中使用到的类的成员函数介绍
  • CRC:
    void GetCRC(quint8 *data, int len, quint16 &crc);//生成一个16位的CRC码
    bool CheckCRC(quint8 *data, int len);//校验一个CRC码
  • Command:
    //给指令数据计算CRC并加入包头和包尾,完成指令的封包
    QByteArray* Pack(QByteArray &data);
    //一条指令封包的实例
    inline static QByteArray* GetTemperature() {
    QByteArray data;
    data.append(0x01); //指令类型码
    data.append(0x01); //指令码
    return Pack(data);
    }
  • Worker:
    int failed_times; //用于记录此条指令发送失败的重试次数
    QByteArray data;//用于记录指令封包的数据
    bool CheckResponse(quint8 * return_pkg);//用于检测本指令对应的“反馈指令”是否和本指令匹配
  • WorkerManager:
    QList<Work*> work_list_;//用于记录待发送指令的列表
    void AddWork(QByteArray &pkg);//给列表增加一条待发送指令
    void CleanWorkList();//清空指令列表
    void FinishAWork();//删除列表首位的指令,表示这条指令发送完成
    Work* GetCurrentWork();//获取列表首位的那一条指令
    bool IsHasWork();//查询列表中是否有指令待发
  • ProtocolManager:
        通信层对象,用于收发数据
        Thread用于从发送队列获取待发送数据并发送
        Timer 用于检测发送是否超时
        通信层接收数据回调函数 {
            从接收的数组中分离出一个完整的数据包
            拆掉包头,包尾,并验证CRC是否正确
            验证一条命了处理完成
            如果收到的反馈包中含有数据,反馈数据到UI层
            接收下位机主动反馈数据
        }

五、框架对于流式数据的处理说明

前一章提到了,像UDP通信这样的有消息边界的通信方式,一个UDP封包中可以只包含一个自定义协议封包,因此极大的方便了编程,并且可以稳定使用。如果是像TCP或串口这样的无消息边界的通信方式中,想要实现自定义通信封包则相对复杂。因为会出现多个自定义封包的的“粘包”的现象。如下图所示:

首先要说明本框架是支持,无消息边界情况下的自定义数据封包传输。下面结合代码详细说明一下本框架在这方面的设计思想。

从上图可以得出,如果框架可以处理“无消息边界的通信方式”,那么一定可以处理“有消息边界的通信方式”,反之则不行。也就可以理解为“有消息边界通信方式”是”无消息边界通信方式“的子集。因此,在框架设计中,只要处理好”无消息边界的通信方式“,就可以应对所有通信方式了。以下为本框架识别并拆分“自定义协议封包”的代码:

//通信接收数据回调函数
void ProtocolManager::onRecv(QByteArray *data, QString ip, quint16 port) {//把接收到的数据,依单个byte处理,并不区分有没有消息边界,都以“流式通信”处理。//(前提是,无论通信协议是否有消息边界,都使用使用自定义协议)for(int a = 0; a < data->length(); a ++) {Process(static_cast<quint8>(data->at(a)));}
}
void ProtocolManager::Process(quint8 b) {if (package_on_ == true) //如果找到包头则开始此封包的后续识别{if (pos > CACHE_LENGTH) {return;}cache[pos++] = b;//识别包尾if (pos > 4 &&cache[pos - 4] == tail[0] &&    //a pkg is finishedcache[pos - 3] == tail[1] &&cache[pos - 2] == tail[2] &&cache[pos - 1] == tail[3]) {package_on_ = false;quint8* pkg = &cache[1];int pkg_len = pos - 5;//进行CRC校验if (Crc::CheckCRC(pkg, pkg_len) == true) {ProcessMetaPkg(pkg, pkg_len-2);//-2 : remove crc byte} else {CleanWorkList();emit onConnectionError("crc check error");}}//识别包头} else if (package_on_ == false && b == head[0]) { //start a pkgpackage_on_ = true;pos = 0;cache[pos++] = b;}
}
//此函数用于处理封包中的内容,根据不同指令做不同解析,详见源代码
void ProtocolManager::ProcessMetaPkg(quint8 *pkg, int len) {}

3.2-上位机与下位机的“私有协议”通信构架设计相关推荐

  1. 上下位机通讯协议_上位机与下位机的区别通讯

    上位机是指可以直接发出操控命令的计算机,一般是PC/host computer/master computer/upper computer,屏幕上显示各种信号变化(液压,水位,温度等).下位机是直接 ...

  2. 上位机和下位机的概念,理解如何实现PC从PLC中读取数据?

    市面上的PLC有上百种, 西门子的, 三菱的, 欧姆龙的等等. 上位机和下位机的理解: 上位机是指可以直接发出操控命令的计算机,一般是PC/host computer/master computer/ ...

  3. 打开单片机世界的大门——上位机控制下位机实例详解

    上位机控制下位机实例详解 一.基本概念 上位机与下位机 串口 数据表达 二.下位机程序 三.上位机程序 四.总结 一.基本概念 在开始讲解前,先来看几个基本概念,如果是有基础的大佬,请直接跳到下一节. ...

  4. 超详细Klipper 上位机与下位机配置

    (适用多数Mega2560芯片打印机主板,本文使用香橙派ZERO2作为上位机) 上位机:ZERO2 下位机:打印机主板 下载镜像系统 首先,去Armbian官网下载Buster系统镜像:Armbian ...

  5. 计算机基础-工控机、上位机、下位机、stm32、单片机

    工控机 定义:(Industrial Personal Computer,IPC)即工业控制计算机,主要用于工业生产上. 性能:采用全钢机箱,抗震性能好,抗电磁干扰,抗冲击. 结构:包括CPU.io外 ...

  6. 什么是上位机、下位机

    上位机 上位机是指可以直接发出操控命令的计算机, 一般是PC/host computer/master computer/upper computer, 屏幕上显示各种信号变化(液压,水位,温度等). ...

  7. java实现上位机与下位机串口通信

    串口通信是在工程应用中很常见.在上位机与下位机通讯过程中常通过有线的串口进行通信,在低速传输模式下串口通信得到广泛使用.在说个之前先来简单解释一下上位机与下位机的概念. 上位机与下位机 通常上位机指的 ...

  8. QT5实现串口收发数据(上位机与下位机通信)

    最近帮老师做一个应用程序,通过上位机与下位机进行串口通信,最后实现实时绘图,通过几天努力,成功实现蓝牙串口通信. 参考博客1 注意:代码中一些与串口无关代码,可以忽略掉 一.QT5串口基础知识 1. ...

  9. 【CNC——第6篇】PMAC上位机编程基础篇(上位机和下位机如何通信)

    拓展链接: PAMC官网:DELTA TAU. 官网手册:手册大全 PMAC官网: PCOMM32PRO用户手册 PMAC 的内部变量 内部变量分为四种,I 变量为电机等常用基本控制变量,P 变量为全 ...

最新文章

  1. PicGo github配置
  2. HMM算例 python 有代码
  3. 一款炫酷Loading动画--载入成功
  4. SAP Fiori里的Adapt UI按钮,神出鬼没的奥秘
  5. 3ds Max 2018 在安装后无法启动或出现不稳定
  6. CSU-1982 小M的移动硬盘
  7. 第十一届蓝桥杯省赛C++组试题 第5题
  8. 指针与数组的关系---初始化
  9. pytorch---模型加载与保存(5)使用在不同模型参数下的热启动模式
  10. C# 调用系统API 内核 简单样例
  11. qq象棋辅助 android,QQ象棋自动下棋
  12. qqxml代码-班级作业xml卡片代码班级作业
  13. popwindow 加个边框_PopupWindow仿微信浮层弹出框效果
  14. python 接入百度地图数据包下载_Python爬虫-利用百度地图API接口爬取数据并保存至MySQL数据库...
  15. centos7 安装/卸载wps 无法启动 字体缺失(亲测有效)
  16. ERD Online 4.0.5 在线数据库建模、元数据管理(免费、私有部署)
  17. mysql查询是第几条记录_MySQL查询第几行到第几行记录
  18. 更改SQL Server数据库名、数据库文件名的方法
  19. 计算机网络(各章节精华版)
  20. 注塑成型(注射模塑成型)

热门文章

  1. 网易im 服务器消息格式,机器人消息体模板说明-IM即时通讯-网易云信开发文档...
  2. Python fractions模块 —— 分数相关函数
  3. 快牛智能凉经(数据挖掘日常实习)
  4. 算法---逆向旋转矩阵法求解矩阵绕圈走
  5. 高德地图上画图!和3D绘制区域
  6. 基于STM32设计物联网在线智能称重系统(OneNet)_2022
  7. 【Java编程练习】司机肇事后逃跑,现场三人半瞎系列
  8. HOS Develop Notes-开启ssh服务
  9. LeetCode第9题 回文数(Palindrome Number)
  10. 什么样的公司才需要办理ICP经营许可证?