23#include <open62541/plugin/historydata/history_data_backend_memory.h>
24#include <open62541/plugin/historydata/history_data_gathering_default.h>
25#include <open62541/plugin/historydata/history_database_default.h>
26#include <open62541/plugin/historydatabase.h>
27#include <open62541/plugin/log_stdout.h>
28#include <open62541/server.h>
41#include "csa_config.h"
59 this->mappingExceptions = UA_FALSE;
60 this->fileHandler = std::make_shared<xml_file_handler>(configFile);
62 this->constructServer();
63 this->mapSelfToNamespace();
68 for(
auto ptr : variables)
delete ptr;
69 for(
auto ptr : additionalVariables)
delete ptr;
70 for(
auto ptr : mappedVariables)
delete ptr;
71 for(
auto dir : folderVector) {
72 UA_NodeId_clear(&dir.folderNodeId);
74 UA_NodeId_clear(&this->ownNodeId);
75 UA_NodeId_clear(&this->configNodeId);
78 static string cleanUri(
string s) {
80 std::regex r1(
"\\s+");
81 s = std::regex_replace(s, r1,
" ");
82 s = std::regex_replace(s, r1,
"_");
84 std::regex r2(
"[^ -~]+");
85 s = std::regex_replace(s, r2,
"");
89 static UA_ByteString loadFile(
const char*
const path) {
90 UA_ByteString fileContents = UA_STRING_NULL;
93 FILE* fp = fopen(path,
"rb");
100 fseek(fp, 0, SEEK_END);
101 fileContents.length = (size_t)ftell(fp);
102 fileContents.data = (UA_Byte*)UA_malloc(fileContents.length *
sizeof(UA_Byte));
103 if(fileContents.data) {
104 fseek(fp, 0, SEEK_SET);
105 size_t read = fread(fileContents.data,
sizeof(UA_Byte), fileContents.length, fp);
106 if(read != fileContents.length) UA_ByteString_clear(&fileContents);
109 fileContents.length = 0;
118 char hostname[HOST_NAME_MAX];
119 gethostname(hostname, HOST_NAME_MAX);
120 string hostname_uri =
"urn:";
121 hostname_uri.append(hostname);
122 hostname_uri.append(
":ChimeraTK:" + this->serverConfig.
applicationName);
124 config->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
125 UA_String* hostName = UA_String_new();
126 *hostName = UA_STRING_ALLOC(hostname);
127 config->applicationDescription.discoveryUrls = hostName;
128 config->applicationDescription.discoveryUrlsSize = 1;
129 UA_String_clear(&config->applicationDescription.applicationUri);
130 config->applicationDescription.applicationUri = UA_STRING_ALLOC(
const_cast<char*
>(cleanUri(hostname_uri).c_str()));
131 UA_String_clear(&config->buildInfo.manufacturerName);
132 config->buildInfo.manufacturerName = UA_STRING_ALLOC(
const_cast<char*
>(
"ChimeraTK Team"));
135 "open62541: " xstr(UA_OPEN62541_VER_MAJOR)
"." xstr(UA_OPEN62541_VER_MINOR)
"." xstr(UA_OPEN62541_VER_PATCH)
", ControlSystemAdapter-OPC-UA-Adapter: " xstr(
136 PROJECT_VER_MAJOR)
"." xstr(PROJECT_VER_MINOR)
"." xstr(PTOJECT_VER_PATCH)
"";
137 UA_String_clear(&config->buildInfo.softwareVersion);
138 config->buildInfo.softwareVersion = UA_STRING_ALLOC(
const_cast<char*
>(versionString.c_str()));
139 config->buildInfo.buildDate = UA_DateTime_now();
140 UA_String_clear(&config->buildInfo.buildNumber);
141 config->buildInfo.buildNumber = UA_STRING_ALLOC(
const_cast<char*
>(
""));
143 UA_String_clear(&config->buildInfo.productName);
144 config->buildInfo.productName = UA_STRING_ALLOC(
const_cast<char*
>(this->serverConfig.
applicationName.c_str()));
145 string product_urn =
"urn:ChimeraTK:" + this->serverConfig.
applicationName;
146 UA_String_clear(&config->buildInfo.productUri);
147 config->buildInfo.productUri = UA_STRING_ALLOC(
const_cast<char*
>(cleanUri(product_urn).c_str()));
148 UA_LocalizedText_clear(&config->applicationDescription.applicationName);
149 config->applicationDescription.applicationName = UA_LOCALIZEDTEXT_ALLOC(
150 const_cast<char*
>(
"en_US"),
const_cast<char*
>(this->serverConfig.
applicationName.c_str()));
153 void ua_uaadapter::constructServer() {
154 auto config = (UA_ServerConfig*)UA_calloc(1,
sizeof(UA_ServerConfig));
155 config->logging = &logger;
157 UA_ServerConfig_setMinimal(config, this->serverConfig.
opcuaPort,
nullptr);
159 config->eventLoop->logger = &logger;
161 UA_ByteString certificate = UA_BYTESTRING_NULL;
162 UA_ByteString privateKey = UA_BYTESTRING_NULL;
163 size_t trustListSize = 0;
164 UA_ByteString* trustList =
nullptr;
165 size_t issuerListSize = 0;
166 UA_ByteString* issuerList =
nullptr;
167 size_t blockListSize = 0;
168 UA_ByteString* blockList =
nullptr;
171 certificate = loadFile(this->serverConfig.
certPath.c_str());
172 privateKey = loadFile(this->serverConfig.
keyPath.c_str());
173 if(UA_ByteString_equal(&certificate, &UA_BYTESTRING_NULL) ||
174 UA_ByteString_equal(&privateKey, &UA_BYTESTRING_NULL)) {
175 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
176 "Invalid security configuration. Can't load private key or certificate.");
180 struct dirent* entry =
nullptr;
184 while((entry = readdir(dp))) {
185 if(!strcmp(entry->d_name,
".") || !strcmp(entry->d_name,
".."))
continue;
187 trustList = (UA_ByteString*)UA_realloc(trustList,
sizeof(UA_ByteString) * trustListSize);
189 sprintf(sbuf,
"%s/%s", this->serverConfig.
allowListFolder.c_str(), entry->d_name);
190 printf(
"Trust List entry: %s\n", entry->d_name);
191 trustList[trustListSize - 1] = loadFile(sbuf);
200 while((entry = readdir(dp))) {
201 if(!strcmp(entry->d_name,
".") || !strcmp(entry->d_name,
".."))
continue;
203 issuerList = (UA_ByteString*)UA_realloc(issuerList,
sizeof(UA_ByteString) * issuerListSize);
205 sprintf(sbuf,
"%s/%s", this->serverConfig.
issuerListFolder.c_str(), entry->d_name);
206 printf(
"Issuer List entry: %s\n", entry->d_name);
207 issuerList[issuerListSize - 1] = loadFile(sbuf);
216 while((entry = readdir(dp))) {
217 if(!strcmp(entry->d_name,
".") || !strcmp(entry->d_name,
".."))
continue;
219 blockList = (UA_ByteString*)UA_realloc(blockList,
sizeof(UA_ByteString) * blockListSize);
221 sprintf(sbuf,
"%s/%s", this->serverConfig.
blockListFolder.c_str(), entry->d_name);
222 printf(
"Block List entry: %s\n", entry->d_name);
223 blockList[blockListSize - 1] = loadFile(sbuf);
229 UA_StatusCode retval = UA_ServerConfig_setDefaultWithSecurityPolicies(config, this->serverConfig.
opcuaPort,
230 &certificate, &privateKey, trustList, trustListSize, issuerList, issuerListSize, blockList, blockListSize);
232 if(retval != UA_STATUSCODE_GOOD) {
233 throw std::runtime_error(
"Failed setting up server endpoints.");
237 for(
size_t i = 0; i < config->endpointsSize; i++) {
238 UA_EndpointDescription* ep = &config->endpoints[i];
239 if(ep->securityMode != UA_MESSAGESECURITYMODE_NONE)
continue;
241 UA_EndpointDescription_clear(ep);
243 if(i + 1 < config->endpointsSize) {
244 config->endpoints[i] = config->endpoints[config->endpointsSize - 1];
247 config->endpointsSize--;
250 if(config->endpointsSize == 0) {
251 UA_free(config->endpoints);
252 config->endpoints =
nullptr;
256 UA_ByteString_clear(&certificate);
257 UA_ByteString_clear(&privateKey);
258 for(
size_t i = 0; i < trustListSize; i++) UA_ByteString_clear(&trustList[i]);
261 for(
size_t i = 0; i < config->endpointsSize; ++i) {
262 UA_ApplicationDescription_clear(&config->endpoints[i].server);
263 UA_ApplicationDescription_copy(&config->applicationDescription, &config->endpoints[i].server);
267 this->server_config = UA_Server_getConfig(this->
mappedServer);
269 customType[0] = LoggingLevelType;
270 this->customDataTypes.reset(
new UA_DataTypeArray{this->server_config->customDataTypes, 1, customType, UA_FALSE});
271 this->server_config->customDataTypes = customDataTypes.get();
274 auto* usernamePasswordLogins =
new UA_UsernamePasswordLogin;
275 usernamePasswordLogins->password = UA_STRING_ALLOC(
const_cast<char*
>(this->serverConfig.
password.c_str()));
276 usernamePasswordLogins->username = UA_STRING_ALLOC(
const_cast<char*
>(this->serverConfig.
username.c_str()));
278 &this->server_config->securityPolicies[this->server_config->securityPoliciesSize - 1].policyUri, 1,
279 usernamePasswordLogins);
281 this->
baseNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
284 UA_String_clear(&usernamePasswordLogins->password);
285 UA_String_clear(&usernamePasswordLogins->username);
286 delete usernamePasswordLogins;
290 string xpath =
"//config";
291 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(xpath);
294 xmlNodeSetPtr nodeset = result->nodesetval;
295 if(nodeset->nodeNr > 1) {
296 throw std::runtime_error(
"To many <config>-Tags in config file");
313 vector<xmlNodePtr> mappingExceptionsVector =
315 if(mappingExceptionsVector.empty()) {
316 this->mappingExceptions = UA_FALSE;
320 transform(placeHolder.begin(), placeHolder.end(), placeHolder.begin(), ::toupper);
321 if(placeHolder ==
"TRUE") {
322 this->mappingExceptions = UA_TRUE;
325 this->mappingExceptions = UA_FALSE;
329 xmlXPathObjectPtr sub_result = this->fileHandler->getNodeSet(xpath +
"//server");
331 xmlNodeSetPtr nodeset = sub_result->nodesetval;
332 if(nodeset->nodeNr > 1) {
333 throw std::runtime_error(
"To many <server>-Tags in config file");
337 if(!placeHolder.empty()) {
338 transform(placeHolder.begin(), placeHolder.end(), placeHolder.begin(), ::toupper);
339 if(placeHolder.compare(
"TRACE") == 0) {
340 this->serverConfig.
logLevel = UA_LOGLEVEL_TRACE;
342 else if(placeHolder.compare(
"DEBUG") == 0) {
343 this->serverConfig.
logLevel = UA_LOGLEVEL_DEBUG;
345 else if(placeHolder.compare(
"INFO") == 0) {
346 this->serverConfig.
logLevel = UA_LOGLEVEL_INFO;
348 else if(placeHolder.compare(
"WARNING") == 0) {
349 this->serverConfig.
logLevel = UA_LOGLEVEL_WARNING;
351 else if(placeHolder.compare(
"ERROR") == 0) {
352 this->serverConfig.
logLevel = UA_LOGLEVEL_ERROR;
354 else if(placeHolder.compare(
"FATAL") == 0) {
355 this->serverConfig.
logLevel = UA_LOGLEVEL_FATAL;
361 UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
362 "Unknown logLevel (\"%s\") found in config file. INFO is used instead!", placeHolder.c_str());
368 UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
369 "No 'logLevel'-Attribute set in config file. Use default logging level: info");
371 logger = UA_Log_Stdout_withLevel(this->serverConfig.
logLevel);
374 if(!opcuaPort.empty()) {
375 this->serverConfig.
opcuaPort = std::stoi(opcuaPort);
379 &logger, UA_LOGCATEGORY_USERLAND,
"No 'port'-Attribute in config file is set. Use default Port: 16664");
383 if(!placeHolder.empty()) {
390 string applicationName;
392 string applicationName = ApplicationBase::getInstance().getName();
395 catch(ChimeraTK::logic_error) {
397 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
398 "No 'applicationName'-Attribute is set in config file. Use default application-name.");
405 xmlXPathFreeObject(sub_result);
408 logger = UA_Log_Stdout_withLevel(this->serverConfig.
logLevel);
409 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
410 "No <server>-Tag in config file. Use default port 16664 and application name configuration.");
416 if(!historizing.empty()) {
417 xmlXPathObjectPtr sub_sub_result = this->fileHandler->getNodeSet(
"//historizing");
419 xmlNodeSetPtr node_set = sub_sub_result->nodesetval;
420 for(int32_t i = 0; i < node_set->nodeNr; i++) {
421 vector<xmlNodePtr> nodeVectorhistorizingSetUp =
424 for(
auto& nodeHistorizingPath : nodeVectorhistorizingSetUp) {
428 bool incomplete =
false;
429 if(!history_name.empty()) {
430 temp.
name = history_name;
433 raiseError(
"Missing history parameter 'name'.",
434 "History configuration is not added to the list of available history configurations",
435 nodeHistorizingPath->line);
438 string history_buffer_length =
440 if(!history_buffer_length.empty()) {
441 sscanf(history_buffer_length.c_str(),
"%zu", &temp.
buffer_length);
444 raiseError(
"Missing history parameter 'buffer_length'.",
445 "History configuration " + history_name +
446 " is not added to the list of available history configurations",
447 nodeHistorizingPath->line);
450 string history_entries_per_response =
452 if(!history_entries_per_response.empty()) {
456 raiseError(
"Missing history parameter 'entries_per_response'.",
457 "History configuration " + history_name +
458 " is not added to the list of available history configurations",
459 nodeHistorizingPath->line);
463 if(!history_interval.empty()) {
464 sscanf(history_interval.c_str(),
"%zu", &temp.
interval);
467 raiseError(
"Missing history parameter 'interval'.",
468 "History configuration " + history_name +
469 " is not added to the list of available history configurations",
470 nodeHistorizingPath->line);
475 bool existing =
false;
476 for(
size_t j = 0; j < this->serverConfig.
history.size(); j++) {
477 if(history_name == this->serverConfig.
history[j].name) {
483 this->serverConfig.
history.insert(this->serverConfig.
history.end(), temp);
487 raiseError(
"Redefinition of history configuration.",
488 "History configuration " + history_name +
489 " is not added to the list of available history configurations");
497 if(!placeHolder.empty()) {
502 if(!placeHolder.empty()) {
505 xmlXPathFreeObject(result);
508 logger = UA_Log_Stdout_withLevel(this->serverConfig.
logLevel);
510 result = this->fileHandler->getNodeSet(xpath +
"//login");
512 xmlNodeSetPtr nodeset = result->nodesetval;
513 if(nodeset->nodeNr > 1) {
514 throw std::runtime_error(
"To many <login>-Tags in config file");
518 if(!placeHolder.empty()) {
519 this->serverConfig.
password = placeHolder;
522 throw std::runtime_error(
"<login>-Tag requires username");
526 if(!placeHolder.empty()) {
527 this->serverConfig.
username = placeHolder;
530 throw std::runtime_error(
"<login>-Tag requires password");
533 xmlXPathFreeObject(result);
539 result = this->fileHandler->getNodeSet(xpath +
"//security");
541 xmlNodeSetPtr nodeset = result->nodesetval;
542 if(nodeset->nodeNr > 1) {
543 throw std::runtime_error(
"To many <security>-Tags in config file");
547 if(!unsecure.empty()) {
548 transform(unsecure.begin(), unsecure.end(), unsecure.begin(), ::toupper);
549 if(unsecure.compare(
"TRUE") == 0) {
553 this->serverConfig.
unsecure =
false;
557 this->serverConfig.
unsecure =
false;
558 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
559 "No 'unsecure'-Attribute in config file is set. Disable unsecure endpoints");
562 if(!certPath.empty()) {
563 this->serverConfig.
certPath = certPath;
566 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
567 "Invalid security configuration. No 'certificate'-Attribute in config file is set.");
570 if(!keyPath.empty()) {
571 this->serverConfig.
keyPath = keyPath;
574 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
575 "Invalid security configuration. No 'privatekey'-Attribute in config file is set.");
578 if(!allowListFolder.empty()) {
582 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
583 "Invalid security configuration. No 'trustlist'-Attribute in config file is set.");
586 if(!blockListFolder.empty()) {
590 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
"No 'blockListFolder'-Attribute in config file is set.");
593 if(!issuerListFolder.empty()) {
597 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
"No 'issuerListFolder'-Attribute in config file is set.");
599 xmlXPathFreeObject(result);
602 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
603 "No <security>-Tag in config file. Use default configuration with unsecure endpoint.");
606 result = this->fileHandler->getNodeSet(xpath +
"//process_variable_hierarchy");
608 xmlNodeSetPtr nodeset = result->nodesetval;
609 vector<xmlNodePtr> nodeVectorUnrollPathPV =
611 for(
auto nodeUnrollPath : nodeVectorUnrollPathPV) {
613 transform(unrollSepEnabled.begin(), unrollSepEnabled.end(), unrollSepEnabled.begin(), ::toupper);
614 if(unrollSepEnabled ==
"TRUE") {
618 xmlXPathFreeObject(result);
621 UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
622 "No <process_variable_hierarchy>-Tag in config file. Use default hierarchical mapping with '/'.");
623 this->pvSeperator =
"/";
626 xmlXPathObjectPtr result_exclude = this->fileHandler->getNodeSet(
"//exclude");
627 xmlNodeSetPtr nodeset;
629 nodeset = result_exclude->nodesetval;
630 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
632 if(!exclude_string.empty()) {
633 this->
exclude.insert(this->
exclude.begin(),
"/" + exclude_string);
637 xmlXPathFreeObject(result_exclude);
639 xmlXPathObjectPtr folder_with_his = this->fileHandler->getNodeSet(
"//folder");
640 xmlNodeSetPtr nodeset_folder_with_history;
641 if(folder_with_his) {
642 nodeset_folder_with_history = folder_with_his->nodesetval;
643 for(int32_t i = 0; i < nodeset_folder_with_history->nodeNr; i++) {
648 if(!folder.empty() && !history.empty()) {
653 xmlXPathFreeObject(folder_with_his);
657 return this->serverConfig;
671 UA_LOG_DEBUG(server_config->logging, UA_LOGCATEGORY_USERLAND,
"No server mapped");
679 vector<UA_NodeId> historizing_nodes;
680 vector<string> historizing_setup;
681 UA_HistoryDataGathering gathering =
683 this->serverConfig.
history, this->serverConfig.historyfolders, this->serverConfig.historyvariables);
684 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
"Starting the server worker thread");
691 this->serverConfig.
historyfolders, this->serverConfig.historyvariables, this->server_config);
694 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
"Stopped the server worker thread");
698 vector<string> varPathVector;
699 if(!seperator.empty()) {
701 varPathVector.insert(varPathVector.end(), newPathVector.begin(), newPathVector.end());
703 if(!varPathVector.empty()) {
704 varPathVector.pop_back();
707 return UA_NODEID_NULL;
711 const std::string& varName,
const boost::shared_ptr<ControlSystemPVManager>&
csManager) {
714 if(!UA_NodeId_isNull(&folderPathNodeId)) {
721 varName.substr(1, varName.size() - 1),
csManager, server_config->logging);
723 this->variables.push_back(processvariable);
725 UA_Server_writeDisplayName(this->
mappedServer, tmpNodeId,
726 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
728 UA_NodeId_clear(&tmpNodeId);
732 const boost::shared_ptr<ControlSystemPVManager>&
csManager, UA_NodeId layer, UA_NodeId target) {
734 UA_BrowseDescription bd;
735 bd.includeSubtypes =
false;
737 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
738 bd.resultMask = UA_BROWSERESULTMASK_NONE;
739 bd.nodeClassMask = UA_NODECLASS_VARIABLE;
740 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
741 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, UA_UINT32_MAX, &bd);
742 for(
size_t j = 0; j < br.referencesSize; ++j) {
743 UA_ReferenceDescription rd = br.references[j];
745 UA_LocalizedText foundPVName;
746 string foundPVNameCPP;
747 UA_Server_readDisplayName(this->
mappedServer, rd.nodeId.nodeId, &foundPVName);
750 string pvSourceNameid;
751 UA_String foundPVSourceName;
752 string foundPVSourceNameCPP;
756 this->
mappedServer, UA_NODEID_STRING(1,
const_cast<char*
>((pvSourceNameid +
"/Name").c_str())), &value);
757 foundPVSourceName = *((UA_String*)value.data);
761 this->
mappedServer, target, foundPVSourceNameCPP,
csManager, server_config->logging, foundPVNameCPP);
762 this->variables.push_back(processvariable);
763 UA_Variant_clear(&value);
764 UA_LocalizedText_clear(&foundPVName);
766 UA_BrowseResult_clear(&br);
768 bd.includeSubtypes =
false;
770 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
771 bd.resultMask = UA_BROWSERESULTMASK_NONE;
772 bd.nodeClassMask = UA_NODECLASS_OBJECT;
773 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
774 br = UA_Server_browse(this->
mappedServer, UA_UINT32_MAX, &bd);
775 for(
size_t j = 0; j < br.referencesSize; ++j) {
776 UA_ReferenceDescription rd = br.references[j];
777 UA_String folderName, description;
778 UA_LocalizedText foundFolderName, foundFolderDescription;
779 UA_LocalizedText_init(&foundFolderDescription);
780 string foundFolderNameCPP;
781 UA_Server_readDescription(this->
mappedServer, rd.nodeId.nodeId, &foundFolderDescription);
782 UA_StatusCode result = UA_Server_readDisplayName(this->
mappedServer, rd.nodeId.nodeId, &foundFolderName);
784 if(result != UA_STATUSCODE_GOOD) {
788 UA_NodeId newRootFolder =
createFolder(target, foundFolderNameCPP);
789 if(foundFolderDescription.text.length > 0)
790 UA_Server_writeDescription(this->
mappedServer, newRootFolder, foundFolderDescription);
792 UA_LocalizedText_clear(&foundFolderName);
793 UA_LocalizedText_clear(&foundFolderDescription);
795 UA_BrowseResult_clear(&br);
799 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//folder");
800 xmlNodeSetPtr nodeset;
802 nodeset = result->nodesetval;
804 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
805 UA_NodeId folderPathNodeId;
806 string destination, description, folder;
807 vector<xmlNodePtr> nodeFolderPath =
810 vector<xmlNodePtr> nodeDescription =
814 transform(copy.begin(), copy.end(), copy.begin(), ::toupper);
815 if(!nodeFolderPath.empty()) {
818 if(!nodeDescription.empty()) {
821 if(!nodeFolder.empty()) {
825 if(!history.empty()) {
828 if(sourceName.empty()) {
829 raiseError(
"Can not add history if no sourceName attribute is set.",
"", nodeset->nodeTab[i]->line);
831 if(!nodeFolder.empty()) {
833 if(!nodeFolderPath.empty()) {
834 if(strlen(destination.c_str()) == 0) {
835 folderNodeId = this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
838 folderNodeId = this->serverConfig.
rootFolder +
"/" + destination +
"/" + folder +
"Dir";
842 folderNodeId = this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
847 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(folderNodeId.c_str()));
850 UA_String out = UA_STRING_NULL;
851 UA_print(&temp.
folder_id, &UA_TYPES[UA_TYPES_NODEID], &out);
852 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
853 "Adding history for folder from destination and name %.*s ", (
int)out.length, out.data);
854 UA_String_clear(&out);
856 else if(copy.empty() || copy ==
"FALSE") {
859 string folderNodeId = this->serverConfig.
rootFolder +
"/" + sourceName +
"Dir";
860 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(folderNodeId.c_str()));
863 UA_String out = UA_STRING_NULL;
864 UA_print(&temp.
folder_id, &UA_TYPES[UA_TYPES_NODEID], &out);
865 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
866 "Adding history for folder from source name %.*s ", (
int)out.length, out.data);
867 UA_String_clear(&out);
871 "Can not add history if copy is true and no source name is given.",
"", nodeset->nodeTab[i]->line);
877 if(history.empty()) {
878 raiseError(
"Folder creation failed. Name is missing.",
"Skipping Folder.", nodeset->nodeTab[i]->line);
883 UA_NodeId tmpOutput = UA_NODEID_NULL;
885 if(destination.empty()) {
886 pvNodeString += this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
889 pvNodeString += this->serverConfig.
rootFolder +
"/" + destination +
"/" + folder +
"Dir";
891 UA_NodeId pvNode = UA_NODEID_STRING(1,
const_cast<char*
>(pvNodeString.c_str()));
892 UA_Server_readNodeId(this->
mappedServer, pvNode, &tmpOutput);
893 if(!UA_NodeId_isNull(&tmpOutput)) {
894 UA_NodeId_clear(&tmpOutput);
896 if(sourceName.empty() && !description.empty()) {
898 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
901 raiseError(
"Folder with source mapping failed. Target folder node id exists.",
902 std::string(
"Skipping folder: ") + sourceName, nodeset->nodeTab[i]->line);
905 if(!copy.empty() && sourceName.empty()) {
906 raiseError(std::string(
"Source 'name: ") + folder +
"' folder missing.",
"Skipping Folder.",
907 nodeset->nodeTab[i]->line);
911 if(UA_NodeId_isNull(&folderPathNodeId)) {
912 folderPathNodeId = UA_NODEID_STRING(1,
const_cast<char*
>((this->serverConfig.
rootFolder +
"Dir").c_str()));
915 if(!sourceName.empty()) {
916 if((destination.empty() && sourceName == folder) ||
917 (!destination.empty() && sourceName == destination +
"/" + folder)) {
919 "Folder creation failed. Source and Destination equal.",
"Skipping Folder.", nodeset->nodeTab[i]->line);
923 string sourceFolder = this->serverConfig.
rootFolder +
"/" + sourceName +
"Dir";
924 bool isFolderType =
false;
925 UA_NodeId sourceFolderId = UA_NODEID_STRING(1,
const_cast<char*
>(sourceFolder.c_str()));
927 UA_BrowseDescription bd;
928 bd.includeSubtypes =
false;
929 bd.nodeId = sourceFolderId;
930 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
931 bd.resultMask = UA_BROWSERESULTMASK_ALL;
932 bd.nodeClassMask = UA_NODECLASS_OBJECTTYPE;
933 bd.browseDirection = UA_BROWSEDIRECTION_BOTH;
934 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, 1000, &bd);
935 for(
size_t j = 0; j < br.referencesSize; ++j) {
936 UA_ReferenceDescription rd = br.references[j];
937 UA_NodeId folderType = UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE);
938 if(UA_NodeId_equal(&rd.nodeId.nodeId, &folderType)) {
942 UA_BrowseResult_clear(&br);
944 raiseError(std::string(
"Folder creation failed. No corresponding source folder: ") + sourceName,
945 "Skipping Folder.", nodeset->nodeTab[i]->line);
950 bool sourceAndDestinationEqual =
false;
951 if(!destination.empty()) {
952 if(sourceName == destination +
"/" + folder) sourceAndDestinationEqual =
true;
955 if(sourceName == folder) sourceAndDestinationEqual =
true;
957 if(sourceAndDestinationEqual) {
958 raiseError(std::string(
"Folder creation failed. Source and destination must be different for '") +
959 sourceName +
"' folder.",
960 "Skipping Folder.", nodeset->nodeTab[i]->line);
964 UA_LocalizedText foundFolderName;
965 UA_NodeId copyRoot =
createFolder(folderPathNodeId, folder);
966 if(UA_NodeId_isNull(©Root)) {
967 string existingDestinationFolderString;
968 if(destination.empty()) {
969 existingDestinationFolderString += this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
972 existingDestinationFolderString = this->serverConfig.
rootFolder +=
973 "/" + destination +
"/" + folder +
"Dir";
975 copyRoot = UA_NODEID_STRING(1,
const_cast<char*
>(existingDestinationFolderString.c_str()));
978 if(!description.empty()) {
979 UA_Server_writeDescription(this->
mappedServer, copyRoot,
980 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
984 string existingDestinationFolderString;
985 UA_NodeId copyRoot =
createFolder(folderPathNodeId, folder);
986 if(UA_NodeId_isNull(©Root)) {
987 if(destination.empty()) {
988 existingDestinationFolderString = this->serverConfig.
rootFolder +
"/" + folder;
991 existingDestinationFolderString = this->serverConfig.
rootFolder +
"/" + destination +
"/" + folder;
993 copyRoot = UA_NODEID_STRING(1,
const_cast<char*
>(existingDestinationFolderString.c_str()));
996 if(!description.empty()) {
997 UA_Server_writeDescription(this->
mappedServer, copyRoot,
998 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
1001 UA_BrowseDescription bd;
1002 bd.includeSubtypes =
false;
1003 bd.nodeId = sourceFolderId;
1004 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
1005 bd.resultMask = UA_BROWSERESULTMASK_ALL;
1006 bd.nodeClassMask = UA_NODECLASS_OBJECT;
1007 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
1008 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, 1000, &bd);
1009 for(
size_t j = 0; j < br.referencesSize; ++j) {
1010 UA_ExpandedNodeId enid;
1011 enid.serverIndex = 0;
1012 enid.namespaceUri = UA_STRING_NULL;
1013 enid.nodeId = br.references[j].nodeId.nodeId;
1014 UA_Server_addReference(
1015 this->
mappedServer, copyRoot, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), enid, UA_TRUE);
1017 UA_BrowseResult_clear(&br);
1018 bd.nodeId = sourceFolderId;
1019 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
1020 bd.resultMask = UA_BROWSERESULTMASK_ALL;
1021 bd.nodeClassMask = UA_NODECLASS_VARIABLE;
1022 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
1024 for(
size_t j = 0; j < br.referencesSize; ++j) {
1025 UA_ExpandedNodeId enid;
1026 enid.serverIndex = 0;
1027 enid.namespaceUri = UA_STRING_NULL;
1028 enid.nodeId = br.references[j].nodeId.nodeId;
1029 UA_Server_addReference(
1030 this->
mappedServer, copyRoot, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), enid, UA_TRUE);
1032 UA_BrowseResult_clear(&br);
1037 UA_NodeId retnode =
createFolder(folderPathNodeId, folder);
1039 if(copy.empty() && sourceName.empty() && !description.empty()) {
1040 UA_Server_writeDescription(this->
mappedServer, retnode,
1041 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
1045 xmlXPathFreeObject(result);
1050 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//process_variable");
1051 xmlNodeSetPtr nodeset;
1053 nodeset = result->nodesetval;
1054 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1055 string sourceName, copy, destination, name, unit, description, unrollPath, history;
1056 vector<xmlNodePtr> nodeDestination =
1061 vector<xmlNodePtr> nodeDescription =
1065 transform(copy.begin(), copy.end(), copy.begin(), ::toupper);
1069 if(!history.empty()) {
1072 string targetNodeId;
1074 if(!nodeName.empty()) {
1075 if(nodeDestination.empty()) {
1077 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(targetNodeId.c_str()));
1082 targetNodeId = this->serverConfig.
rootFolder +
"/" +
1085 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(targetNodeId.c_str()));
1090 if(!sourceName.empty() && (copy.empty() || copy ==
"FALSE")) {
1092 targetNodeId = this->serverConfig.
rootFolder +
"/" + sourceName;
1093 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(targetNodeId.c_str()));
1099 if(!nodeDestination.empty()) {
1103 if(!nodeName.empty()) {
1106 if(!nodeUnit.empty()) {
1109 if(!nodeDescription.empty()) {
1113 if(sourceName.empty()) {
1115 raiseError(std::string(
"PV mapping failed. SourceName missing for 'name' : '") + name,
1116 "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1119 raiseError(
"PV mapping failed. SourceName is missing.",
"Skipping PV mapping.", nodeset->nodeTab[i]->line);
1124 if(((destination +
"/" + name) == sourceName) && copy ==
"FALSE") {
1126 string parentSourceFolder = this->serverConfig.
rootFolder +
"/" +
1129 UA_NodeId parentSourceFolderId = UA_NODEID_STRING(1,
const_cast<char*
>(parentSourceFolder.c_str()));
1130 UA_NodeId pvNodeId = UA_NODEID_NULL;
1131 UA_BrowseDescription bd;
1132 bd.includeSubtypes =
false;
1133 bd.nodeId = parentSourceFolderId;
1134 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
1135 bd.resultMask = UA_BROWSERESULTMASK_ALL;
1136 bd.nodeClassMask = UA_NODECLASS_VARIABLE;
1137 bd.browseDirection = UA_BROWSEDIRECTION_BOTH;
1138 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, 20, &bd);
1139 for(
size_t j = 0; j < br.referencesSize; ++j) {
1140 UA_ReferenceDescription rd = br.references[j];
1144 UA_NodeId_copy(&br.references[j].nodeId.nodeId, &pvNodeId);
1147 UA_BrowseResult_clear(&br);
1148 if(UA_NodeId_isNull(&pvNodeId)) {
1150 raiseError(std::string(
"PV mapping failed. No corresponding source pv for 'name' : '") + name,
1151 "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1155 "PV mapping failed. No corresponding source pv.",
"Skipping PV mapping.", nodeset->nodeTab[i]->line);
1161 for(
auto& variable : this->variables) {
1162 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1163 if(UA_NodeId_equal(&tmpNodeId, &pvNodeId)) {
1164 variable->setEngineeringUnit(unit);
1166 UA_NodeId_clear(&tmpNodeId);
1169 if(!description.empty()) {
1171 for(
auto& variable : this->variables) {
1172 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1173 if(UA_NodeId_equal(&tmpNodeId, &pvNodeId)) {
1174 variable->setDescription(description);
1176 UA_NodeId_clear(&tmpNodeId);
1180 if(!UA_NodeId_isNull(&pvNodeId)) {
1181 UA_NodeId_clear(&pvNodeId);
1186 string parentSourceString = this->serverConfig.
rootFolder +
"/" + sourceName;
1187 UA_NodeId parentSourceId = UA_NODEID_STRING(1,
const_cast<char*
>(parentSourceString.c_str()));
1188 UA_NodeId tmpOutput = UA_NODEID_NULL;
1189 UA_Server_readNodeId(this->
mappedServer, parentSourceId, &tmpOutput);
1190 if(UA_NodeId_isNull(&tmpOutput)) {
1192 raiseError(std::string(
"PV mapping failed. Source PV not found 'name' : '") + name,
"Skipping PV mapping.",
1193 nodeset->nodeTab[i]->line);
1196 raiseError(
"PV mapping failed. Source PV not found",
"Skipping PV mapping.", nodeset->nodeTab[i]->line);
1201 UA_NodeId_clear(&tmpOutput);
1202 UA_NodeId_init(&tmpOutput);
1205 UA_NodeId createdNodeId = UA_NODEID_NULL;
1206 if(copy ==
"TRUE") {
1207 if(sourceName == (destination +
"/" + name)) {
1208 raiseError(
"PV mapping failed. Source and destination must be different if copy='true'.",
1209 std::string(
"Skipping PV ") + sourceName, nodeset->nodeTab[i]->line);
1212 UA_NodeId destinationFolderNodeId = UA_NODEID_NULL;
1213 if(destination.empty()) {
1214 destinationFolderNodeId = this->ownNodeId;
1217 if(unrollPath.empty()) {
1222 if(destination.find(
"Variables" + unrollPath) == 0) {
1223 string requestedBrowseName = destination;
1224 requestedBrowseName.erase(0, (
"Variables" + unrollPath).length());
1226 string requestedPVBrowseName = this->serverConfig.
rootFolder +
"/Variables/" + requestedBrowseName;
1227 UA_NodeId requestedPVBrowseId = UA_NODEID_STRING(1,
const_cast<char*
>(requestedPVBrowseName.c_str()));
1228 UA_NodeId tmpOutput = UA_NODEID_NULL;
1229 UA_Server_readNodeId(this->
mappedServer, requestedPVBrowseId, &tmpOutput);
1230 if(!UA_NodeId_isNull(&tmpOutput)) {
1231 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
1232 "Using legacy code. Mapped PV is used as PV mapping target");
1233 UA_NodeId_copy(&requestedPVBrowseId, &destinationFolderNodeId);
1236 destinationFolderNodeId =
1241 destinationFolderNodeId =
1250 if(!UA_NodeId_isNull(&destinationFolderNodeId)) {
1253 UA_NodeId tmpProcessVariableNodeId = processvariable->
getOwnNodeId();
1254 if(UA_NodeId_isNull(&tmpProcessVariableNodeId)) {
1255 raiseError(
"PV creation failed. PV with same name mapped.", std::string(
"Skipping PV ") + sourceName,
1256 nodeset->nodeTab[i]->line);
1260 UA_NodeId_clear(&tmpProcessVariableNodeId);
1262 this->variables.push_back(processvariable);
1265 raiseError(
"Folder creation failed.", std::string(
"Skipping PV ") + sourceName, nodeset->nodeTab[i]->line);
1268 UA_NodeId tmpPVNodeId = processvariable->
getOwnNodeId();
1269 UA_NodeId_copy(&tmpPVNodeId, &createdNodeId);
1270 UA_NodeId_clear(&tmpPVNodeId);
1276 name = sourceVarName;
1278 if(sourceVarName != name) {
1279 if(history.empty()) {
1280 raiseError(
"PV mapping failed. The pv name can't changed if copy is false.",
1281 std::string(
"Skipping PV mapping of pv ") + name);
1287 UA_ExpandedNodeId enid;
1288 enid.serverIndex = 0;
1289 enid.namespaceUri = UA_STRING_NULL;
1290 enid.nodeId = parentSourceId;
1292 UA_StatusCode addRef = UA_Server_addReference(
1293 this->
mappedServer, destinationFolder, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), enid,
true);
1294 if(sourceVarName != name) {
1295 raiseError(
"PV mapping failed. Can't create reference to original pv.",
1296 std::string(
"Skipping PV mapping of pv ") + name);
1299 UA_NodeId_copy(&parentSourceId, &createdNodeId);
1302 for(
auto& variable : this->variables) {
1303 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1304 if(UA_NodeId_equal(&tmpNodeId, &createdNodeId)) {
1305 variable->setEngineeringUnit(unit);
1307 UA_NodeId_clear(&tmpNodeId);
1310 if(!description.empty()) {
1311 for(
auto& variable : this->variables) {
1312 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1313 if(UA_NodeId_equal(&tmpNodeId, &createdNodeId)) {
1314 variable->setDescription(description);
1316 UA_NodeId_clear(&tmpNodeId);
1319 UA_Server_writeDisplayName(this->
mappedServer, createdNodeId,
1320 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(name.c_str())));
1321 UA_NodeId_clear(&createdNodeId);
1324 xmlXPathFreeObject(result);
1329 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//additional_variable");
1330 xmlNodeSetPtr nodeset;
1332 nodeset = result->nodesetval;
1333 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1334 string destination, name, description, value;
1335 vector<xmlNodePtr> nodeDestination =
1338 vector<xmlNodePtr> nodeDescription =
1342 if(!nodeDestination.empty()) {
1345 if(!nodeName.empty()) {
1348 if(!nodeDescription.empty()) {
1351 if(!nodeValue.empty()) {
1356 raiseError(
"Additional variable node creation failed. Additional variable name is mandatory.",
1357 "Skipping additional variable.", nodeset->nodeTab[i]->line);
1361 UA_NodeId tmpOutput = UA_NODEID_NULL;
1362 string avNodeString;
1363 if(destination.empty()) {
1364 avNodeString = this->serverConfig.
rootFolder +
"/" + name +
"AdditionalVariable";
1367 avNodeString = this->serverConfig.
rootFolder +
"/" + destination +
"/" + name +
"AdditionalVariable";
1369 UA_NodeId avNode = UA_NODEID_STRING(1,
const_cast<char*
>(avNodeString.c_str()));
1370 UA_Server_readNodeId(this->
mappedServer, avNode, &tmpOutput);
1371 if(!UA_NodeId_isNull(&tmpOutput)) {
1372 UA_NodeId_clear(&tmpOutput);
1373 UA_NodeId_init(&tmpOutput);
1374 raiseError(
"Additional variable node creation failed. Additional variable already exists.",
1375 std::string(
"Skipping additional variable ") + name, nodeset->nodeTab[i]->line);
1379 UA_NodeId_clear(&tmpOutput);
1380 string pvNodeString;
1381 if(destination.empty()) {
1382 pvNodeString = this->serverConfig.
rootFolder +
"/" + name +
"Value";
1385 pvNodeString = this->serverConfig.
rootFolder +
"/" + destination +
"/" + name +
"Value";
1387 UA_NodeId pvNode = UA_NODEID_STRING(1,
const_cast<char*
>(pvNodeString.c_str()));
1388 UA_Server_readNodeId(this->
mappedServer, pvNode, &tmpOutput);
1389 if(!UA_NodeId_isNull(&tmpOutput)) {
1390 UA_NodeId_clear(&tmpOutput);
1391 UA_NodeId_init(&tmpOutput);
1392 raiseError(
"Additional variable node creation failed. PV with same name already exists.",
1393 std::string(
"Skipping additional variable ") + name, nodeset->nodeTab[i]->line);
1397 UA_NodeId additionalVarFolderPath = UA_NODEID_NULL;
1398 string additionalVarFolderPathNodeId;
1399 if(!destination.empty()) {
1403 additionalVarFolderPathNodeId += this->serverConfig.
rootFolder +
"Dir";
1404 additionalVarFolderPath = UA_NODEID_STRING_ALLOC(1,
const_cast<char*
>(additionalVarFolderPathNodeId.c_str()));
1406 if(UA_NodeId_isNull(&additionalVarFolderPath)) {
1407 raiseError(
"Creation of additional variable folder failed.",
"Skipping additional variable.",
1408 nodeset->nodeTab[i]->line);
1411 auto* additionalvariable =
1413 this->additionalVariables.push_back(additionalvariable);
1414 if(destination.empty()) {
1415 UA_NodeId_clear(&additionalVarFolderPath);
1419 xmlXPathFreeObject(result);
1424 return this->variables;
1427 UA_NodeId ua_uaadapter::createUAFolder(
1428 UA_NodeId basenodeid,
const std::string& folderName,
const std::string& description) {
1430 UA_StatusCode retval = UA_STATUSCODE_GOOD;
1431 UA_NodeId createdNodeId = UA_NODEID_NULL;
1433 if(UA_NodeId_equal(&
baseNodeId, &createdNodeId) == UA_TRUE) {
1434 return createdNodeId;
1438 UA_ObjectAttributes oAttr;
1439 UA_ObjectAttributes_init(&oAttr);
1441 oAttr.displayName = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(folderName.c_str()));
1442 oAttr.description = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str()));
1444 string parentNodeIdString;
1445 if(basenodeid.identifierType == UA_NODEIDTYPE_STRING) {
1447 if(!parentNodeIdString.empty()) {
1448 parentNodeIdString.resize(parentNodeIdString.size() - 3);
1450 parentNodeIdString +=
'/' + folderName +
"Dir";
1452 else if(basenodeid.identifierType == UA_NODEIDTYPE_NUMERIC) {
1453 parentNodeIdString +=
'/' + std::to_string(basenodeid.identifier.numeric) +
"Dir";
1457 UA_NODEID_STRING(1,
const_cast<char*
>(parentNodeIdString.c_str())),
1458 basenodeid, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
1459 UA_QUALIFIEDNAME(1,
const_cast<char*
>(folderName.c_str())), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), oAttr,
1463 return createdNodeId;
1466 void ua_uaadapter::raiseError(std::string errorMesssage, std::string consequenceMessage,
const int& line) {
1467 std::string lineMessage(
"");
1469 lineMessage = std::string(
" Mapping line number: ") + std::to_string(line) +
".";
1471 std::string tmp[2] = {errorMesssage, consequenceMessage};
1472 if(errorMesssage.back() !=
'.') {
1473 errorMesssage +=
".";
1475 if(consequenceMessage.back() !=
'.') {
1476 consequenceMessage +=
".";
1478 if(this->mappingExceptions) {
1479 throw std::runtime_error(std::string(
"Error! ") + errorMesssage + lineMessage);
1482 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
"%s %s", errorMesssage.c_str(),
1483 (consequenceMessage + lineMessage).c_str());
1487 &logger, UA_LOGCATEGORY_USERLAND,
"%s %s", errorMesssage.c_str(), (consequenceMessage + lineMessage).c_str());
1491 UA_StatusCode ua_uaadapter::mapSelfToNamespace() {
1492 UA_StatusCode retval = UA_STATUSCODE_GOOD;
1493 UA_NodeId createdNodeId = UA_NODEID_NULL;
1495 if(UA_NodeId_equal(&this->
baseNodeId, &createdNodeId) == UA_TRUE) {
1501 UA_ObjectAttributes oAttr;
1502 UA_ObjectAttributes_init(&oAttr);
1505 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(this->serverConfig.
rootFolder.c_str()));
1507 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(this->serverConfig.
descriptionFolder.c_str()));
1510 UA_NODEID_STRING(1, (
const_cast<char*
>((this->serverConfig.
rootFolder +
"Dir").c_str()))),
1511 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
1512 UA_QUALIFIEDNAME(1,
const_cast<char*
>(this->serverConfig.
rootFolder.c_str())),
1513 UA_NODEID_NUMERIC(CSA_NSID, UA_NS2ID_CTKMODULE), oAttr, &
ownedNodes, &createdNodeId);
1515 this->ownNodeId = createdNodeId;
1520 UA_ObjectAttributes oAttr;
1521 UA_ObjectAttributes_init(&oAttr);
1523 oAttr.displayName = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(
"ServerConfiguration"));
1525 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(
"Here adapter configurations are placed."));
1526 UA_Server_addObjectNode(this->
mappedServer, UA_NODEID_STRING(1,
const_cast<char*
>(
"ServerConfigurationDir")),
1527 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
1528 UA_QUALIFIEDNAME(1,
const_cast<char*
>(
"ServerConfiguration")),
1529 UA_NODEID_NUMERIC(CSA_NSID, UA_NS2ID_CTKMODULE), oAttr, &
ownedNodes, &createdNodeId);
1530 configNodeId = createdNodeId;
1534 UA_VariableAttributes attr = UA_VariableAttributes_default;
1535 attr.displayName = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en-US"),
const_cast<char*
>(
"logLevel"));
1536 attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
1537 attr.valueRank = -1;
1538 attr.dataType = LoggingLevelType.typeId;
1540 auto status = UA_Variant_setScalarCopy(&attr.value, &l, &LoggingLevelType);
1542 UA_NodeId currentNodeId = UA_NODEID_STRING(1,
const_cast<char*
>(
"logLevel"));
1543 UA_QualifiedName currentName = UA_QUALIFIEDNAME(1,
const_cast<char*
>(
"logLevel"));
1544 UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
1545 UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
1547 UA_DataSource logLevelDataSource;
1550 UA_Server_addDataSourceVariableNode(this->
mappedServer, currentNodeId, createdNodeId, parentReferenceNodeId,
1551 currentName, variableTypeNodeId, attr, logLevelDataSource,
this, NULL);
1552 UA_Variant_clear(&attr.value);
1555 return UA_STATUSCODE_GOOD;
1559 return this->ownNodeId;
1563 UA_NodeId lastNodeId = basenodeid;
1564 for(
const std::string& t : folderPath) {
1566 if(UA_NodeId_isNull(&lastNodeId)) {
1567 return UA_NODEID_NULL;
1574 UA_NodeId lastNodeId = UA_NODEID_NULL;
1575 for(
auto& i : this->folderVector) {
1576 if((i.folderName == folder) && (UA_NodeId_equal(&i.prevFolderNodeId, &basenodeid))) {
1577 return i.folderNodeId;
1580 return UA_NODEID_NULL;
1584 if(UA_NodeId_isNull(&basenodeid)) {
1585 return UA_NODEID_NULL;
1589 int32_t starter4Folder = 0;
1590 UA_NodeId nextNodeId = basenodeid;
1591 UA_NodeId startNodeId = basenodeid;
1592 if(UA_NodeId_isNull(&toCheckNodeId)) {
1593 bool setted =
false;
1595 for(uint32_t m = 0; m < folderPath.size(); m++) {
1596 for(
auto& i : this->folderVector) {
1598 if(!setted && (folderPath.at(m) == i.folderName) && (UA_NodeId_equal(&i.prevFolderNodeId, &nextNodeId)) &&
1599 ((m + 1) < folderPath.size())) {
1602 starter4Folder = m + 1;
1603 nextNodeId = i.folderNodeId;
1615 return toCheckNodeId;
1618 UA_NodeId prevNodeId = nextNodeId;
1620 for(uint32_t m = starter4Folder; m < folderPath.size(); m++) {
1621 prevNodeId = this->
createFolder(prevNodeId, folderPath.at(m));
1628 if(UA_NodeId_isNull(&basenodeid)) {
1629 return UA_NODEID_NULL;
1632 UA_NodeId toCheckNodeId =
existFolder(basenodeid, folderName);
1634 if(UA_NodeId_isNull(&toCheckNodeId)) {
1636 newFolder.
folderNodeId = this->createUAFolder(basenodeid, folderName, description);
1638 this->folderVector.push_back(newFolder);
1644 vector<string> mappedPvSources;
1645 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//process_variable");
1647 xmlNodeSetPtr nodeset = result->nodesetval;
1648 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1649 mappedPvSources.insert(
1653 xmlXPathFreeObject(result);
1654 return mappedPvSources;
1658 vector<string> notMappableVariablesNames;
1659 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//process_variable");
1662 xmlNodeSetPtr nodeset = result->nodesetval;
1663 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1665 bool mapped =
false;
1668 if(var->getName() == mappedVar) {
1675 string mappedVarHist = serverConfig.
rootFolder +
"/" + mappedVar;
1678 if(tmp.compare(mappedVarHist) == 0) {
1685 notMappableVariablesNames.push_back(mappedVar);
1689 xmlXPathFreeObject(result);
1692 return notMappableVariablesNames;
1696 return UA_DateTime_now();
1704 void* ,
const UA_NodeId* ,
void* nodeContext, UA_Boolean ,
1705 const UA_NumericRange* , UA_DataValue* dataValue) {
1708 UA_Variant_setScalarCopy(&dataValue->value, &l, &LoggingLevelType);
1709 dataValue->hasValue =
true;
1710 return UA_STATUSCODE_GOOD;
1714 void* ,
const UA_NodeId* ,
void* nodeContext,
const UA_NumericRange* ,
1715 const UA_DataValue* data) {
1717 const UA_Variant* var = &data->value;
1718 adapter->serverConfig.logLevel = toLogLevel(*((
UA_LoggingLevel*)var->data));
1719 adapter->logger = UA_Log_Stdout_withLevel(adapter->serverConfig.logLevel);
1720 adapter->server_config->logging = &adapter->logger;
1721 UA_LOG_INFO(adapter->server_config->logging, UA_LOGCATEGORY_USERLAND,
"Set log level to %d",
1722 adapter->serverConfig.logLevel);
1723 return UA_STATUSCODE_GOOD;
This class represent a additional variable from <variableMap.xml> in the information model of a OPC U...
This class mapped all inforamtion into the opca server.
This class represent a processvariable of the controlsystemadapter in the information model of a OPC ...
UA_NodeId getOwnNodeId()
Get node id of this processvariable instance.
This class provide the opcua server and manage the variable mapping.
void fillBuildInfo(UA_ServerConfig *config) const
Fill server build information.
void workerThread()
Create and start a thread for the opcua server instance.
void implicitVarMapping(const std::string &varName, const boost::shared_ptr< ControlSystemPVManager > &csManager)
Start implicit mapping process.
UA_NodeId createFolder(UA_NodeId basenodeid, const string &folderName, const string &description="")
Creates a folder in the given parent node.
static UA_StatusCode writeLogLevel(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *data)
callback function used to change the servers logging level.
vector< ua_processvariable * > getVariables()
Methode that returns all <ua_processvariable> of the class.
UA_NodeId existFolder(UA_NodeId basenodeid, const string &folderName)
Check if a folder exist in opcua server.
void buildFolderStructure(const boost::shared_ptr< ControlSystemPVManager > &csManager)
Read mapping file and and add contained folders to the server.
void addAdditionalVariables()
Read mapping file and add contained additional variables to the server.
UA_NodeId getOwnNodeId()
Methode that returns the node id of the instanced class.
UA_DateTime getSourceTimeStamp()
Return the timestamp of the node.
void deepCopyHierarchicalLayer(const boost::shared_ptr< ControlSystemPVManager > &csManager, UA_NodeId layer, UA_NodeId target)
Copy (recursively) the content of a folder to a new location.
UA_Server * getMappedServer()
Return the OPC UA Server instance.
void explicitVarMapping(const boost::shared_ptr< ControlSystemPVManager > &csManager)
Read mapping file and apply contained PV mappings.
UA_NodeId existFolderPath(UA_NodeId basenodeid, const vector< string > &folderPath)
Check if a folder path exist in opcua server.
void readConfig()
This Methode reads the config-tag form the given <variableMap.xml>.
UA_NodeId createFolderPath(UA_NodeId basenodeid, vector< string > folderPathVector)
Create a path of folders in the given parent node.
vector< string > getAllMappedPvSourceNames()
UA_NodeId enrollFolderPathFromString(const string &path, const string &seperator)
Create folder structure based on the given path.
void applyMapping(const boost::shared_ptr< ControlSystemPVManager > &csManager)
Read mapping file and apply the contained folders, additional variables and pv mappings.
vector< string > folder_with_history
ua_uaadapter(const string &configPath)
Constructor of the class.
virtual ~ua_uaadapter()
Destrructor of the class.
vector< string > getAllNotMappableVariablesNames()
Methode to get all names from all potential VarableNodes from XML-Mappingfile which could not allocat...
static UA_StatusCode readLogLevel(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *dataValue)
callback function used to read the servers logging level.
ServerConfig get_server_config()
get ServerConfig.
static std::vector< xmlNodePtr > getNodesByName(xmlNodePtr startNode, const std::string &nodeName)
This methode return a list of all nodes with the given name nodeName starting by the given startNode.
static std::string getContentFromNode(xmlNode *node)
This methode returns the value between a xml tag.
static std::vector< std::string > parseVariablePath(const std::string &variablePath, const std::string &seperator="/")
This methode splitt a given string bey the given seperators.
static std::string getAttributeValueFromNode(xmlNode *node, const std::string &attributeName)
This methode returns a value of the given attribute from the given node you want to know.
UA_StatusCode csa_namespace_init(UA_Server *server)
boost::shared_ptr< ChimeraTK::ControlSystemPVManager > csManager
UA_StatusCode ua_mapInstantiatedNodes(UA_NodeId objectId, UA_NodeId definitionId, void *handle)
Node function and proxy mapping for new nodes.
void clear_history(UA_HistoryDataGathering gathering, vector< UA_NodeId > &historizing_nodes, vector< string > &historizing_setup, UA_Server *mappedServer, vector< AdapterFolderHistorySetup > historyfolders, vector< AdapterPVHistorySetup > historyvariables, UA_ServerConfig *server_config)
UA_HistoryDataGathering add_historizing_nodes(vector< UA_NodeId > &historizing_nodes, vector< string > &historizing_setup, UA_Server *mappedServer, UA_ServerConfig *server_config, vector< AdapterHistorySetup > history, vector< AdapterFolderHistorySetup > historyfolders, vector< AdapterPVHistorySetup > historyvariables)
string folder_historizing
size_t entries_per_response
string variable_historizing
This struct represents a folder in OPCUA with its own node id and with his parent and child node id.
string folderName
Name of the folder.
UA_NodeId folderNodeId
NodeId from the folder from opcua server.
UA_NodeId prevFolderNodeId
NodeId from the parent folder.
This struct represents a server config.
UA_Boolean UsernamePasswordLogin
vector< AdapterPVHistorySetup > historyvariables
vector< AdapterHistorySetup > history
vector< AdapterFolderHistorySetup > historyfolders
#define UASTRING_TO_CPPSTRING(_p_uastring, _p_cppstring)
#define UA_STRING_TO_CPPSTRING_COPY(_p_uastring, _p_cppstring)