diff --git a/untitled/hmidocument.cpp b/untitled/hmidocument.cpp index 4cde5d0..a6c09cf 100644 --- a/untitled/hmidocument.cpp +++ b/untitled/hmidocument.cpp @@ -103,9 +103,9 @@ void HMIDocument::startSimulation() QMessageBox::warning(nullptr, "仿真启动", "没有找到有效的线圈地址绑定"); return; } - - if (!m_modbusSimulator) { - m_modbusSimulator = new ModbusSimulator(this); + m_modbusSimulator = new ModbusSimulator(this); + if (m_modbusSimulator) + { connect(m_modbusSimulator, &ModbusSimulator::coilStatusRead, this, &HMIDocument::onCoilStatusRead); connect(m_modbusSimulator, &ModbusSimulator::errorOccurred, diff --git a/untitled/modbussimulator.cpp b/untitled/modbussimulator.cpp index b5dfc39..80b029e 100644 --- a/untitled/modbussimulator.cpp +++ b/untitled/modbussimulator.cpp @@ -2,13 +2,81 @@ #include #include #include -#include +#include +#include + +// PollingWorker 实现 +PollingWorker::PollingWorker(QModbusRtuSerialMaster *client, + QMutex *mutex, + QSet *addresses, + std::atomic *runningFlag, + QObject *parent) + : QObject(parent), + m_modbusClient(client), + m_mutex(mutex), + m_coilAddresses(addresses), + m_runningFlag(runningFlag), + m_stop(false) +{ +} + +void PollingWorker::startPolling(int interval) +{ + while (!m_stop) { + QThread::msleep(interval); // 使用线程休眠替代定时器 + if (!m_stop && *m_runningFlag) { + performCoilReading(); + } + } +} + +void PollingWorker::performCoilReading() +{ + QSet addressesCopy; + { + QMutexLocker locker(m_mutex); + if (m_coilAddresses->isEmpty()) return; + addressesCopy = *m_coilAddresses; + } + + int minAddress = *std::min_element(addressesCopy.begin(), addressesCopy.end()); + int maxAddress = *std::max_element(addressesCopy.begin(), addressesCopy.end()); + int count = maxAddress - minAddress + 1; + + QModbusDataUnit readUnit(QModbusDataUnit::Coils, minAddress, count); + if (auto *reply = m_modbusClient->sendReadRequest(readUnit, 1)) { + if (!reply->isFinished()) { + auto safeAddresses = QSharedPointer>::create(addressesCopy); + connect(reply, &QModbusReply::finished, [this, reply, safeAddresses]() { + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + for (int i = 0; i < unit.valueCount(); ++i) { + int address = unit.startAddress() + i; + bool state = unit.value(i); + if (safeAddresses->contains(address)) { + emit coilReadResult(address, state); + } + } + } else { + emit errorOccurred(reply->errorString()); + } + reply->deleteLater(); + }); + } else { + delete reply; + } + } +} + +// ModbusSimulator 实现 ModbusSimulator::ModbusSimulator(QObject *parent) - : QObject(parent), m_coilAddresses(),m_running(false), m_mutex() + : QObject(parent), + m_coilAddresses(), + m_running(false), + m_workerThread(nullptr), + m_worker(nullptr) { m_modbusClient = new QModbusRtuSerialMaster(this); - m_readTimer = new QTimer(this); - connect(m_readTimer, &QTimer::timeout, this, &ModbusSimulator::readCoils); } ModbusSimulator::~ModbusSimulator() @@ -23,9 +91,7 @@ bool ModbusSimulator::isRunning() const void ModbusSimulator::startSimulation() { - qDebug() << "进入 startSimulation() 函数"; if (m_running) return; - qDebug() << "启动Modbus仿真555..."; // 配置Modbus连接 m_modbusClient->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1"); m_modbusClient->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity); @@ -38,35 +104,64 @@ void ModbusSimulator::startSimulation() emit errorOccurred("无法连接Modbus设备"); return; } - qDebug() << "Modbus设备连接成功"; + m_modbusClient->moveToThread(m_workerThread); m_running = true; - m_readTimer->start(1000); // 每秒读取一次 + + // 创建工作线程和Worker + m_workerThread = new QThread(this); + m_worker = new PollingWorker(m_modbusClient, &m_mutex, &m_coilAddresses, &m_running); + m_worker->moveToThread(m_workerThread); + + // 连接信号槽 + connect(m_worker, &PollingWorker::coilReadResult, + this, &ModbusSimulator::coilStatusRead); + connect(m_worker, &PollingWorker::errorOccurred, + this, &ModbusSimulator::errorOccurred); + connect(m_workerThread, &QThread::started, [this]() { + m_worker->startPolling(500); // 每秒轮询一次 + }); + connect(m_workerThread, &QThread::finished, + m_worker, &QObject::deleteLater); + connect(m_workerThread, &QThread::finished, + m_workerThread, &QObject::deleteLater); + + m_workerThread->start(); } void ModbusSimulator::stopSimulation() { if (!m_running) return; - m_readTimer->stop(); - m_modbusClient->disconnectDevice(); m_running = false; + + if (m_worker) { + m_worker->stop(); // 通过公共接口设置停止标志 + } + + if (m_workerThread && m_workerThread->isRunning()) { + m_workerThread->quit(); + m_workerThread->wait(); + } + + if (m_modbusClient) { + m_modbusClient->disconnectDevice(); + } } void ModbusSimulator::setCoilAddresses(const QSet &addresses) -{ - qDebug() <<"执行到此"; - qDebug() << "setCoilAddresses called, this:" << this; // 打印对象地址 - QMutexLocker locker(&m_mutex); // 必须加锁 - m_coilAddresses = QSet(addresses); +{ //qDebug()<<"这里"; + QMutexLocker locker(&m_mutex); + qDebug()<<"这里2"; + m_coilAddresses = addresses; + } void ModbusSimulator::writeCoil(int address, bool state) { if (!m_running) return; - if (auto reply = m_modbusClient->sendWriteRequest( - QModbusDataUnit(QModbusDataUnit::Coils, address, {state}), 1)) { - + QModbusDataUnit(QModbusDataUnit::Coils, address, {state}), 1)) + { connect(reply, &QModbusReply::finished, [reply, address]() { if (reply->error() != QModbusDevice::NoError) { qWarning() << "写线圈失败:" << reply->errorString() << "地址:" << address; @@ -75,40 +170,3 @@ void ModbusSimulator::writeCoil(int address, bool state) }); } } - -void ModbusSimulator::readCoils() -{ - QSet addressesCopy; - { - QMutexLocker locker(&m_mutex); - if (!m_running || m_coilAddresses.isEmpty()) return; - addressesCopy = m_coilAddresses; // 在锁保护下复制 - } - int minAddress = *std::min_element(addressesCopy.begin(), addressesCopy.end()); - int maxAddress = *std::max_element(addressesCopy.begin(), addressesCopy.end()); - int count = maxAddress - minAddress + 1; - - // 创建读请求 - QModbusDataUnit readUnit(QModbusDataUnit::Coils, minAddress, count); - if (auto *reply = m_modbusClient->sendReadRequest(readUnit, 1)) { - if (!reply->isFinished()) { - // 使用智能指针确保地址集合在回调中有效 - auto safeAddresses = QSharedPointer>::create(addressesCopy); - connect(reply, &QModbusReply::finished, this, [this, reply, safeAddresses]() { - if (reply->error() == QModbusDevice::NoError) { - const QModbusDataUnit unit = reply->result(); - for (int i = 0; i < unit.valueCount(); ++i) { - int address = unit.startAddress() + i; - bool state = unit.value(i); - if (safeAddresses->contains(address)) { - emit coilStatusRead(address, state); - } - } - } - reply->deleteLater(); - }); - } else { - delete reply; - } - } -} diff --git a/untitled/modbussimulator.h b/untitled/modbussimulator.h index d0b3a7f..660d1b4 100644 --- a/untitled/modbussimulator.h +++ b/untitled/modbussimulator.h @@ -3,9 +3,13 @@ #include #include -#include #include -#include // 新增:互斥锁头文件 +#include +#include +#include + +class PollingWorker; // 前向声明 + class ModbusSimulator : public QObject { Q_OBJECT @@ -25,15 +29,42 @@ signals: void coilStatusRead(int address, bool state); void errorOccurred(const QString &message); -private slots: - void readCoils(); - private: QModbusRtuSerialMaster *m_modbusClient; - QTimer *m_readTimer; QSet m_coilAddresses; - bool m_running; - QMutex m_mutex; // 新增:保护m_coilAddresses的互斥锁 + std::atomic m_running; + QMutex m_mutex; + QThread *m_workerThread; + PollingWorker *m_worker; +}; + +// 轮询工作线程类 +class PollingWorker : public QObject +{ + Q_OBJECT +public: + void stop() { m_stop = true; } // 新增公共接口 + explicit PollingWorker(QModbusRtuSerialMaster *client, + QMutex *mutex, + QSet *addresses, + std::atomic *runningFlag, + QObject *parent = nullptr); + +public slots: + void startPolling(int interval); + +signals: + void coilReadResult(int address, bool state); + void errorOccurred(const QString &message); + +private: + void performCoilReading(); + + QModbusRtuSerialMaster *m_modbusClient; + QMutex *m_mutex; + QSet *m_coilAddresses; + std::atomic *m_runningFlag; + std::atomic m_stop; }; #endif // MODBUSSIMULATOR_H diff --git a/untitled/untitled.pro.user b/untitled/untitled.pro.user index c515fcd..a95c0b9 100644 --- a/untitled/untitled.pro.user +++ b/untitled/untitled.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId