-
Notifications
You must be signed in to change notification settings - Fork 0
/
mainwindow.cpp
642 lines (487 loc) · 19.3 KB
/
mainwindow.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QSerialPortInfo>
#include <QDebug>
#include <QFileDialog>
#include <QFile>
#include <QStandardPaths>
#include <QImageReader>
#include <QHBoxLayout>
#include <QRegularExpression>
#include <QDateTime>
#include <QThread>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
fillSerialPortInfo();
connect(&serialPort, &QSerialPort::readyRead, this, &MainWindow::handleReadyRead);
connect(&timeoutTimer, &QTimer::timeout, this, &MainWindow::on_timeout);
timeoutTimer.setSingleShot(false);
ui->progressBar->setValue(0);
// 初始化时禁用文件选择和发送按钮
ui->pushButtonFile->setEnabled(false);
ui->pushButtonTransmit->setEnabled(false);
ui->testButton->setEnabled(false);
ui->read->setEnabled(false);
// 设置lineEditFile为只读
ui->lineEditFile->setReadOnly(true);
ui->textEditLog->setReadOnly(true);
connect(&rfTimer, &QTimer::timeout, this, &MainWindow::testTimer_timeout);
rfTimer.setInterval(timeoutValue);
rfTimer.setSingleShot(true);
}
// MainWindow 析构函数
MainWindow::~MainWindow()
{
delete ui;
}
// 填充串口信息
void MainWindow::fillSerialPortInfo() {
auto portsInfo = QSerialPortInfo::availablePorts();
for (const auto& info : portsInfo) {
ui->comboBoxUart->addItem(info.portName() + " " + info.description(), info.portName());
}
}
// 处理打开/关闭串口的按钮
void MainWindow::on_pushButtonUart_released()
{
if (serialPort.isOpen()) {
serialPort.close();
ui->pushButtonUart->setText("Open Port");
// 串口关闭时禁用文件选择和发送按钮
ui->pushButtonFile->setEnabled(false);
ui->pushButtonTransmit->setEnabled(false);
ui->testButton->setEnabled(false);
ui->read->setEnabled(false);
ui->comboBoxUart->setEnabled(true);
resetTransmissionState();
} else {
auto portName = ui->comboBoxUart->currentData().toString();
serialPort.setPortName(portName);
serialPort.setBaudRate(QSerialPort::Baud115200);
serialPort.setDataBits(QSerialPort::Data8);
serialPort.setParity(QSerialPort::NoParity);
serialPort.setStopBits(QSerialPort::OneStop);
serialPort.setFlowControl(QSerialPort::NoFlowControl);
if (!serialPort.open(QIODevice::ReadWrite)) {
QMessageBox::warning(this, "Warning", portName + " open failed: " + serialPort.errorString());
} else {
ui->pushButtonUart->setText("Close Port");
// 串口打开成功时启用文件选择和发送按钮
ui->pushButtonFile->setEnabled(true);
ui->pushButtonTransmit->setEnabled(true);
ui->testButton->setEnabled(true);
ui->read->setEnabled(true);
ui->comboBoxUart->setEnabled(false);
QString confCmd = QString("AT+NWM=0\r\n");
serialPort.write(confCmd.toLocal8Bit());
}
}
}
void MainWindow::on_pushButtonFile_released()
{
auto filename = QFileDialog::getOpenFileName(this, "Select File",
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
"Images (*.jpg;*.png);;All Files (*.*)");
if (!filename.isEmpty()) {
ui->lineEditFile->setText(filename);
// 获取文件大小信息
QFileInfo fileInfo(filename);
qint64 sizeInBytes = fileInfo.size();
double sizeInKB = sizeInBytes / 1024.0; // 转换为千字节
// 设置 QLabel 显示文件大小
ui->labelPictureSize->setText("size: " + QString::number(sizeInKB, 'f', 2) + " KB");
// 使用QFileInfo来获取纯文件名
QString fileOnlyName = fileInfo.fileName(); // 获取不包含路径的文件名
currentFileName = fileOnlyName; // currentFileName 已经在类中声明
}
}
// 发送文件按钮
void MainWindow::on_pushButtonTransmit_clicked()
{
resetTransmissionState();
isTestRunning = false;
isTransmitImage = true; // 在开始传输时设置标志
//打开文件
auto filename = ui->lineEditFile->text();
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, "Warning", filename + " open failed: " + file.errorString());
return;
}
fileData = file.readAll();
fileSize = fileData.size();
offset = 0;
//打开串口的接收模式
QString confString = QString("AT+PRECV=0\r\n");
serialPort.write(confString.toLocal8Bit());
confString = QString("AT+PRECV=65533\r\n");
serialPort.write(confString.toLocal8Bit());
// 发送开始命令
QByteArray fileNameBytes = currentFileName.toUtf8();
// 将QByteArray转换为16进制表示
QString hexFileName = fileNameBytes.toHex();
packetType = StartPacket;
QString startCommand = QString("AT+PSEND=000055550000")+ hexFileName +"\r\n";
serialPort.write(startCommand.toLocal8Bit());
//等待txdone 在启动超时
while(!this->isTxDone )
{
QApplication::processEvents();
}
timeoutTimer.start(timeoutValue);
//设置相关状态栏失能
ui->pushButtonTransmit->setEnabled(false);
ui->lineEditFile->setEnabled(false);
ui->pushButtonFile->setEnabled(false);
ui->testButton->setEnabled(false);
ui->read->setEnabled(false);
//停止测试模式
//to do
imageStartTime = QDateTime::currentMSecsSinceEpoch();
}
void MainWindow::sendNextChunk() {
timeoutTimer.stop();
const int chunkSize = ui->lineEditFile_mtu->text().toInt();
currentChunkSize = qMin(chunkSize, fileSize - offset); // 更新 currentChunkSize
QByteArray chunk = fileData.mid(offset, currentChunkSize);
QString hexData = chunk.toHex();
currentPacketIndex++;
QString hexString = QString("%1").arg(currentPacketIndex, 2, 16, QChar('0'));
packetType = DataPacket;
QString command = "AT+PSEND="+ hexString + hexData + "\r\n";
serialPort.write(command.toLocal8Bit());
isTxDone = false;
while(!this->isTxDone )
{
QApplication::processEvents();
}
timeoutTimer.start(timeoutValue);
qDebug()<<"SendNextChunk";
//统计
int progress = static_cast<int>((static_cast<double>(offset) / fileSize) * 100);
ui->progressBar->setValue(progress);
double lossRatePercentage = 0.0;
if (ackReceived > 0) { // 防止除以零
lossRatePercentage = (double)ackReceived / (double)packetsSent * 100.0;
}
double bytesPerSecond =(double)offset /((QDateTime::currentMSecsSinceEpoch() - this->imageStartTime)/1000.0) * 8 / 1000;
ui->labelRate->setText("Rate: "+QString::number(bytesPerSecond,'f', 3)+" kbps"+"\t\t"+QString::number(QDateTime::currentMSecsSinceEpoch()/1000 - this->imageStartTime/1000)\
+" s");
ui->labelRate_2->setText(QString::number(ackReceived)+"/"+QString::number(packetsSent) + "\t\t"+ QString::number(lossRatePercentage, 'f', 2) + "%");
packetsSent++;
}
void MainWindow::handleReadyRead() {
QByteArray responseData = serialPort.readAll();
qDebug()<<"responseData"<<responseData;
QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz");
QString logMessage = QString("[%1] %2").arg(timestamp).arg(QString::fromUtf8(responseData));
ui->textEditLog->append(logMessage);
ui->textEditLog->verticalScrollBar();
this->accumulatedData += responseData;
//qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz")<<"accumulatedData"<<accumulatedData;
QString rssi(accumulatedData); //这个要放在前面 不然放在后面都清空了
//提取RSSI 和 SNR
QRegExp rx("\\+EVT:RXP2P:([-]?\\d+):([-]?\\d+)");
if (rx.indexIn(rssi) != -1) {
QString rssi = rx.cap(1);
QString snr = rx.cap(2);
ui->rssi->setText(rssi);
ui->snr->setText(snr);
} else {
}
// 创建正则表达式以匹配特定模式
QRegularExpression re("55AA55([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})");
QRegularExpressionMatch match = re.match(accumulatedData);
if (match.hasMatch()) {
QString firstByte = match.captured(1); // D1
QString secondByte = match.captured(2); // 08
// qDebug() << "First Byte:" << firstByte;
// qDebug() << "Second Byte:" << secondByte;
bool ok;
int downlinkRSSI = firstByte.toUInt(&ok,16);
int downlinkSNR = secondByte.toInt(&ok,16);
ui->rssi_2->setText(QString::number((int8_t)downlinkRSSI));
ui->snr_2->setText(QString::number((int8_t)downlinkSNR));
} else {
//qDebug() << "Pattern not found!";
}
if (accumulatedData.contains("+EVT:TXP2P DONE")) //不区分测试模式还是图传
{
qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") << "+EVT:TXP2P DONE";
this->isTxDone = true; //点可能在这 这里是true 会引起一个sendTestCmd发包
accumulatedData.clear();
}
if (accumulatedData.contains("55AA55")) { //收到ACK
qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") << "ACK";
accumulatedData.clear(); //这里是重点 在调用sendTestCmd之前要清一下 其实handleReadyRead 应该触发一个槽函数是最合理的 不应该直接在这里处理 这里只处理底层
if(isTestRunning)
{
acknowledgedPackets += 1;
if(isTxDone)
{
sendTestCmd();//但是同时也收到了 55AA55 又来了一次sendTestCmd
}else
{
qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz")<<"无效接收ACK"; //响应来晚了 已经有新一包的数据了
}
}
if(isTransmitImage)
{
retryCount = 0;
if(packetType == StartPacket)
{
sendNextChunk();
}else if(packetType == DataPacket)
{
// 成功确认,继续发送下一块数据
offset += currentChunkSize;
if(offset < fileSize)
{
sendNextChunk();
ackReceived ++ ;
}else
{
timeoutTimer.stop();
//发送结尾包
packetType = EndPacket;
sendEndPacket();
}
// 更新进度条
}else if(packetType == EndPacket)
{
}else
{
}
}
}
//大于100字节 清空buffer
if(accumulatedData.size()>256)
{
accumulatedData.clear();
}
QDateTime currentDateTime = QDateTime::currentDateTime();
ui->time->setText(currentDateTime.toString());
}
void MainWindow::on_timeout() {
retryCount++;
qDebug() << "Timeout reached, retry count: " << retryCount;
if (retryCount <= 50) {
if(packetType == DataPacket)
{
sendNextChunk();
}
} else {
QMessageBox::critical(this, "Error", "Transmission failed after 3 retries.");
retryCount = 0; // 重置重试计数
timeoutTimer.stop();
//清空标志位 重新传输
//to do
}
}
void MainWindow::resetTransmissionState() {
// 重置传输相关的变量和状态
accumulatedData.clear(); // Also clear the accumulated data buffer
fileData.clear();
fileSize = 0;
offset = 0;
isTransmitImage = false;
retryCount = 0;
timeoutTimer.stop();
ui->progressBar->setValue(0);
qDebug() << "Transmission state has been reset.";
packetsSent = 0; // 实际发包数量
ackReceived = 0; // 收到ACK的发包数量
}
void MainWindow::sendEndPacket() {
QString stopCommand = "AT+PSEND=FEFDFC\r\n";
serialPort.write(stopCommand.toLocal8Bit());
// stopCommand = "AT+PRECV=0\r\n";
// serialPort.write(stopCommand.toLocal8Bit());
isTransmitImage = false;
QMessageBox::information(this, "Transfer Complete", "The file has been successfully sent!");
ui->progressBar->setValue(100);
}
void MainWindow::on_updateTimer_timeout() {
}
void MainWindow::on_read_released()
{
int currentIndex;
QString confCmd;
// confCmd = "AT+NWM=0r\n";
// serialPort.write(confCmd.toLocal8Bit());
// QThread::msleep(1000); // 睡眠500毫秒
confCmd = "AT+PRECV=0\r\n";
serialPort.write(confCmd.toLocal8Bit());
currentIndex = ui->channelBox->currentIndex();
QString channeString = ui->channelBox->itemText(currentIndex);
confCmd = "AT+PFREQ="+ channeString+ "\r\n";
serialPort.write(confCmd.toLocal8Bit());
currentIndex = ui->bwBox->currentIndex();
QString bwString = ui->bwBox->itemText(currentIndex);
confCmd = "AT+PBW="+ bwString + "\r\n";
serialPort.write(confCmd.toLocal8Bit());
currentIndex = ui->sf->currentIndex();
QString sfString = ui->sf->itemText(currentIndex);
confCmd = "AT+PSF="+ sfString + "\r\n";
serialPort.write(confCmd.toLocal8Bit());
confCmd = "AT+PTP=22\r\n";
serialPort.write(confCmd.toLocal8Bit());
if(sfString.toInt()==5||sfString.toInt()==6 )
{
confCmd = "AT+SYNCWORD=1424\r\n";
serialPort.write(confCmd.toLocal8Bit());
}else
{
confCmd = "AT+SYNCWORD=3444\r\n";
serialPort.write(confCmd.toLocal8Bit());
}
}
void MainWindow::on_testButton_released()
{
if(ui->testButton->text() == "Start Test")
{
ui->testButton->setText("Stop Test");
isTestRunning = true;
sendTestCmd();
totalPacketsSent= 0;
acknowledgedPackets = 0;
testStartTime = QDateTime::currentMSecsSinceEpoch();
}
else
{
ui->testButton->setText("Start Test");
rfTimer.stop();
isTestRunning = false;
}
}
void MainWindow::sendTestCmd()
{
QString conf = "AT+PRECV=0\r\n"; //先退出接收模式
serialPort.write(conf.toLocal8Bit());
this->isTxDone = false;
rfTimer.stop();
qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz")<<"TX";
int mtu = ui->lineEditFile_mtu->text().toInt();
uint64_t maxPacket = ui->MaxPacket->text().toInt();
if(acknowledgedPackets > maxPacket)
{
ui->testButton->setText("Start Test");
rfTimer.stop();
isTestRunning = false;
totalPacketsSent = 0;
acknowledgedPackets = 0;
return;
}
QByteArray payload;
for (int i = 0; i < mtu; i++) {
// 添加一个字节到 QByteArray,这里我们简单地添加了循环的索引值
payload.append(static_cast<char>(i));
}
QString testCmd = "AT+PSEND=" + payload.toHex().toUpper() + "\r\n";
serialPort.write(testCmd.toLocal8Bit());
totalPacketsSent += 1;
// 设置超时时间(毫秒)
int timeout = 1000 + timeoutValue; //
qint64 startTime = QDateTime::currentMSecsSinceEpoch();
qint64 elapsedTime = 0;
while(!this->isTxDone && elapsedTime < timeout)
//while(!this->isTxDone)
{
QApplication::processEvents();
elapsedTime = QDateTime::currentMSecsSinceEpoch() - startTime;
}
// if (!this->isTxDone) {
// // 处理超时情况,如重试或报错
// conf = "ATZ\r\n"; //先退出接收模式 //忘记一点 死机之后ATZ都不行了
// serialPort.write(conf.toLocal8Bit());
// }
conf = "AT+PRECV=65535\r\n"; //开启接收模式 //收到发送中断开启接收 //收到数据会自动退出接收模式 //超时的话 我强关接收模式
serialPort.write(conf.toLocal8Bit());
rfTimer.start(); //这里要改一下收到+EVT:TXP2P DONE 在开始结算超时
double lossRatePercentage = 0.0;
if (totalPacketsSent > 0) { // 防止除以零
lossRatePercentage = (double)acknowledgedPackets / totalPacketsSent * 100.0;
}
ui->lostRate->setText(QString::number(acknowledgedPackets) + "/" +
QString::number(totalPacketsSent) + "\t\t" +
QString::number(lossRatePercentage, 'f', 2) + "%");
double bytesPerSecond =(double)acknowledgedPackets*mtu /((QDateTime::currentMSecsSinceEpoch() - this->testStartTime)/1000.0) * 8 / 1000;
ui->bytesPerSecond->setText("Rate: "+QString::number(bytesPerSecond,'f', 3)+" kbps"+"\t\t"+QString::number(QDateTime::currentMSecsSinceEpoch()/1000 - this->testStartTime/1000)\
+" s");
}
void MainWindow::testTimer_timeout() //先捋一下问题 就是超时之后 会发送一个sendTestCmd 但是这个时候也会收到55AA55 也会触发一个sendTestCmd 就会出现连续两次的写
{
if(isTestRunning)
{
qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz")<<"testTimer_timeout";
sendTestCmd();
}
}
void MainWindow::on_pushButtonTransmit_released()
{
}
//一个完整的测试流 是先发送收到ACK 或者发送超时 如果一个正在执行单次未完成 是不能进行第二次发送的
//为了放在在没有收到TX DONE 同时收到了上一包的55AA55 需要手动控制接收 比较好
//现在手动控制也有问题 就是刚要关闭RX的时候 收到包了 就是单片机在操作的射频的时候 来了一个AT中断引起的异常
//accumulatedData:[0] "+EVT:RXP2P:-7:6:55AA55\r\n" 可能是收的时候 我调用了发送(会导致单片机异常) 增加超时时间可以避免这个问题 应该是正在接收 打断了接收中断
//无效接收ACK
//accumulatedData:[0] "OK\r\n"
//accumulatedData:[0] "OK\r\n+EVT:TXP2P DONE\r\n"
//+EVT:TXP2P DONE
//testTimer_timeout
//sendTestCmd
//accumulatedData:[0] "+EVT:RXP2P:-7:6:55AA55\r\n"
//无效接收ACK
void MainWindow::on_ate_clicked()
{
QString confCmd;
confCmd = "ATE\r\n";
serialPort.write(confCmd.toLocal8Bit());
}
void MainWindow::on_sf_activated(const QString &arg1) {
qDebug()<<"on_sf_activated"<<arg1;
int index = arg1.toInt();
int fixValue = 100;
switch (index) {
case 5:
// 处理SF为5的逻辑
timeoutValue = fixValue + 2*8;
break;
case 6:
// 处理SF为6的逻辑
timeoutValue = fixValue + 2*16;
break;
case 7:
// 处理SF为7的逻辑
timeoutValue = fixValue + 2*30;
break;
case 8:
// 处理SF为8的逻辑
timeoutValue = fixValue + 2*62;
break;
case 9:
// 处理SF为9的逻辑
timeoutValue = fixValue + 2*103;
break;
case 10:
// 处理SF为10的逻辑
timeoutValue = fixValue + 2*206;
break;
case 11:
// 处理SF为11的逻辑
timeoutValue = fixValue + 2*413;
break;
case 12:
// qDebug()<<"12"<<arg1;
// 处理SF为12的逻辑
timeoutValue = fixValue + 2*827;
break;
default:
qDebug() << "Unknown SF value:" << arg1;
break;
}
rfTimer.setInterval(timeoutValue);
timeoutTimer.setInterval(timeoutValue);
}