| /******************************************************************************* |
| * Copyright (c) 2005 - 2018 Profactor GmbH, ACIN, fortiss GmbH, |
| * Johannes Kepler University |
| * |
| * 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: |
| * Thomas Strasser, Gunnar Grabmaier, Alois Zoitl, Smodic Rene, Ingo Hegny, |
| * Gerhard Ebenhofer, Michael Hofmann, Martin Melik Merkumians, Monika Wenger, |
| * Matthias Plasch |
| * - initial implementation and rework communication infrastructure |
| * Alois Zoitl - introduced new CGenFB class for better handling generic FBs |
| *******************************************************************************/ |
| #include "funcbloc.h" |
| #ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP |
| #include "funcbloc_gen.cpp" |
| #endif |
| #include "adapter.h" |
| #include "device.h" |
| #include "utils/criticalregion.h" |
| #include "../arch/timerha.h" |
| #include <string.h> |
| #include <stdlib.h> |
| |
| CFunctionBlock::CFunctionBlock(CResource *pa_poSrcRes, const SFBInterfaceSpec *pa_pstInterfaceSpec, const CStringDictionary::TStringId pa_nInstanceNameId, TForteByte *pa_acFBConnData, TForteByte *pa_acFBVarsData) : |
| mEOConns(0), m_apoDIConns(0), mDOConns(0), |
| m_poInvokingExecEnv(0), m_apoAdapters(0), m_poResource(pa_poSrcRes), m_aoDIs(0), m_aoDOs(0), m_nFBInstanceName(pa_nInstanceNameId), |
| m_enFBState(e_KILLED), //put the FB in the killed state so that reseting it after creation will correctly initialize it |
| m_bDeletable(true){ |
| |
| #ifdef FORTE_SUPPORT_MONITORING |
| mEIMonitorCount = 0; |
| mEOMonitorCount = 0; |
| #endif |
| |
| setupFBInterface(pa_pstInterfaceSpec, pa_acFBConnData, pa_acFBVarsData); |
| } |
| |
| CFunctionBlock::~CFunctionBlock(){ |
| freeAllData(); |
| } |
| |
| void CFunctionBlock::freeAllData(){ |
| if(0 != m_pstInterfaceSpec){ |
| for(int i = 0; i < m_pstInterfaceSpec->m_nNumEOs; ++i){ |
| (mEOConns + i)->~CEventConnection(); |
| } |
| |
| for(int i = 0; i < m_pstInterfaceSpec->m_nNumDIs; ++i){ |
| getDI(i)->~CIEC_ANY(); |
| } |
| |
| for(int i = 0; i < m_pstInterfaceSpec->m_nNumDOs; ++i){ |
| getDO(i)->~CIEC_ANY(); |
| (mDOConns + i)->~CDataConnection(); |
| } |
| |
| for(unsigned int i = 0; i < m_pstInterfaceSpec->m_nNumAdapters; ++i){ |
| delete m_apoAdapters[i]; |
| } |
| } |
| |
| #ifdef FORTE_SUPPORT_MONITORING |
| delete[] mEOMonitorCount; |
| mEOMonitorCount = 0; |
| delete[] mEIMonitorCount; |
| mEIMonitorCount = 0; |
| #endif //FORTE_SUPPORT_MONITORING |
| } |
| |
| void CFunctionBlock::setupAdapters(const SFBInterfaceSpec *pa_pstInterfaceSpec, TForteByte *pa_acFBData){ |
| if((0 != pa_pstInterfaceSpec) && (0 != pa_acFBData) && (pa_pstInterfaceSpec->m_nNumAdapters)) { |
| m_apoAdapters = reinterpret_cast<TAdapterPtr *>(pa_acFBData); |
| for(unsigned int i = 0; i < pa_pstInterfaceSpec->m_nNumAdapters; ++i) { |
| //set pointer to right place in pa_acFBData |
| m_apoAdapters[i] = CTypeLib::createAdapter(pa_pstInterfaceSpec->m_pstAdapterInstanceDefinition[i].m_nAdapterNameID, |
| pa_pstInterfaceSpec->m_pstAdapterInstanceDefinition[i].m_nAdapterTypeNameID, getResourcePtr(), |
| pa_pstInterfaceSpec->m_pstAdapterInstanceDefinition[i].m_bIsPlug); |
| if(0 != m_apoAdapters[i]) { |
| m_apoAdapters[i]->setParentFB(this, static_cast<TForteUInt8>(i)); |
| } |
| } |
| } |
| } |
| |
| CTimerHandler& CFunctionBlock::getTimer(void){ |
| return m_poResource->getDevice().getTimer(); |
| } |
| |
| CEventConnection *CFunctionBlock::getEOConnection(CStringDictionary::TStringId paEONameId) const{ |
| CEventConnection *retVal = 0; |
| TPortId portId = getPortId(paEONameId, m_pstInterfaceSpec->m_nNumEOs, m_pstInterfaceSpec->m_aunEONames); |
| if(cg_unInvalidPortId != portId){ |
| retVal = getEOConUnchecked(portId); |
| } |
| return retVal; |
| } |
| |
| bool CFunctionBlock::connectDI(TPortId paDIPortId, CDataConnection *pa_poDataCon){ |
| bool bRetVal = false; |
| |
| if(m_pstInterfaceSpec->m_nNumDIs > paDIPortId){ //catch invalid ID |
| if(0 == pa_poDataCon){ |
| m_apoDIConns[paDIPortId] = 0; |
| bRetVal = true; |
| } |
| else{ |
| //only perform connection checks if it is not a disconnection request. |
| if(0 != m_apoDIConns[paDIPortId]){ |
| if(m_apoDIConns[paDIPortId] == pa_poDataCon){ |
| //we have a reconfiguration attempt |
| configureGenericDI(paDIPortId, pa_poDataCon->getValue()); |
| bRetVal = true; |
| }else{ |
| DEVLOG_ERROR("%s cannot connect input data %s to more sources, using the latest connection attempt\n", CStringDictionary::getInstance().get(getFBTypeId()), CStringDictionary::getInstance().get(m_pstInterfaceSpec->m_aunDINames[paDIPortId])); |
| } |
| }else{ |
| m_apoDIConns[paDIPortId] = pa_poDataCon; |
| configureGenericDI(paDIPortId, pa_poDataCon->getValue()); |
| bRetVal = true; |
| } |
| } |
| } |
| return bRetVal; |
| } |
| |
| void CFunctionBlock::configureGenericDI(TPortId paDIPortId, const CIEC_ANY* paRefValue) { |
| if(getDI(paDIPortId)->getDataTypeID() == CIEC_ANY::e_ANY && (0 != paRefValue)) { |
| paRefValue->clone(reinterpret_cast<TForteByte *>(getDI(paDIPortId))); |
| } |
| } |
| |
| CDataConnection *CFunctionBlock::getDOConnection(CStringDictionary::TStringId paDONameId) const{ |
| CDataConnection *retVal = 0; |
| TPortId doPortID = getDOID(paDONameId); |
| if(cg_unInvalidPortId != doPortID){ |
| retVal = mDOConns + doPortID; |
| } |
| return retVal; |
| } |
| |
| CDataConnection *CFunctionBlock::getDIConnection(CStringDictionary::TStringId paDINameId) const{ |
| CDataConnection *retVal = 0; |
| TPortId diPortID = getDIID(paDINameId); |
| if(cg_unInvalidPortId != diPortID){ |
| retVal = m_apoDIConns[diPortID]; |
| } |
| return retVal; |
| } |
| |
| bool CFunctionBlock::configureGenericDO(TPortId paDOPortId, const CIEC_ANY &paRefValue){ |
| bool retVal = false; |
| |
| if(m_pstInterfaceSpec->m_nNumDOs > paDOPortId){ |
| CIEC_ANY *dataOutput = getDO(paDOPortId); |
| if(dataOutput->getDataTypeID() == CIEC_ANY::e_ANY){ |
| paRefValue.clone(reinterpret_cast<TForteByte *>(dataOutput)); |
| retVal = true; |
| } |
| } |
| return retVal; |
| } |
| |
| CIEC_ANY *CFunctionBlock::getDataOutput(CStringDictionary::TStringId pa_unDONameId) const { |
| CIEC_ANY *poRetVal = 0; |
| unsigned int unDID = getDOID(pa_unDONameId); |
| |
| if(cg_unInvalidPortId != unDID){ |
| poRetVal = getDO(unDID); |
| } |
| return poRetVal; |
| } |
| |
| CIEC_ANY *CFunctionBlock::getDataInput(CStringDictionary::TStringId pa_unDINameId) const { |
| CIEC_ANY *poRetVal = 0; |
| unsigned int unDID = getDIID(pa_unDINameId); |
| |
| if(cg_unInvalidPortId != unDID){ |
| poRetVal = getDI(unDID); |
| } |
| return poRetVal; |
| } |
| |
| CIEC_ANY* CFunctionBlock::getDIFromPortId(TPortId paDIPortId) const{ |
| CIEC_ANY *retVal = 0; |
| if(paDIPortId < m_pstInterfaceSpec->m_nNumDIs){ |
| retVal = getDI(paDIPortId); |
| } |
| return retVal; |
| } |
| |
| CIEC_ANY* CFunctionBlock::getDOFromPortId(TPortId paDOPortId) const{ |
| CIEC_ANY *retVal = 0; |
| if(paDOPortId < m_pstInterfaceSpec->m_nNumDOs){ |
| retVal = getDO(paDOPortId); |
| } |
| return retVal; |
| } |
| |
| CIEC_ANY *CFunctionBlock::getVar(CStringDictionary::TStringId *paNameList, |
| unsigned int paNameListSize){ |
| |
| CIEC_ANY *poRetVal = 0; |
| if(1 == paNameListSize){ |
| unsigned int portId = getDIID(*paNameList); |
| if(cg_unInvalidPortId != portId){ |
| poRetVal = getDI(portId); |
| } |
| else{ |
| portId = getDOID(*paNameList); |
| if(cg_unInvalidPortId != portId){ |
| poRetVal = getDO(portId); |
| } |
| } |
| } |
| return poRetVal; |
| } |
| |
| CAdapter *CFunctionBlock::getAdapter(CStringDictionary::TStringId paAdapterNameId) const{ |
| TPortId adpPortId = getAdapterPortId(paAdapterNameId); |
| |
| if(cg_unInvalidPortId != adpPortId){ |
| return m_apoAdapters[adpPortId]; |
| } |
| return 0; |
| } |
| |
| TPortId CFunctionBlock::getAdapterPortId(CStringDictionary::TStringId paAdapterNameId) const{ |
| for(TPortId i = 0; i < m_pstInterfaceSpec->m_nNumAdapters; ++i){ |
| if(m_apoAdapters[i]->getInstanceNameId() == paAdapterNameId){ |
| return i; |
| } |
| } |
| return cg_unInvalidPortId; |
| } |
| |
| void CFunctionBlock::sendOutputEvent(size_t paEO){ |
| FORTE_TRACE("OutputEvent: Function Block sending event: %d (maxid: %d)\n", paEO, m_pstInterfaceSpec->m_nNumEOs - 1); |
| if(paEO < m_pstInterfaceSpec->m_nNumEOs) { |
| if(0 != m_pstInterfaceSpec->m_anEOWithIndexes && -1 != m_pstInterfaceSpec->m_anEOWithIndexes[paEO]) { |
| const TDataIOID *eiWithStart = &(m_pstInterfaceSpec->m_anEOWith[m_pstInterfaceSpec->m_anEOWithIndexes[paEO]]); |
| //TODO think on this lock |
| CCriticalRegion criticalRegion(m_poResource->m_oResDataConSync); |
| for(size_t i = 0; eiWithStart[i] != scmWithListDelimiter; ++i) { |
| CDataConnection *con = getDOConUnchecked(eiWithStart[i]); |
| if(con->isConnected()) { |
| CIEC_ANY *dataOutput = getDO(eiWithStart[i]); |
| #ifdef FORTE_SUPPORT_MONITORING |
| if(dataOutput->isForced() != true) { |
| #endif //FORTE_SUPPORT_MONITORING |
| con->writeData(dataOutput); |
| #ifdef FORTE_SUPPORT_MONITORING |
| } else { |
| //when forcing we write back the value from the connection to keep the forced value on the output |
| con->readData(dataOutput); |
| } |
| #endif //FORTE_SUPPORT_MONITORING |
| } |
| } |
| } |
| |
| getEOConUnchecked(static_cast<TPortId>(paEO))->triggerEvent(*m_poInvokingExecEnv); |
| |
| #ifdef FORTE_SUPPORT_MONITORING |
| // Count Event for monitoring |
| mEOMonitorCount[paEO]++; |
| #endif //FORTE_SUPPORT_MONITORING |
| } |
| } |
| |
| void CFunctionBlock::sendAdapterEvent(size_t paAdapterID, size_t paEID) const{ |
| if((paAdapterID < m_pstInterfaceSpec->m_nNumAdapters) && (0 != m_apoAdapters[paAdapterID])){ |
| m_apoAdapters[paAdapterID]->receiveInputEvent(paEID, *m_poInvokingExecEnv); |
| } |
| } |
| |
| bool CFunctionBlock::configureFB(const char *){ |
| return true; |
| } |
| |
| void CFunctionBlock::receiveInputEvent(size_t paEIID, CEventChainExecutionThread &paExecEnv){ |
| FORTE_TRACE("InputEvent: Function Block (%s) got event: %d (maxid: %d)\n", CStringDictionary::getInstance().get(getInstanceNameId()), paEIID, m_pstInterfaceSpec->m_nNumEIs - 1); |
| |
| if(e_RUNNING == getState()){ |
| if(paEIID < m_pstInterfaceSpec->m_nNumEIs) { |
| if(0 != m_pstInterfaceSpec->m_anEIWithIndexes && -1 != m_pstInterfaceSpec->m_anEIWithIndexes[paEIID]) { |
| const TDataIOID *eiWithStart = &(m_pstInterfaceSpec->m_anEIWith[m_pstInterfaceSpec->m_anEIWithIndexes[paEIID]]); |
| |
| // TODO think on this lock |
| CCriticalRegion criticalRegion(m_poResource->m_oResDataConSync); |
| for(size_t i = 0; eiWithStart[i] != scmWithListDelimiter; ++i) { |
| if(0 != m_apoDIConns[eiWithStart[i]]) { |
| CIEC_ANY *di = getDI(eiWithStart[i]); |
| #ifdef FORTE_SUPPORT_MONITORING |
| if(true != di->isForced()) { |
| #endif //FORTE_SUPPORT_MONITORING |
| m_apoDIConns[eiWithStart[i]]->readData(di); |
| #ifdef FORTE_SUPPORT_MONITORING |
| } |
| #endif //FORTE_SUPPORT_MONITORING |
| } |
| } |
| } |
| #ifdef FORTE_SUPPORT_MONITORING |
| // Count Event for monitoring |
| mEIMonitorCount[paEIID]++; |
| #endif //FORTE_SUPPORT_MONITORING |
| } |
| m_poInvokingExecEnv = &paExecEnv; |
| executeEvent(paEIID); |
| } |
| } |
| |
| EMGMResponse CFunctionBlock::changeFBExecutionState(EMGMCommandType pa_unCommand){ |
| EMGMResponse nRetVal = e_INVALID_STATE; |
| switch (pa_unCommand){ |
| case cg_nMGM_CMD_Start: |
| if((e_IDLE == m_enFBState) || (e_STOPPED == m_enFBState)){ |
| m_enFBState = e_RUNNING; |
| nRetVal = e_RDY; |
| } |
| break; |
| case cg_nMGM_CMD_Stop: |
| if(e_RUNNING == m_enFBState){ |
| m_enFBState = e_STOPPED; |
| nRetVal = e_RDY; |
| } |
| break; |
| case cg_nMGM_CMD_Kill: |
| if(e_RUNNING == m_enFBState){ |
| m_enFBState = e_KILLED; |
| nRetVal = e_RDY; |
| } |
| break; |
| case cg_nMGM_CMD_Reset: |
| if((e_STOPPED == m_enFBState) || (e_KILLED == m_enFBState)){ |
| m_enFBState = e_IDLE; |
| nRetVal = e_RDY; |
| setInitialValues(); |
| } |
| break; |
| default: |
| nRetVal = e_INVALID_OPERATION; |
| break; |
| } |
| |
| if(e_RDY == nRetVal && 0 != m_pstInterfaceSpec) { |
| for(int i = 0; i < m_pstInterfaceSpec->m_nNumAdapters; ++i) { |
| if(0 != m_apoAdapters[i]) { |
| m_apoAdapters[i]->changeFBExecutionState(pa_unCommand); |
| } |
| } |
| } |
| return nRetVal; |
| } |
| |
| CIEC_ANY *CFunctionBlock::createDataPoint(const CStringDictionary::TStringId **pa_panDataTypeIds, TForteByte *pa_acDataBuf){ |
| CIEC_ANY *poRetVal = CTypeLib::createDataTypeInstance(**pa_panDataTypeIds, pa_acDataBuf); |
| ++(*pa_panDataTypeIds); |
| #ifdef FORTE_SUPPORT_ARRAYS |
| if(g_nStringIdARRAY == (*pa_panDataTypeIds)[-1]){ |
| if(0 != poRetVal){ |
| //For an array we have to do more |
| (static_cast<CIEC_ARRAY *>(poRetVal))->setup(static_cast<TForteUInt16>(**pa_panDataTypeIds), (*pa_panDataTypeIds)[1]); |
| } |
| (*pa_panDataTypeIds) += 2; |
| } |
| #endif |
| return poRetVal; |
| } |
| |
| void CFunctionBlock::setupFBInterface(const SFBInterfaceSpec *pa_pstInterfaceSpec, TForteByte *pa_acFBConnData, TForteByte *pa_acFBVarsData){ |
| m_pstInterfaceSpec = const_cast<SFBInterfaceSpec *>(pa_pstInterfaceSpec); |
| |
| if(0 != pa_pstInterfaceSpec){ |
| if((0 != pa_acFBConnData) && (0 != pa_acFBVarsData)){ |
| TPortId i; |
| if(m_pstInterfaceSpec->m_nNumEOs){ |
| mEOConns = reinterpret_cast<CEventConnection *>(pa_acFBConnData); |
| |
| for(i = 0; i < m_pstInterfaceSpec->m_nNumEOs; ++i){ |
| //create an event connection for each event output and initialize its source port |
| new (pa_acFBConnData)CEventConnection(this, i); |
| pa_acFBConnData += sizeof(CEventConnection); |
| } |
| } |
| else{ |
| mEOConns = 0; |
| } |
| |
| const CStringDictionary::TStringId *pnDataIds; |
| if(m_pstInterfaceSpec->m_nNumDIs){ |
| m_apoDIConns = reinterpret_cast<TDataConnectionPtr *>(pa_acFBConnData); |
| pa_acFBConnData += sizeof(TDataConnectionPtr) * m_pstInterfaceSpec->m_nNumDIs; |
| |
| //let m_aoDIs point to the first data input |
| m_aoDIs = reinterpret_cast<CIEC_ANY *>(pa_acFBVarsData); |
| |
| pnDataIds = pa_pstInterfaceSpec->m_aunDIDataTypeNames; |
| for(i = 0; i < m_pstInterfaceSpec->m_nNumDIs; ++i){ |
| m_apoDIConns[i] = 0; |
| createDataPoint(&pnDataIds, pa_acFBVarsData); |
| pa_acFBVarsData += sizeof(CIEC_ANY); |
| } |
| } |
| else{ |
| m_apoDIConns = 0; |
| m_aoDIs = 0; |
| } |
| |
| if(m_pstInterfaceSpec->m_nNumDOs){ |
| //let mDOConns point to the first data output connection |
| mDOConns = reinterpret_cast<CDataConnection *>(pa_acFBConnData); |
| |
| //let m_aoDIs point to the first data output |
| m_aoDOs = reinterpret_cast<CIEC_ANY *>(pa_acFBVarsData); |
| |
| pnDataIds = pa_pstInterfaceSpec->m_aunDODataTypeNames; |
| for(i = 0; i < m_pstInterfaceSpec->m_nNumDOs; ++i){ |
| createDataPoint(&pnDataIds, pa_acFBVarsData); |
| pa_acFBVarsData += sizeof(CIEC_ANY); |
| //create an data connection for each data output and initialize its source port |
| new (pa_acFBConnData)CDataConnection(this, i, getDO(i)); |
| pa_acFBConnData += sizeof(CDataConnection); |
| } |
| } |
| else{ |
| mDOConns = 0; |
| m_aoDOs = 0; |
| } |
| if(m_pstInterfaceSpec->m_nNumAdapters){ |
| setupAdapters(pa_pstInterfaceSpec, pa_acFBVarsData); |
| } |
| } |
| #ifdef FORTE_SUPPORT_MONITORING |
| setupEventMonitoringData(); |
| #endif |
| } |
| |
| } |
| |
| TPortId CFunctionBlock::getPortId(CStringDictionary::TStringId pa_unPortNameId, TPortId pa_unMaxPortNames, const CStringDictionary::TStringId* pa_aunPortNames){ |
| for(TPortId i = 0; i < pa_unMaxPortNames; ++i){ |
| if(pa_unPortNameId == pa_aunPortNames[i]){ |
| return i; |
| } |
| } |
| return cg_unInvalidPortId; |
| } |
| |
| //********************************** below here are monitoring specific functions ********************************************************** |
| #ifdef FORTE_SUPPORT_MONITORING |
| void CFunctionBlock::setupEventMonitoringData(){ |
| if(0 != m_pstInterfaceSpec->m_nNumEIs){ |
| mEIMonitorCount = new TForteUInt32[m_pstInterfaceSpec->m_nNumEIs]; |
| memset(mEIMonitorCount, 0, sizeof(TForteUInt32) * m_pstInterfaceSpec->m_nNumEIs); |
| } |
| |
| if(0 != m_pstInterfaceSpec->m_nNumEOs){ |
| mEOMonitorCount = new TForteUInt32[m_pstInterfaceSpec->m_nNumEOs]; |
| memset(mEOMonitorCount, 0, sizeof(TForteUInt32) * m_pstInterfaceSpec->m_nNumEOs); |
| } |
| } |
| |
| |
| CFunctionBlock *CFunctionBlock::getFB(forte::core::TNameIdentifier::CIterator &paNameListIt){ |
| CFunctionBlock *retVal = 0; |
| |
| if(paNameListIt.isLastEntry()){ |
| //only check for adpaters if it we have the last entry in the line |
| retVal = getAdapter(*paNameListIt); |
| } |
| |
| return retVal; |
| } |
| |
| TForteUInt32 &CFunctionBlock::getEIMonitorData(TEventID paEIID){ |
| return mEIMonitorCount[paEIID]; |
| } |
| |
| TForteUInt32 &CFunctionBlock::getEOMonitorData(TEventID paEOID){ |
| return mEOMonitorCount[paEOID]; |
| } |
| |
| #endif //FORTE_SUPPORT_MONITORING |