25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

171 satır
5.2 KiB

  1. #include "modbussimulator.h"
  2. #include <QSerialPort>
  3. #include <QDebug>
  4. #include <algorithm>
  5. #include <QCoreApplication>
  6. #include <atomic>
  7. // PollingWorker 实现
  8. PollingWorker::PollingWorker(QModbusRtuSerialMaster *client,
  9. QMutex *mutex,
  10. QSet<int> *addresses,
  11. std::atomic<bool> *runningFlag,
  12. QObject *parent)
  13. : QObject(parent),
  14. m_modbusClient(client),
  15. m_mutex(mutex),
  16. m_coilAddresses(addresses),
  17. m_runningFlag(runningFlag),
  18. m_stop(false)
  19. {
  20. }
  21. void PollingWorker::startPolling(int interval)
  22. {
  23. while (!m_stop) {
  24. QThread::msleep(interval); // 使用线程休眠替代定时器
  25. if (!m_stop && *m_runningFlag) {
  26. performCoilReading();
  27. }
  28. }
  29. }
  30. void PollingWorker::performCoilReading()
  31. {
  32. QSet<int> addressesCopy;
  33. {
  34. QMutexLocker locker(m_mutex);
  35. if (m_coilAddresses->isEmpty()) return;
  36. addressesCopy = *m_coilAddresses;
  37. }
  38. int minAddress = *std::min_element(addressesCopy.begin(), addressesCopy.end());
  39. int maxAddress = *std::max_element(addressesCopy.begin(), addressesCopy.end());
  40. int count = maxAddress - minAddress + 1;
  41. QModbusDataUnit readUnit(QModbusDataUnit::Coils, minAddress, count);
  42. if (auto *reply = m_modbusClient->sendReadRequest(readUnit, 1)) {
  43. if (!reply->isFinished()) {
  44. auto safeAddresses = QSharedPointer<QSet<int>>::create(addressesCopy);
  45. connect(reply, &QModbusReply::finished, [this, reply, safeAddresses]() {
  46. if (reply->error() == QModbusDevice::NoError) {
  47. const QModbusDataUnit unit = reply->result();
  48. for (int i = 0; i < unit.valueCount(); ++i) {
  49. int address = unit.startAddress() + i;
  50. bool state = unit.value(i);
  51. if (safeAddresses->contains(address)) {
  52. emit coilReadResult(address, state);
  53. }
  54. }
  55. } else {
  56. emit errorOccurred(reply->errorString());
  57. }
  58. reply->deleteLater();
  59. });
  60. } else {
  61. delete reply;
  62. }
  63. }
  64. }
  65. // ModbusSimulator 实现
  66. ModbusSimulator::ModbusSimulator(QObject *parent)
  67. : QObject(parent),
  68. m_coilAddresses(),
  69. m_running(false),
  70. m_workerThread(nullptr),
  71. m_worker(nullptr)
  72. {
  73. m_modbusClient = new QModbusRtuSerialMaster(this);
  74. }
  75. ModbusSimulator::~ModbusSimulator()
  76. {
  77. stopSimulation();
  78. }
  79. bool ModbusSimulator::isRunning() const
  80. {
  81. return m_running;
  82. }
  83. void ModbusSimulator::startSimulation()
  84. {
  85. if (m_running) return;
  86. // 配置Modbus连接
  87. m_modbusClient->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");
  88. m_modbusClient->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
  89. m_modbusClient->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
  90. m_modbusClient->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
  91. m_modbusClient->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
  92. if (!m_modbusClient->connectDevice())
  93. {
  94. emit errorOccurred("无法连接Modbus设备");
  95. return;
  96. }
  97. m_modbusClient->moveToThread(m_workerThread);
  98. m_running = true;
  99. // 创建工作线程和Worker
  100. m_workerThread = new QThread(this);
  101. m_worker = new PollingWorker(m_modbusClient, &m_mutex, &m_coilAddresses, &m_running);
  102. m_worker->moveToThread(m_workerThread);
  103. // 连接信号槽
  104. connect(m_worker, &PollingWorker::coilReadResult,
  105. this, &ModbusSimulator::coilStatusRead);
  106. connect(m_worker, &PollingWorker::errorOccurred,
  107. this, &ModbusSimulator::errorOccurred);
  108. connect(m_workerThread, &QThread::started, [this]() {
  109. m_worker->startPolling(500); // 每秒轮询一次
  110. });
  111. connect(m_workerThread, &QThread::finished,
  112. m_worker, &QObject::deleteLater);
  113. connect(m_workerThread, &QThread::finished,
  114. m_workerThread, &QObject::deleteLater);
  115. m_workerThread->start();
  116. }
  117. void ModbusSimulator::stopSimulation()
  118. {
  119. if (!m_running) return;
  120. m_running = false;
  121. if (m_worker) {
  122. m_worker->stop(); // 通过公共接口设置停止标志
  123. }
  124. if (m_workerThread && m_workerThread->isRunning()) {
  125. m_workerThread->quit();
  126. m_workerThread->wait();
  127. }
  128. if (m_modbusClient) {
  129. m_modbusClient->disconnectDevice();
  130. }
  131. }
  132. void ModbusSimulator::setCoilAddresses(const QSet<int> &addresses)
  133. {
  134. QMutexLocker locker(&m_mutex);
  135. m_coilAddresses = addresses;
  136. }
  137. void ModbusSimulator::writeCoil(int address, bool state)
  138. {
  139. if (!m_running) return;
  140. if (auto reply = m_modbusClient->sendWriteRequest(
  141. QModbusDataUnit(QModbusDataUnit::Coils, address, {state}), 1))
  142. {
  143. connect(reply, &QModbusReply::finished, [reply, address]() {
  144. if (reply->error() != QModbusDevice::NoError) {
  145. qWarning() << "写线圈失败:" << reply->errorString() << "地址:" << address;
  146. }
  147. reply->deleteLater();
  148. });
  149. }
  150. }