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 93
int 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协议(三)