Qt实现XYModem协议(六)

news2024/11/26 17:35:17

1 概述

  XMODEM协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据,并且每个块都使用一个校验和过程来进行错误检测。使用循环冗余校验的与XMODEM相应的一种协议称为XMODEM-CRC。还有一种是XMODEM-1K,它以1024字节一块来传输数据。YMODEM也是一种XMODEM的实现。它包括XMODEM-1K的所有特征,另外在一次单一会话期间为发送一组文件,增加了批处理文件传输模式。

本文利用C++实现XYModem-1K协议,并利用Qt串口类QSerialPort实现数据读写。

3 实现

3.5 XYModemRecvFile

该模块实现XYModem协议接收文件。

流程图:
流程图

3.5.1 XYModemRecvFile定义

class QSerialPort;
class XYModemRecvFile : public QObject, public YModem
{
    Q_OBJECT
public:
    explicit XYModemRecvFile(QSerialPort *serial, QObject *parent = nullptr);

public slots:
    void startYModem(QString const& fileName);
    void startXModem(QString const& fileName);
    void stop();
    void cancel();
signals:
    void gotFileSize(quint64 filesize);
    void progressInfo(quint32 blockNumber, quint64 bytesOfRecv);
    void error(QString const& e);
    void finished();

protected:
    uint32_t write(uint8_t const *data, uint32_t size) override;
    uint32_t read(uint8_t *data, uint32_t size) override;
    uint8_t get_code(bool isWait = true) override;
    uint32_t do_recv(uint8_t code);
private:
    bool singled() { return signal_; }
    void doSignal() { signal_ = true; };
    void doError(QString const& text);
    const char* data() { return reinterpret_cast<const char*>(data_ + ID); }
    uint64_t get_filesize(const char* data, uint32_t size);
private:
     QSerialPort* serial_;
     volatile bool signal_;
     uint8_t data_[DATA_SIZE2];
};

公共接口:

  • startYModem 开始YModem协议接收文件
  • startXModem 开始XModem协议接收文件
  • stop 停止发送
  • cancel 取消发送

信号:

  • gotFileSize 文件大小信号
  • progressInfo 传输进度信号
  • error 出错信号
  • finished 传输结束信号

重载接口:

  • write 向串口写数据
  • read 从串口读取数据
  • get_code 读取操作码
  • do_recv 接收数据并验证

3.5.2 XYModemRecvFile实现

3.5.2.1 startYModem
void XYModemRecvFile::startYModem(QString const& fileName)
{
    uint8_t code = wait_start();
    if(code == MAX)
    {
        doError("cannot get start code!");
        return;
    }

    uint32_t size = do_recv(code);
    if(size == 0)
    {
        doError("cannot get start data!");
        return;
    }
    uint64_t fileSize = get_filesize(data(), size);
    QFile file(fileName);
    if(!file.open(QIODevice::WriteOnly))
    {
        doError(QString("%1 cannot be opened!").arg(fileName));
        return;
    }

    emit gotFileSize(fileSize);
    quint32 blockNumber = 0;
    uint64_t bytesOfRecv = 0;
    while(!singled())
    {
        code = get_code();
        if(code == EOT)
        {
            tx_code(ACK);
            break;
        }

        size = do_recv(code);
        if(size == 0)
            tx_code(NAK);
        else
        {
            if(file.write(data(), size) != size)
            {
                doError("write file error!");
                return;
            }
            blockNumber++;
            bytesOfRecv += size;
            tx_code(ACK);
            emit progressInfo(blockNumber, bytesOfRecv);
        }
    }

    emit finished();
    serial_->moveToThread(QApplication::instance()->thread());
}

函数流程:

  • 发送C码并等待操作码
  • 如果是无效操作码,发送错误信号并返回
  • 接收第一数据包
  • 如果收到数据大小为0,发送错误信号并返回
  • 从第一个数据包中获取文件大小
  • 打开文件,如果打开文件失败发送错误信号并返回
  • 发送文件大小信号gotFileSize
  • 操作码是EOT发送ACK,结束文件传送并返回
  • 读取数据
  • 如果没读到数据则发送NAK码要求发送发重传
  • 保存数据,如果保存失败发送错误信号并返回
  • 发送ACK码,发送进度信号
  • 重复上述5步,直到文件发送完毕或停止
  • 发送传输结束信号finished
3.5.2.2 startXModem
void XYModemRecvFile::startXModem(QString const& fileName)
{
    Q_UNUSED(fileName)
    uint8_t code = wait_start();
    if(code == MAX)
    {
        doError("cannot get start code!");
        return;
    }
    QFile file(fileName);
    if(!file.open(QIODevice::WriteOnly))
    {
        doError(QString("%1 cannot be opened!").arg(fileName));
        return;
    }
    quint32 blockNumber = 0;
    uint64_t bytesOfRecv = 0;
    do
    {
        uint32_t size = do_recv(code);
        if(size == 0)
            tx_code(NAK);
        else
        {
            if(file.write(data(), size) != size)
            {
                doError("write file error!");
                return;
            }
            tx_code(ACK);
            blockNumber++;
            bytesOfRecv += size;
            emit progressInfo(blockNumber, bytesOfRecv);
        }
        code = get_code();
        if(code == EOT)
        {
            tx_code(ACK);
            break;
        }
    }while(!singled());

    emit finished();
    serial_->moveToThread(QApplication::instance()->thread());
}

