|
|
@@ -2,13 +2,81 @@ |
|
|
|
#include <QSerialPort> |
|
|
|
#include <QDebug> |
|
|
|
#include <algorithm> |
|
|
|
#include<QThread> |
|
|
|
#include <QCoreApplication> |
|
|
|
#include <atomic> |
|
|
|
|
|
|
|
// PollingWorker 实现 |
|
|
|
PollingWorker::PollingWorker(QModbusRtuSerialMaster *client, |
|
|
|
QMutex *mutex, |
|
|
|
QSet<int> *addresses, |
|
|
|
std::atomic<bool> *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<int> 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<QSet<int>>::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<int> &addresses) |
|
|
|
{ |
|
|
|
qDebug() <<"执行到此"; |
|
|
|
qDebug() << "setCoilAddresses called, this:" << this; // 打印对象地址 |
|
|
|
QMutexLocker locker(&m_mutex); // 必须加锁 |
|
|
|
m_coilAddresses = QSet<int>(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<int> 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<QSet<int>>::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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |