#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 ÃüÁîÊäÈëÅäÖã¨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ÈíÔª¼þÀàÐͺ꣨ÓÃÓÚÓ¦´ð¡¢Êý¾ÝдÈ룩=== #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£© #define IS_RISING_EDGE(prev, curr) (!(prev) && (curr)) CPLCSignalListener::CPLCSignalListener() = default; CPLCSignalListener::~CPLCSignalListener() { Stop(); } bool CPLCSignalListener::Initialize(StationIdentifier station, int nIntervalMs/* = 200*/) { m_pPlc = std::make_unique(); 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; CString strError; strError.Format(_T("PLC¿ØÖÆÆ÷Á¬½Óʧ°Ü£¬´íÎóÂ룺%d"), ret); LOG_MSG(strError, LOG_TYPE_ERROR); return false; } m_bConnected = true; m_station = station; m_nIntervalMs = nIntervalMs; m_vecPrevBits.assign(PLC_CMD_BIT_COUNT, false); return true; } void CPLCSignalListener::SetStartCallback(Callback cb) { m_cbStart = std::move(cb); } void CPLCSignalListener::SetStopCallback(Callback cb) { m_cbStop = std::move(cb); } void CPLCSignalListener::SetAnalyzeCallback(AnalyzeCallback cb) { m_cbAnalyze = std::move(cb); } void CPLCSignalListener::SetLogCallback(LogCallback cb) { m_cbLog = std::move(cb); } bool CPLCSignalListener::Start() { 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); StartHeartbeatMonitor(); return true; } void CPLCSignalListener::Stop() { m_bRunning = false; if (m_thread.joinable()) { m_thread.join(); } StopHeartbeatMonitor(); } void CPLCSignalListener::LogInfo(const CString& strText, int nType) { 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->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]) { 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); CString strError; strError.Format(_T("PLC¶ÁȡλÊý¾Ýʧ°Ü£¬´íÎóÂ룺%d"), ret); LOG_MSG(strError, LOG_TYPE_ERROR); 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; 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; } } if (m_cbAnalyze) { auto results = m_cbAnalyze(); WriteOutValues(results); } 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 (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(std::round(values[i] * 100.0)); WordContainer vec = { 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; } } return true; }