mrDarker
6 天以前 829fe6c6bc33d53fda9c31fd45a37e1df87befff
SourceCode/Bond/SGMeasurement/PLCSignalListener.h
@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "CCLinkIEControl.h"
@@ -15,62 +15,184 @@
class CPLCSignalListener
{
public:
    CPLCSignalListener();
    ~CPLCSignalListener();
   CPLCSignalListener();
   ~CPLCSignalListener();
    bool Initialize(StationIdentifier station, int nIntervalMs = 200);
   /**
    * @brief 初始化 PLC 信号监听器。
    *
    * @param station      目标 PLC 的站号标识符。
    * @param nIntervalMs  轮询周期(单位:毫秒),默认 200ms。
    * @return true        初始化成功。
    * @return false       初始化失败。
    */
   bool Initialize(StationIdentifier station, int nIntervalMs = 200);
    void SetStartCallback(Callback cb);
    void SetStopCallback(Callback cb);
    void SetAnalyzeCallback(AnalyzeCallback cb);
    void SetLogCallback(LogCallback cb);
   /**
    * @brief 设置开始命令的回调函数(对应 PLC 的 Start 信号)。
    *
    * @param cb  用户自定义的开始回调函数。
    */
   void SetStartCallback(Callback cb);
    bool Start();
    void Stop();
   /**
    * @brief 设置停止命令的回调函数(对应 PLC 的 Stop 信号)。
    *
    * @param cb  用户自定义的停止回调函数。
    */
   void SetStopCallback(Callback cb);
    bool WriteOutValues(const OutValuesArray& values);
   /**
    * @brief 设置分析计算回调函数,在接收到停止命令后调用。
    *
    * @param cb  返回计算结果数组(OUT1~OUT4)的函数。
    */
   void SetAnalyzeCallback(AnalyzeCallback cb);
   /**
    * @brief 设置日志输出回调函数。
    *
    * @param cb  用户提供的日志输出接口(包含日志内容和日志类型)。
    */
   void SetLogCallback(LogCallback cb);
   /**
    * @brief 启动信号监听线程。
    *
    * @return true   启动成功。
    * @return false  启动失败(可能已运行或未初始化)。
    */
   bool Start();
   /**
    * @brief 停止信号监听线程。
    */
   void Stop();
   /**
    * @brief 向 PLC 写入分析结果值(通常为 OUT1 ~ OUT4)。
    *
    * @param values  包含四个 double 类型的结果值,将转换为整数后写入 PLC。
    * @return true   写入成功。
    * @return false  写入失败。
    */
   bool WriteOutValues(const OutValuesArray& values);
   /**
    * @brief 读取 PLC 内部的产品 ID(字符串形式)。
    *
    * @param strProductID  输出参数,存储读取到的产品 ID。
    * @return true         读取成功。
    * @return false        读取失败。
    */
   bool ReadProductID(std::string& strProductID);
   /**
    * @brief 将产品 ID(字符串形式)写入 PLC 内部。
    *
    * @param strProductID  输入参数,要写入的产品 ID。
    *                      字符串会按字节对齐,每两个字节组成一个 Word,
    *                      写入 PLC 的指定寄存器区域,不足时自动补零。
    * @return true         写入成功。
    * @return false        写入失败。
    */
   bool WriteProductID(const std::string& strProductID);
private:
    void LogInfo(const CString& strText, int nType);
    bool SendHeartbeat();
    bool CheckHeartbeat();
    bool MonitorHeartbeat();
    void StartHeartbeatMonitor();
    void StopHeartbeatMonitor();
    void PulseBitDevice(DeviceType eDevType, long nBitNo, int nDelayMs = 50);
    void HandleAckLife(int i, bool bCurrTriggerBit);
    void ThreadProc();
   /**
    * @brief 输出日志信息(封装日志回调)。
    *
    * @param strText 日志内容文本。
    * @param nType   日志类型(LOG_TYPE_NORMAL / ERROR / WARNING 等)。
    */
   void LogInfo(const CString& strText, int nType);
   /**
    * @brief 向 PLC 写入心跳信号(交替位)。
    *
    * @return true  写入成功。
    * @return false 写入失败(如未连接)。
    */
   bool SendHeartbeat();
   /**
    * @brief 检查从 PLC 读取到的心跳信号是否发生变化。
    *
    * @return true  心跳有变化(PLC 正常在线)。
    * @return false 心跳无变化(可能离线)。
    */
   bool CheckHeartbeat();
   /**
    * @brief 监控 PLC 心跳是否中断,并自动记录状态。
    *
    * @return true  PLC 在线或在允许的未响应次数内。
    * @return false PLC 心跳中断,超过允许阈值。
    */
   bool MonitorHeartbeat();
   /**
    * @brief 启动心跳监控线程(独立于信号监听线程)。
    */
   void StartHeartbeatMonitor();
   /**
    * @brief 停止心跳监控线程。
    */
   void StopHeartbeatMonitor();
   /**
    * @brief 向指定软元件位写入一个脉冲(先写1,延时后自动写0)。
    *
    * @param eDevType 位软元件类型(如 M/B)。
    * @param nBitNo   位地址编号。
    * @param nDelayMs 脉冲持续时间,默认50毫秒。
    */
   void PulseBitDevice(DeviceType eDevType, long nBitNo, int nDelayMs = 50);
   /**
    * @brief 管理 ACK 响应的生命周期,超时自动清除。
    *
    * @param i                控制位索引(0=Start,1=Stop)。
    * @param bCurrTriggerBit  当前从 PLC 读取到的触发位状态。
    */
   void HandleAckLife(int i, bool bCurrTriggerBit);
   /**
    * @brief 主监听线程逻辑,循环读取 PLC 控制信号并处理触发。
    */
   void ThreadProc();
private:
    // === PLC 通信核心对象 ===
    std::unique_ptr<CCCLinkIEControl> m_pPlc; // PLC 通信控制器
    StationIdentifier m_station;              // PLC 站号
    std::atomic<bool> m_bConnected{ false };  // 是否成功连接
   // === PLC 通信核心对象 ===
   std::unique_ptr<CCCLinkIEControl> m_pPlc; // PLC 通信控制器
   StationIdentifier m_station;              // PLC 站号
   std::atomic<bool> m_bConnected{ false };  // 是否成功连接
    // === 控制参数 ===
    int m_nIntervalMs = 200;                  // 轮询周期(ms)
   // === 控制参数 ===
   int m_nIntervalMs = 200;                  // 轮询周期(ms)
    // === 命令触发状态缓存 ===
    std::vector<bool> m_vecPrevBits;          // 上一周期的命令位状态(用于检测上升沿)
   // === 命令触发状态缓存 ===
   std::vector<bool> m_vecPrevBits;          // 上一周期的命令位状态(用于检测上升沿)
    // === 回调函数(控制/计算/日志)===
    Callback m_cbStart;                       // Start 命令回调
    Callback m_cbStop;                        // Stop 命令回调
    AnalyzeCallback m_cbAnalyze;              // Analyze 计算回调(返回 OUT1~OUT4)
    LogCallback m_cbLog;                      // 日志输出回调
   // === 回调函数(控制/计算/日志)===
   Callback m_cbStart;                       // Start 命令回调
   Callback m_cbStop;                        // Stop 命令回调
   AnalyzeCallback m_cbAnalyze;              // Analyze 计算回调(返回 OUT1~OUT4)
   LogCallback m_cbLog;                      // 日志输出回调
    // === 主线程控制 ===
    std::atomic<bool> m_bRunning{ false };    // 主监听线程是否运行
    std::thread m_thread;                     // 主监听线程对象
   // === 主线程控制 ===
   std::atomic<bool> m_bRunning{ false };    // 主监听线程是否运行
   std::thread m_thread;                     // 主监听线程对象
    // === ACK 信号状态 ===
    std::array<bool, 2> m_vecAckSent = { false, false }; // 是否已发送应答信号(B10/B11)
    std::array<int, 2>  m_vecAckCounter = { 0, 0 };      // 对应应答信号的生命周期计数器
   // === ACK 信号状态 ===
   std::array<bool, 2> m_vecAckSent = { false, false }; // 是否已发送应答信号(B10/B11)
   std::array<int, 2>  m_vecAckCounter = { 0, 0 };      // 对应应答信号的生命周期计数器
    // === 心跳检测相关 ===
    std::thread m_heartbeatThread;                  // 心跳检测线程对象
    std::atomic<bool> m_bHeartbeatRunning = false;  // 心跳线程运行标志
   std::atomic<bool> m_bHeartbeatLost = false;     // 心跳丢失标志
    int m_nMissedHeartbeatCount = 0;                // 心跳未变化次数(用于检测 PLC 掉线)
   // === 心跳检测相关 ===
   std::thread m_heartbeatThread;                  // 心跳检测线程对象
   std::atomic<bool> m_bHeartbeatRunning = false;  // 心跳线程运行标志
   std::atomic<bool> m_bHeartbeatLost = false;     // 心跳丢失标志
   int m_nMissedHeartbeatCount = 0;                // 心跳未变化次数(用于检测 PLC 掉线)
};