mrDarker
2025-10-22 e8a27bb203fe2aff70390a5eca002d7438da9b0f
SourceCode/Bond/BoounionPLC/PLC.cpp
@@ -1,9 +1,42 @@
#include "stdafx.h"
#include "PLC.h"
#include "Log.h"
#include "ToolUnits.h"
#define ADDR_NIGHT_SHIFT_CAPACTITY      1627
void CALLBACK TimerFileProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
{
   CPLC* pPlc = (CPLC*)dwUser;
   SetEvent(pPlc->m_hTimeEvent);
}
unsigned __stdcall McMonitorThreadFunction(LPVOID lpParam)
{
   CPLC* pPlc = (CPLC*)lpParam;
   return pPlc->onMonitor();
}
CPLC::CPLC()
{
   m_pChannel = nullptr;
   m_state = PLCSTATE::READY;
   m_listener.onStateChanged = nullptr;
   m_listener.onMonitorData = nullptr;
   m_listener.onAlarm = nullptr;
   m_nUnHeartBeat = 0;
   m_nActionInterval = 500;
   m_hTimeEvent = nullptr;
   m_hMcMonitorStop = nullptr;
   m_hMcMonitorThreadHandle = nullptr;
   m_mcMonitorThrdaddr = 0;
   m_nTimerId = 0;
   m_hTimeEvent = nullptr;
   m_bMute = false;
   m_pPlcData = new char[4096];
   m_nVelocityRatio = 0;
   InitializeCriticalSection(&m_criticalSection);
}
CPLC::CPLC(const char* pszName, const char* pszIp, const unsigned int port)
@@ -11,10 +44,49 @@
   m_strName = pszName;
   m_strIp = pszIp;
   m_nPort = port;
   m_pChannel = nullptr;
   m_state = PLCSTATE::READY;
   m_listener.onStateChanged = nullptr;
   m_listener.onMonitorData = nullptr;
   m_listener.onAlarm = nullptr;
   m_nUnHeartBeat = 0;
   m_hTimeEvent = nullptr;
   m_hMcMonitorStop = nullptr;
   m_hMcMonitorThreadHandle = nullptr;
   m_mcMonitorThrdaddr = 0;
   m_nTimerId = 0;
   m_hTimeEvent = nullptr;
   m_pPlcData = new char[4096];
   m_nVelocityRatio = 0;
   m_dTactTime = 0.0;
   m_nDayShiftCapacity = 0;
   m_nNightShiftCapacity = 0;
   for (int i = 0; i < 7; i++) {
      m_bBlBtnsStates[i] = false;
   }
   InitializeCriticalSection(&m_criticalSection);
}
CPLC::~CPLC()
{
   if (m_pPlcData != nullptr) {
      delete[] m_pPlcData;
      m_pPlcData = nullptr;
   }
   DeleteCriticalSection(&m_criticalSection);
}
void CPLC::setListener(PLCListener& listener)
{
   m_listener.onStateChanged = listener.onStateChanged;
   m_listener.onMonitorData = listener.onMonitorData;
   m_listener.onAlarm = listener.onAlarm;
}
void CPLC::setWorkDir(const char* pszDir)
{
   m_strWorkDir = pszDir;
}
std::string& CPLC::getName()
@@ -31,3 +103,460 @@
{
   return m_nPort;
}
bool CPLC::isMute()
{
   return m_bMute;
}
CAlarmMonitor* CPLC::getAlarmMonitor()
{
   return (CAlarmMonitor*)getComponent("警告信息");
}
int CPLC::addMonitor(int id, int beginAddr, int endAddr, MC::SOFT_COMPONENT softComponent, char* pszRecvBuffer)
{
   // 检查是否有重复的
   Lock();
   for (auto& m : m_monitors) {
      if (m.id == id) {
         Unlock();
         return -1;
      }
   }
   MONITOR m;
   memset(&m, 0, sizeof(MONITOR));
   m.id = id;
   m.beginAddr = beginAddr;
   m.readLen = (endAddr - beginAddr + 1) * 2;
   m.softComponent = softComponent;
   m.szRecvBuffer = pszRecvBuffer;
   m.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
   m_monitors.push_back(m);
   Unlock();
   return 0;
}
void CPLC::init()
{
   // mc channel
   McChannelListener m_mcChannellistener;
   m_mcChannellistener.funOnConnected = [&](IMcChannel* pChannel, int nErrorCode) -> void {
      LOGI("<PLC-%s>连接结果<code= %d>", m_strName.c_str(), nErrorCode);
      if (nErrorCode == 0) {
         setState(PLCSTATE::CONNECTED);
      }
      else {
         setState(PLCSTATE::DISCONNECTED);
      }
   };
   m_mcChannellistener.funOnClose = [&](IMcChannel* pChannel) -> void {
      setState(PLCSTATE::DISCONNECTED);
   };
   m_mcChannellistener.funOnClosing = [&](IMcChannel* pChannel) -> void {
   };
   m_mcChannellistener.funOnRead = [&](IMcChannel* pChannel, char* pData, unsigned int nDataSize, int nDecodeRet) -> void {
      CString strText;
      dataToHexString(pData, nDataSize, strText);
      if (nDecodeRet != 0) {
         LOGE("<PLC-%s>funOnRead[%s], nDecodeRet=%d", m_strName.c_str(), (LPTSTR)(LPCTSTR)strText, nDecodeRet);
      }
      m_nUnHeartBeat = 0;
   };
   m_mcChannellistener.funOnWrite = [&](IMcChannel* pChannel) -> void {
   };
   if (0 == MCL_CreateChannel(m_pChannel, m_strName.c_str(), m_strIp.c_str(), m_nPort, 0)
      && m_pChannel != NULL) {
      m_pChannel->setChannelListener(&m_mcChannellistener);
      m_pChannel->setActionInterval(m_nActionInterval);
      LOGI("<PLC-%s>正在连接PLC.", m_strName.c_str());
      setState(PLCSTATE::CONNECTING);
      m_pChannel->connect();
   }
   else if (m_pChannel != NULL) {
      m_pChannel->setChannelListener(&m_mcChannellistener);
      m_pChannel->setActionInterval(m_nActionInterval);
   }
   addMonitor(MONITOR_ID_ALARM, 10001, 10064, MC::SOFT_COMPONENT::M, &m_pPlcData[600]);
   // 警告监控
   CString strAlarmFile;
   strAlarmFile.Format(_T("%s\\AlarmList.txt"), m_strWorkDir.c_str());
   CAlarmMonitor* pAlarmMonitor = new CAlarmMonitor();
   pAlarmMonitor->setName("警告信息");
   pAlarmMonitor->setDescription("警告信息监控");
   pAlarmMonitor->setIndex(0);
   pAlarmMonitor->readAlarmListFromFile((LPTSTR)(LPCTSTR)strAlarmFile);
   addComponent(pAlarmMonitor);
   pAlarmMonitor->init();
   // 定时器
   m_hTimeEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
   timeBeginPeriod(1);
   m_nTimerId = timeSetEvent(200, 1, TimerFileProc, (DWORD_PTR)this, TIME_PERIODIC);
   // 数据监控线程
   if (m_hMcMonitorStop != NULL) return;
   m_hMcMonitorStop = ::CreateEvent(NULL, TRUE, FALSE, NULL);
   m_hMcMonitorThreadHandle = (HANDLE)_beginthreadex(NULL, 0, ::McMonitorThreadFunction, this,
      0, &m_mcMonitorThrdaddr);
}
void CPLC::term()
{
   timeKillEvent(m_nTimerId);
   timeEndPeriod(1);      // 清除前面对定时器的设置
   for (auto item : m_components) {
      delete item;
   }
   m_components.clear();
   ASSERT(m_hMcMonitorStop);
   SetEvent(m_hMcMonitorStop);
   if (m_hMcMonitorThreadHandle != NULL) {
      WaitForSingleObject(m_hMcMonitorThreadHandle, INFINITE);
      CloseHandle(m_hMcMonitorThreadHandle);
      m_hMcMonitorThreadHandle = NULL;
   }
   CloseHandle(m_hMcMonitorStop);
   m_hMcMonitorStop = NULL;
   for (auto& m : m_monitors) {
      CloseHandle(m.hEvent);
   }
}
bool CPLC::isConnected()
{
   return m_pChannel != nullptr && m_pChannel->isConnected();
}
void CPLC::connect()
{
   if (m_pChannel != nullptr && !m_pChannel->isConnected()) {
      m_pChannel->connect();
   }
}
void CPLC::setState(PLCSTATE state)
{
   m_state = state;
   if (m_listener.onStateChanged != nullptr) {
      m_listener.onStateChanged(this, (int)m_state);
   }
}
void CPLC::setActionInterval(unsigned int nInterval)
{
   m_nActionInterval = nInterval;
}
CString& CPLC::dataToHexString(const char* pData, const int size, CString& strOut)
{
   strOut.Empty();
   for (int i = 0; i < size; i++) {
      if (i < size - 1) {
         strOut.AppendFormat(_T("%02X "), (BYTE)pData[i]);
      }
      else {
         strOut.AppendFormat(_T("%02X"), (BYTE)pData[i]);
      }
   }
   return strOut;
}
unsigned CPLC::onMonitor()
{
   HANDLE hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
   int nReadLen = 60 * 2;
   HANDLE hEvents[2] = { m_hMcMonitorStop, m_hTimeEvent };
   while (1) {
      int nRet = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
      ResetEvent(m_hTimeEvent);
      if (nRet == WAIT_OBJECT_0) {
         break;
      }
      if (/*!m_bRunning || */!isConnected()) {
         continue;
      }
      for (auto& m : m_monitors) {
         monitorReadData(m);
      }
   }
   TRACE("CPLC::onMonitor 线程退出\n");
   return 0;
}
void CPLC::monitorReadData(MONITOR& monitor)
{
   BOOL bOutputLog = FALSE;
   BOOL bReadOk;
   // 批量读数据再解释
   auto funOnReadData = [&](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) -> void {
      if (flag == 0) {
         if (bOutputLog) {
            CString s;
            s.Format(_T("CPLC::monitorReadData::funOnReadData %d ["), nDataSize);
            for (unsigned int i = 0; i < nDataSize; i++) {
               s.AppendFormat(" %x", (BYTE)pData[i]);
            }
            s.Append("]");
            LOGD("<CPLC-%s>Received plc data.%s.monitor=%d", m_strName.c_str(), monitor.id, (LPTSTR)(LPCTSTR)s, monitor.id);
         }
      }
      else {
         LOGE("<CPLC-%s>PLC批读取数据位超时.monitor=%d, flag=%d", m_strName.c_str(), monitor.id, flag);
      }
      if (nDataSize == monitor.readLen && flag == 0) {
         memcpy(monitor.szRecvBuffer, pData, nDataSize);
         monitor.readCount++;
         bReadOk = TRUE;
      }
      SetEvent(monitor.hEvent);
   };
   bReadOk = FALSE;
   m_pChannel->readData(monitor.softComponent, monitor.beginAddr, monitor.readLen, funOnReadData);
   WaitForSingleObject(monitor.hEvent, INFINITE);
   ResetEvent(monitor.hEvent);
   if (bReadOk) {
      onMonitorData(monitor);
   }
}
int CPLC::onMonitorData(MONITOR& monitor)
{
   // 转发到警告模块处理解释数据
   if (monitor.id == MONITOR_ID_ALARM) {
      for (auto c : m_components) {
         c->onData(monitor.id, monitor.szRecvBuffer, monitor.readLen);
      }
   }
   if (m_listener.onMonitorData) {
      m_listener.onMonitorData(this, monitor.id);
   }
   return 0;
}
int CPLC::readWord(MC::SOFT_COMPONENT softComponent, unsigned int addr,
   ONREAD funOnRead)
{
   return m_pChannel->readWord(softComponent, addr, funOnRead);
}
int CPLC::readData(MC::SOFT_COMPONENT softComponent, unsigned int addr,
   unsigned int nReadLen, ONREADDATA funOnReadData)
{
   return m_pChannel->readData(softComponent, addr, nReadLen, funOnReadData);
}
int CPLC::writeBit(MC::SOFT_COMPONENT softComponent, unsigned int addr,
   BOOL bValue, ONWRITE funOnWrite)
{
   return m_pChannel->writeBit(softComponent, addr, bValue, funOnWrite);
}
int CPLC::writeWord(MC::SOFT_COMPONENT softComponent, unsigned int addr,
   int value, ONWRITE funOnWrite)
{
   return m_pChannel->writeWord(softComponent, addr, value, funOnWrite);
}
int CPLC::writeDWord(MC::SOFT_COMPONENT softComponent, unsigned int addr,
   int value, ONWRITE funOnWrite)
{
   return m_pChannel->writeDWord(softComponent, addr, value, funOnWrite);
}
int CPLC::writeData(MC::SOFT_COMPONENT softComponent, unsigned int addr,
   const char* pszData, unsigned int length, ONWRITE funOnWrite)
{
   return m_pChannel->writeData(softComponent, addr, pszData, length, funOnWrite);
}
void CPLC::addComponent(CComponent* pComponent)
{
   ASSERT(pComponent);
   pComponent->setPlc(this);
   m_components.push_back(pComponent);
}
CComponent* CPLC::getComponent(const char* pszName)
{
   for (auto c : m_components) {
      if (c->getName().compare(pszName) == 0) {
         return c;
      }
   }
   return nullptr;
}
void CPLC::sendBroadcast(CComponent* pSender, CIntent* pIntent)
{
   for (auto item : m_components) {
      if (item != pSender) {
         item->onRecvBroadcast(pSender, pIntent);
      }
   }
   this->onRecvBroadcast(pSender, pIntent);
}
void CPLC::onRecvBroadcast(CComponent* pSender, CIntent* pIntent)
{
   int code = pIntent->getCode();
   if (BC_CODE_ALARM_ON == code) {
      if (m_listener.onAlarm != nullptr) {
         m_listener.onAlarm(this, (CAlarm*)pIntent->getContext(), 1);
      }
   }
   else if (BC_CODE_ALARM_OFF == code) {
      if (m_listener.onAlarm != nullptr) {
         m_listener.onAlarm(this, (CAlarm*)pIntent->getContext(), 0);
      }
   }
}
void CPLC::readPLCDataRegularly()
{
   if (!isConnected()) return;
   {
      auto funOnReadData = [this](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) -> void {
         if (nDataSize == 2 && flag == 0) {
            int nVelocityRatio = CToolUnits::toInt16(&pData[0]);
            if (nVelocityRatio != m_nVelocityRatio) {
               m_nVelocityRatio = nVelocityRatio;
               //notifyInt(RX_CODE_VELOCITY_RATIO, m_nVelocityRatio);
            }
         }
      };
      readData(MC::D, 530, 2, funOnReadData);
   }
   {
      auto funOnReadData = [this](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) -> void {
         if (nDataSize == 2 && flag == 0) {
            double dTactTime = (double)CToolUnits::toInt16(&pData[0]);
            if (dTactTime != m_dTactTime) {
               m_dTactTime = dTactTime;
               // notifyDouble(RX_CODE_TACT_TIME, m_dTactTime);
            }
         }
      };
      readData(MC::ZR, 1500, 2, funOnReadData);
   }
   {
      auto funOnReadData = [this](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) -> void {
         if (nDataSize == (ADDR_NIGHT_SHIFT_CAPACTITY - 1612 + 1) * 2 && flag == 0) {
            int nDayShiftCapacity = CToolUnits::toInt16(&pData[0]);
            int nNightShiftCapacity = CToolUnits::toInt16(&pData[(ADDR_NIGHT_SHIFT_CAPACTITY - 1612) * 2]);
            if (nDayShiftCapacity != m_nDayShiftCapacity) {
               m_nDayShiftCapacity = nDayShiftCapacity;
               // notifyInt(RX_CODE_DAY_SHIFT_CAPACTITY, nDayShiftCapacity);
            }
            if (nNightShiftCapacity != m_nNightShiftCapacity) {
               m_nNightShiftCapacity = nNightShiftCapacity;
               // notifyInt(RX_CODE_NIGHT_SHIFT_CAPACTITY, nNightShiftCapacity);
            }
         }
      };
      readData(MC::ZR, 1612, (ADDR_NIGHT_SHIFT_CAPACTITY - 1612 + 1) * 2, funOnReadData);
   }
   {
      int nStartAddress = 1000;
      int nEndAddress = 1200;
      int nReadSize = (nEndAddress - nStartAddress + 1) * 2;
      auto funOnReadData = [this, nStartAddress, nReadSize](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) -> void {
         if (nDataSize == nReadSize && flag == 0) {
            bool bRun = CToolUnits::toInt16(&pData[(1103 - nStartAddress) * 2]) != 0;      // 启动
            bool bAuto = CToolUnits::toInt16(&pData[(1100 - nStartAddress) * 2]) != 0;      // 自动
            bool bPuase = CToolUnits::toInt16(&pData[(1104 - nStartAddress) * 2]) != 0;      // 暂停
            bool bManual = CToolUnits::toInt16(&pData[(1100 - nStartAddress) * 2]) != 0;   // 手动
            bool bBeep = CToolUnits::toInt16(&pData[(1003 - nStartAddress) * 2]) != 0;      // 静音
            bool bResetting = CToolUnits::toInt16(&pData[(1150 - nStartAddress) * 2]) != 0; // 复位
            bool bStop = CToolUnits::toInt16(&pData[(1114 - nStartAddress) * 2]) != 0;      // ֹͣ
            if (m_bBlBtnsStates[0] != bRun) {
               m_bBlBtnsStates[0] = bRun;
               // notifyInt(RX_CODE_ACTIVATE, bRun);
            }
            if (m_bBlBtnsStates[1] != bAuto) {
               m_bBlBtnsStates[1] = bAuto;
               // notifyInt(RX_CODE_AUTO, bAuto);
            }
            if (m_bBlBtnsStates[2] != bPuase) {
               m_bBlBtnsStates[2] = bPuase;
               // notifyInt(RX_CODE_PUASE, bPuase);
            }
            if (m_bBlBtnsStates[3] != bManual) {
               m_bBlBtnsStates[3] = bManual;
               // notifyInt(RX_CODE_MANUAL, bManual);
            }
            if (m_bBlBtnsStates[4] != bBeep) {
               m_bBlBtnsStates[4] = bBeep;
               // notifyInt(RX_CODE_BEEP, bBeep);
            }
            if (m_bBlBtnsStates[5] != bResetting) {
               m_bBlBtnsStates[5] = bResetting;
               // notifyInt(RX_CODE_RESETTING, bResetting);
            }
            if (m_bBlBtnsStates[6] != bStop) {
               m_bBlBtnsStates[6] = bStop;
               // notifyInt(RX_CODE_STOP, bStop);
            }
         }
      };
      readData(MC::M, nStartAddress, nReadSize, funOnReadData);
   }
}
int CPLC::getVelocityRatio()
{
   return m_nVelocityRatio;
}
double CPLC::getTackTime()
{
   return m_dTactTime;
}
int CPLC::getDayShiftCapacity()
{
   return m_nDayShiftCapacity;
}
int CPLC::getNightShiftCapacity()
{
   return m_nNightShiftCapacity;
}