Skip to content

Commit

Permalink
fix and move ldsMe to its own class (eclipse-4diac#239)
Browse files Browse the repository at this point in the history
 * fix and move ldsMe to its own class 
 * add documentation 
 * update for v1.4.5
 * remove not needed include
  • Loading branch information
cochicde authored Dec 18, 2024
1 parent ca8346d commit 2cd4a3f
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 110 deletions.
2 changes: 2 additions & 0 deletions src/com/opc_ua/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ if (FORTE_COM_OPC_UA)

add_subdirectory(FBs)
add_subdirectory(types)
add_subdirectory(detail)


if(FORTE_COM_OPC_UA_ENCRYPTION)
SET(FORTE_COM_OPC_UA_ENCRYPTION_INCLUDE_DIR "" CACHE STRING "ABSOLUTE path to encryption headers")
Expand Down
4 changes: 4 additions & 0 deletions src/com/opc_ua/detail/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

if(FORTE_COM_OPC_UA_MULTICAST)
forte_add_sourcefile_hcpp(lds_me_handler)
endif(FORTE_COM_OPC_UA_MULTICAST)
100 changes: 100 additions & 0 deletions src/com/opc_ua/detail/lds_me_handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

#include "lds_me_handler.h"

#include "devlog.h"

using namespace forte::com::opc_ua::detail;

LdsMeHandler::LdsMeHandler(UA_Server& paUaServer) : mUaServer{paUaServer} {
UA_Server_setServerOnNetworkCallback(&mUaServer, serverOnNetworkCallback, this);
}

LdsMeHandler::~LdsMeHandler() {
for(const auto& registeredServer : mRegisteredServers){
deregisterDiscoveryServer(registeredServer.mString);
}
UA_Server_setServerOnNetworkCallback(&mUaServer, nullptr, nullptr);
}

void LdsMeHandler::configureServer(UA_ServerConfig& paUaServerConfig, const std::string& paServerName) {
UA_String_clear(&paUaServerConfig.mdnsConfig.mdnsServerName);
paUaServerConfig.mdnsConfig.mdnsServerName = UA_String_fromChars(paServerName.c_str());
// Enable the mDNS announce and response functionality
paUaServerConfig.mdnsEnabled = true;
}

void LdsMeHandler::serverOnNetworkCallback(const UA_ServerOnNetwork* paServerOnNetwork, UA_Boolean paIsServerAnnounce, UA_Boolean paIsTxtReceived, void* paData) {

if(!paIsTxtReceived) {
return; // we wait until the corresponding TXT record is announced.
}

DEVLOG_DEBUG("[OPC UA LDS ME]: mDNS %s '%.*s' with url '%.*s'\n", paIsServerAnnounce ? "announce" : "remove", paServerOnNetwork->serverName.length,
paServerOnNetwork->serverName.data, paServerOnNetwork->discoveryUrl.length, paServerOnNetwork->discoveryUrl.data);

// check if server is LDS
bool isServerLDS = false;

UA_StringRAII ldsStr("LDS");
for(unsigned int i = 0; i < paServerOnNetwork->serverCapabilitiesSize; i++) {
if(UA_String_equal(&paServerOnNetwork->serverCapabilities[i], &ldsStr.mString)) {
isServerLDS = true;
break;
}
}

// skip non-LDS servers
if(!isServerLDS) {
return;
}

auto handler = static_cast<LdsMeHandler*>(paData);

auto foundDiscoveryUrl = handler->mRegisteredServers.end();
for(auto iter = handler->mRegisteredServers.begin(); iter != handler->mRegisteredServers.end(); iter++) {
if(UA_String_equal(&paServerOnNetwork->discoveryUrl, &iter->mString)) {
foundDiscoveryUrl = iter;
break;
}
}

// if a new server is being announced
if(paIsServerAnnounce && foundDiscoveryUrl == handler->mRegisteredServers.end()) {
if(handler->registerDiscoveryServer(paServerOnNetwork->discoveryUrl)){
handler->mRegisteredServers.emplace_back(paServerOnNetwork->discoveryUrl);
}
// if a known server is being un-announced
} else if(!paIsServerAnnounce && foundDiscoveryUrl != handler->mRegisteredServers.end()) {
handler->deregisterDiscoveryServer(foundDiscoveryUrl->mString);
handler->mRegisteredServers.erase(foundDiscoveryUrl);
}
}

bool LdsMeHandler::registerDiscoveryServer(const UA_String& paDiscoveryUrl) {

UA_ClientConfig clientConfig;
memset(&clientConfig, 0, sizeof(UA_ClientConfig));
UA_ClientConfig_setDefault(&clientConfig);
auto retVal = UA_Server_registerDiscovery(&mUaServer, &clientConfig, paDiscoveryUrl, UA_STRING_NULL);
if( UA_STATUSCODE_GOOD != retVal) {
DEVLOG_ERROR("[OPC UA LDSME]: Could not register with LDS. Error: %s\n", UA_StatusCode_name(retVal));
return false;
}
DEVLOG_INFO("[OPC UA LDSME]: Registered to LDS '%.*s'\n", paDiscoveryUrl.length, paDiscoveryUrl.data);
return true;
}

void LdsMeHandler::deregisterDiscoveryServer(const UA_String& paDiscoveryUrl) {
UA_ClientConfig clientConfig;
memset(&clientConfig, 0, sizeof(UA_ClientConfig));
UA_ClientConfig_setDefault(&clientConfig);

auto retVal = UA_Server_deregisterDiscovery(&mUaServer, &clientConfig, paDiscoveryUrl);
// if unregister fails, we don't do anything else
if( UA_STATUSCODE_GOOD != retVal) {
DEVLOG_ERROR("[OPC UA LDSME]: Could not deregister with LDS. Error: %s\n", UA_StatusCode_name(retVal));
return;
}

DEVLOG_INFO("[OPC UA LDSME]: Unregistered from LDS '%.*s'\n", paDiscoveryUrl.length, paDiscoveryUrl.data);
}
101 changes: 101 additions & 0 deletions src/com/opc_ua/detail/lds_me_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*******************************************************************************
* Copyright (c) 2024 Jose Cabral
*
* 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 integration of the OPC-UA protocol
*******************************************************************************/

#ifndef SRC_COM_OPC_UA_DETAIL_LDSMEHANDLER_H_
#define SRC_COM_OPC_UA_DETAIL_LDSMEHANDLER_H_

#include <open62541.h>

#include <string>
#include <vector>

namespace forte::com::opc_ua::detail {

/**
* @brief Handles all things related to LDS-ME
*
* Configures the server to accept LDS-ME messages and handles them accordingly.
*/
class LdsMeHandler {
public:

/**
* @brief Register this instance as handler for incoming discovery message
*
* @param paUaServer OpcUa server to handle
*/
LdsMeHandler(UA_Server& paUaServer);

/**
* @brief Un-register this instance as handler incoming discovery message
*
*/
~LdsMeHandler();


/**
* @brief Sets the proper values to the server configuration so it accepts LDS-ME messages
*
* @param paUaServerConfig configuration to be set
* @param paServerName server name to be used when registering to the discovery server
*/
static void configureServer(UA_ServerConfig& paUaServerConfig, const std::string& paServerName);

private:

bool registerDiscoveryServer(const UA_String& paDiscoveryUrl);
void deregisterDiscoveryServer(const UA_String& paDiscoveryUrl);

static void serverOnNetworkCallback(const UA_ServerOnNetwork* paServerOnNetwork, UA_Boolean paIsServerAnnounce, UA_Boolean paIsTxtReceived, void* paData);

UA_Server& mUaServer;

// Handle the lifetime of a UA_String nicely
class UA_StringRAII {
public:
UA_StringRAII(const UA_String& paString) {
UA_String_copy(&paString, &mString);
}

UA_StringRAII(const char* paString) {
mString = UA_String_fromChars(paString);
}

~UA_StringRAII() {
UA_String_clear(&mString);
}

UA_StringRAII(UA_StringRAII&& paOther) {
*this = std::move(paOther);
}

UA_StringRAII& operator=(UA_StringRAII&& paOther) {
mString = paOther.mString;
UA_String_init(&paOther.mString);
return *this;
};


UA_StringRAII(const UA_StringRAII&) = delete;
UA_StringRAII& operator=(const UA_StringRAII&) = delete;

UA_String mString;
};

/// List of discovery servers where this instance is already registered
std::vector<UA_StringRAII> mRegisteredServers;
};
}

#endif // SRC_COM_OPC_UA_DETAIL_LDSMEHANDLER_H_
106 changes: 13 additions & 93 deletions src/com/opc_ua/opcua_local_handler.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2023 Florian Froschermeier <[email protected]>,
* Copyright (c) 2015, 2024 Florian Froschermeier <[email protected]>,
* fortiss GmbH, Primetals Technologies Austria GmbH
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -30,6 +30,9 @@
#include "forte_printer.h"
#include "../../arch/utils/mainparam_utils.h"
#include "opcua_local_handler.h"
#ifdef FORTE_COM_OPC_UA_MULTICAST
#include "detail/lds_me_handler.h"
#endif //FORTE_COM_OPC_UA_MULTICAST
#include <string>

#ifndef FORTE_COM_OPC_UA_CUSTOM_HOSTNAME
Expand Down Expand Up @@ -57,12 +60,6 @@ COPC_UA_Local_Handler::~COPC_UA_Local_Handler() {
UA_NodeId_delete(const_cast<UA_NodeId*>(iter->mNodeId));
}
mNodesReferences.clear();

#ifdef FORTE_COM_OPC_UA_MULTICAST
for(CSinglyLinkedList<UA_String*>::Iterator iter = mRegisteredWithLds.begin(); iter != mRegisteredWithLds.end(); ++iter) {
UA_String_delete(*iter);
}
#endif //FORTE_COM_OPC_UA_MULTICAST
}