函数流程:

  • 发送C码并等待操作码
  • 如果是无效操作码,发送错误信号并返回
  • 打开文件,如果打开文件失败发送错误信号并返回
  • 读取数据
  • 如果没读到数据则发送NAK码要求发送发重传
  • 保存数据,如果保存失败发送错误信号并返回
  • 发送ACK码,发送进度信号
  • 获取操作码
  • 操作码是EOT发送ACK,结束文件传送并返回
  • 重复上述6步,直到文件发送完毕或停止
  • 发送传输结束信号finished
3.5.2.3 do_recv
uint32_t XYModemRecvFile::do_recv(uint8_t code)
{
    uint32_t size = 0;
    if(code == SOH)
        size = read(data_, DATA_SIZE1);
    else if(code == STX)
        size = read(data_, DATA_SIZE2);

    if(size < ID + CRC16)
        return 0;

    uint16_t crc = (data_[size - 2] << 8) | data_[size - 1];
    if(crc16(data_ + ID, size - ID - CRC16) == crc)
        return size - ID + CRC16;

    return 0;
}

函数流程:

  • 根据code是SOH还是STX读取不同数据包
  • 如果读到数据太小返回0
  • 对收到数据做CRC校准与数据包末尾CRC比较,如果相同返回读取数据字节数
  • 失败返回0
3.5.2.4 write/read
uint32_t XYModemSendFile::write(uint8_t const *data, uint32_t size)
{
    return serial_->write((const char *)data, size);
}
uint32_t XYModemSendFile::read(uint8_t *data, uint32_t size)
{
    return serial_->read((char *)data, size);
}

函数说明:

  • 直接调用串口读写接口实现读写接口。
3.4.2.5 get_code
uint8_t XYModemSendFile::get_code(bool isWait)
{
    while(!singled())
    {
        if(serial_->waitForReadyRead(10))
        {
            uint8_t data[1] = { 0 };
            read(data, sizeof(data));
            return data[0];
        }
        if(!isWait)
            break;
    }
    return MAX;
}

函数流程:

  • 等待串口输入
  • 如果有输入则读取一字节做为code码并返回.
  • 如果一直没有输入直到调用stop后返回无效码MAX.
3.4.2.6 doError
void XYModemSendFile::doError(QString const& text)
{
    emit error(text);
    serial_->moveToThread(QApplication::instance()->thread());
    emit finished();
}

函数流程:

  • 发送错误信号
  • 发送结束信号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1606804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式系统的监控基础架构Dapper

文章目录 基本设计目标监控系统设计基本要求三个基本设计目标 Dapper监控系统简介三个基本概念区间Helper.Call的详细信息监控信息的汇总监控数据汇总单独进行的原因 关键性技术轻量级核心功能库二次抽样技术 常用Dapper工具Dapper存储API 基本设计目标 监控系统设计基本要求 …

力扣(leetcode) 42. 接雨水 (带你逐步思考)

力扣(leetcode) 42. 接雨水 &#xff08;带你逐步思考&#xff09; 链接&#xff1a;https://leetcode.cn/problems/trapping-rain-water/ 难度&#xff1a;hard 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多…

「GO基础」在Windows上配置VS Code GO语言开发环境

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【数据结构(八)上】二叉树经典习题

❣博主主页: 33的博客❣ ▶文章专栏分类: Java从入门到精通◀ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你学更多数据结构的知识 目录 1.前言2.经典习题2.1相同的树2.2另一棵子树2.3翻转二叉树2.4平衡二叉树2.5对…

安宝特方案 | AR工业解决方案系列-工厂督查

在工业4.0时代&#xff0c;增强现实&#xff08;AR&#xff09;技术正全面重塑传统工业生产&#xff0c;在工厂监督领域&#xff0c;其应用不仅大幅提升了生产效率、监测准确性和规范执行程度&#xff0c;而且为整体生产力带来了质的飞跃。 01 传统挑战与痛点 在制造业生产流程…

机器视觉系统:磁瓦尺寸瑕疵缺陷检测的精准“裁判”(官网)

在电子、通讯和汽车行业中&#xff0c;磁瓦作为关键组件&#xff0c;其尺寸精度和表面质量至关重要。然而&#xff0c;在生产过程中&#xff0c;由于各种因素的影响&#xff0c;磁瓦可能会出现尺寸上的瑕疵和缺陷&#xff0c;如尺寸不符、厚度不均、边缘破损等。这些缺陷不仅影…

浅析STM32H750启动文件

目录 概述 1 启动文件介绍 1.1 启动文件功能 1.2 汇编语言指令 2 启动代码细节 2.1 分配栈空间 2.2 分配堆空间 2.3 中断向量表 2.4 复位程序 2.5 中断服务程序 2.5.1 CPU内部中断程序 2.5.2 CPU内部扩展中断程序 2.6 用户堆栈初始化 3 总结 概述 本文以startup_stm3…

