Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create OPC UA namespace during runtime #69

Merged
merged 3 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/com/opc_ua/opcua_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,8 @@ EComResponse COPC_UA_Layer::openConnection(char *paLayerParameter) {
if(COPC_UA_ObjectStruct_Helper::isStructType(*this, isPublisher) && mStructObjectHelper->checkStructTypeConnection(isPublisher) && (CActionInfo::eWrite == action || CActionInfo::eRead == action) ) {
mIsObjectNodeStruct = true;
response = mStructObjectHelper->createObjectNode(*mActionInfo, isPublisher);

CCriticalRegion criticalRegion(mRDBufferMutex);
response = e_InitOk;
if(!isPublisher) {
if(!isPublisher && (response == e_InitOk)) {
CCriticalRegion criticalRegion(mRDBufferMutex);
mRDBuffer = mStructObjectHelper->initializeRDBuffer();
}
}
Expand Down
91 changes: 65 additions & 26 deletions src/com/opc_ua/opcua_objectstruct_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,25 @@
*******************************************************************************/

#include "opcua_objectstruct_helper.h"
#ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP
#include "opcua_objectstruct_helper_gen.cpp"
#endif
#include "opcua_layer.h"
#include "struct_action_info.h"
#include "../../core/cominfra/basecommfb.h"
#include "opcua_local_handler.h"
#include <sstream>

using namespace forte::com_infra;

const UA_UInt16 COPC_UA_ObjectStruct_Helper::smOpcuaNamespaceIndex = 2;
const std::string COPC_UA_ObjectStruct_Helper::smStructTypesBrowsePath = "/Types/0:ObjectTypes/0:BaseObjectType/%d:";

const std::string COPC_UA_ObjectStruct_Helper::smStructTypesBrowsePath = "/Types/0:ObjectTypes/0:BaseObjectType/2:";
const std::string COPC_UA_ObjectStruct_Helper::smMemberNamespaceIndex = "/%d:";

const std::string COPC_UA_ObjectStruct_Helper::smMemberNamespaceIndex = "/2:";

char COPC_UA_ObjectStruct_Helper::smEmptyLocale[] = "";
char COPC_UA_ObjectStruct_Helper::smEmptyString[] = "";

COPC_UA_ObjectStruct_Helper::COPC_UA_ObjectStruct_Helper(COPC_UA_Layer &paLayer, COPC_UA_HandlerAbstract *paHandler):
mLayer(paLayer), mHandler(paHandler) {
mLayer(paLayer), mHandler(paHandler), mOpcuaNamespaceIndex(1) {
}

COPC_UA_ObjectStruct_Helper::~COPC_UA_ObjectStruct_Helper() {
Expand All @@ -40,14 +42,15 @@ COPC_UA_ObjectStruct_Helper::~COPC_UA_ObjectStruct_Helper() {

void COPC_UA_ObjectStruct_Helper::uninitializeStruct() {
for(std::shared_ptr<CActionInfo> actionInfo : mStructMemberActionInfos) {
mHandler->uninitializeAction(*actionInfo);
}
if(mCreateNodeActionInfo) {
mHandler->uninitializeAction(*mCreateNodeActionInfo);
mHandler->uninitializeAction(*actionInfo);
}
if(mCreateNodeActionInfo) {
mHandler->uninitializeAction(*mCreateNodeActionInfo);
}
}

bool COPC_UA_ObjectStruct_Helper::checkStructTypeConnection(bool paIsPublisher) {
checkOPCUANamespace();
std::string browsePath(getStructBrowsePath(smStructTypesBrowsePath, paIsPublisher));
if(isOPCUAObjectPresent(browsePath)) {
return true;
Expand Down Expand Up @@ -81,18 +84,34 @@ bool COPC_UA_ObjectStruct_Helper::createOPCUAStructType(const std::string &paStr
return true;
}

bool COPC_UA_ObjectStruct_Helper::createOPCUANamespace(char* nsName) {
mHandler->enableHandler();
COPC_UA_Local_Handler* localHandler = static_cast<COPC_UA_Local_Handler*>(mHandler);
if(!localHandler) {
DEVLOG_ERROR("[OPC UA OBJECT STRUCT HELPER]: Failed to get LocalHandler because LocalHandler is null!\n");
return false;
}
UA_Server *server = localHandler->getUAServer();
UA_UInt16 nsIndex = UA_Server_addNamespace(server, nsName);
if(nsIndex <= 0) {
return false;
}
mOpcuaNamespaceIndex = nsIndex;
return true;
}

bool COPC_UA_ObjectStruct_Helper::defineOPCUAStructTypeNode(UA_Server *paServer, UA_NodeId &paNodeId, const std::string &paStructTypeName) {
char* structTypeName = new char[paStructTypeName.length() +1];
strncpy(structTypeName, paStructTypeName.c_str(), paStructTypeName.length());
structTypeName[paStructTypeName.length()] = '\0';
mStructTypeNames.push_back(structTypeName);
paNodeId = UA_NODEID_NUMERIC(smOpcuaNamespaceIndex, 0);
paNodeId = UA_NODEID_NUMERIC(mOpcuaNamespaceIndex, 0);
UA_ObjectTypeAttributes oAttr = UA_ObjectTypeAttributes_default;
oAttr.displayName = UA_LOCALIZEDTEXT(smEmptyLocale, structTypeName);
oAttr.displayName = UA_LOCALIZEDTEXT(smEmptyString, structTypeName);
UA_StatusCode status = UA_Server_addObjectTypeNode(paServer, paNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
UA_QUALIFIEDNAME(smOpcuaNamespaceIndex, structTypeName), oAttr,
UA_QUALIFIEDNAME(mOpcuaNamespaceIndex, structTypeName), oAttr,
nullptr, &paNodeId);

if (status != UA_STATUSCODE_GOOD) {
Expand All @@ -108,18 +127,18 @@ bool COPC_UA_ObjectStruct_Helper::addOPCUAStructTypeComponent(UA_Server *paServe
memberName[paStructMemberName.length()] = '\0';
mStructTypeNames.push_back(memberName);
UA_VariableAttributes vAttr = UA_VariableAttributes_default;
vAttr.displayName = UA_LOCALIZEDTEXT(smEmptyLocale, memberName);
vAttr.displayName = UA_LOCALIZEDTEXT(smEmptyString, memberName);
vAttr.valueRank = UA_VALUERANK_SCALAR;
vAttr.minimumSamplingInterval = 0.000000;
vAttr.userAccessLevel = 1;
vAttr.accessLevel = 3;
vAttr.dataType = COPC_UA_Helper::getOPCUATypeFromAny(*paStructMember)->typeId;//UA_NODEID_NUMERIC(0, 1); // 1 is Boolean
vAttr.userAccessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
vAttr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
vAttr.dataType = COPC_UA_Helper::getOPCUATypeFromAny(*paStructMember)->typeId;

UA_NodeId memberNodeId = UA_NODEID_NUMERIC(smOpcuaNamespaceIndex, 0);
UA_StatusCode status = UA_Server_addVariableNode(paServer, UA_NODEID_NULL, paParentNodeId,
UA_NodeId memberNodeId = UA_NODEID_NUMERIC(mOpcuaNamespaceIndex, 0);
UA_StatusCode status = UA_Server_addVariableNode(paServer, memberNodeId, paParentNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_QUALIFIEDNAME(smOpcuaNamespaceIndex, memberName),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), vAttr, NULL, &memberNodeId);
UA_QUALIFIEDNAME(mOpcuaNamespaceIndex, memberName),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), vAttr, nullptr, &memberNodeId);
if(status != UA_STATUSCODE_GOOD) {
DEVLOG_ERROR("[OPC UA OBJECT STRUCT HELPER]: Failed to add Member to OPC UA Struct Type Node for Member %s, Status Code: %s\n", paStructMemberName.c_str(), UA_StatusCode_name(status));
return false;
Expand Down Expand Up @@ -259,7 +278,6 @@ forte::com_infra::EComResponse COPC_UA_ObjectStruct_Helper::initializeMemberActi
bool COPC_UA_ObjectStruct_Helper::isOPCUAObjectPresent(std::string &paBrowsePath) {
COPC_UA_Local_Handler* localHandler = static_cast<COPC_UA_Local_Handler*>(mHandler);
if(localHandler) {
// CActionInfo::CNodePairInfo* nodePair = new CActionInfo::CNodePairInfo(nullptr, paBrowsePath);
CActionInfo::CNodePairInfo nodePair(nullptr, paBrowsePath);
bool retVal = localHandler->isOPCUAObjectPresent(nodePair);
if(nodePair.mNodeId) delete nodePair.mNodeId;
Expand All @@ -270,16 +288,37 @@ bool COPC_UA_ObjectStruct_Helper::isOPCUAObjectPresent(std::string &paBrowsePath
return false;
}

void COPC_UA_ObjectStruct_Helper::checkOPCUANamespace() {
if(!mLayer.getCommFB()->getResource()->getFBInterfaceSpec()) {
return;
}
CIEC_WSTRING* configPort = static_cast<CIEC_WSTRING*>(mLayer.getCommFB()->getResource()->getDataInput(g_nStringIdOPCUA_Namespace));
if(configPort && configPort->length() > 0) {
if(!createOPCUANamespace(configPort->getValue())) {
DEVLOG_ERROR("[OPC UA OBJECT STRUCT HELPER]: Failed to create OPC UA Namespace with value: %s", configPort->getValue());
}
}
}

std::string COPC_UA_ObjectStruct_Helper::getStructBrowsePath(const std::string &paPathPrefix, bool paIsPublisher) {
std::string structTypeName(getStructTypeName(paIsPublisher));
if(!structTypeName.empty()) {
return paPathPrefix + structTypeName;
if(structTypeName.empty()) {
return std::string();
}
return std::string();
std::stringstream ss;
char buf[100];
snprintf(buf, sizeof(buf), paPathPrefix.c_str(), mOpcuaNamespaceIndex);
ss << buf << structTypeName;
return ss.str();
}


std::string COPC_UA_ObjectStruct_Helper::getStructMemberBrowsePath(std::string &paBrowsePathPrefix, const CStringDictionary::TStringId structMemberNameId) {
return paBrowsePathPrefix + smMemberNamespaceIndex + CStringDictionary::getInstance().get(structMemberNameId);
std::stringstream ss;
char buf[100];
snprintf(buf, sizeof(buf), smMemberNamespaceIndex.c_str(), mOpcuaNamespaceIndex);
ss << paBrowsePathPrefix << buf << CStringDictionary::getInstance().get(structMemberNameId);
return ss.str();
}

std::string COPC_UA_ObjectStruct_Helper::getStructTypeName(bool paIsPublisher) {
Expand Down
26 changes: 20 additions & 6 deletions src/com/opc_ua/opcua_objectstruct_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include "opcua_layer.h"
#include <memory>

// class COPC_UA_Layer;
class COPC_UA_HandlerAbstract;
class CActionInfo;

Expand Down Expand Up @@ -111,9 +110,10 @@ class COPC_UA_ObjectStruct_Helper {
COPC_UA_HandlerAbstract *mHandler;

/**
* OPC UA Object Struct Namespace Index
* OPC UA Object Struct Namespace Index.
* The default NamespaceIndex is 1.
*/
static const UA_UInt16 smOpcuaNamespaceIndex;
UA_UInt16 mOpcuaNamespaceIndex;

/**
* BrowsePath to folder that contains Object Node Struct Types
Expand All @@ -125,7 +125,7 @@ class COPC_UA_ObjectStruct_Helper {
*/
static const std::string smMemberNamespaceIndex;

static char smEmptyLocale[];
static char smEmptyString[];

std::vector<char*> mStructTypeNames;

Expand Down Expand Up @@ -167,6 +167,13 @@ class COPC_UA_ObjectStruct_Helper {
*/
forte::com_infra::EComResponse initializeMemberAction(CActionInfo& paActionInfo, std::string &paBrowsePath, bool paIsPublisher);

/**
* Check if OPC UA namespace is given by the Resource configuration.
* If the configuration is set, change the namespace index.
* Otherwise, leave it in default state.
*/
void checkOPCUANamespace();

/**
* Get the BrowsePath to the OPC UA Struct Object Type from the local Struct Type
* @param paPathPrefix The BrowsePath directory with namespace (e.g. /Objects/1:)
Expand All @@ -184,10 +191,17 @@ class COPC_UA_ObjectStruct_Helper {
* @param paBrowsePathPrefix BrowsePath to the Struct Object Node
* @param structMemberNameId Name Id of Object Node Struct member
*/
static std::string getStructMemberBrowsePath(std::string &paBrowsePathPrefix, const CStringDictionary::TStringId structMemberNameId);
std::string getStructMemberBrowsePath(std::string &paBrowsePathPrefix, const CStringDictionary::TStringId structMemberNameId);

/**
* Creates an OPC UA namespace with the given name and assigns the
* namespace index to the mOpcuaNamespaceIndex member variable.
* @param nsName The name of the OPC UA Namespace
* @return true if namespace was successfully created or if it already exists, false otherwise
*/
bool createOPCUANamespace(char* nsName);

bool defineOPCUAStructTypeNode(UA_Server *paServer, UA_NodeId &paNodeId, const std::string &paStructTypeName);

bool addOPCUAStructTypeComponent(UA_Server *paServer, UA_NodeId &paParentNodeId, CIEC_ANY *paStructMember, const std::string &paStructMemberName);

};
2 changes: 1 addition & 1 deletion src/stdfblib/ita/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ SET(SOURCE_GROUP ${SOURCE_GROUP}\\ita)
forte_add_sourcefile_hcpp(DEV_MGR EMB_RES RMT_DEV RMT_RES)

if (FORTE_COM_OPC_UA)
forte_add_sourcefile_hcpp(OPCUA_DEV OPCUA_MGR)
forte_add_sourcefile_hcpp(OPCUA_DEV OPCUA_MGR Config_EMB_RES)
endif (FORTE_COM_OPC_UA)

forte_add_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
Expand Down
38 changes: 38 additions & 0 deletions src/stdfblib/ita/Config_EMB_RES.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright (c) 2024 Primetals Technologies Austria 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:
* Markus Meingast
* - initial API and implementation and/or initial documentation
*******************************************************************************/
#include <stringdict.h>
#include "Config_EMB_RES.h"
#ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP
#include "Config_EMB_RES_gen.cpp"
#endif

DEFINE_FIRMWARE_FB(Config_EMB_RES, g_nStringIdConfig_EMB_RES);

const CStringDictionary::TStringId Config_EMB_RES::scmVarInputNameIds[] = {g_nStringIdOPCUA_Namespace};
const CStringDictionary::TStringId Config_EMB_RES::scmDIDataTypeIds[] = {g_nStringIdWSTRING};

const SFBInterfaceSpec Config_EMB_RES::scmFBInterfaceSpec = {
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
1, scmVarInputNameIds, scmDIDataTypeIds,
0, nullptr, nullptr,
0, nullptr,
0, nullptr
};

Config_EMB_RES::Config_EMB_RES(CStringDictionary::TStringId paInstanceNameId, forte::core::CFBContainer &paDevice) :
EMB_RES(paInstanceNameId, paDevice) {
mInterfaceSpec = &scmFBInterfaceSpec;
}

Config_EMB_RES::~Config_EMB_RES() = default;
29 changes: 29 additions & 0 deletions src/stdfblib/ita/Config_EMB_RES.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2024 Primetals Technologies Austria 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:
* Markus Meingast
* - initial API and implementation and/or initial documentation
*******************************************************************************/
#pragma once

#include "EMB_RES.h"

class Config_EMB_RES : public EMB_RES {
DECLARE_FIRMWARE_FB(Config_EMB_RES);

public:
Config_EMB_RES(CStringDictionary::TStringId paInstanceNameId, forte::core::CFBContainer &paDevice);
~Config_EMB_RES() override;

private:
static const SFBInterfaceSpec scmFBInterfaceSpec;

static const CStringDictionary::TStringId scmVarInputNameIds[];
static const CStringDictionary::TStringId scmDIDataTypeIds[];
};