| /******************************************************************************* | |
| * Copyright (c) 2012 -2021 AIT, ACIN, fortiss GmbH, Hit robot group | |
| * This program and the accompanying materials are made available under the | |
| * terms of the Eclipse Public License 2.0 which is available at | |
| * http://www.eclipse.org/legal/epl-2.0. | |
| * | |
| * SPDX-License-Identifier: EPL-2.0 | |
| * | |
| * Contributors: | |
| * Filip Andren, Patrick Smejkal, Alois Zoitl, Martin Melik-Merkumians - initial API and implementation and/or initial documentation | |
| * ys guo - Fix opc module compilation errors and deadlock bug | |
| *******************************************************************************/ | |
| #include "opccomlayer.h" | |
| #include "../../arch/devlog.h" | |
| #include "commfb.h" | |
| #include "opcconnection.h" | |
| #include "opcconnectionhandler.h" | |
| #include "Variant.h" | |
| #include <criticalregion.h> | |
| using namespace forte::com_infra; | |
| COpcComLayer::COpcComLayer(CComLayer* pa_poUpperLayer, CBaseCommFB* pa_poComFB) : | |
| CComLayer(pa_poUpperLayer, pa_poComFB), m_acHost(0), m_acServerName(0), m_nUpdateRate(0), m_nDeadBand(0), m_bLayerParamsOK(false), m_pOpcConnection(0), m_eInterruptResp(e_Nothing){ | |
| m_acOpcGroupName = m_poFb->getInstanceName(); | |
| } | |
| COpcComLayer::~COpcComLayer(){ | |
| } | |
| EComResponse COpcComLayer::sendData(void *pa_pvData, unsigned int pa_unSize){ | |
| EComResponse eRetVal = e_ProcessDataOk; | |
| if((0 != m_poFb)){ | |
| if(m_pOpcConnection->isConnected()){ | |
| switch (m_poFb->getComServiceType()){ | |
| case e_Server: | |
| //TODO | |
| break; | |
| case e_Client: { | |
| convertInputData(pa_pvData, pa_unSize); | |
| TOpcProcessVarList::Iterator itEnd = m_lFBInputVars.end(); | |
| for(TOpcProcessVarList::Iterator it = m_lFBInputVars.begin(); it != itEnd; ++it){ | |
| if(it->getIsActive()) { | |
| m_pOpcConnection->send_sendItemData((*it)); | |
| } | |
| } | |
| break; | |
| } | |
| case e_Publisher: | |
| //do nothing as OPC cannot be publisher | |
| break; | |
| case e_Subscriber: | |
| //do nothing as subscribers do not send data | |
| break; | |
| } | |
| } | |
| } | |
| return eRetVal; | |
| } | |
| EComResponse COpcComLayer::processInterrupt(){ | |
| EComResponse eRet = e_Nothing; | |
| EComResponse currentComResponse; | |
| { | |
| CCriticalRegion criticalRegion(m_oSync); | |
| TComResponseList::Iterator comIt(m_lComResponses.begin()); | |
| currentComResponse = *comIt; | |
| m_lComResponses.popFront(); | |
| } | |
| switch (currentComResponse){ | |
| case e_ProcessDataOk: | |
| switch (m_eConnectionState){ | |
| case e_Connected: { | |
| CIEC_ANY *apoRDs = m_poFb->getRDs(); | |
| unsigned int nrRDs = m_poFb->getNumRD(); | |
| TOpcProcessVarList::Iterator itEnd = m_lFBOutputVars.end(); | |
| TOpcProcessVarList::Iterator it = m_lFBOutputVars.begin(); | |
| for(unsigned int i = 0; i < nrRDs && it != itEnd; i++, ++it){ | |
| setOutputValue(&apoRDs[i], &it->updateValue()); | |
| } | |
| break; | |
| } | |
| case e_Disconnected: | |
| case e_Listening: | |
| case e_ConnectedAndListening: | |
| default: | |
| break; | |
| } | |
| eRet = e_ProcessDataOk; | |
| break; | |
| case e_InitInvalidId: | |
| eRet = e_InitInvalidId; | |
| break; | |
| case e_InitTerminated: | |
| eRet = e_InitTerminated; | |
| break; | |
| } | |
| return eRet; | |
| } | |
| EComResponse COpcComLayer::recvData(const void *, unsigned int){ | |
| EComResponse eRet = e_Nothing; | |
| switch (m_poFb->getComServiceType()){ | |
| case e_Server: | |
| //TODO | |
| break; | |
| case e_Client: { | |
| switch (m_pOpcConnection->getConnectionState()){ | |
| case COpcConnection::e_Connected: | |
| // Successfully connected --> adding OPC items | |
| if(addOpcItems() < 0){ | |
| //TODO | |
| } else { | |
| m_eConnectionState = e_Connected; | |
| } | |
| break; | |
| case COpcConnection::e_ConnectionFailed: | |
| { | |
| CCriticalRegion criticalRegion(m_oSync); | |
| m_lComResponses.pushBack(e_InitTerminated); | |
| } | |
| m_poFb->interruptCommFB(this); | |
| eRet = e_InitTerminated; | |
| break; | |
| case COpcConnection::e_ItemAddedOk: | |
| //do nothing | |
| break; | |
| case COpcConnection::e_ItemAddedFailed: | |
| { | |
| CCriticalRegion criticalRegion(m_oSync); | |
| m_lComResponses.pushBack(e_InitTerminated); | |
| } | |
| m_poFb->interruptCommFB(this); | |
| eRet = e_InitTerminated; | |
| break; | |
| case COpcConnection::e_DataReceived: { | |
| int nRetVal = m_pOpcConnection->receiveData(m_acOpcGroupName, &m_lFBOutputVars); | |
| if (nRetVal > 0) { | |
| //we successfully received data | |
| CCriticalRegion criticalRegion(m_oSync); | |
| m_lComResponses.pushBack(e_ProcessDataOk); | |
| } | |
| m_poFb->interruptCommFB(this); | |
| eRet = e_ProcessDataOk; | |
| break; | |
| } | |
| case COpcConnection::e_Disconnected: | |
| //TODO error during connection try | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| } | |
| case e_Publisher: | |
| //do nothing as publisher cannot receive data | |
| case e_Subscriber: | |
| //do nothing as OPC cannot be subscribers | |
| break; | |
| } | |
| return eRet; | |
| } | |
| EComResponse COpcComLayer::openConnection(char *pa_acLayerParameter){ | |
| EComResponse eRetVal = e_InitInvalidId; | |
| m_eConnectionState = e_Disconnected; | |
| switch (m_poFb->getComServiceType()){ | |
| case e_Server: | |
| //TODO | |
| break; | |
| case e_Client: | |
| processClientParams(pa_acLayerParameter); | |
| if(m_bLayerParamsOK){ | |
| eRetVal = e_InitOk; | |
| m_pOpcConnection = COpcConnectionHandler::getInstance().getOpcConnection(m_acHost, m_acServerName, m_acOpcGroupName, m_nUpdateRate, m_nDeadBand, this); | |
| m_pOpcConnection->send_connect(); | |
| COpcConnection::EOpcConnectionEvents connState = m_pOpcConnection->getConnectionState(); | |
| if (connState == COpcConnection::e_Connected) { | |
| m_eConnectionState = e_Connected; | |
| addOpcItems(); | |
| } | |
| else if(connState == COpcConnection::e_ConnectionFailed) { | |
| eRetVal = e_InitTerminated; | |
| } | |
| } | |
| break; | |
| case e_Publisher: | |
| //do nothing, OPC cannot be publisher | |
| break; | |
| case e_Subscriber: | |
| //do nothing, OPC cannot be subscriber | |
| break; | |
| } | |
| return eRetVal; | |
| } | |
| void COpcComLayer::closeConnection(){ | |
| DEVLOG_DEBUG("COpcComLayer::closeConnection() \n"); | |
| COpcConnectionHandler::getInstance().removeOpcConnection(m_pOpcConnection->getHost(), m_pOpcConnection->getServerName(), m_acOpcGroupName); | |
| m_eConnectionState = e_Disconnected; | |
| } | |
| int COpcComLayer::addOpcItems(){ | |
| int result = 0; | |
| // Add input items | |
| TOpcProcessVarList::Iterator itEnd = m_lFBInputVars.end(); | |
| for(TOpcProcessVarList::Iterator it = m_lFBInputVars.begin(); it != itEnd; ++it){ | |
| result = m_pOpcConnection->send_addItem(*it); | |
| } | |
| // Add output items | |
| itEnd = m_lFBOutputVars.end(); | |
| for(TOpcProcessVarList::Iterator it = m_lFBOutputVars.begin(); it != itEnd; ++it){ | |
| result = m_pOpcConnection->send_addItem(*it); | |
| } | |
| return result; | |
| } | |
| void COpcComLayer::setOutputValue(CIEC_ANY *pa_pDataOut, Variant * pa_pValue){ | |
| switch (pa_pDataOut->getDataTypeID()){ | |
| case CIEC_ANY::e_BOOL: | |
| pa_pDataOut->setValue(CIEC_BOOL(pa_pValue->get<bool>())); | |
| break; | |
| case CIEC_ANY::e_SINT: | |
| pa_pDataOut->setValue(CIEC_SINT(pa_pValue->get<TForteInt8>())); | |
| break; | |
| case CIEC_ANY::e_INT: | |
| pa_pDataOut->setValue(CIEC_INT(pa_pValue->get<TForteInt16>())); | |
| break; | |
| case CIEC_ANY::e_DINT: | |
| pa_pDataOut->setValue(CIEC_DINT(pa_pValue->get<TForteInt32>())); | |
| break; | |
| case CIEC_ANY::e_LINT: | |
| pa_pDataOut->setValue(CIEC_LINT(pa_pValue->get<TForteInt64>())); | |
| break; | |
| case CIEC_ANY::e_USINT: | |
| pa_pDataOut->setValue(CIEC_USINT(pa_pValue->get<TForteUInt8>())); | |
| break; | |
| case CIEC_ANY::e_UINT: | |
| pa_pDataOut->setValue(CIEC_UINT(pa_pValue->get<TForteUInt16>())); | |
| break; | |
| case CIEC_ANY::e_UDINT: | |
| pa_pDataOut->setValue(CIEC_UDINT(pa_pValue->get<TForteUInt32>())); | |
| break; | |
| case CIEC_ANY::e_ULINT: | |
| pa_pDataOut->setValue(CIEC_ULINT(pa_pValue->get<TForteUInt64>())); | |
| break; | |
| case CIEC_ANY::e_BYTE: | |
| pa_pDataOut->setValue(CIEC_BYTE(pa_pValue->get<TForteByte>())); | |
| break; | |
| case CIEC_ANY::e_WORD: | |
| pa_pDataOut->setValue(CIEC_WORD(pa_pValue->get<TForteWord>())); | |
| break; | |
| case CIEC_ANY::e_DWORD: | |
| pa_pDataOut->setValue(CIEC_DWORD(pa_pValue->get<TForteDWord>())); | |
| break; | |
| case CIEC_ANY::e_LWORD: | |
| pa_pDataOut->setValue(CIEC_LWORD(pa_pValue->get<TForteLWord>())); | |
| break; | |
| case CIEC_ANY::e_REAL: | |
| pa_pDataOut->setValue(CIEC_REAL(pa_pValue->get<TForteFloat>())); | |
| break; | |
| case CIEC_ANY::e_LREAL: | |
| pa_pDataOut->setValue(CIEC_LREAL(pa_pValue->get<TForteDFloat>())); | |
| break; | |
| default: | |
| //TODO | |
| break; | |
| } | |
| } | |
| void COpcComLayer::processClientParams(char* pa_acLayerParams){ | |
| char *chrStorage; | |
| char *chrHost; | |
| char *chrServer; | |
| char *temp; | |
| // Get Host | |
| chrStorage = strchr(pa_acLayerParams, ':'); | |
| if(chrStorage == 0) { | |
| return; | |
| } | |
| *chrStorage = '\0'; | |
| ++chrStorage; | |
| chrHost = (char*) malloc(strlen(pa_acLayerParams) + 1); | |
| strcpy(chrHost, pa_acLayerParams); | |
| if(strcmp(chrHost, "127.0.0.1") == 0 || strcmp(chrHost, "localhost") == 0) { | |
| m_acHost = ""; | |
| } else { | |
| m_acHost = chrHost; | |
| } | |
| // Get server name | |
| temp = chrStorage; | |
| chrStorage = strchr(chrStorage, ':'); | |
| if(chrStorage == 0){ | |
| if (chrHost){ | |
| free(chrHost); | |
| } | |
| return; | |
| } | |
| *chrStorage = '\0'; | |
| ++chrStorage; | |
| chrServer = (char*) malloc(strlen(temp) + 1); | |
| strcpy(chrServer, temp); | |
| m_acServerName = chrServer; | |
| // Get update rate | |
| m_nUpdateRate = atol(chrStorage); | |
| chrStorage = strchr(chrStorage, ':'); | |
| if(chrStorage == 0){ | |
| if (chrHost){ | |
| free(chrHost); | |
| } | |
| return; | |
| } | |
| *chrStorage = '\0'; | |
| ++chrStorage; | |
| // Get dead band | |
| m_nDeadBand = (float) atof(chrStorage); | |
| chrStorage = strchr(chrStorage, ':'); | |
| if(chrStorage == 0){ | |
| if (chrHost){ | |
| free(chrHost); | |
| } | |
| return; | |
| } | |
| *chrStorage = '\0'; | |
| ++chrStorage; | |
| // Get FB input items | |
| char * inputItems = chrStorage; | |
| chrStorage = strchr(chrStorage, ':'); | |
| if(chrStorage == 0){ | |
| if (chrHost){ | |
| free(chrHost); | |
| } | |
| return; | |
| } | |
| *chrStorage = '\0'; | |
| ++chrStorage; | |
| int nrItems = 0; | |
| char *pch; | |
| pch = strtok(inputItems, ","); | |
| while(pch != NULL){ | |
| char *itemName = (char*) malloc(strlen(pch) + 1); | |
| strcpy(itemName, pch); | |
| m_lFBInputVars.pushBack(new COpcProcessVar(m_acOpcGroupName, itemName, COpcProcessVar::e_FBInput)); | |
| nrItems++; | |
| pch = strtok(NULL, ","); | |
| } | |
| // Get FB output items | |
| pch = strtok(chrStorage, ","); | |
| while(pch != NULL){ | |
| char *itemName = (char*) malloc(strlen(pch) + 1); | |
| strcpy(itemName, pch); | |
| m_lFBOutputVars.pushBack(new COpcProcessVar(m_acOpcGroupName, itemName, COpcProcessVar::e_FBOutput)); | |
| nrItems++; | |
| pch = strtok(NULL, ","); | |
| } | |
| if(nrItems > 0) { | |
| m_bLayerParamsOK = true; | |
| } | |
| } | |
| void COpcComLayer::convertInputData(void *pa_pData, unsigned int pa_nSize){ | |
| CIEC_ANY *apoSDs = static_cast<CIEC_ANY*>(pa_pData); | |
| unsigned int nrSDs = pa_nSize; | |
| unsigned int sdIndex = 0; | |
| TOpcProcessVarList::Iterator it_var = m_lFBInputVars.begin(); | |
| TOpcProcessVarList::Iterator itEnd = m_lFBInputVars.end(); | |
| while(sdIndex < nrSDs && it_var != itEnd){ | |
| CIEC_ANY *dataIn = &apoSDs[sdIndex]; | |
| Variant newVariant; | |
| getInputValueSize(dataIn, &newVariant); | |
| it_var->setNewValue(newVariant); | |
| ++it_var; | |
| ++sdIndex; | |
| } | |
| } | |
| template<typename T> | |
| void COpcComLayer::getInputValue(void * pa_pData, Variant * pa_pNewValue){ | |
| T* vData = (T*) pa_pData; | |
| T data = vData[0]; | |
| pa_pNewValue->set<T>(data); | |
| } | |
| unsigned int COpcComLayer::getInputValueSize(CIEC_ANY* pa_pData, Variant * pa_pNewValue){ | |
| switch (pa_pData->getDataTypeID()){ | |
| case CIEC_ANY::e_BOOL: | |
| { | |
| pa_pNewValue->set<bool>((bool) *(dynamic_cast<CIEC_BOOL*>(pa_pData))); | |
| return sizeof(bool); | |
| } | |
| case CIEC_ANY::e_SINT: | |
| { | |
| pa_pNewValue->set<CHAR>((CHAR) *(dynamic_cast<CIEC_SINT*>(pa_pData))); | |
| return sizeof(TForteInt8); | |
| } | |
| case CIEC_ANY::e_INT: | |
| { | |
| CIEC_INT* tempInt = dynamic_cast<CIEC_INT*>(pa_pData); | |
| TForteInt16 forteInt = (TForteInt16) (*tempInt); | |
| pa_pNewValue->set<TForteInt16>(forteInt); | |
| return sizeof(TForteInt16); | |
| } | |
| case CIEC_ANY::e_DINT: | |
| { | |
| pa_pNewValue->set<TForteInt32>((TForteInt32) *(dynamic_cast<CIEC_DINT*>(pa_pData))); | |
| return sizeof(TForteInt32); | |
| } | |
| case CIEC_ANY::e_LINT: | |
| { | |
| pa_pNewValue->set<TForteInt64>((TForteInt64) *(dynamic_cast<CIEC_LINT*>(pa_pData))); | |
| return sizeof(TForteInt64); | |
| } | |
| case CIEC_ANY::e_USINT: | |
| { | |
| pa_pNewValue->set<TForteUInt8>((TForteUInt8) *(dynamic_cast<CIEC_USINT*>(pa_pData))); | |
| return sizeof(TForteUInt8); | |
| } | |
| case CIEC_ANY::e_UINT: | |
| { | |
| pa_pNewValue->set<TForteUInt16>((TForteUInt16) *(dynamic_cast<CIEC_UINT*>(pa_pData))); | |
| return sizeof(TForteUInt16); | |
| } | |
| case CIEC_ANY::e_UDINT: | |
| { | |
| pa_pNewValue->set<TForteUInt32>((TForteUInt32) *(dynamic_cast<CIEC_UDINT*>(pa_pData))); | |
| return sizeof(TForteUInt32); | |
| } | |
| case CIEC_ANY::e_ULINT: | |
| { | |
| pa_pNewValue->set<TForteUInt64>((TForteUInt64) *(dynamic_cast<CIEC_ULINT*>(pa_pData))); | |
| return sizeof(TForteUInt64); | |
| } | |
| case CIEC_ANY::e_BYTE: | |
| { | |
| pa_pNewValue->set<TForteByte>((TForteByte) *(dynamic_cast<CIEC_BYTE*>(pa_pData))); | |
| return sizeof(TForteByte); | |
| } | |
| case CIEC_ANY::e_WORD: | |
| { | |
| pa_pNewValue->set<TForteWord>((TForteWord) *(dynamic_cast<CIEC_WORD*>(pa_pData))); | |
| return sizeof(TForteWord); | |
| } | |
| case CIEC_ANY::e_DWORD: | |
| { | |
| pa_pNewValue->set<TForteDWord>((TForteDWord) *(dynamic_cast<CIEC_DWORD*>(pa_pData))); | |
| return sizeof(TForteDWord); | |
| } | |
| case CIEC_ANY::e_LWORD: | |
| { | |
| pa_pNewValue->set<TForteLWord>((TForteLWord) *(dynamic_cast<CIEC_LWORD*>(pa_pData))); | |
| return sizeof(TForteLWord); | |
| } | |
| case CIEC_ANY::e_REAL: | |
| { | |
| pa_pNewValue->set<TForteFloat>((TForteFloat) *(dynamic_cast<CIEC_REAL*>(pa_pData))); | |
| return sizeof(TForteFloat); | |
| } | |
| case CIEC_ANY::e_LREAL: | |
| { | |
| pa_pNewValue->set<TForteDFloat>((TForteDFloat) *(dynamic_cast<CIEC_LREAL*>(pa_pData))); | |
| return sizeof(TForteDFloat); | |
| } | |
| default: | |
| //TODO | |
| break; | |
| } | |
| return 0; | |
| } |