3 实现
3.2 KermitSendFile
该模块实现了Kermit发送文件功能。
序列图如下:
3.2.1 KermitSendFile定义
class QSerialPort;
class KermitSendFile : public QObject, public Kermit
{Q_OBJECT
public:explicit KermitSendFile(QSerialPort *serial, QObject *parent = nullptr);
public slots:void start(QString const& fileName);void stop();void cancel();signals:void gotFileSize(quint64 filesize);void progressInfo(quint32 blockNumber, quint64 bytesOfSend);void error(QString const& e);void finished();protected:int write(char const *data, int size) override;int read(char *data, int size) override;char getc() override;void on_ack(int seq, const char* data, int size) override;void on_nack(int seq, const char* data, int size) override;void on_error(int seq, const char* data, int size) override;
private:void send_data(int seq);int next_seq(int seq) { return (seq + 1) % 64; }bool singled() { return signal_; }void doSignal() { signal_ = true; };bool getchar(char & ch);
private:QSerialPort* serial_;volatile bool signal_;State state_ = SSNUL;QFile file_;int timeMs = 0;quint32 blockNumber = 0;quint64 bytesOfSend_ = 0;
};
公共接口:
- start 开始发送文件
- stop 停止传输文件
- cancel 中断传输文件
信号:
- gotFileSize 文件大小信号
- progressInfo 传输进度信号
- error 出错信号
- finished 传输结束信号
重载接口:
- on_ack 处理应答包
- on_nack 处理否定应答包
- on_error 处理错误包
- write 向串口写数据
- read 从串口读数据
- getc 从串口读取一个字符
其它接口:
- send_data 从文件读取数据编码并发送
3.2.2 KermitSendFile实现
3.2.2.1 start/stop/cancel
void KermitSendFile::start(QString const& fileName)
{QFileInfo fileInfo(fileName);emit gotFileSize(fileInfo.size());send_init();state_ = SSINT;file_.setFileName(fileName);while(!singled() && state_ != SSBRK)recv_packet();emit finished();serial_->moveToThread(QApplication::instance()->thread());
}void KermitSendFile::stop() { doSignal(); }void KermitSendFile::cancel() { state_ = SSBRK; }
函数说明:
- start
- 发送文件大小信号
- 发送开始传输包
- 循环接收数据包,知道收到停止或终止信号
- stop 发送停止信号
- cancel 设置终止信号
3.2.2.2 on_ack/on_nack/on_error
void KermitSendFile::on_ack(int seq, const char* data, int size)
{if(seq == 0 && state_ == SSINT){state_ = SSDAT;file_.open(QIODevice::ReadOnly);Kermit::on_ack(seq, data, size);}if(state_ == SSEND) {state_ = SSBRK;return;}if(!file_.atEnd())send_data(next_seq(seq));else{send_break(next_seq(seq));state_ = SSEND;}
}void KermitSendFile::on_nack(int seq, const char* data, int size)
{if(state_ == SSDAT)resend();Kermit::on_nack(seq, data, size);
}void KermitSendFile::on_error(int seq, const char* data, int size)
{emit error(QString::fromStdString(data, size));Kermit::on_error(seq, data, size);
}
函数说明:
- on_ack
- 如果是第一个包,打开文件并调用父类on_ack来处理包
- 如果状态是结束则退出
- 如果文件没有发送完,则发送数据,发送完毕则设置状态为结束
- on_nack 如果是在收发数据状态,则重传
- on_error 发送出错信号
3.2.2.3 write/read/getc/send_data
int KermitSendFile::write(char const *data, int size) {return serial_->write(data, size);
}int KermitSendFile::read(char *data, int size)
{int read_size = 0;while(!singled() && read_size < size){if(serial_->waitForReadyRead(timeMs))read_size += serial_->read(data + read_size, size - read_size);}return read_size;
}char KermitSendFile::getc()
{char c = NUL;while(!singled()){if(serial_->waitForReadyRead(timeMs)){serial_->getChar(&c);break;}}return c;
}void KermitSendFile::send_data(int seq)
{char data[MaxLen];//94 93int size = 0;int readSize = 0;char c;while(!singled() && size < MaxLen - 1){if(!getchar(c))break;size += encode(c, data + size);readSize++;}if(size > 0){Kermit::send_data(seq, data, size);blockNumber++;bytesOfSend_ += readSize;emit progressInfo(blockNumber, bytesOfSend_);}
}
函数说明:
- write 向串口写数据
- read 从串口读取指定大小数据
- getc 从串口读取一个字符
- send_data
- 从文件读取指定数据并编码
- 发送数据并发送进度信号
Qt实现Kermit协议(二) Qt实现Kermit协议(三)