1 概述
XMODEM协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据,并且每个块都使用一个校验和过程来进行错误检测。使用循环冗余校验的与XMODEM相应的一种协议称为XMODEM-CRC。还有一种是XMODEM-1K,它以1024字节一块来传输数据。YMODEM也是一种XMODEM的实现。它包括XMODEM-1K的所有特征,另外在一次单一会话期间为发送一组文件,增加了批处理文件传输模式。
本文利用C++实现XYModem-1K协议,并利用Qt串口类QSerialPort实现数据读写。
2 概要设计
2.1 类图
类型说明:
- Modem XYModem协议基类,实现CRC16校验,并定义读写接口。
- XModem 实现XModem协议。
- YModem 实现YModem协议。
- XYModemSendFile 实现XYModem协议发送文件。
- XYModemRecvFile 实现XYModem协议接收文件。
- XYModemFileSender 实现在线程中异步发送文件。
- XYModemFileRecver 实现在线程中异步接收文件。
2.2 模块
整个代码分为7个模块.
2.2.1 Modem
该模块定义和实现了Modem类型。
Modem类型是纯虚类型,其派生类需要实现下面两个虚函数:
- write 写数据接口
- read 读数据接口
文件列表:
- modem.h
- modem.cpp
2.2.2 XModem
该模块定义和实现了XModem类型。
XModem类型实现了XModem协议,定义了纯虚接口:
- get_code 获取操作码Code
并定义了如下接口:
- tx_send 发送数据
- tx_eot 判断发送是否结束。
- tx_code 发送操作码
- wait_start 发送等待接收码
- tx_cancel 发送取消包
- not_id id的反码
- next_id 下一ID
文件列表:
- xmodem.h
- xmodem.cpp
2.2.3 YModem
该模块定义和实现了YModem类型。
YModem类型在XModem基础上实现了YModem协议,定义接口:
- tx_start 开始文件发送
- tx_end 判断是否是最后一个包
文件列表:
- ymodem.h
- ymodem.cpp
2.2.4 XYModemSendFile
该模块定义和实现了XYModemSendFile类型。
XYModemSendFile类型实现XYModem协议文件发送,接口如下:
- startYModem 开始YModem协议发送文件
- startXModem 开始XModem协议发送文件
- stop 停止发送
- cancel 取消发送
实现如下接口:
- write 向串口写数据
- read 从串口读取数据
- get_code 读取操作码
文件列表:
- xmodemsendfile.h
- xmodemsendfile.cpp
2.2.5 XYModemRecvFile
该模块定义和实现了XYModemRecvFile类型。
XYModemRecvFile类型实现XYModem协议文件接收,接口如下:
- startYModem 开始YModem协议接收文件
- startXModem 开始XModem协议接收文件
- stop 停止发送
- cancel 取消发送
实现如下接口:
- write 向串口写数据
- read 从串口读取数据
- get_code 读取操作码
文件列表:
- xmodemrecvfile.h
- xmodemrecvfile.cpp
2.2.6 XYModemFileSender
该模块定义和实现了XYModemFileSender类型。
XYModemSendFile发送文件是同步操作,XYModemFileSender将发送文件操作放入线程中实现异步调用。
文件列表:
- xymodemfilesender.h
- xymodemfilesender.cpp
2.2.7 XYModemFileRecver
该模块定义和实现了XYModemFileRecver类型。
XYModemFileRecver发送文件是同步操作,XYModemFileRecver将接收文件操作放入线程中实现异步调用。
文件列表:
- xymodemfilerecver.h
- xymodemfilerecver.cpp
使用
发送文件
void SerialPortWidget::sendFileByXYModem(QString const& fileName, bool isYModem)
{
QObject::disconnect(serial, SIGNAL(readyRead()), this, SLOT(onData()));
FileProgressDialog dialog(this);
XYModemFileSender sender(serial, isYModem);
connect(&sender, &XYModemFileSender::gotFileSize, &dialog, &FileProgressDialog::setFileSize);
connect(&sender, &XYModemFileSender::progressInfo, &dialog, &FileProgressDialog::setProgressInfo);
connect(&sender, &XYModemFileSender::finished, &dialog, &FileProgressDialog::finished);
connect(&sender, &XYModemFileSender::error, &dialog, &FileProgressDialog::error);
if(isYModem)
{
dialog.setTitle(tr("YMODEM Send"));
dialog.setProtocol(tr("1K YMODEM"));
}
else
{
dialog.setTitle(tr("XMODEM Send"));
dialog.setProtocol(tr("1K XMODEM"));
}
dialog.setFilename(QFileInfo(fileName).fileName());
dialog.setModal(true);
dialog.setVisible(true);
sender.start(fileName);
while(!dialog.isFinished())
{
if(dialog.isCancel())
{
sender.stop();
while(!dialog.isFinished())
QApplication::processEvents();
sender.cancel();
}
QApplication::processEvents();
}
connect(serial, SIGNAL(readyRead()), this, SLOT(onData()));
}
接收文件
void SerialPortWidget::recvFileByXYModem(QString const& fileName, bool isYModem)
{
QObject::disconnect(serial, SIGNAL(readyRead()), this, SLOT(onData()));
FileProgressDialog dialog(this);
XYModemFileRecver recver(serial, isYModem);
connect(&recver, &XYModemFileRecver::gotFileSize, &dialog, &FileProgressDialog::setFileSize);
connect(&recver, &XYModemFileRecver::progressInfo, &dialog, &FileProgressDialog::setProgressInfo);
connect(&recver, &XYModemFileRecver::finished, &dialog, &FileProgressDialog::finished);
connect(&recver, &XYModemFileRecver::error, &dialog, &FileProgressDialog::error);
if(isYModem)
{
dialog.setTitle(tr("YMODEM Recv"));
dialog.setProtocol(tr("1K YMODEM"));
}
else
{
dialog.setTitle(tr("XMODEM Recv"));
dialog.setProtocol(tr("1K XMODEM"));
}
dialog.setFilename(QFileInfo(fileName).fileName());
dialog.setModal(true);
dialog.setVisible(true);
recver.start(fileName);
while(!dialog.isFinished())
{
if(dialog.isCancel())
{
recver.stop();
while(!dialog.isFinished())
QApplication::processEvents();
recver.cancel();
}
QApplication::processEvents();
}
connect(serial, SIGNAL(readyRead()), this, SLOT(onData()));
}