chenluhua1980
2026-01-24 fbc7719e6c3386418014b92c033d84a3c930e58b
SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp
@@ -1,309 +1,363 @@
#include "pch.h"
#include "pch.h"
#include "PLCSignalListener.h"
// === 日志打印类型 ===
// === 日志打印类型 ===
#define LOG_TYPE_ERROR     -1
#define LOG_TYPE_SUCCESS    0
#define LOG_TYPE_WARNING    1
#define LOG_TYPE_NORMAL     2
// === 日志打印宏定义 ===
// === 日志打印宏定义 ===
#define LOG_MSG(msg, type) LogInfo(msg, type)
// === 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 心跳相关配置 ===
#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 命令输入配置(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        5       // PLC响应信号最大保留周期数(每周期为 m_nIntervalMs 毫秒)
#define PLC_ACK_BASE_BIT        0x1060  // PLC应答起始地址(B1060表示B6CD3的应答;B1061表示B6CD4的应答)
// === 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)
// === PLC软元件类型宏(用于应答、数据写入)===
#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结果寄存器地址配置 ===
#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))
CPLCSignalListener::CPLCSignalListener() = default;
CPLCSignalListener::~CPLCSignalListener() {
    Stop();
   Stop();
}
bool CPLCSignalListener::Initialize(StationIdentifier station, int nIntervalMs/* = 200*/)
{
    m_pPlc = std::make_unique<CCCLinkIEControl>();
    if (!m_pPlc) {
        LOG_MSG(_T("PLC控制器初始化失败,无法创建 CCCLinkIEControl 实例。"), LOG_TYPE_ERROR);
        return false;
    }
   m_pPlc = std::make_unique<CCCLinkIEControl>();
   if (!m_pPlc) {
      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;
   int ret = m_pPlc->Connect(CC_LINK_IE_CONTROL_CHANNEL(1));
   if (ret != 0) {
      m_bConnected = false;
        CString strError;
        strError.Format(_T("PLC控制器连接失败,错误码:%d"), ret);
        LOG_MSG(strError, LOG_TYPE_ERROR);
      CString strError;
      strError.Format(_T("PLC控制器连接失败,错误码:%d"), ret);
      LOG_MSG(strError, LOG_TYPE_ERROR);
        return false;
    }
      return false;
   }
    m_bConnected = true;
    m_station = station;
    m_nIntervalMs = nIntervalMs;
   m_bConnected = true;
   m_station = station;
   m_nIntervalMs = nIntervalMs;
    m_vecPrevBits.assign(PLC_CMD_BIT_COUNT, false);
   m_vecPrevBits.assign(PLC_CMD_BIT_COUNT, false);
    return true;
   return true;
}
void CPLCSignalListener::SetStartCallback(Callback cb)
{
    m_cbStart = std::move(cb);
   m_cbStart = std::move(cb);
}
void CPLCSignalListener::SetStopCallback(Callback cb)
{
    m_cbStop = std::move(cb);
   m_cbStop = std::move(cb);
}
void CPLCSignalListener::SetAnalyzeCallback(AnalyzeCallback cb)
{
    m_cbAnalyze = std::move(cb);
   m_cbAnalyze = std::move(cb);
}
void CPLCSignalListener::SetLogCallback(LogCallback cb)
{
    m_cbLog = std::move(cb);
   m_cbLog = std::move(cb);
}
bool CPLCSignalListener::Start()
{
    if (m_bRunning || !m_pPlc) {
        LOG_MSG(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR);
        return false;
    }
   if (m_bRunning || !m_pPlc) {
      LOG_MSG(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR);
      return false;
   }
    m_bRunning = true;
    m_thread = std::thread(&CPLCSignalListener::ThreadProc, this);
   m_bRunning = true;
   m_thread = std::thread(&CPLCSignalListener::ThreadProc, this);
    StartHeartbeatMonitor();
    return true;
   StartHeartbeatMonitor();
   return true;
}
void CPLCSignalListener::Stop()
{
    m_bRunning = false;
    if (m_thread.joinable()) {
        m_thread.join();
    }
   m_bRunning = false;
   if (m_thread.joinable()) {
      m_thread.join();
   }
    StopHeartbeatMonitor();
   StopHeartbeatMonitor();
}
void CPLCSignalListener::LogInfo(const CString& strText, int nType)
{
    if (m_cbLog) {
        m_cbLog(strText, nType);
    }
   if (m_cbLog) {
      m_cbLog(strText, nType);
   }
}
bool CPLCSignalListener::SendHeartbeat()
{
    if (!m_pPlc || !m_bConnected) {
        return false;
    }
   if (!m_pPlc || !m_bConnected) {
      return false;
   }
    static bool bToggle = false;
    bToggle = !bToggle;
   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 });
   int ret = m_pPlc->WriteBitDataEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_HEARTBEAT_PC_TO_PLC_ADDR, BitContainer{ bToggle });
    return (ret == 0);
   return (ret == 0);
}
bool CPLCSignalListener::CheckHeartbeat()
{
    static bool bLastHeartbeat = false;
   static bool bLastHeartbeat = false;
    if (!m_pPlc || !m_bConnected) {
        return 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;
    }
   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;
   bool bCurrent = vec[0];
   bool bChanged = (bCurrent != bLastHeartbeat);
   bLastHeartbeat = bCurrent;
    return bChanged;
   return bChanged;
}
bool CPLCSignalListener::MonitorHeartbeat()
{
    if (CheckHeartbeat()) {
        m_nMissedHeartbeatCount = 0;
   if (CheckHeartbeat()) {
      m_nMissedHeartbeatCount = 0;
        if (m_bHeartbeatLost) {
            m_bHeartbeatLost = false;
            LOG_MSG(_T("PLC心跳恢复!"), LOG_TYPE_SUCCESS);
        }
      if (m_bHeartbeatLost) {
         m_bHeartbeatLost = false;
         LOG_MSG(_T("PLC心跳恢复!"), LOG_TYPE_SUCCESS);
      }
        return true;
    }
    else {
        m_nMissedHeartbeatCount++;
      return true;
   }
   else {
      m_nMissedHeartbeatCount++;
        if (m_nMissedHeartbeatCount > MAX_MISSED_HEARTBEAT) {
            if (!m_bHeartbeatLost) {
                m_bHeartbeatLost = true;
      if (m_nMissedHeartbeatCount > MAX_MISSED_HEARTBEAT) {
         if (!m_bHeartbeatLost) {
            m_bHeartbeatLost = true;
            m_nMissedHeartbeatCount = 0;
                LOG_MSG(_T("PLC心跳信号中断!"), LOG_TYPE_ERROR);
            }
            return false;
        }
    }
            LOG_MSG(_T("PLC心跳信号中断!"), LOG_TYPE_ERROR);
         }
         return false;
      }
   }
    return true;
   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));
        }
    });
   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();
    }
   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->ResetBitDeviceEx(m_station, eDevType, nBitNo);
   m_pPlc->SetBitDeviceEx(m_station, eDevType, nBitNo);
   ::Sleep(nDelayMs);
   m_pPlc->ResetBitDeviceEx(m_station, eDevType, nBitNo);
}
void CPLCSignalListener::HandleAckLife(int i, bool bCurrTriggerBit)
{
    if (m_vecAckSent[i] && !bCurrTriggerBit) {
        m_pPlc->ResetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, long(PLC_ACK_BASE_BIT + i));
        m_vecAckSent[i] = false;
    }
   if (m_vecAckSent[i] && !bCurrTriggerBit) {
      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->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->ResetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, long(PLC_ACK_BASE_BIT + i));
         m_vecAckSent[i] = false;
      }
   }
}
void CPLCSignalListener::ThreadProc()
{
    while (m_bRunning && m_bConnected) {
        BitContainer vecBits;
        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);
   while (m_bRunning && m_bConnected) {
      BitContainer vecBits;
      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);
            CString strError;
            strError.Format(_T("PLC读取位数据失败,错误码:%d"), ret);
            LOG_MSG(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) {
            if (IS_RISING_EDGE(m_vecPrevBits[i], vecBits[i])) {
                // 上升沿触发
                switch (i) {
                case 0:
                    if (m_cbStart) {
                        m_cbStart();
                        WriteOutValues(OutValuesArray{ 0.0, 0.0, 0.0, 0.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;
                        }
                    }
                    break;
      for (int i = 0; i < PLC_CMD_BIT_COUNT; ++i) {
         if (IS_RISING_EDGE(m_vecPrevBits[i], vecBits[i])) {
            // 上升沿触发
            switch (i) {
            case 0:
               // Start 命令
               if (m_cbStart) {
                  m_cbStart();
                  WriteOutValues(OutValuesArray{ 0.0, 0.0, 0.0, 0.0 });
                case 1:
                    if (m_cbStop) {
                        m_cbStop();
                        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);
                  }
               }
                    if (m_cbAnalyze) {
                        auto results = m_cbAnalyze();
                        WriteOutValues(results);
                    }
                    break;
                }
            }
               // 发送应答信号
               if (m_pPlc->SetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) {
                  m_vecAckSent[i] = true;
                  m_vecAckCounter[i] = 0;
               }
               break;
            HandleAckLife(i, vecBits[i]);
            m_vecPrevBits[i] = vecBits[i];
        }
            case 1:
               // Stop 命令
               if (m_cbStop) {
                  m_cbStop();
               }
        ::Sleep(m_nIntervalMs);
    }
               // Analyze 命令
               if (m_cbAnalyze) {
                  auto results = m_cbAnalyze();
                  WriteOutValues(results);
               }
               // 发送应答信号
               if (m_pPlc->SetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) {
                  m_vecAckSent[i] = true;
                  m_vecAckCounter[i] = 0;
               }
               break;
            }
         }
         HandleAckLife(i, vecBits[i]);
         m_vecPrevBits[i] = vecBits[i];
      }
      ::Sleep(m_nIntervalMs);
   }
}
bool CPLCSignalListener::WriteOutValues(const OutValuesArray& values)
{
    if (!m_pPlc || !m_bConnected) {
        LOG_MSG(_T("PLC未连接或未初始化,无法写入输出值。"), LOG_TYPE_ERROR);
        return false;
    }
   if (!m_pPlc || !m_bConnected) {
      LOG_MSG(_T("PLC未连接或未初始化,无法写入输出值。"), LOG_TYPE_ERROR);
      return false;
   }
    if (PLC_RESULT_ADDR_COUNT != 4) {
        LOG_MSG(_T("PLC结果寄存器数量配置错误,必须为4个。"), LOG_TYPE_ERROR);
        return false;
    }
   if (PLC_RESULT_ADDR_COUNT != 4) {
      LOG_MSG(_T("PLC结果寄存器数量配置错误,必须为4个。"), LOG_TYPE_ERROR);
      return false;
   }
    for (int i = 0; i < PLC_RESULT_ADDR_COUNT; ++i) {
        // 放大100倍并四舍五入,转为PLC整数
        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) };
        short nTargetAddr = PLC_RESULT_ADDR_START + i * 2;
        int ret = m_pPlc->WriteWordDataEx(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;
        }
    }
      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;
   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;
}