blob: 3158a2be70cd14b160d458761a2e944b01b79328 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 fortiss GmbH
*
* 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:
* Jose Cabral - initial implementation
*******************************************************************************/
#include "opcua_action_info.h"
#include "../../core/utils/parameterParser.h"
#include "../../core/cominfra/basecommfb.h"
const char * const CActionInfo::mActionNames[] = { "READ", "WRITE", "CREATE_METHOD", "CALL_METHOD", "SUBSCRIBE", "CREATE_OBJECT", "DELETE_OBJECT" };
CActionInfo::CActionInfo(COPC_UA_Layer &paLayer, UA_ActionType paAction, CIEC_STRING &paEndpoint) :
mAction(paAction), mLayer(paLayer), mEndpoint(paEndpoint) {
}
CActionInfo::~CActionInfo() {
for(CSinglyLinkedList<CNodePairInfo*>::Iterator it = mNodePair.begin(); it != mNodePair.end(); ++it) {
if((*it)->mNodeId) {
UA_NodeId_delete((*it)->mNodeId);
}
delete (*it);
}
}
bool CActionInfo::isRemote() {
return ("" != mEndpoint);
}
CActionInfo* CActionInfo::getActionInfoFromParams(const char* paParams, COPC_UA_Layer &paLayer) {
CActionInfo *retVal = 0;
CParameterParser mainParser(paParams, ';');
size_t amountOfParameters = mainParser.parseParameters();
if(scmMinimumAmounOfParameters <= amountOfParameters) {
CActionInfo::UA_ActionType action = CActionParser::getActionEnum(mainParser[IDPositions::eActionType]);
if(CActionInfo::eActionUnknown != action) {
CIEC_STRING endpoint;
size_t startOfNodePairs = IDPositions::eNodePairs;
if(!CActionParser::getEndpoint(mainParser[IDPositions::eEndpoint], endpoint)) {
startOfNodePairs--;
}
if(CActionInfo::eCreateMethod == action) {
retVal = new CLocalMethodInfo(paLayer, endpoint);
} else {
retVal = new CActionInfo(paLayer, action, endpoint);
}
bool somethingFailed = false;
for(size_t i = startOfNodePairs; i < amountOfParameters; i++) {
if(!CActionParser::handlePair(mainParser[i], retVal->getNodePairInfo())) {
somethingFailed = true;
break;
}
}
if(!somethingFailed) {
somethingFailed = !retVal->checkAction();
}
if(somethingFailed) {
delete retVal;
retVal = 0;
}
}
} else {
DEVLOG_ERROR("[OPC UA ACTION]: Parameters %s should have at least %d parts, separated by a semicolon\n", paParams, scmMinimumAmounOfParameters);
}
return retVal;
}
const CIEC_ANY *CActionInfo::getDataToSend() {
return mLayer.getCommFB()->getSDs();
}
CIEC_ANY *CActionInfo::getDataToReceive() {
return mLayer.getCommFB()->getRDs();
}
size_t CActionInfo::getSendSize() {
return static_cast<size_t>(mLayer.getCommFB()->getNumSD());
}
size_t CActionInfo::getReceiveSize() {
return static_cast<size_t>(mLayer.getCommFB()->getNumRD());
}
bool CActionInfo::checkAction() {
bool retVal = false;
if(checkNodePairInfo()) {
forte::com_infra::EComServiceType fbType = mLayer.getCommFB()->getComServiceType();
unsigned int noOfRDs = mLayer.getCommFB()->getNumRD();
unsigned int noOfSDs = mLayer.getCommFB()->getNumSD();
switch(mAction){
case eRead:
retVal = checkReadAction(fbType, noOfRDs, noOfSDs);
break;
case eWrite:
retVal = checkWriteAction(fbType, noOfRDs, noOfSDs);
break;
case eCreateMethod:
retVal = checkCreateMethodAction(fbType, noOfRDs, noOfSDs);
break;
case eCallMethod:
retVal = checkCallMethodAction(fbType, noOfRDs, noOfSDs);
break;
case eSubscribe:
retVal = checkSubscribeAction(fbType, noOfRDs, noOfSDs);
break;
case eCreateObject:
retVal = checkCreateObjectAction(fbType, noOfRDs, noOfSDs);
break;
case eDeleteObject:
retVal = checkDeleteObjectAction(fbType, noOfRDs, noOfSDs);
break;
default:
DEVLOG_ERROR("[OPC UA ACTION]: Unknown action %d\n", mAction);
}
}
return retVal;
}
bool CActionInfo::checkNodePairInfo() {
bool retVal = true;
for(CSinglyLinkedList<CNodePairInfo*>::Iterator it = mNodePair.begin(); it != mNodePair.end(); ++it) {
if("" == (*it)->mBrowsePath && 0 == (*it)->mNodeId) { //browsePath AND/OR NodeId must be given. If both are empty there's a problem
DEVLOG_ERROR("[OPC UA ACTION]: BrowsePath and NodeId are empty in FB %s\n", mLayer.getCommFB()->getInstanceName());
retVal = false;
break;
}
}
return retVal;
}
bool CActionInfo::checkReadAction(forte::com_infra::EComServiceType paFbType, unsigned int paNoOfRDs, unsigned int paNoOfSDs) {
bool retVal = false;
if("" == mEndpoint) {
if(forte::com_infra::EComServiceType::e_Subscriber == paFbType && paNoOfRDs == getNoOfNodePairs()) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: In FB %s: Local %s action is only allowed using a Subscriber FB and the amount of BrowseName,NodeId pairs should match the number of RDs\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eRead]);
}
} else {
if(forte::com_infra::EComServiceType::e_Client == paFbType && paNoOfRDs == getNoOfNodePairs() && 0 == paNoOfSDs) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: Remote %s action is only allowed using a Client FB, the amount of BrowseName,NodeId pairs should match the number of RDs, and must have no SDs\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eRead]);
}
}
return retVal;
}
bool CActionInfo::checkWriteAction(forte::com_infra::EComServiceType paFbType, unsigned int paNoOfRDs, unsigned int paNoOfSDs) {
bool retVal = false;
if("" == mEndpoint) {
if(forte::com_infra::EComServiceType::e_Publisher == paFbType && paNoOfSDs == getNoOfNodePairs()) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: In FB %s: Local action %s is only allowed using a Publisher FB and the amount of BrowseName,NodeId pairs should match the number of SDs\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eWrite]);
}
} else {
if(forte::com_infra::EComServiceType::e_Client == paFbType && paNoOfSDs == getNoOfNodePairs() && 0 == paNoOfRDs) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: In FB %s: Remote action %s is only allowed using a Client FB, the amount of BrowseName,NodeId pairs should match the number of SDs and must have no RDs\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eWrite]);
}
}
return retVal;
}
bool CActionInfo::checkCreateMethodAction(forte::com_infra::EComServiceType paFbType, unsigned int, unsigned int) {
bool retVal = false;
if(forte::com_infra::EComServiceType::e_Server == paFbType && 1 == getNoOfNodePairs()) {
retVal = true;
} else {
DEVLOG_ERROR("[OPC UA ACTION]: In FB %s: %s action is only allowed using a Server FB, the amount of BrowseName,NodeId pairs should be 1\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eCreateMethod]);
}
return retVal;
}
bool CActionInfo::checkCallMethodAction(forte::com_infra::EComServiceType paFbType, unsigned int, unsigned int) {
bool retVal = false;
if(forte::com_infra::EComServiceType::e_Client == paFbType && 1 == getNoOfNodePairs() && "" != (*(mNodePair.begin()))->mBrowsePath) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: In FB %s: %s action is only allowed using a Client FB, the amount of BrowseName,NodeId pairs should be 1, and the browsepath shouldn't be empty\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eCallMethod]);
}
return retVal;
}
bool CActionInfo::checkSubscribeAction(forte::com_infra::EComServiceType paFbType, unsigned int paNoOfRDs, unsigned int) {
bool retVal = false;
if(forte::com_infra::EComServiceType::e_Subscriber == paFbType && paNoOfRDs == getNoOfNodePairs()) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: In FB %s: %s action is only allowed using a Subscribe FB, the amount of BrowseName,NodeId pairs should match the number of RDs\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eSubscribe]);
}
return retVal;
}
bool CActionInfo::checkCreateObjectAction(forte::com_infra::EComServiceType paFbType, unsigned int, unsigned int paNoOfSDs) {
bool retVal = false;
if(forte::com_infra::EComServiceType::e_Publisher == paFbType && 2 == getNoOfNodePairs() && 0 == paNoOfSDs) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: In FB %s: %s action is only allowed using a Publish FB, the amount of BrowseName,NodeId pairs should be 2, and no SD must be provided\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eCreateObject]);
}
return retVal;
}
bool CActionInfo::checkDeleteObjectAction(forte::com_infra::EComServiceType paFbType, unsigned int, unsigned int paNoOfSDs) {
bool retVal = false;
if(forte::com_infra::EComServiceType::e_Publisher == paFbType && 1 == getNoOfNodePairs() && 0 == paNoOfSDs) {
retVal = true;
} else {
DEVLOG_ERROR(
"[OPC UA ACTION]: In FB %s: %s action is only allowed using a Publish FB, the amount of BrowseName,NodeId pairs should be 1, and no SD must be provided\n",
mLayer.getCommFB()->getInstanceName(), CActionInfo::mActionNames[eDeleteObject]);
}
return retVal;
}
// ************** ACTION PARSER ******************* //
CActionInfo::UA_ActionType CActionInfo::CActionParser::getActionEnum(const char *paActionString) {
CActionInfo::UA_ActionType action = CActionInfo::eActionUnknown;
for(size_t i = 0; i < eActionUnknown; i++) {
if(0 == strcmp(paActionString, CActionInfo::mActionNames[i])) {
action = static_cast<UA_ActionType>(i);
break;
}
}
if(eActionUnknown == action) {
DEVLOG_ERROR("[OPC UA ACTION]: The action %s is unrecognized\n", paActionString);
}
return action;
}
bool CActionInfo::CActionParser::getEndpoint(const char *paEndpoint, CIEC_STRING &paResult) {
if(0 != strchr(paEndpoint, '#')) {
paResult = paEndpoint;
paResult.assign(paResult.getValue(), static_cast<TForteUInt16>(paResult.length() - 1)); //remove #
return true;
}
return false;
}
bool CActionInfo::CActionParser::handlePair(const char *paPair, CSinglyLinkedList<CNodePairInfo*> &paResult) {
bool retVal = false;
CParameterParser pairParser(paPair, ',');
CIEC_STRING browsePathResult;
UA_NodeId* nodeIdResult = 0;
size_t noOfParameters = pairParser.parseParameters();
if(NodePairPositions::eMaxNumberOfPositions == noOfParameters) {
browsePathResult = pairParser[NodePairPositions::eBrowseName];
nodeIdResult = parseNodeId(pairParser[NodePairPositions::eNodeId]);
retVal = nodeIdResult;
} else if(NodePairPositions::eMaxNumberOfPositions - 1 == noOfParameters) { //no NodeId was provided
browsePathResult = pairParser[NodePairPositions::eBrowseName];
retVal = true;
} else {
DEVLOG_ERROR("[OPC UA ACTION]: The pair %s doesn't have the proper format BROWSENAME,NODEID\n", paPair);
}
if(retVal) {
paResult.pushBack(new CNodePairInfo(nodeIdResult, browsePathResult.getValue()));
}
return retVal;
}
UA_NodeId* CActionInfo::CActionParser::parseNodeId(const char *paNodeIdString) {
bool somethingFailed = false;
UA_NodeId *resultNodeId = UA_NodeId_new();
UA_NodeId_init(resultNodeId); //will set to default namespace 0. When the nodeId is later used, the default namespace should come from the browsename
unsigned int identifierPosition = 0;
CParameterParser mainParser(paNodeIdString, ':'); //namespace and identifier should be divided by a colon. If first parameter is omitted, namespace 0 is assumed
size_t numberOfParameters = mainParser.parseParameters();
switch(numberOfParameters){
case NodeIdPositions::eMaxNumberOfNodeIdPositions: //Namespace is present
identifierPosition++;
if(!parseNamespace(mainParser[0], *resultNodeId)) {
somethingFailed = true;
break;
}
// fall through
case NodeIdPositions::eMaxNumberOfNodeIdPositions - 1: //NOSONAR
if(!parseIdentifier(mainParser[identifierPosition], *resultNodeId)) {
somethingFailed = true;
}
break;
default:
DEVLOG_ERROR("[OPC UA ACTION]: NodeId %s has wrong number of colon\n", paNodeIdString);
somethingFailed = true;
break;
}
if(somethingFailed) {
DEVLOG_ERROR(
"[OPC UA ACTION]: Parsing the NodeId %s failed. The format should follow the notation '<namespaceIndex>:<identifiertype>=<identifier>'. if the part before the : is omitted, namespace 0 is assumed\n",
paNodeIdString);
UA_NodeId_delete(resultNodeId);
resultNodeId = 0;
}
return resultNodeId;
}
bool CActionInfo::CActionParser::parseNamespace(const char *paNamespace, UA_NodeId &paResult) {
paResult.namespaceIndex = static_cast<UA_UInt16>(forte::core::util::strtoul(paNamespace, 0, 10)); //TODO: should we check for return value here?
return true;
}
bool CActionInfo::CActionParser::parseIdentifier(const char *paIdentifier, UA_NodeId &paResult) {
CParameterParser identifierParser(paIdentifier, '='); //<identifiertype>=<identifier>
if(NodeIdItenfierPositions::eMaxNumberOfNodeIdIdenfiertPositions == identifierParser.parseParameters()) {
if(0 == strcmp(identifierParser[NodeIdItenfierPositions::eIdenfierType], "i")) { //numeric
paResult.identifierType = UA_NODEIDTYPE_NUMERIC;
paResult.identifier.numeric = static_cast<UA_UInt32>(forte::core::util::strtoul(identifierParser[NodeIdItenfierPositions::eIdenfierValue], 0, 10)); //TODO: should we check for return value here?
} else if(0 == strcmp(identifierParser[NodeIdItenfierPositions::eIdenfierType], "s")) { //string
paResult.identifierType = UA_NODEIDTYPE_STRING;
paResult.identifier.string = UA_String_fromChars(identifierParser[NodeIdItenfierPositions::eIdenfierValue]);
} else if(0 == strcmp(identifierParser[NodeIdItenfierPositions::eIdenfierType], "g")) { //GUID
DEVLOG_ERROR("[OPC UA ACTION]:GUID type is not yet implemented\n");
return false;
} else if(0 == strcmp(identifierParser[NodeIdItenfierPositions::eIdenfierType], "b")) { //byteString
paResult.identifierType = UA_NODEIDTYPE_BYTESTRING;
paResult.identifier.byteString = UA_BYTESTRING_ALLOC(identifierParser[NodeIdItenfierPositions::eIdenfierValue]);
} else {
DEVLOG_ERROR("[OPC UA ACTION]: The identifier type %s wasn't recognized among the possible values [i, s, b]\n", identifierParser[0]);
return false;
}
} else {
DEVLOG_ERROR("[OPC UA ACTION]: Parsing the identifier %s failed. The format should follow the notation '<identifiertype>=<identifier>'\n", paIdentifier);
return false;
}
return true;
}
// **** METHOD ACTION *****//
CLocalMethodInfo::CLocalMethodInfo(COPC_UA_Layer &paLayer, CIEC_STRING &paEndpoint) :
CActionInfo(paLayer, eCreateMethod, paEndpoint) {
}
CLocalMethodInfo::~CLocalMethodInfo() {
}
CSyncObject& CLocalMethodInfo::getMutex() {
return mMutex;
}
forte::arch::CSemaphore& CLocalMethodInfo::getResultReady() {
return mResultIsReady;
}