From 8d55ed4a167d9b09d65e4a26ece287c6e37a9c84 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期五, 12 九月 2025 10:58:58 +0800
Subject: [PATCH] 1. 修复SG精度检编辑框修改覆盖问题(界面频繁刷新导致)

---
 SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp |  247 ++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 200 insertions(+), 47 deletions(-)

diff --git a/SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp b/SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp
index 27ad0c9..1de8be5 100644
--- a/SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp
+++ b/SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp
@@ -7,17 +7,33 @@
 #define LOG_TYPE_WARNING    1
 #define LOG_TYPE_NORMAL     2
 
-// === PLC 控制命令输入位配置 ===
-#define PLC_CMD_BIT_START       0     // PLC命令起始位(通常为B0)
-#define PLC_CMD_BIT_COUNT       2     // 总共几个命令位(B0=Start, B1=Stop)
+// === 日志打印宏定义 ===
+#define LOG_MSG(msg, type) LogInfo(msg, type)
 
-// === PLC 信号监听器相关宏定义 ===
-#define PLC_ACK_MAX_LIFE        5     // PLC响应信号最大保留周期数(每周期为 m_nIntervalMs 毫秒)
-#define PLC_ACK_BASE_BIT        10    // PLC应答起始地址(B10表示B0的应答;B11表示B1)
+// === PLC 心跳相关配置 ===
+#define PLC_HEARTBEAT_PC_TO_PLC_ADDR   0x107F   // PC -> PLC:PC 写入心跳
+#define PLC_HEARTBEAT_PLC_TO_PC_ADDR   0x6C40   // PLC -> PC:PC 读取 PLC 写入的心跳
+#define MAX_MISSED_HEARTBEAT           5        // 允许连续丢失心跳的最大次数,超过则判定 PLC 掉线
+
+// === PLC 命令输入配置(PLC -> PC) ===
+#define PLC_CMD_BIT_START       0x6CD3  // PLC命令起始位(通常为B6CD3)
+#define PLC_CMD_BIT_COUNT       2       // 总共几个命令位(B6CD3=Start, B6CD4=Stop)
+
+// === PLC 应答输出配置(PC -> PLC) ===
+#define PLC_ACK_MAX_LIFE        25      // PLC响应信号最大保留周期数(每周期为 m_nIntervalMs 毫秒)
+#define PLC_ACK_BASE_BIT        0x1060  // PLC应答起始地址(B1060表示B6CD3的应答;B1061表示B6CD4的应答)
 
 // === PLC软元件类型宏(用于应答、数据写入)===
-#define PLC_BIT_DEVICE_TYPE    DeviceType::B   // 位操作设备类型(如M、B)
-#define PLC_WORD_DEVICE_TYPE   DeviceType::W   // 字操作设备类型(如D、W)
+#define PLC_BIT_DEVICE_TYPE     DeviceType::B   // 位操作设备类型(如M、B)
+#define PLC_WORD_DEVICE_TYPE    DeviceType::W   // 字操作设备类型(如D、W)
+
+// === PLC结果寄存器地址配置 ===
+#define PLC_RESULT_ADDR_START   0x37B0  // PLC结果寄存器起始地址(如W37B0)
+#define PLC_RESULT_ADDR_COUNT   4       // 结果寄存器数量(如W37B0, W37B2, W37B4, W37B6)
+
+// === PLC 产品ID配置(PLC -> PC)===
+#define PLC_PRODUCT_ID_ADDR      0x1B160   // 产品ID起始地址 (W1B160)
+#define PLC_PRODUCT_ID_WORDS     10        // 产品ID长度(10个Word)
 
 #define IS_RISING_EDGE(prev, curr) (!(prev) && (curr))
 