void COPC_UA_Local_Handler::enableHandler() {
Expand Down Expand Up @@ -91,12 +88,15 @@ void COPC_UA_Local_Handler::run() {
configureUAServer(serverStrings, *uaServerConfig);

if(initializeNodesets(*mUaServer)) {
#ifdef FORTE_COM_OPC_UA_MULTICAST
UA_Server_setServerOnNetworkCallback(mUaServer, serverOnNetworkCallback, this);
#endif //FORTE_COM_OPC_UA_MULTICAST

UA_StatusCode retVal = UA_Server_run_startup(mUaServer);
if(UA_STATUSCODE_GOOD == retVal) {
{
#ifdef FORTE_COM_OPC_UA_MULTICAST
// the extra curly brace on top is to limit the lifetime of this object.
// it was avoided to have inside this ifdef to also avoid the closing brace
// with the exta ifdef making it cleaner this way
auto mLdsMeHandler = forte::com::opc_ua::detail::LdsMeHandler(*mUaServer);
#endif //FORTE_COM_OPC_UA_MULTICAST
mServerStarted.inc();
while(isAlive()) {
UA_UInt16 timeToSleepMs;
Expand All @@ -110,6 +110,7 @@ void COPC_UA_Local_Handler::run() {

mServerNeedsIteration.timedWait(static_cast<TForteUInt64>(timeToSleepMs) * 1000000ULL);
}
}
retVal = UA_Server_run_shutdown(mUaServer);
if(UA_STATUSCODE_GOOD == retVal) {
DEVLOG_INFO("[OPC UA LOCAL]: Server successfully stopped\n");
Expand Down Expand Up @@ -166,10 +167,7 @@ void COPC_UA_Local_Handler::generateServerStrings(TForteUInt16 paUAServerPort, U

void COPC_UA_Local_Handler::configureUAServer(UA_ServerStrings &paServerStrings, UA_ServerConfig &paUaServerConfig) const {
#ifdef FORTE_COM_OPC_UA_MULTICAST
paUaServerConfig.applicationDescription.applicationType = UA_APPLICATIONTYPE_DISCOVERYSERVER;
// hostname will be added by mdns library
UA_String_clear(&paUaServerConfig.mdnsConfig.mdnsServerName);
paUaServerConfig.mdnsConfig.mdnsServerName = UA_String_fromChars(paServerStrings.mMdnsServerName.c_str());
forte::com::opc_ua::detail::LdsMeHandler::configureServer(paUaServerConfig, paServerStrings.mMdnsServerName);
#endif //FORTE_COM_OPC_UA_MULTICAST

UA_Array_delete(paUaServerConfig.serverUrls, paUaServerConfig.serverUrlsSize, &UA_TYPES[UA_TYPES_STRING]);
Expand Down Expand Up @@ -280,85 +278,7 @@ void COPC_UA_Local_Handler::getNodesReferencedByAction(const CActionInfo &paActi
}
}

#ifdef FORTE_COM_OPC_UA_MULTICAST

const UA_String* COPC_UA_Local_Handler::getDiscoveryUrl() const {

UA_ServerConfig *mServerConfig = UA_Server_getConfig(mUaServer); //change mServerConfig to serverConfig when only master branch is present
if(0 == mServerConfig->networkLayersSize) {
return 0;
}
return &mServerConfig->networkLayers[0].discoveryUrl;
}

void COPC_UA_Local_Handler::serverOnNetworkCallback(const UA_ServerOnNetwork *paServerOnNetwork, UA_Boolean paIsServerAnnounce, UA_Boolean paIsTxtReceived,
void *paData) { //NOSONAR
COPC_UA_Local_Handler *handler = static_cast<COPC_UA_Local_Handler*>(paData);

const UA_String *ownDiscoverUrl = handler->getDiscoveryUrl();

if(!ownDiscoverUrl || UA_String_equal(&paServerOnNetwork->discoveryUrl, ownDiscoverUrl)) {
// skip self
return;
}

if(!paIsTxtReceived) {
return; // we wait until the corresponding TXT record is announced.
}

DEVLOG_DEBUG("[OPC UA LOCAL]: mDNS %s '%.*s' with url '%.*s'\n", paIsServerAnnounce ? "announce" : "remove", paServerOnNetwork->serverName.length,
paServerOnNetwork->serverName.data, paServerOnNetwork->discoveryUrl.length, paServerOnNetwork->discoveryUrl.data);

// check if server is LDS, and then register
UA_String ldsStr = UA_String_fromChars("LDS");
for(unsigned int i = 0; i < paServerOnNetwork->serverCapabilitiesSize; i++) {
if(UA_String_equal(&paServerOnNetwork->serverCapabilities[i], &ldsStr)) {
if(paIsServerAnnounce) {
handler->registerWithLds(&paServerOnNetwork->discoveryUrl);
} else {
handler->removeLdsRegister(&paServerOnNetwork->discoveryUrl);
}
break;
}
}
UA_String_clear(&ldsStr);
}

void COPC_UA_Local_Handler::registerWithLds(const UA_String *paDiscoveryUrl) {
// check if already registered with the given LDS
for(CSinglyLinkedList<UA_String*>::Iterator iter = mRegisteredWithLds.begin(); iter != mRegisteredWithLds.end(); ++iter) {
if(UA_String_equal(paDiscoveryUrl, *iter)) {
return;
}
}

// will be freed when removed from list
UA_String *discoveryUrlChar = 0;
UA_String_copy(paDiscoveryUrl, discoveryUrlChar);

mRegisteredWithLds.pushFront(discoveryUrlChar);
DEVLOG_INFO("[OPC UA LOCAL]: Registering with LDS '%.*s'\n", paDiscoveryUrl->length, paDiscoveryUrl->data);
UA_StatusCode retVal = UA_Server_addPeriodicServerRegisterCallback(mUaServer, 0, reinterpret_cast<const char*>(discoveryUrlChar->data), 10 * 60 * 1000, 500, 0);
if( UA_STATUSCODE_GOOD != retVal) {
DEVLOG_ERROR("[OPC UA LOCAL]: Could not register with LDS. Error: %s\n", UA_StatusCode_name(retVal));
}
}

void COPC_UA_Local_Handler::removeLdsRegister(const UA_String *paDiscoveryUrl) {
UA_String *toDelete = 0;
for(CSinglyLinkedList<UA_String*>::Iterator iter = mRegisteredWithLds.begin(); iter != mRegisteredWithLds.end(); ++iter) {
if(UA_String_equal(paDiscoveryUrl, *iter)) {
toDelete = *iter;
break;
}
}
if(toDelete) {
mRegisteredWithLds.erase(toDelete);
UA_String_delete(toDelete);
}
}

#endif //FORTE_COM_OPC_UA_MULTICAST

UA_StatusCode COPC_UA_Local_Handler::initializeAction(CActionInfo &paActionInfo) {
enableHandler();
Expand Down
17 changes: 0 additions & 17 deletions src/com/opc_ua/opcua_local_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -694,23 +694,6 @@ class COPC_UA_Local_Handler : public COPC_UA_HandlerAbstract, public CThread {
* Default description for variable nodes
*/
static const char *const mDefaultDescriptionForVariableNodes;

#ifdef FORTE_COM_OPC_UA_MULTICAST
# ifndef UA_ENABLE_DISCOVERY_MULTICAST
# error open62541 needs to be built with UA_ENABLE_DISCOVERY=ON and UA_ENABLE_DISCOVERY_MULTICAST=ON
# else // UA_ENABLE_DISCOVERY_MULTICAST
/**
* List of LDS servers where this instance is already registered.
*/
CSinglyLinkedList<UA_String*> mRegisteredWithLds;

const UA_String* getDiscoveryUrl() const;
void registerWithLds(const UA_String *paDiscoveryUrl);
void removeLdsRegister(const UA_String *paDiscoveryUrl);
static void serverOnNetworkCallback(const UA_ServerOnNetwork *paServerOnNetwork, UA_Boolean paIsServerAnnounce, UA_Boolean paIsTxtReceived, void *paData);
# endif //UA_ENABLE_DISCOVERY_MULTICAST
#endif //FORTE_COM_OPC_UA_MULTICAST

};

#endif /* SRC_MODULES_OPC_UA_OPCUALOCALHANDLER_H_ */

0 comments on commit 2cd4a3f

Please sign in to comment.