@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE QtCreatorProject> | |||
<!-- Written by QtCreator 4.0.3, 2025-08-01T15:38:03. --> | |||
<!-- Written by QtCreator 4.0.3, 2025-08-04T09:20:47. --> | |||
<qtcreator> | |||
<data> | |||
<variable>EnvironmentId</variable> | |||
@@ -25,19 +25,25 @@ SerialCommunicator::SerialCommunicator(QObject *parent) : QObject(parent) | |||
, serialPort_(new QSerialPort(this)) | |||
, recvTimer_(new QTimer(this)) | |||
, resendTimer_(new QTimer(this)) | |||
, stationCheck_(new QTimer(this)) | |||
, checkTimeOut_(new QTimer(this)) | |||
, comCount_(0) | |||
, maxRetry_(3) | |||
, recvTimeout_(50) | |||
, resendTimeout_(1000) | |||
, test_(QByteArray::fromHex("010100010001AC0A")) | |||
{ | |||
// 配置定时器(单次触发) | |||
recvTimer_->setSingleShot(true); | |||
resendTimer_->setSingleShot(true); | |||
stationCheck_->setSingleShot(true); | |||
// 连接信号槽 | |||
connect(serialPort_, &QSerialPort::readyRead, this, &SerialCommunicator::onReadyRead); | |||
connect(recvTimer_, &QTimer::timeout, this, &SerialCommunicator::onRecvTimeout); | |||
connect(resendTimer_, &QTimer::timeout, this, &SerialCommunicator::onResendTimeout); | |||
connect(stationCheck_, QTimer::timeout, this, SerialCommunicator::stationCheck); | |||
connect(checkTimeOut_, QTimer::timeout, this, SerialCommunicator::checkTimeOut); | |||
connect(serialPort_, SIGNAL(error(QSerialPort::SerialPortError)), | |||
this, SLOT(handleSerialError(QSerialPort::SerialPortError))); | |||
} | |||
@@ -119,6 +125,7 @@ bool SerialCommunicator::open() | |||
if (serialPort_->open(QIODevice::ReadWrite)) | |||
{ | |||
emit statusChanged("串口连接成功"); | |||
stationCheck_->start(1000); | |||
return true; | |||
} | |||
else | |||
@@ -145,8 +152,11 @@ void SerialCommunicator::close() | |||
//关闭所有定时器 | |||
recvTimer_->stop(); | |||
resendTimer_->stop(); | |||
stationCheck_->stop(); | |||
checkTimeOut_->stop(); | |||
// 清除可能存在的待处理数据 | |||
emit stationConnect(false); | |||
serialPort_->clear(); | |||
// 关闭串口 | |||
@@ -175,6 +185,8 @@ void SerialCommunicator::sendData(const QByteArray &data) | |||
emit statusChanged("串口未打开,无法发送数据"); | |||
return; | |||
} | |||
stationCheck_->stop(); | |||
checkTimeOut_->stop(); | |||
// 保存待重发数据,初始化计数器,启动重发定时器 | |||
currentData_ = data; | |||
comCount_ = 0; | |||
@@ -233,6 +245,11 @@ bool SerialCommunicator::isOpen() const | |||
return serialPort_->isOpen(); | |||
} | |||
void SerialCommunicator::setCheckMsg(QByteArray msg) | |||
{ | |||
this->test_ = msg; | |||
} | |||
// ====================== 私有槽函数 ====================== // | |||
/** | |||
@@ -245,10 +262,20 @@ bool SerialCommunicator::isOpen() const | |||
*/ | |||
void SerialCommunicator::onReadyRead() | |||
{ | |||
// 读取数据到缓冲区 | |||
recvBuffer_.append(serialPort_->readAll()); | |||
//重置接收超时定时器 | |||
recvTimer_->start(recvTimeout_); | |||
if(checkTimeOut_->isActive() || stationCheck_->isActive()) | |||
{ | |||
checkTimeOut_->stop(); | |||
serialPort_->clear(); | |||
emit stationConnect(true); | |||
stationCheck_->start(1000); | |||
} | |||
else | |||
{ | |||
// 读取数据到缓冲区 | |||
recvBuffer_.append(serialPort_->readAll()); | |||
//重置接收超时定时器 | |||
recvTimer_->start(recvTimeout_); | |||
} | |||
} | |||
/** | |||
@@ -262,6 +289,7 @@ void SerialCommunicator::onReadyRead() | |||
void SerialCommunicator::onRecvTimeout() | |||
{ | |||
// 接收完成,发送完整数据信号,停止重发定时器 | |||
stationCheck_->start(1000); | |||
resendTimer_->stop(); | |||
emit dataReceived(recvBuffer_); | |||
recvBuffer_.clear(); | |||
@@ -289,6 +317,7 @@ void SerialCommunicator::onResendTimeout() | |||
{ | |||
// 达到最大重发次数,触发超时信号 | |||
resendTimer_->stop(); | |||
stationCheck_->start(1000); | |||
emit timeoutOccurred(); | |||
} | |||
} | |||
@@ -382,3 +411,16 @@ void SerialCommunicator::handleSerialError(QSerialPort::SerialPortError error) | |||
} | |||
} | |||
void SerialCommunicator::stationCheck() | |||
{ | |||
serialPort_->write(test_); | |||
checkTimeOut_->setSingleShot(true); | |||
checkTimeOut_->start(200); | |||
} | |||
void SerialCommunicator::checkTimeOut() | |||
{ | |||
emit stationConnect(false); | |||
stationCheck_->start(1000); | |||
} |
@@ -117,6 +117,8 @@ public: | |||
*/ | |||
bool isOpen() const; | |||
void setCheckMsg(QByteArray msg); | |||
signals: | |||
/** | |||
* @brief 接收到完整数据信号 | |||
@@ -143,6 +145,8 @@ signals: | |||
*/ | |||
void physicalDisconnected(); | |||
void stationConnect(bool online); | |||
private slots: | |||
/** | |||
* @brief 串口数据到达处理 | |||
@@ -168,16 +172,22 @@ private slots: | |||
*/ | |||
void handleSerialError(QSerialPort::SerialPortError error); | |||
void stationCheck(); | |||
void checkTimeOut(); | |||
private: | |||
QSerialPort *serialPort_; // 串口对象 | |||
QTimer *recvTimer_; // 接收完成判断定时器 | |||
QTimer *resendTimer_; // 超时重发定时器 | |||
QTimer *stationCheck_; // 检测是否连接到从站 | |||
QTimer *checkTimeOut_; | |||
QByteArray recvBuffer_; // 接收缓冲区 | |||
QByteArray currentData_; // 当前待重发数据 | |||
int comCount_; // 重发计数器 | |||
int maxRetry_; // 最大重发次数 | |||
int recvTimeout_; // 接收超时时间(ms) | |||
int resendTimeout_; // 重发间隔(ms) | |||
QByteArray test_; | |||
// 串口参数 | |||
QString portName_; | |||
@@ -56,6 +56,8 @@ Widget::Widget(QWidget *parent) : | |||
this, &Widget::onBtnBatchInputClicked); | |||
connect(ui_->line_input, &QLineEdit::textChanged, | |||
this, &Widget::onTextChanged); | |||
connect(ui_->btn_checkmsg, &QPushButton::clicked, | |||
this, &Widget::onBtnCheckmsgClicked); | |||
//当串口接收到完整数据后,serialComm_会发出dataReceived信号 | |||
connect(serialComm_, &SerialCommunicator::dataReceived, | |||
this, &Widget::onSerialDataReceived); | |||
@@ -68,6 +70,8 @@ Widget::Widget(QWidget *parent) : | |||
//当串口连接出现错误时(例如串口突然断开),serialComm_会发出physicalDisconnected信号 | |||
connect(serialComm_, &SerialCommunicator::physicalDisconnected, | |||
this, &Widget::sicalDisconnected); | |||
connect(serialComm_, &SerialCommunicator::stationConnect, | |||
this, &Widget::staionConneting); | |||
//设置默认波特率、数据位、校验位 | |||
ui_->combo_baud_rate->setCurrentIndex(3); | |||
@@ -492,6 +496,18 @@ void Widget::onTextChanged() | |||
ui_->line_number->setText(QString::number(data.size())); | |||
} | |||
void Widget::staionConneting(bool online) | |||
{ | |||
if(online) | |||
{ | |||
ui_->label_staion->setText("成功连接到从站"); | |||
} | |||
else | |||
{ | |||
ui_->label_staion->setText("未连接到从站"); | |||
} | |||
} | |||
/** | |||
* @brief 串口通信超时处理 | |||
* | |||
@@ -535,3 +551,17 @@ void Widget::onBtnBatchInputClicked() | |||
ui_->line_input->insert(inputData); | |||
} | |||
} | |||
void Widget::onBtnCheckmsgClicked() | |||
{ | |||
bool ok; | |||
quint16 stationAddress = ui_->line_station_address->text().toUShort(&ok); | |||
if (!ok || 0 == stationAddress) | |||
{ | |||
QMessageBox::warning(this, "输入错误", "从站地址应为大于0的整数"); | |||
return; | |||
} | |||
modbus_->setStation(stationAddress,0x01,0x01,0x01); | |||
modbus_->readCoilAndReg(); | |||
serialComm_->setCheckMsg(modbus_->sendCommand()); | |||
} |
@@ -84,6 +84,8 @@ private slots: | |||
void onTextChanged(); | |||
void staionConneting(bool online); | |||
// 串口通信超时处理槽函数 | |||
void onSerialTimeout(); | |||
@@ -91,6 +93,8 @@ private slots: | |||
void onBtnBatchInputClicked(); | |||
void onBtnCheckmsgClicked(); | |||
private: | |||
Ui::Widget *ui_; // 指向用户界面的指针 | |||
SerialCommunicator *serialComm_; // 串口通信处理对象 | |||
@@ -7,7 +7,7 @@ | |||
<x>0</x> | |||
<y>0</y> | |||
<width>700</width> | |||
<height>500</height> | |||
<height>503</height> | |||
</rect> | |||
</property> | |||
<property name="windowTitle"> | |||
@@ -309,9 +309,9 @@ | |||
<property name="geometry"> | |||
<rect> | |||
<x>240</x> | |||
<y>20</y> | |||
<y>30</y> | |||
<width>211</width> | |||
<height>271</height> | |||
<height>261</height> | |||
</rect> | |||
</property> | |||
<property name="title"> | |||
@@ -335,9 +335,9 @@ | |||
<property name="geometry"> | |||
<rect> | |||
<x>470</x> | |||
<y>20</y> | |||
<y>30</y> | |||
<width>221</width> | |||
<height>271</height> | |||
<height>261</height> | |||
</rect> | |||
</property> | |||
<property name="title"> | |||
@@ -508,6 +508,32 @@ | |||
<bool>true</bool> | |||
</property> | |||
</widget> | |||
<widget class="QWidget" name=""> | |||
<property name="geometry"> | |||
<rect> | |||
<x>251</x> | |||
<y>1</y> | |||
<width>241</width> | |||
<height>30</height> | |||
</rect> | |||
</property> | |||
<layout class="QHBoxLayout" name="horizontalLayout_2"> | |||
<item> | |||
<widget class="QLabel" name="label_staion"> | |||
<property name="text"> | |||
<string>未连接到从站</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QPushButton" name="btn_checkmsg"> | |||
<property name="text"> | |||
<string>重新生成检测报文</string> | |||
</property> | |||
</widget> | |||
</item> | |||
</layout> | |||
</widget> | |||
</widget> | |||
<layoutdefault spacing="6" margin="11"/> | |||
<resources/> | |||