|
- #pragma once
-
- #include "services/plc_communication_gateway.h"
- #include "domain/register_repository.h"
-
- #include <QModbusDevice>
- #include <QObject>
- #include <QTimer>
-
- #include <cstdint>
- #include <memory>
- #include <string>
- #include <vector>
-
- class PlcRegisterRepository;
- class QModbusReply;
- class QModbusRtuSerialMaster;
- struct PlcCommunicationFailure;
-
- struct PlcPollBlock
- {
- RegisterArea area = RegisterArea::M;
- int startAddress = 0;
- int count = 1;
- };
-
- // 基于 Qt Modbus RTU 的异步 PLC 通信实现
- // 负责连接、轮询、单点写入、故障恢复和首读资格,不阻塞 UI 线程
- class PlcCommunicationService final : public QObject, public PlcCommunicationGateway
- {
- Q_OBJECT
-
- public:
- // 创建通信服务,并把 PLC 缓存仓库交给它管理
- explicit PlcCommunicationService(
- PlcRegisterRepository &repository,
- QObject *parent = nullptr);
- // 释放 Modbus 主站和未完成的通信资源
- ~PlcCommunicationService() override;
-
- // 按给定串口参数连接 PLC,并开始异步轮询
- PlcCommunicationResult connectDevice(
- const PlcSerialConfiguration &configuration) override;
- // 停止轮询并断开 PLC
- void disconnectDevice() override;
- // 设置需要周期性读取的 M/D 地址集合
- PlcCommunicationResult setPollAddresses(
- const std::vector<RegisterAddress> &addresses) override;
- PlcCommunicationResult setPollAddresses(
- const std::vector<RegisterAddress> &addresses,
- const std::vector<RegisterWordRange> &multi_word_ranges) override;
-
- // 返回当前连接状态
- PlcConnectionState state() const override;
- // 判断本次连接是否已经完成所有轮询块的首次读取
- bool initialReadCompleted() const override;
- // 返回最近一次通信错误的类型
- PlcCommunicationError lastErrorType() const override;
- // 返回最近一次通信错误的文字
- const std::string &lastError() const override;
- // 注册状态、首读、缓存更新和错误通知回调
- void setCallbacks(
- std::function<void()> state_changed,
- std::function<void(bool)> initial_read_changed,
- std::function<void()> cache_updated,
- std::function<void()> poll_cycle_completed,
- std::function<void(const std::string &)> error_reported) override;
-
- signals:
- // 连接状态发生变化
- void stateChanged();
- // 首次完整读取资格发生变化
- void initialReadCompletedChanged(bool completed);
- // PLC 缓存收到新的成功读数
- void cacheUpdated();
- // 全部轮询块成功更新一轮
- void pollCycleCompleted();
- // 通信错误的可读提示文字
- void communicationError(const QString &message);
-
- private:
- // 把去重后的地址集合压缩为有限数量的连续读块
- void rebuildPollBlocks();
- // 应用新的轮询集合;有请求在途时由 pending_* 延后应用
- void applyPollAddresses(const std::vector<RegisterAddress> &addresses);
- // 发送下一段异步读取请求
- void pollNextBlock();
- // Faulted 状态下用轻量探测判断通信是否恢复
- void probeRecovery();
- // 处理恢复探测失败,并安排下一次探测
- void handleRecoveryProbeFailure(QModbusDevice::Error error);
- // 探测成功后恢复轮询,但仍需重新完成首读
- void restoreCommunication();
- // 处理读回复并把值写入 PLC 缓存
- void handleReadFinished(QModbusReply *reply, PlcPollBlock block);
- // 发送单点 M/D 写请求;缓存等待后续轮询确认
- RegisterWriteResult sendBitWrite(const RegisterAddress &address, bool value);
- RegisterWriteResult sendWordWrite(
- const RegisterAddress &address, std::int16_t value);
- RegisterWriteResult sendWordsWrite(
- const RegisterAddress &address,
- const std::vector<std::int16_t> &values);
- // 更新“所有轮询块均成功读取”的真机进入资格
- void updateInitialReadCompleted(bool completed, bool force_notification = false);
- // 处理没有主动断开时发生的串口断线
- void handleUnexpectedDisconnect();
- // 把 Qt Modbus 错误转换成项目自己的通信错误
- void handleModbusError(QModbusDevice::Error error);
- // 关闭串口并清理当前连接的请求和状态
- void closeSerialSession();
- // 修改连接状态并通知外部观察者
- void setState(PlcConnectionState state);
- // 保存错误信息、更新状态并通知外部观察者
- void setError(const PlcCommunicationFailure &failure);
-
- PlcRegisterRepository &repository_; // 用于保存 PLC 最近一次成功读回的 M/D 值
- std::unique_ptr<QModbusRtuSerialMaster> master_; // Qt Modbus RTU 主站对象
- QTimer poll_timer_; // 周期性触发下一轮轮询
- QTimer recovery_timer_; // Faulted 状态下定时发起恢复探测
- PlcSerialConfiguration configuration_; // 当前串口和站号配置
- std::vector<RegisterAddress> poll_addresses_; // 当前生效的轮询地址
- std::vector<RegisterAddress> pending_poll_addresses_; // 请求在途时暂存的新地址
- std::vector<RegisterWordRange> poll_multi_word_ranges_; // 当前轮询集合中的多字范围
- std::vector<RegisterWordRange> pending_poll_multi_word_ranges_; // 请求在途时暂存的多字范围
- std::vector<PlcPollBlock> poll_blocks_; // 根据地址合并出的连续读块
- std::size_t next_poll_block_ = 0; // 下一次要读取的读块下标
- QModbusReply *pending_reply_ = nullptr; // 当前未完成的读请求或恢复探测
- QModbusReply *pending_write_reply_ = nullptr; // 当前未完成的单点写请求
- bool poll_update_pending_ = false; // 是否有等待请求完成后应用的新轮询集合
- bool disconnecting_ = false; // 是否正在执行主动断开
- bool serial_session_opened_ = false; // 本次串口会话是否曾经成功打开
- bool received_valid_response_ = false; // 本次连接是否收到过有效 PLC 回复
- std::uint64_t connection_generation_ = 0; // 连接代次,用于丢弃旧连接的异步回复
- PlcConnectionState state_ = PlcConnectionState::Disconnected; // 当前通信状态
- bool initial_read_completed_ = false; // 是否已完成首读,可作为真机运行门槛
- // 首读阶段每个读块的完成标记,全部为 true 后才允许进入真机
- std::vector<bool> initial_blocks_read_;
- PlcCommunicationError last_error_type_ = PlcCommunicationError::None; // 最近一次错误类型
- std::string last_error_; // 最近一次错误的可读文字
- std::function<void()> state_changed_callback_; // 状态变化时调用
- std::function<void(bool)> initial_read_changed_callback_; // 首读资格变化时调用
- std::function<void()> cache_updated_callback_; // 缓存更新后调用
- std::function<void()> poll_cycle_completed_callback_; // 完整轮询一轮后调用
- std::function<void(const std::string &)> error_reported_callback_; // 发生错误时调用
- };
|