| | |
| | | #include "stdafx.h" |
| | | #include "PLC.h" |
| | | #include "Log.h" |
| | | |
| | | |
| | | 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; |
| | | } |
| | | |
| | | CPLC::CPLC(const char* pszName, const char* pszIp, const unsigned int port) |
| | |
| | | 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_nUnHeartBeat = 0; |
| | | m_hTimeEvent = nullptr; |
| | | m_hMcMonitorStop = nullptr; |
| | | m_hMcMonitorThreadHandle = nullptr; |
| | | m_mcMonitorThrdaddr = 0; |
| | | m_nTimerId = 0; |
| | | m_hTimeEvent = nullptr; |
| | | } |
| | | |
| | | CPLC::~CPLC() |
| | | { |
| | | } |
| | | |
| | | void CPLC::setWorkDir(const char* pszDir) |
| | | { |
| | | m_strWorkDir = pszDir; |
| | | } |
| | | |
| | | std::string& CPLC::getName() |
| | |
| | | { |
| | | return m_nPort; |
| | | } |
| | | |
| | | bool CPLC::isMute() |
| | | { |
| | | return m_bMute; |
| | | } |
| | | |
| | | CAlarmMonitor* CPLC::getAlarmMonitor() |
| | | { |
| | | return (CAlarmMonitor*)getComponent("PLC(1)"); |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | |
| | | // 警告监控 |
| | | CString strAlarmFile; |
| | | strAlarmFile.Format(_T("%s\\%s\\AlarmList.txt"), m_strWorkDir.c_str(), m_strName.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::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) { |
| | | ASSERT(m_listener.onMonitorData); |
| | | m_listener.onMonitorData(this, monitor.id); |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | |