综合平台编程器项目的远程存储
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

146 wiersze
6.9 KiB

  1. #pragma once
  2. #include "services/plc_communication_gateway.h"
  3. #include "domain/register_repository.h"
  4. #include <QModbusDevice>
  5. #include <QObject>
  6. #include <QTimer>
  7. #include <cstdint>
  8. #include <memory>
  9. #include <string>
  10. #include <vector>
  11. class PlcRegisterRepository;
  12. class QModbusReply;
  13. class QModbusRtuSerialMaster;
  14. struct PlcCommunicationFailure;
  15. struct PlcPollBlock
  16. {
  17. RegisterArea area = RegisterArea::M;
  18. int startAddress = 0;
  19. int count = 1;
  20. };
  21. // 基于 Qt Modbus RTU 的异步 PLC 通信实现
  22. // 负责连接、轮询、单点写入、故障恢复和首读资格,不阻塞 UI 线程
  23. class PlcCommunicationService final : public QObject, public PlcCommunicationGateway
  24. {
  25. Q_OBJECT
  26. public:
  27. // 创建通信服务,并把 PLC 缓存仓库交给它管理
  28. explicit PlcCommunicationService(
  29. PlcRegisterRepository &repository,
  30. QObject *parent = nullptr);
  31. // 释放 Modbus 主站和未完成的通信资源
  32. ~PlcCommunicationService() override;
  33. // 按给定串口参数连接 PLC,并开始异步轮询
  34. PlcCommunicationResult connectDevice(
  35. const PlcSerialConfiguration &configuration) override;
  36. // 停止轮询并断开 PLC
  37. void disconnectDevice() override;
  38. // 设置需要周期性读取的 M/D 地址集合
  39. PlcCommunicationResult setPollAddresses(
  40. const std::vector<RegisterAddress> &addresses) override;
  41. PlcCommunicationResult setPollAddresses(
  42. const std::vector<RegisterAddress> &addresses,
  43. const std::vector<RegisterWordRange> &multi_word_ranges) override;
  44. // 返回当前连接状态
  45. PlcConnectionState state() const override;
  46. // 判断本次连接是否已经完成所有轮询块的首次读取
  47. bool initialReadCompleted() const override;
  48. // 返回最近一次通信错误的类型
  49. PlcCommunicationError lastErrorType() const override;
  50. // 返回最近一次通信错误的文字
  51. const std::string &lastError() const override;
  52. // 注册状态、首读、缓存更新和错误通知回调
  53. void setCallbacks(
  54. std::function<void()> state_changed,
  55. std::function<void(bool)> initial_read_changed,
  56. std::function<void()> cache_updated,
  57. std::function<void()> poll_cycle_completed,
  58. std::function<void(const std::string &)> error_reported) override;
  59. signals:
  60. // 连接状态发生变化
  61. void stateChanged();
  62. // 首次完整读取资格发生变化
  63. void initialReadCompletedChanged(bool completed);
  64. // PLC 缓存收到新的成功读数
  65. void cacheUpdated();
  66. // 全部轮询块成功更新一轮
  67. void pollCycleCompleted();
  68. // 通信错误的可读提示文字
  69. void communicationError(const QString &message);
  70. private:
  71. // 把去重后的地址集合压缩为有限数量的连续读块
  72. void rebuildPollBlocks();
  73. // 应用新的轮询集合;有请求在途时由 pending_* 延后应用
  74. void applyPollAddresses(const std::vector<RegisterAddress> &addresses);
  75. // 发送下一段异步读取请求
  76. void pollNextBlock();
  77. // Faulted 状态下用轻量探测判断通信是否恢复
  78. void probeRecovery();
  79. // 处理恢复探测失败,并安排下一次探测
  80. void handleRecoveryProbeFailure(QModbusDevice::Error error);
  81. // 探测成功后恢复轮询,但仍需重新完成首读
  82. void restoreCommunication();
  83. // 处理读回复并把值写入 PLC 缓存
  84. void handleReadFinished(QModbusReply *reply, PlcPollBlock block);
  85. // 发送单点 M/D 写请求;缓存等待后续轮询确认
  86. RegisterWriteResult sendBitWrite(const RegisterAddress &address, bool value);
  87. RegisterWriteResult sendWordWrite(
  88. const RegisterAddress &address, std::int16_t value);
  89. RegisterWriteResult sendWordsWrite(
  90. const RegisterAddress &address,
  91. const std::vector<std::int16_t> &values);
  92. // 更新“所有轮询块均成功读取”的真机进入资格
  93. void updateInitialReadCompleted(bool completed, bool force_notification = false);
  94. // 处理没有主动断开时发生的串口断线
  95. void handleUnexpectedDisconnect();
  96. // 把 Qt Modbus 错误转换成项目自己的通信错误
  97. void handleModbusError(QModbusDevice::Error error);
  98. // 关闭串口并清理当前连接的请求和状态
  99. void closeSerialSession();
  100. // 修改连接状态并通知外部观察者
  101. void setState(PlcConnectionState state);
  102. // 保存错误信息、更新状态并通知外部观察者
  103. void setError(const PlcCommunicationFailure &failure);
  104. PlcRegisterRepository &repository_; // 用于保存 PLC 最近一次成功读回的 M/D 值
  105. std::unique_ptr<QModbusRtuSerialMaster> master_; // Qt Modbus RTU 主站对象
  106. QTimer poll_timer_; // 周期性触发下一轮轮询
  107. QTimer recovery_timer_; // Faulted 状态下定时发起恢复探测
  108. PlcSerialConfiguration configuration_; // 当前串口和站号配置
  109. std::vector<RegisterAddress> poll_addresses_; // 当前生效的轮询地址
  110. std::vector<RegisterAddress> pending_poll_addresses_; // 请求在途时暂存的新地址
  111. std::vector<RegisterWordRange> poll_multi_word_ranges_; // 当前轮询集合中的多字范围
  112. std::vector<RegisterWordRange> pending_poll_multi_word_ranges_; // 请求在途时暂存的多字范围
  113. std::vector<PlcPollBlock> poll_blocks_; // 根据地址合并出的连续读块
  114. std::size_t next_poll_block_ = 0; // 下一次要读取的读块下标
  115. QModbusReply *pending_reply_ = nullptr; // 当前未完成的读请求或恢复探测
  116. QModbusReply *pending_write_reply_ = nullptr; // 当前未完成的单点写请求
  117. bool poll_update_pending_ = false; // 是否有等待请求完成后应用的新轮询集合
  118. bool disconnecting_ = false; // 是否正在执行主动断开
  119. bool serial_session_opened_ = false; // 本次串口会话是否曾经成功打开
  120. bool received_valid_response_ = false; // 本次连接是否收到过有效 PLC 回复
  121. std::uint64_t connection_generation_ = 0; // 连接代次,用于丢弃旧连接的异步回复
  122. PlcConnectionState state_ = PlcConnectionState::Disconnected; // 当前通信状态
  123. bool initial_read_completed_ = false; // 是否已完成首读,可作为真机运行门槛
  124. // 首读阶段每个读块的完成标记,全部为 true 后才允许进入真机
  125. std::vector<bool> initial_blocks_read_;
  126. PlcCommunicationError last_error_type_ = PlcCommunicationError::None; // 最近一次错误类型
  127. std::string last_error_; // 最近一次错误的可读文字
  128. std::function<void()> state_changed_callback_; // 状态变化时调用
  129. std::function<void(bool)> initial_read_changed_callback_; // 首读资格变化时调用
  130. std::function<void()> cache_updated_callback_; // 缓存更新后调用
  131. std::function<void()> poll_cycle_completed_callback_; // 完整轮询一轮后调用
  132. std::function<void(const std::string &)> error_reported_callback_; // 发生错误时调用
  133. };