| /******************************************************************************* |
| * Copyright (c) 2015, 2018 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: |
| * Alois Zoitl |
| * - initial implementation and rework communication infrastructure |
| *******************************************************************************/ |
| #include "monitoring.h" |
| #include "resource.h" |
| #include "device.h" |
| #include "ecet.h" |
| #include "utils/criticalregion.h" |
| #include "utils/string_utils.h" |
| |
| |
| using namespace forte::core; |
| |
| CMonitoringHandler::CMonitoringHandler(CResource &paResource) : |
| mTriggerEvent(0, 0), |
| mResource(paResource){ |
| } |
| |
| EMGMResponse CMonitoringHandler::executeMonitoringCommand(SManagementCMD &paCommand){ |
| EMGMResponse retVal = e_UNSUPPORTED_CMD; |
| |
| switch (paCommand.mCMD){ |
| case cg_nMGM_CMD_Monitoring_Add_Watch: |
| retVal = addWatch(paCommand.mFirstParam); |
| break; |
| case cg_nMGM_CMD_Monitoring_Remove_Watch: |
| retVal = removeWatch(paCommand.mFirstParam); |
| break; |
| case cg_nMGM_CMD_Monitoring_Read_Watches: |
| retVal = readWatches(paCommand.mMonitorResponse); |
| break; |
| case cg_nMGM_CMD_Monitoring_Force: |
| retVal = mResource.writeValue(paCommand.mFirstParam, paCommand.mAdditionalParams, true); |
| break; |
| case cg_nMGM_CMD_Monitoring_ClearForce: |
| retVal = clearForce(paCommand.mFirstParam); |
| break; |
| case cg_nMGM_CMD_Monitoring_Trigger_Event: |
| retVal = triggerEvent(paCommand.mFirstParam); |
| break; |
| case cg_nMGM_CMD_Monitoring_Reset_Event_Count: |
| retVal = resetEventCount(paCommand.mFirstParam); |
| break; |
| default: |
| break; |
| } |
| return retVal; |
| } |
| |
| CFunctionBlock* CMonitoringHandler::getFB(forte::core::TNameIdentifier &paNameList){ |
| forte::core::TNameIdentifier::CIterator runner(paNameList.begin()); |
| |
| CFunctionBlock *fb = mResource.getContainedFB(runner); |
| if((0 != fb) && (!runner.isLastEntry())){ |
| ++runner; |
| fb = fb->getFB(runner); |
| } |
| return fb; |
| } |
| |
| EMGMResponse CMonitoringHandler::addWatch(forte::core::TNameIdentifier &paNameList){ |
| EMGMResponse eRetVal = e_NO_SUCH_OBJECT; |
| |
| CStringDictionary::TStringId portName = paNameList.back(); |
| paNameList.popBack(); |
| CFunctionBlock *fB = getFB(paNameList); |
| |
| if(0 != fB){ |
| SFBMonitoringEntry &fbMonitoringEntry(findOrCreateFBMonitoringEntry(fB, paNameList)); |
| |
| CIEC_ANY *dataVal = fB->getVar(&portName, 1); |
| if(0 != dataVal){ |
| addDataWatch(fbMonitoringEntry, portName, *dataVal); |
| eRetVal = e_RDY; |
| } |
| else{ |
| TEventID eventId = fB->getEIID(portName); |
| if(cg_nInvalidEventID != eventId){ |
| addEventWatch(fbMonitoringEntry, portName, fB->getEIMonitorData(eventId)); |
| eRetVal = e_RDY; |
| } |
| else{ |
| eventId = fB->getEOID(portName); |
| if(cg_nInvalidEventID != eventId){ |
| addEventWatch(fbMonitoringEntry, portName, fB->getEOMonitorData(eventId)); |
| eRetVal = e_RDY; |
| } |
| } |
| } |
| } |
| |
| return eRetVal; |
| } |
| |
| EMGMResponse CMonitoringHandler::removeWatch(forte::core::TNameIdentifier &paNameList){ |
| EMGMResponse eRetVal = e_NO_SUCH_OBJECT; |
| |
| CStringDictionary::TStringId portName = paNameList.back(); |
| paNameList.popBack(); |
| CFunctionBlock *fB = getFB(paNameList); |
| if(0 != fB){ |
| TFBMonitoringList::Iterator itRefNode = mFBMonitoringList.end(); //needed for deleting the entry from the list |
| TFBMonitoringList::Iterator itRunner = mFBMonitoringList.begin(); |
| |
| while(itRunner != mFBMonitoringList.end()){ |
| if(itRunner->m_poFB == fB){ |
| SFBMonitoringEntry &poFBMonitoringEntry(*itRunner); |
| |
| if(removeDataWatch(poFBMonitoringEntry, portName) || removeEventWatch(poFBMonitoringEntry, portName)){ //if element is not watched, end search and return error |
| |
| if(poFBMonitoringEntry.m_lstWatchedDataPoints.isEmpty() && (poFBMonitoringEntry.m_lstWatchedEventPoints.isEmpty())){ |
| //no further values are monitored so remove the entry |
| if(itRefNode == mFBMonitoringList.end()){ |
| //we have the first entry in the list |
| mFBMonitoringList.popFront(); |
| }else{ |
| mFBMonitoringList.eraseAfter(itRefNode); |
| } |
| } |
| eRetVal = e_RDY; |
| } |
| break; |
| } |
| |
| itRefNode = itRunner; |
| ++itRunner; |
| } |
| } |
| return eRetVal; |
| } |
| |
| EMGMResponse CMonitoringHandler::readWatches(CIEC_STRING &paResponse){ |
| paResponse.clear(); |
| if(0 == mResource.getResourcePtr()){ |
| //we are in the device |
| for(CFBContainer::TFunctionBlockList::Iterator itRunner = mResource.getFBList().begin(); |
| itRunner != mResource.getFBList().end(); |
| ++itRunner){ |
| ((CResource*) (*itRunner))->getMonitoringHandler().readResourceWatches(paResponse); |
| } |
| } |
| else{ |
| //we are within a resource |
| readResourceWatches(paResponse); |
| } |
| |
| return e_RDY; |
| } |
| |
| EMGMResponse CMonitoringHandler::clearForce(forte::core::TNameIdentifier &paNameList){ |
| EMGMResponse eRetVal = e_NO_SUCH_OBJECT; |
| CStringDictionary::TStringId portName = paNameList.back(); |
| paNameList.popBack(); |
| CFunctionBlock *fB = getFB(paNameList); |
| |
| if(0 != fB){ |
| CIEC_ANY *poDataVal = fB->getVar(&portName, 1); |
| if(0 != poDataVal){ |
| poDataVal->setForced(false); |
| eRetVal = e_RDY; |
| } |
| } |
| return eRetVal; |
| } |
| |
| EMGMResponse CMonitoringHandler::triggerEvent(forte::core::TNameIdentifier &paNameList){ |
| EMGMResponse eRetVal = e_NO_SUCH_OBJECT; |
| CStringDictionary::TStringId portName = paNameList.back(); |
| paNameList.popBack(); |
| CFunctionBlock *fB = getFB(paNameList); |
| if(0 != fB){ |
| TEventID eventId = fB->getEIID(portName); |
| if(cg_nInvalidEventID != eventId){ |
| //only a single event can be triggered simultaneously (until it is executed by ecet!) |
| //otherwise the triggerEvent structure needs to be moved to another place! |
| mTriggerEvent.mFB = fB; |
| mTriggerEvent.mPortId = eventId; |
| mResource.getResourceEventExecution()->startEventChain(&mTriggerEvent); |
| eRetVal = e_RDY; |
| } |
| else{ |
| eventId = fB->getEOID(portName); |
| if(cg_nInvalidEventID != eventId){ |
| fB->m_poInvokingExecEnv = mResource.getResourceEventExecution(); |
| fB->sendOutputEvent(eventId); |
| mResource.getResourceEventExecution()->resumeSelfSuspend(); |
| eRetVal = e_RDY; |
| } |
| } |
| |
| } |
| return eRetVal; |
| } |
| |
| EMGMResponse CMonitoringHandler::resetEventCount(forte::core::TNameIdentifier &paNameList){ |
| EMGMResponse eRetVal = e_NO_SUCH_OBJECT; |
| CStringDictionary::TStringId portName = paNameList.back(); |
| paNameList.popBack(); |
| CFunctionBlock *fB = getFB(paNameList); |
| if(0 != fB){ |
| TEventID eventId = fB->getEIID(portName); |
| TForteUInt32 *eventMonitorData = 0; |
| |
| if(cg_nInvalidEventID != eventId){ |
| eventMonitorData = &fB->getEIMonitorData(eventId); |
| } |
| else{ |
| eventId = fB->getEOID(portName); |
| if(cg_nInvalidEventID != eventId){ |
| eventMonitorData = &fB->getEOMonitorData(eventId); |
| } |
| } |
| if(0 != eventMonitorData){ |
| CCriticalRegion criticalRegion(fB->getResource().m_oResDataConSync); |
| *eventMonitorData = 0; |
| eRetVal = e_RDY; |
| } |
| } |
| return eRetVal; |
| } |
| |
| CMonitoringHandler::SFBMonitoringEntry &CMonitoringHandler::findOrCreateFBMonitoringEntry( |
| CFunctionBlock *paFB, forte::core::TNameIdentifier &paNameList){ |
| for(TFBMonitoringList::Iterator itRunner = mFBMonitoringList.begin(); |
| itRunner != mFBMonitoringList.end(); ++itRunner){ |
| if(itRunner->m_poFB == paFB){ |
| return *itRunner; |
| } |
| } |
| |
| mFBMonitoringList.pushBack(SFBMonitoringEntry()); |
| TFBMonitoringList::Iterator itLastEntry(mFBMonitoringList.back()); |
| itLastEntry->m_poFB = paFB; |
| createFullFBName(itLastEntry->mFullFBName, paNameList); |
| return *itLastEntry; |
| } |
| |
| void CMonitoringHandler::addDataWatch(SFBMonitoringEntry &paFBMonitoringEntry, |
| CStringDictionary::TStringId paPortId, CIEC_ANY &paDataVal){ |
| for(TDataWatchList::Iterator itRunner = paFBMonitoringEntry.m_lstWatchedDataPoints.begin(); |
| itRunner != paFBMonitoringEntry.m_lstWatchedDataPoints.end(); ++itRunner){ |
| if(itRunner->mPortId == paPortId){ |
| //the data point is already in the watch list |
| return; |
| } |
| } |
| paFBMonitoringEntry.m_lstWatchedDataPoints.pushBack(SDataWatchEntry(paPortId, paDataVal)); |
| } |
| |
| bool CMonitoringHandler::removeDataWatch(SFBMonitoringEntry &paFBMonitoringEntry, |
| CStringDictionary::TStringId paPortId){ |
| bool bRetVal = false; |
| |
| TDataWatchList::Iterator itRunner = paFBMonitoringEntry.m_lstWatchedDataPoints.begin(); |
| TDataWatchList::Iterator itRefNode = paFBMonitoringEntry.m_lstWatchedDataPoints.end(); |
| |
| while(itRunner != paFBMonitoringEntry.m_lstWatchedDataPoints.end()){ |
| if(itRunner->mPortId == paPortId){ |
| if(itRefNode == paFBMonitoringEntry.m_lstWatchedDataPoints.end()){ |
| //we have the first entry in the list |
| paFBMonitoringEntry.m_lstWatchedDataPoints.popFront(); |
| } |
| else{ |
| paFBMonitoringEntry.m_lstWatchedDataPoints.eraseAfter(itRefNode); |
| } |
| bRetVal = true; |
| break; |
| } |
| |
| itRefNode = itRunner; |
| ++itRunner; |
| } |
| |
| return bRetVal; |
| } |
| |
| void CMonitoringHandler::addEventWatch(SFBMonitoringEntry &paFBMonitoringEntry, CStringDictionary::TStringId paPortId, TForteUInt32 &paEventData){ |
| for(TEventWatchList::Iterator itRunner = paFBMonitoringEntry.m_lstWatchedEventPoints.begin(); |
| itRunner != paFBMonitoringEntry.m_lstWatchedEventPoints.end(); ++itRunner){ |
| if(itRunner->mPortId == paPortId){ |
| //the event point is already in the watch list |
| return; |
| } |
| } |
| paFBMonitoringEntry.m_lstWatchedEventPoints.pushBack(SEventWatchEntry(paPortId, paEventData)); |
| } |
| |
| bool CMonitoringHandler::removeEventWatch(SFBMonitoringEntry &paFBMonitoringEntry, CStringDictionary::TStringId paPortId){ |
| bool bRetVal = false; |
| |
| TEventWatchList::Iterator itRunner = paFBMonitoringEntry.m_lstWatchedEventPoints.begin(); |
| TEventWatchList::Iterator itRefNode = paFBMonitoringEntry.m_lstWatchedEventPoints.end(); |
| |
| while(itRunner != paFBMonitoringEntry.m_lstWatchedEventPoints.end()){ |
| if(itRunner->mPortId == paPortId){ |
| if(itRefNode == paFBMonitoringEntry.m_lstWatchedEventPoints.end()){ |
| //we have the first entry in the list |
| paFBMonitoringEntry.m_lstWatchedEventPoints.popFront(); |
| } |
| else{ |
| paFBMonitoringEntry.m_lstWatchedEventPoints.eraseAfter(itRefNode); |
| } |
| bRetVal = true; |
| break; |
| } |
| |
| itRefNode = itRunner; |
| ++itRunner; |
| } |
| |
| return bRetVal; |
| } |
| |
| void CMonitoringHandler::readResourceWatches(CIEC_STRING &paResponse){ |
| if(!mFBMonitoringList.isEmpty()){ |
| paResponse.append("<Resource name=\""); |
| paResponse.append(mResource.getInstanceName()); |
| paResponse.append("\">"); |
| |
| { //begin critical region |
| CCriticalRegion criticalRegion(mResource.m_oResDataConSync); |
| |
| for(TFBMonitoringList::Iterator itRunner = mFBMonitoringList.begin(); |
| itRunner != mFBMonitoringList.end(); ++itRunner){ |
| paResponse.append("<FB name=\""); |
| paResponse.append(itRunner->mFullFBName.getValue()); |
| paResponse.append("\">"); |
| |
| //add the data watches |
| for(TDataWatchList::Iterator itDataRunner = itRunner->m_lstWatchedDataPoints.begin(); |
| itDataRunner != itRunner->m_lstWatchedDataPoints.end(); ++itDataRunner){ |
| appendDataWatch(paResponse, *itDataRunner); |
| } |
| |
| //add the event watches |
| for(TEventWatchList::Iterator itEventRunner = itRunner->m_lstWatchedEventPoints.begin(); |
| itEventRunner != itRunner->m_lstWatchedEventPoints.end(); |
| ++itEventRunner){ |
| appendEventWatch(paResponse, *itEventRunner); |
| } |
| |
| paResponse.append("</FB>"); |
| } |
| } //end critical region |
| paResponse.append("</Resource>"); |
| } |
| } |
| |
| void CMonitoringHandler::appendDataWatch(CIEC_STRING &paResponse, |
| SDataWatchEntry &paDataWatchEntry){ |
| size_t bufferSize = paDataWatchEntry.mDataValue.getToStringBufferSize() + getExtraSizeForEscapedChars(paDataWatchEntry.mDataValue); |
| appendPortTag(paResponse, paDataWatchEntry.mPortId); |
| paResponse.append("<Data value=\""); |
| char* acDataValue = new char [bufferSize]; //TODO try to directly use the response string instead |
| int consumedBytes = -1; |
| switch (paDataWatchEntry.mDataValue.getDataTypeID()){ |
| case CIEC_ANY::e_WSTRING: |
| case CIEC_ANY::e_STRING: |
| consumedBytes = static_cast<CIEC_WSTRING&>(paDataWatchEntry.mDataValue).toUTF8(acDataValue, bufferSize, false); |
| if(bufferSize != paDataWatchEntry.mDataValue.getToStringBufferSize() && 0 < consumedBytes) { //avoid re-running on strings which were already proven not to have any special character |
| consumedBytes += static_cast<int>(forte::core::util::transformNonEscapedToEscapedXMLText(acDataValue)); |
| } |
| break; |
| case CIEC_ANY::e_ARRAY: |
| case CIEC_ANY::e_STRUCT: |
| consumedBytes = paDataWatchEntry.mDataValue.toString(acDataValue, bufferSize); |
| if(bufferSize != paDataWatchEntry.mDataValue.getToStringBufferSize() && 0 < consumedBytes) { //avoid re-running on elements which were already proven not to have any special character |
| consumedBytes += static_cast<int>(forte::core::util::transformNonEscapedToEscapedXMLText(acDataValue)); |
| } |
| break; |
| default: |
| consumedBytes = paDataWatchEntry.mDataValue.toString(acDataValue, bufferSize); |
| break; |
| } |
| if(-1 != consumedBytes){ |
| acDataValue[consumedBytes] = '\0'; |
| } |
| paResponse.append(acDataValue); |
| paResponse.append("\" forced=\""); |
| paResponse.append((paDataWatchEntry.mDataValue.isForced()) ? "true" : "false"); |
| paResponse.append("\"/></Port>"); |
| delete [] acDataValue; |
| } |
| |
| size_t CMonitoringHandler::getExtraSizeForEscapedChars(const CIEC_ANY& paDataValue){ |
| size_t retVal = 0; |
| |
| switch(paDataValue.getDataTypeID()){ |
| case CIEC_ANY::e_WSTRING: |
| case CIEC_ANY::e_STRING: |
| retVal = forte::core::util::getExtraSizeForEscapedChars(static_cast<const CIEC_WSTRING&>(paDataValue).getValue()); |
| break; |
| case CIEC_ANY::e_ARRAY: |
| retVal = getExtraSizeForEscapedCharsArray(static_cast<const CIEC_ARRAY&>(paDataValue)); |
| break; |
| case CIEC_ANY::e_STRUCT: |
| retVal = getExtraSizeForEscapedCharsStruct(static_cast<const CIEC_STRUCT&>(paDataValue)); |
| break; |
| default: |
| break; |
| } |
| |
| return retVal; |
| } |
| |
| size_t CMonitoringHandler::getExtraSizeForEscapedCharsArray(const CIEC_ARRAY& paDataValue){ |
| size_t retVal = 0; |
| |
| switch(paDataValue[0]->getDataTypeID()){ |
| case CIEC_ANY::e_WSTRING: |
| case CIEC_ANY::e_STRING: |
| for(size_t i = 0; i < paDataValue.size(); i++) { |
| retVal += forte::core::util::getExtraSizeForEscapedChars(static_cast<const CIEC_WSTRING*>(paDataValue[static_cast<TForteUInt16>(i)])->getValue()) + 10; //for opening and closing quotes or apos |
| } |
| break; |
| case CIEC_ANY::e_STRUCT: |
| for(size_t i = 0; i < paDataValue.size(); i++) { |
| retVal += getExtraSizeForEscapedCharsStruct(*static_cast<const CIEC_STRUCT*>(paDataValue[static_cast<TForteUInt16>(i)])); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return retVal; |
| } |
| |
| size_t CMonitoringHandler::getExtraSizeForEscapedCharsStruct(const CIEC_STRUCT& paDataValue) { |
| size_t retVal = 0; |
| |
| const CIEC_ANY* members = paDataValue.getMembers(); |
| for(size_t i = 0; i < paDataValue.getStructSize(); i++) { |
| switch(members[i].getDataTypeID()){ |
| case CIEC_ANY::e_WSTRING: |
| case CIEC_ANY::e_STRING: |
| retVal += forte::core::util::getExtraSizeForEscapedChars(static_cast<const CIEC_WSTRING&>(members[i]).getValue()) + 10; //for opening and closing quotes or apos |
| break; |
| case CIEC_ANY::e_ARRAY: |
| retVal += getExtraSizeForEscapedCharsArray(static_cast<const CIEC_ARRAY&>(members[i])); |
| break; |
| case CIEC_ANY::e_STRUCT: |
| retVal += getExtraSizeForEscapedCharsStruct(static_cast<const CIEC_STRUCT&>(members[i])); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return retVal; |
| } |
| |
| void CMonitoringHandler::appendPortTag(CIEC_STRING &paResponse, |
| CStringDictionary::TStringId paPortId){ |
| paResponse.append("<Port name=\""); |
| paResponse.append(CStringDictionary::getInstance().get(paPortId)); |
| paResponse.append("\">"); |
| } |
| |
| void CMonitoringHandler::appendEventWatch(CIEC_STRING &paResponse, SEventWatchEntry &paEventWatchEntry){ |
| appendPortTag(paResponse, paEventWatchEntry.mPortId); |
| |
| CIEC_UDINT udint(paEventWatchEntry.mEventData); |
| CIEC_ULINT ulint(mResource.getDevice().getTimer().getForteTime()); |
| |
| paResponse.append("<Data value=\""); |
| char buf[21]; // the bigest number in an ulint is 18446744073709551616, TODO directly use pa_roResponse |
| udint.toString(buf, sizeof(buf)); |
| paResponse.append(buf); |
| paResponse.append("\" time=\""); |
| ulint.toString(buf, sizeof(buf)); |
| paResponse.append(buf); |
| paResponse.append("\"/>\n</Port>"); |
| } |
| |
| void CMonitoringHandler::createFullFBName(CIEC_STRING &paFullName, forte::core::TNameIdentifier &paNameList){ |
| for(forte::core::TNameIdentifier::CIterator runner(paNameList.begin()); runner != paNameList.end(); ++runner){ |
| paFullName.append(CStringDictionary::getInstance().get(*runner)); |
| if(!runner.isLastEntry()){ |
| paFullName.append("."); |
| } |
| } |
| } |
| |