chenluhua1980
2026-01-24 8fc148424accf484b4f331c7d5fb11eb7383cf89
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#pragma once
 
#include "CCLinkIEControl.h"
 
#include <thread>
#include <atomic>
#include <functional>
#include <vector>
 
using OutValuesArray = std::array<double, 4>;
using Callback = std::function<void()>;
using AnalyzeCallback = std::function<OutValuesArray()>;
using LogCallback = std::function<void(const CString& strContent, int type)>;
 
class CPLCSignalListener
{
public:
    CPLCSignalListener();
    ~CPLCSignalListener();
 
    /**
     * @brief 初始化 PLC 信号监听器。
     *
     * @param station      目标 PLC 的站号标识符。
     * @param nIntervalMs  轮询周期(单位:毫秒),默认 200ms。
     * @return true        初始化成功。
     * @return false       初始化失败。
     */
    bool Initialize(StationIdentifier station, int nIntervalMs = 200);
 
    /**
     * @brief 设置开始命令的回调函数(对应 PLC 的 Start 信号)。
     *
     * @param cb  用户自定义的开始回调函数。
     */
    void SetStartCallback(Callback cb);
 
    /**
     * @brief 设置停止命令的回调函数(对应 PLC 的 Stop 信号)。
     *
     * @param cb  用户自定义的停止回调函数。
     */
    void SetStopCallback(Callback cb);
 
    /**
     * @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);
 
private:
    /**
     * @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 };  // 是否成功连接
 
    // === 控制参数 ===
    int m_nIntervalMs = 200;                  // 轮询周期(ms)
 
    // === 命令触发状态缓存 ===
    std::vector<bool> m_vecPrevBits;          // 上一周期的命令位状态(用于检测上升沿)
 
    // === 回调函数(控制/计算/日志)===
    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;                     // 主监听线程对象
 
    // === 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 掉线)
};