@@ -31,24 +47,22 @@
 {
     m_pPlc = std::make_unique<CCCLinkIEControl>();
     if (!m_pPlc) {
-		if (m_cbLog) {
-			m_cbLog(_T("PLC控制器初始化失败,无法创建 CCCLinkIEControl 实例。"), LOG_TYPE_ERROR);
-		}
+        LOG_MSG(_T("PLC控制器初始化失败,无法创建 CCCLinkIEControl 实例。"), LOG_TYPE_ERROR);
         return false;
     }
 
     int ret = m_pPlc->Connect(CC_LINK_IE_CONTROL_CHANNEL(1));
     if (ret != 0) {
-		m_bConnected = false;
-		if (m_cbLog) {
-			CString strError;
-			strError.Format(_T("PLC控制器连接失败,错误码:%d"), ret);
-			m_cbLog(strError, LOG_TYPE_ERROR);
-		}
+        m_bConnected = false;
+
+        CString strError;
+        strError.Format(_T("PLC控制器连接失败,错误码:%d"), ret);
+        LOG_MSG(strError, LOG_TYPE_ERROR);
+
         return false;
     }
 
-	m_bConnected = true;
+    m_bConnected = true;
     m_station = station;
     m_nIntervalMs = nIntervalMs;
 
@@ -80,14 +94,14 @@
 bool CPLCSignalListener::Start()
 {
     if (m_bRunning || !m_pPlc) {
-		if (m_cbLog) {
-			m_cbLog(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR);
-		}
+        LOG_MSG(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR);
         return false;
     }
 
     m_bRunning = true;
     m_thread = std::thread(&CPLCSignalListener::ThreadProc, this);
+
+    StartHeartbeatMonitor();
     return true;
 }
 
@@ -97,25 +111,117 @@
     if (m_thread.joinable()) {
         m_thread.join();
     }
+
+    StopHeartbeatMonitor();
 }
 
-void CPLCSignalListener::PulseBitDevice(DeviceType eDevType, short nBitNo, int nDelayMs/* = 50*/)
+void CPLCSignalListener::LogInfo(const CString& strText, int nType)
 {
-    m_pPlc->SetBitDevice(m_station, eDevType, nBitNo);
+    if (m_cbLog) {
+        m_cbLog(strText, nType);
+    }
+}
+
+bool CPLCSignalListener::SendHeartbeat()
+{
+    if (!m_pPlc || !m_bConnected) {
+        return false;
+    }
+
+    static bool bToggle = false;
+    bToggle = !bToggle;
+
+    int ret = m_pPlc->WriteBitDataEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_HEARTBEAT_PC_TO_PLC_ADDR, BitContainer{ bToggle });
+
+    return (ret == 0);
+}
+
+bool CPLCSignalListener::CheckHeartbeat()
+{
+    static bool bLastHeartbeat = false;
+
+    if (!m_pPlc || !m_bConnected) {
+        return false;
+    }
+
+    BitContainer vec;
+    int ret = m_pPlc->ReadBitDataEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_HEARTBEAT_PLC_TO_PC_ADDR, 1, vec);
+    if (ret != 0 || vec.empty()) {
+        return false;
+    }
+
+    bool bCurrent = vec[0];
+    bool bChanged = (bCurrent != bLastHeartbeat);
+    bLastHeartbeat = bCurrent;
+
+    return bChanged;
+}
+
+bool CPLCSignalListener::MonitorHeartbeat()
+{
+    if (CheckHeartbeat()) {
+        m_nMissedHeartbeatCount = 0;
+
+        if (m_bHeartbeatLost) {
+            m_bHeartbeatLost = false;
+            LOG_MSG(_T("PLC心跳恢复!"), LOG_TYPE_SUCCESS);
+        }
+
+        return true;
+    }
+    else {
+        m_nMissedHeartbeatCount++;
+
+        if (m_nMissedHeartbeatCount > MAX_MISSED_HEARTBEAT) {
+            if (!m_bHeartbeatLost) {
+                m_bHeartbeatLost = true;
+				m_nMissedHeartbeatCount = 0;
+                LOG_MSG(_T("PLC心跳信号中断!"), LOG_TYPE_ERROR);
+            }
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void CPLCSignalListener::StartHeartbeatMonitor()
+{
+    m_bHeartbeatRunning = true;
+    m_heartbeatThread = std::thread([this]() {
+        while (m_bHeartbeatRunning) {
+            SendHeartbeat();
+            MonitorHeartbeat();
+            std::this_thread::sleep_for(std::chrono::milliseconds(m_nIntervalMs * 5));
+        }
+    });
+}
+
+void CPLCSignalListener::StopHeartbeatMonitor()
+{
+    m_bHeartbeatRunning = false;
+    if (m_heartbeatThread.joinable()) {
+        m_heartbeatThread.join();
+    }
+}
+
+void CPLCSignalListener::PulseBitDevice(DeviceType eDevType, long nBitNo, int nDelayMs/* = 50*/)
+{
+    m_pPlc->SetBitDeviceEx(m_station, eDevType, nBitNo);
     ::Sleep(nDelayMs);
-    m_pPlc->ResetBitDevice(m_station, eDevType, nBitNo);
+    m_pPlc->ResetBitDeviceEx(m_station, eDevType, nBitNo);
 }
 
 void CPLCSignalListener::HandleAckLife(int i, bool bCurrTriggerBit)
 {
     if (m_vecAckSent[i] && !bCurrTriggerBit) {
-        m_pPlc->ResetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, short(PLC_ACK_BASE_BIT + i));
+        m_pPlc->ResetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, long(PLC_ACK_BASE_BIT + i));
         m_vecAckSent[i] = false;
     }
 
     if (m_vecAckSent[i]) {
         if (++m_vecAckCounter[i] > PLC_ACK_MAX_LIFE) {
-            m_pPlc->ResetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, short(PLC_ACK_BASE_BIT + i));
+            m_pPlc->ResetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, long(PLC_ACK_BASE_BIT + i));
             m_vecAckSent[i] = false;
         }
     }
