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