GreatSQL 死锁案例分析

1.背景概述 客户业务发生死锁的报错&#xff0c;根据业务程序日志及业务流程&#xff0c;发现造成死锁的原因是&#xff1a;事务1 delete insert &#xff0c;事务2 delete insert 2个事务交替执行导致的死锁&#xff1b;由于GAP锁阻塞了插入意向锁&#xff0c;并且当delete…

基于1-wire总线的多路温度监测系统

前言 在现代工业生产和环境监测中&#xff0c;温度是一个关键的参数&#xff0c;它直接影响到生产过程的稳定性和产品质量。为了确保温度控制在安全和有效的范围内&#xff0c;需要一种可靠且高效的多路温度监测系统。随着微电子技术和传感器技术的发展&#xff0c;基于1-Wire…

Redis中的订阅发布(二)

订阅与发布 订阅频道 每当客户端执行SUBSCRIBE命令订阅某个或某些频道的时候&#xff0c;服务器都会将客户端与被订阅的频道 在pubsub_channels字典中进行关联。 根据频道是否已经有其他订阅者&#xff0c;关联操作分为两种情况执行: 1.如果频道已经有其他订阅者&#xff0c…

从零实现诗词GPT大模型:数据集介绍和预处理

专栏规划: https://qibin.blog.csdn.net/article/details/137728228 本章将介绍该系列文章中使用的数据集&#xff0c;并且编写预处理代码&#xff0c;处理成咱们需要的格式。 一、数据集介绍 咱们使用的数据集名称是chinese-poetry&#xff0c;是一个在github上开源的中文诗…

雨润万物生,酒伴谷雨行

谷雨&#xff0c;是中国传统二十四节气之一 标志着中国农历的春季即将结束&#xff0c;夏季即将来临。在古代中国, 谷雨时节是农民开始播种、收获的时节,也是酿酒的好季节。谷雨时节,气温适宜、湿度较高&#xff0c;是酵母繁殖和发酵的好时候。 谷雨时节酿酒不仅仅是普通人们…

37-2 Python 的 requests 库发送 POST 请求

准备 sqlilabs 靶场: 构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、发送 POST 请求 首先使用bp对 sqlilabs 靶场的第12关抓个包,了解这个关卡是如何发包的 打开靶场:本地ip+ /sqli-labs-master/Less-12/ 先随便输入个账号登录如…

Postman之页面简介 V9.31.0

Postman之页面简介 V9.31.0 一、顶部栏二、左部栏三、中部栏四、下部栏 一、顶部栏 &#xff08;1&#xff09;new选项框&#xff0c;生成新建请求、集合、环境等 &#xff08;2&#xff09;import选项框&#xff0c;可以导入文件、文件夹、链接、文本信息等 &#xff08;3&…

(2022级)成都工业学院数据库原理及应用实验四: SQL简单查询

写在前面 1、基于2022级软件工程/计算机科学与技术实验指导书 2、成品仅提供参考 3、如果成品不满足你的要求&#xff0c;请寻求其他的途径 运行环境 window11家庭版 Navicat Premium 16 Mysql 8.0.36 实验要求 在实验三的基础上完成下列查询&#xff1a; 1、查询所有…

穿越物联网的迷雾:深入理解MQTT协议

目录标题 1、MQTT简介核心特性 2、MQTT的工作原理通信过程 3、MQTT的消息质量&#xff08;QoS&#xff09;4、安全机制5、实践应用环境准备示例项目发布者客户端订阅者客户端 6、最佳实践7、结论8、参考资料 在物联网&#xff08;IoT&#xff09;的海洋中&#xff0c;数据像水流…

【ACM列表推荐会议 | EI稳定检索】2024年第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)

2024年第四届人工智能、自动化与高性能计算国际会议&#xff08;AIAHPC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Automation and High Performance Computing 2024第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)将于20…

二叉树的最大深度 - LeetCode 热题 37

大家好&#xff01;我是曾续缘&#x1f61b; 今天是《LeetCode 热题 100》系列 发车第 37 天 二叉树第 2 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最…

麒麟服务器操作系统自动化安装应答文件制作

原文链接&#xff1a;麒麟服务器操作系统自动化安装应答文件制作 Hello&#xff0c;大家好啊&#xff01;今天我们将探讨如何为麒麟服务器操作系统制作自动化安装应答文件。在部署大量服务器时&#xff0c;自动化安装是提高效率和确保安装一致性的关键技术。通过使用应答文件&a…

【计算机网络】ip子网划分--超详细例题解析

Hello!这一篇主要是计算机网络中的ip地址子网划分的例题&#xff0c;这里例举了四个题型。保证即便从0也可以掌握&#xff01;(前面是一些预备知识&#xff0c;不熟悉的小伙伴一定要看下学习下哦&#xff5e;) 这也是博主的学习过程&#xff0c;做题中仅仅我的理解哦。若文章中…