@@ -123,19 +229,17 @@
 
 void CPLCSignalListener::ThreadProc()
 {
-    while (m_bRunning) {
+    while (m_bRunning && m_bConnected) {
         BitContainer vecBits;
-        int ret = m_pPlc->ReadBitData(m_station, PLC_BIT_DEVICE_TYPE, PLC_CMD_BIT_START, PLC_CMD_BIT_COUNT, vecBits);
-        if (ret != 0/*&& vecBits.size() != PLC_CMD_BIT_COUNT*/) {
-			::Sleep(m_nIntervalMs);
+        int ret = m_pPlc->ReadBitDataEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_CMD_BIT_START, PLC_CMD_BIT_COUNT, vecBits);
+        if (ret != 0 && vecBits.size() != PLC_CMD_BIT_COUNT) {
+            ::Sleep(m_nIntervalMs);
 
-			if (m_cbLog) {
-				CString strError;
-				strError.Format(_T("PLC读取位数据失败,错误码:%d"), ret);
-				m_cbLog(strError, LOG_TYPE_ERROR);
-			}
+            CString strError;
+            strError.Format(_T("PLC读取位数据失败,错误码:%d"), ret);
+            LOG_MSG(strError, LOG_TYPE_ERROR);
 
-			continue;
+            continue;
         }
 
         for (int i = 0; i < PLC_CMD_BIT_COUNT; ++i) {
@@ -146,9 +250,16 @@
                     if (m_cbStart) {
                         m_cbStart();
                         WriteOutValues(OutValuesArray{ 0.0, 0.0, 0.0, 0.0 });
-                        if (m_pPlc->SetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) {
+                        if (m_pPlc->SetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) {
                             m_vecAckSent[i] = true;
                             m_vecAckCounter[i] = 0;
+                        }
+
+                        std::string strProductID;
+                        if (ReadProductID(strProductID)) {
+                            CString msg;
+                            msg.Format(_T("读取到产品ID:%s"), strProductID);
+                            LOG_MSG(msg, LOG_TYPE_SUCCESS);
                         }
                     }
                     break;
@@ -156,7 +267,7 @@
                 case 1:
                     if (m_cbStop) {
                         m_cbStop();
-                        if (m_pPlc->SetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) {
+                        if (m_pPlc->SetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) {
                             m_vecAckSent[i] = true;
                             m_vecAckCounter[i] = 0;
                         }
@@ -180,24 +291,66 @@
 
 bool CPLCSignalListener::WriteOutValues(const OutValuesArray& values)
 {
-    if (!m_pPlc) {
-		if (m_cbLog) {
-			m_cbLog(_T("PLC控制器未初始化,无法写入输出值。"), LOG_TYPE_ERROR);
-		}
+    if (!m_pPlc || !m_bConnected) {
+        LOG_MSG(_T("PLC未连接或未初始化,无法写入输出值。"), LOG_TYPE_ERROR);
         return false;
     }
 
-    static const short PLC_RESULT_ADDR[4] = { 100, 102, 104, 106 };
+    if (PLC_RESULT_ADDR_COUNT != 4) {
+        LOG_MSG(_T("PLC结果寄存器数量配置错误,必须为4个。"), LOG_TYPE_ERROR);
+        return false;
+    }
 
-    for (int i = 0; i < 4; ++i) {
-        uint16_t nScaled = static_cast<uint16_t>(std::round(values[i] * 100.0));
-        WordContainer vec = { nScaled };
+    for (int i = 0; i < PLC_RESULT_ADDR_COUNT; ++i) {
+        // 放大1000倍并四舍五入,转为PLC整数
+        int32_t  nScaled = static_cast<int32_t>(std::round(values[i] * 1000.0));
+        DWordContainer vec = { static_cast<uint32_t>(nScaled) };
 
-        int ret = m_pPlc->WriteWordData(m_station, PLC_WORD_DEVICE_TYPE, PLC_RESULT_ADDR[i], vec);
+        short nTargetAddr = PLC_RESULT_ADDR_START + i * 2;
+        int ret = m_pPlc->WriteDWordDataEx(m_station, PLC_WORD_DEVICE_TYPE, nTargetAddr, vec);
         if (ret != 0) {
+            CString msg;
+            msg.Format(_T("写入OUT%d到地址%d失败,值=%.2f"), i + 1, nTargetAddr, values[i]);
+            LOG_MSG(msg, LOG_TYPE_ERROR);
             return false;
         }
     }
 
     return true;
+}
+
+bool CPLCSignalListener::ReadProductID(std::string& strProductID)
+{
+    if (!m_pPlc || !m_bConnected) {
+        LOG_MSG(_T("PLC未连接或未初始化,无法读取产品ID。"), LOG_TYPE_ERROR);
+        return false;
+    }
+
+    WordContainer vec;
+    int ret = m_pPlc->ReadWordDataEx(m_station, PLC_WORD_DEVICE_TYPE, PLC_PRODUCT_ID_ADDR, PLC_PRODUCT_ID_WORDS, vec);
+    if (ret != 0 || vec.size() != PLC_PRODUCT_ID_WORDS) {
+        CString msg;
+        msg.Format(_T("读取产品ID失败,错误码=%d"), ret);
+        LOG_MSG(msg, LOG_TYPE_ERROR);
+        return false;
+    }
+
+    strProductID.clear();
+	strProductID.reserve(PLC_PRODUCT_ID_WORDS * 2);
+    for (auto w : vec) {
+        char c1 = static_cast<char>(w & 0xFF);          // 低字节
+        char c2 = static_cast<char>((w >> 8) & 0xFF);   // 高字节
+
+        if (c1 == '\0') { 
+            break;
+        }
+        strProductID.push_back(c1);
+
+        if (c2 == '\0') { 
+            break;
+        }
+        strProductID.push_back(c2);
+    }
+
+    return true;
 }
\ No newline at end of file

--
Gitblit v1.9.3