22 #include <open62541/plugin/historydata/history_data_backend_memory.h>
23 #include <open62541/plugin/historydata/history_data_gathering_default.h>
24 #include <open62541/plugin/historydata/history_database_default.h>
25 #include <open62541/plugin/historydatabase.h>
26 #include <open62541/plugin/log_stdout.h>
27 #include <open62541/server.h>
40 #include "csa_config.h"
51 #define xstr(a) str(a)
58 this->mappingExceptions = UA_FALSE;
59 this->fileHandler = std::make_shared<xml_file_handler>(configFile);
61 this->constructServer();
62 this->mapSelfToNamespace();
67 for(
auto ptr : variables)
delete ptr;
68 for(
auto ptr : additionalVariables)
delete ptr;
69 for(
auto ptr : mappedVariables)
delete ptr;
70 for(
auto dir : folderVector) {
71 UA_NodeId_clear(&dir.folderNodeId);
73 UA_NodeId_clear(&this->ownNodeId);
74 UA_NodeId_clear(&this->configNodeId);
77 static string cleanUri(
string s) {
79 std::regex r1(
"\\s+");
80 s = std::regex_replace(s, r1,
" ");
81 s = std::regex_replace(s, r1,
"_");
83 std::regex r2(
"[^ -~]+");
84 s = std::regex_replace(s, r2,
"");
88 static UA_ByteString loadFile(
const char*
const path) {
89 UA_ByteString fileContents = UA_STRING_NULL;
92 FILE* fp = fopen(path,
"rb");
99 fseek(fp, 0, SEEK_END);
100 fileContents.length = (size_t)ftell(fp);
101 fileContents.data = (UA_Byte*)UA_malloc(fileContents.length *
sizeof(UA_Byte));
102 if(fileContents.data) {
103 fseek(fp, 0, SEEK_SET);
104 size_t read = fread(fileContents.data,
sizeof(UA_Byte), fileContents.length, fp);
105 if(read != fileContents.length) UA_ByteString_clear(&fileContents);
108 fileContents.length = 0;
117 char hostname[HOST_NAME_MAX];
118 gethostname(hostname, HOST_NAME_MAX);
119 string hostname_uri =
"urn:";
120 hostname_uri.append(hostname);
121 hostname_uri.append(
":ChimeraTK:" + this->serverConfig.
applicationName);
123 config->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
124 UA_String* hostName = UA_String_new();
125 *hostName = UA_STRING_ALLOC(hostname);
126 config->applicationDescription.discoveryUrls = hostName;
127 config->applicationDescription.discoveryUrlsSize = 1;
128 UA_String_clear(&config->applicationDescription.applicationUri);
129 config->applicationDescription.applicationUri = UA_STRING_ALLOC(
const_cast<char*
>(cleanUri(hostname_uri).c_str()));
130 UA_String_clear(&config->buildInfo.manufacturerName);
131 config->buildInfo.manufacturerName = UA_STRING_ALLOC(
const_cast<char*
>(
"ChimeraTK Team"));
134 "open62541: " xstr(UA_OPEN62541_VER_MAJOR)
"." xstr(UA_OPEN62541_VER_MINOR)
"." xstr(UA_OPEN62541_VER_PATCH)
", ControlSystemAdapter-OPC-UA-Adapter: " xstr(
135 PROJECT_VER_MAJOR)
"." xstr(PROJECT_VER_MINOR)
"." xstr(PTOJECT_VER_PATCH)
"";
136 UA_String_clear(&config->buildInfo.softwareVersion);
137 config->buildInfo.softwareVersion = UA_STRING_ALLOC(
const_cast<char*
>(versionString.c_str()));
138 config->buildInfo.buildDate = UA_DateTime_now();
139 UA_String_clear(&config->buildInfo.buildNumber);
140 config->buildInfo.buildNumber = UA_STRING_ALLOC(
const_cast<char*
>(
""));
142 UA_String_clear(&config->buildInfo.productName);
143 config->buildInfo.productName = UA_STRING_ALLOC(
const_cast<char*
>(this->serverConfig.
applicationName.c_str()));
144 string product_urn =
"urn:ChimeraTK:" + this->serverConfig.
applicationName;
145 UA_String_clear(&config->buildInfo.productUri);
146 config->buildInfo.productUri = UA_STRING_ALLOC(
const_cast<char*
>(cleanUri(product_urn).c_str()));
147 UA_LocalizedText_clear(&config->applicationDescription.applicationName);
148 config->applicationDescription.applicationName = UA_LOCALIZEDTEXT_ALLOC(
149 const_cast<char*
>(
"en_US"),
const_cast<char*
>(this->serverConfig.
applicationName.c_str()));
152 void ua_uaadapter::constructServer() {
153 auto config = (UA_ServerConfig*)UA_calloc(1,
sizeof(UA_ServerConfig));
154 config->logging = &logger;
156 UA_ServerConfig_setMinimal(config, this->serverConfig.
opcuaPort,
nullptr);
158 config->eventLoop->logger = &logger;
160 UA_ByteString certificate = UA_BYTESTRING_NULL;
161 UA_ByteString privateKey = UA_BYTESTRING_NULL;
162 size_t trustListSize = 0;
163 UA_ByteString* trustList =
nullptr;
164 size_t issuerListSize = 0;
165 UA_ByteString* issuerList =
nullptr;
166 size_t blockListSize = 0;
167 UA_ByteString* blockList =
nullptr;
170 certificate = loadFile(this->serverConfig.
certPath.c_str());
171 privateKey = loadFile(this->serverConfig.
keyPath.c_str());
172 if(UA_ByteString_equal(&certificate, &UA_BYTESTRING_NULL) ||
173 UA_ByteString_equal(&privateKey, &UA_BYTESTRING_NULL)) {
174 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
175 "Invalid security configuration. Can't load private key or certificate.");
179 struct dirent* entry =
nullptr;
183 while((entry = readdir(dp))) {
184 if(!strcmp(entry->d_name,
".") || !strcmp(entry->d_name,
".."))
continue;
186 trustList = (UA_ByteString*)UA_realloc(trustList,
sizeof(UA_ByteString) * trustListSize);
188 sprintf(sbuf,
"%s/%s", this->serverConfig.
allowListFolder.c_str(), entry->d_name);
189 printf(
"Trust List entry: %s\n", entry->d_name);
190 trustList[trustListSize - 1] = loadFile(sbuf);
199 while((entry = readdir(dp))) {
200 if(!strcmp(entry->d_name,
".") || !strcmp(entry->d_name,
".."))
continue;
202 issuerList = (UA_ByteString*)UA_realloc(issuerList,
sizeof(UA_ByteString) * issuerListSize);
204 sprintf(sbuf,
"%s/%s", this->serverConfig.
issuerListFolder.c_str(), entry->d_name);
205 printf(
"Issuer List entry: %s\n", entry->d_name);
206 issuerList[issuerListSize - 1] = loadFile(sbuf);
215 while((entry = readdir(dp))) {
216 if(!strcmp(entry->d_name,
".") || !strcmp(entry->d_name,
".."))
continue;
218 blockList = (UA_ByteString*)UA_realloc(blockList,
sizeof(UA_ByteString) * blockListSize);
220 sprintf(sbuf,
"%s/%s", this->serverConfig.
blockListFolder.c_str(), entry->d_name);
221 printf(
"Block List entry: %s\n", entry->d_name);
222 blockList[blockListSize - 1] = loadFile(sbuf);
228 UA_StatusCode retval = UA_ServerConfig_setDefaultWithSecurityPolicies(config, this->serverConfig.
opcuaPort,
229 &certificate, &privateKey, trustList, trustListSize, issuerList, issuerListSize, blockList, blockListSize);
231 if(retval != UA_STATUSCODE_GOOD) {
232 throw std::runtime_error(
"Failed setting up server endpoints.");
236 for(
size_t i = 0; i < config->endpointsSize; i++) {
237 UA_EndpointDescription* ep = &config->endpoints[i];
238 if(ep->securityMode != UA_MESSAGESECURITYMODE_NONE)
continue;
240 UA_EndpointDescription_clear(ep);
242 if(i + 1 < config->endpointsSize) {
243 config->endpoints[i] = config->endpoints[config->endpointsSize - 1];
246 config->endpointsSize--;
249 if(config->endpointsSize == 0) {
250 UA_free(config->endpoints);
251 config->endpoints =
nullptr;
255 UA_ByteString_clear(&certificate);
256 UA_ByteString_clear(&privateKey);
257 for(
size_t i = 0; i < trustListSize; i++) UA_ByteString_clear(&trustList[i]);
260 for(
size_t i = 0; i < config->endpointsSize; ++i) {
261 UA_ApplicationDescription_clear(&config->endpoints[i].server);
262 UA_ApplicationDescription_copy(&config->applicationDescription, &config->endpoints[i].server);
266 this->server_config = UA_Server_getConfig(this->
mappedServer);
269 UA_DataType* types = (UA_DataType*)UA_malloc(1 *
sizeof(UA_DataType));
270 types[0] = LoggingLevelType;
271 this->customDataTypes.reset(
new UA_DataTypeArray{this->server_config->customDataTypes, 1, types, UA_FALSE});
272 this->server_config->customDataTypes = customDataTypes.get();
275 auto* usernamePasswordLogins =
new UA_UsernamePasswordLogin;
276 usernamePasswordLogins->password = UA_STRING_ALLOC(
const_cast<char*
>(this->serverConfig.
password.c_str()));
277 usernamePasswordLogins->username = UA_STRING_ALLOC(
const_cast<char*
>(this->serverConfig.
username.c_str()));
279 &this->server_config->securityPolicies[this->server_config->securityPoliciesSize - 1].policyUri, 1,
280 usernamePasswordLogins);
282 this->
baseNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
286 UA_String_clear(&usernamePasswordLogins->password);
287 UA_String_clear(&usernamePasswordLogins->username);
288 delete usernamePasswordLogins;
292 string xpath =
"//config";
293 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(xpath);
296 xmlNodeSetPtr nodeset = result->nodesetval;
297 if(nodeset->nodeNr > 1) {
298 throw std::runtime_error(
"To many <config>-Tags in config file");
315 vector<xmlNodePtr> mappingExceptionsVector =
317 if(mappingExceptionsVector.empty()) {
318 this->mappingExceptions = UA_FALSE;
322 transform(placeHolder.begin(), placeHolder.end(), placeHolder.begin(), ::toupper);
323 if(placeHolder ==
"TRUE") {
324 this->mappingExceptions = UA_TRUE;
327 this->mappingExceptions = UA_FALSE;
331 xmlXPathObjectPtr sub_result = this->fileHandler->getNodeSet(xpath +
"//server");
333 xmlNodeSetPtr nodeset = sub_result->nodesetval;
334 if(nodeset->nodeNr > 1) {
335 throw std::runtime_error(
"To many <server>-Tags in config file");
339 if(!placeHolder.empty()) {
340 transform(placeHolder.begin(), placeHolder.end(), placeHolder.begin(), ::toupper);
341 if(placeHolder.compare(
"TRACE") == 0) {
342 this->serverConfig.
logLevel = UA_LOGLEVEL_TRACE;
344 else if(placeHolder.compare(
"DEBUG") == 0) {
345 this->serverConfig.
logLevel = UA_LOGLEVEL_DEBUG;
347 else if(placeHolder.compare(
"INFO") == 0) {
348 this->serverConfig.
logLevel = UA_LOGLEVEL_INFO;
350 else if(placeHolder.compare(
"WARNING") == 0) {
351 this->serverConfig.
logLevel = UA_LOGLEVEL_WARNING;
353 else if(placeHolder.compare(
"ERROR") == 0) {
354 this->serverConfig.
logLevel = UA_LOGLEVEL_ERROR;
356 else if(placeHolder.compare(
"FATAL") == 0) {
357 this->serverConfig.
logLevel = UA_LOGLEVEL_FATAL;
363 UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
364 "Unknown logLevel (\"%s\") found in config file. INFO is used instead!", placeHolder.c_str());
370 UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
371 "No 'logLevel'-Attribute set in config file. Use default logging level: info");
376 if(!opcuaPort.empty()) {
377 this->serverConfig.
opcuaPort = std::stoi(opcuaPort);
381 &
logger, UA_LOGCATEGORY_USERLAND,
"No 'port'-Attribute in config file is set. Use default Port: 16664");
385 if(!placeHolder.empty()) {
392 string applicationName;
394 string applicationName = ApplicationBase::getInstance().getName();
397 catch(ChimeraTK::logic_error) {
399 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
400 "No 'applicationName'-Attribute is set in config file. Use default application-name.");
407 xmlXPathFreeObject(sub_result);
411 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
412 "No <server>-Tag in config file. Use default port 16664 and application name configuration.");
418 if(!historizing.empty()) {
419 xmlXPathObjectPtr sub_sub_result = this->fileHandler->getNodeSet(
"//historizing");
421 xmlNodeSetPtr node_set = sub_sub_result->nodesetval;
422 for(int32_t i = 0; i < node_set->nodeNr; i++) {
423 vector<xmlNodePtr> nodeVectorhistorizingSetUp =
426 for(
auto& nodeHistorizingPath : nodeVectorhistorizingSetUp) {
430 bool incomplete =
false;
431 if(!history_name.empty()) {
432 temp.
name = history_name;
435 raiseError(
"Missing history parameter 'name'.",
436 "History configuration is not added to the list of available history configurations",
437 nodeHistorizingPath->line);
440 string history_buffer_length =
442 if(!history_buffer_length.empty()) {
443 sscanf(history_buffer_length.c_str(),
"%zu", &temp.
buffer_length);
446 raiseError(
"Missing history parameter 'buffer_length'.",
447 "History configuration " + history_name +
448 " is not added to the list of available history configurations",
449 nodeHistorizingPath->line);
452 string history_entries_per_response =
454 if(!history_entries_per_response.empty()) {
458 raiseError(
"Missing history parameter 'entries_per_response'.",
459 "History configuration " + history_name +
460 " is not added to the list of available history configurations",
461 nodeHistorizingPath->line);
465 if(!history_interval.empty()) {
466 sscanf(history_interval.c_str(),
"%zu", &temp.
interval);
469 raiseError(
"Missing history parameter 'interval'.",
470 "History configuration " + history_name +
471 " is not added to the list of available history configurations",
472 nodeHistorizingPath->line);
477 bool existing =
false;
478 for(
size_t j = 0; j < this->serverConfig.
history.size(); j++) {
479 if(history_name == this->serverConfig.
history[j].name) {
485 this->serverConfig.
history.insert(this->serverConfig.
history.end(), temp);
489 raiseError(
"Redefinition of history configuration.",
490 "History configuration " + history_name +
491 " is not added to the list of available history configurations");
499 if(!placeHolder.empty()) {
504 if(!placeHolder.empty()) {
507 xmlXPathFreeObject(result);
512 result = this->fileHandler->getNodeSet(xpath +
"//login");
514 xmlNodeSetPtr nodeset = result->nodesetval;
515 if(nodeset->nodeNr > 1) {
516 throw std::runtime_error(
"To many <login>-Tags in config file");
520 if(!placeHolder.empty()) {
521 this->serverConfig.
password = placeHolder;
524 throw std::runtime_error(
"<login>-Tag requires username");
528 if(!placeHolder.empty()) {
529 this->serverConfig.
username = placeHolder;
532 throw std::runtime_error(
"<login>-Tag requires password");
535 xmlXPathFreeObject(result);
541 result = this->fileHandler->getNodeSet(xpath +
"//security");
543 xmlNodeSetPtr nodeset = result->nodesetval;
544 if(nodeset->nodeNr > 1) {
545 throw std::runtime_error(
"To many <security>-Tags in config file");
549 if(!unsecure.empty()) {
550 transform(unsecure.begin(), unsecure.end(), unsecure.begin(), ::toupper);
551 if(unsecure.compare(
"TRUE") == 0) {
555 this->serverConfig.
unsecure =
false;
559 this->serverConfig.
unsecure =
false;
560 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
561 "No 'unsecure'-Attribute in config file is set. Disable unsecure endpoints");
564 if(!certPath.empty()) {
565 this->serverConfig.
certPath = certPath;
568 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
569 "Invalid security configuration. No 'certificate'-Attribute in config file is set.");
572 if(!keyPath.empty()) {
573 this->serverConfig.
keyPath = keyPath;
576 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
577 "Invalid security configuration. No 'privatekey'-Attribute in config file is set.");
580 if(!allowListFolder.empty()) {
584 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
585 "Invalid security configuration. No 'trustlist'-Attribute in config file is set.");
588 if(!blockListFolder.empty()) {
592 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
"No 'blockListFolder'-Attribute in config file is set.");
595 if(!issuerListFolder.empty()) {
599 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
"No 'issuerListFolder'-Attribute in config file is set.");
601 xmlXPathFreeObject(result);
604 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
605 "No <security>-Tag in config file. Use default configuration with unsecure endpoint.");
608 result = this->fileHandler->getNodeSet(xpath +
"//process_variable_hierarchy");
610 xmlNodeSetPtr nodeset = result->nodesetval;
611 vector<xmlNodePtr> nodeVectorUnrollPathPV =
613 for(
auto nodeUnrollPath : nodeVectorUnrollPathPV) {
615 transform(unrollSepEnabled.begin(), unrollSepEnabled.end(), unrollSepEnabled.begin(), ::toupper);
616 if(unrollSepEnabled ==
"TRUE") {
620 xmlXPathFreeObject(result);
623 UA_LOG_WARNING(&
logger, UA_LOGCATEGORY_USERLAND,
624 "No <process_variable_hierarchy>-Tag in config file. Use default hierarchical mapping with '/'.");
625 this->pvSeperator =
"/";
628 xmlXPathObjectPtr result_exclude = this->fileHandler->getNodeSet(
"//exclude");
629 xmlNodeSetPtr nodeset;
631 nodeset = result_exclude->nodesetval;
632 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
634 if(!exclude_string.empty()) {
635 this->
exclude.insert(this->
exclude.begin(),
"/" + exclude_string);
639 xmlXPathFreeObject(result_exclude);
641 xmlXPathObjectPtr folder_with_his = this->fileHandler->getNodeSet(
"//folder");
642 xmlNodeSetPtr nodeset_folder_with_history;
643 if(folder_with_his) {
644 nodeset_folder_with_history = folder_with_his->nodesetval;
645 for(int32_t i = 0; i < nodeset_folder_with_history->nodeNr; i++) {
650 if(!folder.empty() && !history.empty()) {
655 xmlXPathFreeObject(folder_with_his);
659 return this->serverConfig;
673 UA_LOG_DEBUG(server_config->logging, UA_LOGCATEGORY_USERLAND,
"No server mapped");
681 vector<UA_NodeId> historizing_nodes;
682 vector<string> historizing_setup;
683 UA_HistoryDataGathering gathering =
685 this->serverConfig.
history, this->serverConfig.historyfolders, this->serverConfig.historyvariables);
686 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
"Starting the server worker thread");
694 this->serverConfig.
historyfolders, this->serverConfig.historyvariables, this->server_config);
697 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
"Stopped the server worker thread");
701 vector<string> varPathVector;
702 if(!seperator.empty()) {
704 varPathVector.insert(varPathVector.end(), newPathVector.begin(), newPathVector.end());
706 if(!varPathVector.empty()) {
707 varPathVector.pop_back();
710 return UA_NODEID_NULL;
714 const std::string& varName,
const boost::shared_ptr<ControlSystemPVManager>&
csManager) {
717 if(!UA_NodeId_isNull(&folderPathNodeId)) {
724 varName.substr(1, varName.size() - 1),
csManager, server_config->logging);
726 this->variables.push_back(processvariable);
728 UA_Server_writeDisplayName(this->
mappedServer, tmpNodeId,
729 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
731 UA_NodeId_clear(&tmpNodeId);
735 const boost::shared_ptr<ControlSystemPVManager>&
csManager, UA_NodeId layer, UA_NodeId target) {
737 UA_BrowseDescription bd;
738 bd.includeSubtypes =
false;
740 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
741 bd.resultMask = UA_BROWSERESULTMASK_NONE;
742 bd.nodeClassMask = UA_NODECLASS_VARIABLE;
743 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
744 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, UA_UINT32_MAX, &bd);
745 for(
size_t j = 0; j < br.referencesSize; ++j) {
746 UA_ReferenceDescription rd = br.references[j];
748 UA_LocalizedText foundPVName;
749 string foundPVNameCPP;
750 UA_Server_readDisplayName(this->
mappedServer, rd.nodeId.nodeId, &foundPVName);
753 string pvSourceNameid;
754 UA_String foundPVSourceName;
755 string foundPVSourceNameCPP;
759 this->
mappedServer, UA_NODEID_STRING(1,
const_cast<char*
>((pvSourceNameid +
"/Name").c_str())), &value);
760 foundPVSourceName = *((UA_String*)value.data);
764 this->
mappedServer, target, foundPVSourceNameCPP,
csManager, server_config->logging, foundPVNameCPP);
765 this->variables.push_back(processvariable);
766 UA_Variant_clear(&value);
767 UA_LocalizedText_clear(&foundPVName);
769 UA_BrowseResult_clear(&br);
771 bd.includeSubtypes =
false;
773 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
774 bd.resultMask = UA_BROWSERESULTMASK_NONE;
775 bd.nodeClassMask = UA_NODECLASS_OBJECT;
776 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
777 br = UA_Server_browse(this->
mappedServer, UA_UINT32_MAX, &bd);
778 for(
size_t j = 0; j < br.referencesSize; ++j) {
779 UA_ReferenceDescription rd = br.references[j];
780 UA_String folderName, description;
781 UA_LocalizedText foundFolderName, foundFolderDescription;
782 UA_LocalizedText_init(&foundFolderDescription);
783 string foundFolderNameCPP;
784 UA_Server_readDescription(this->
mappedServer, rd.nodeId.nodeId, &foundFolderDescription);
785 UA_StatusCode result = UA_Server_readDisplayName(this->
mappedServer, rd.nodeId.nodeId, &foundFolderName);
787 if(result != UA_STATUSCODE_GOOD) {
791 UA_NodeId newRootFolder =
createFolder(target, foundFolderNameCPP);
792 if(foundFolderDescription.text.length > 0)
793 UA_Server_writeDescription(this->
mappedServer, newRootFolder, foundFolderDescription);
795 UA_LocalizedText_clear(&foundFolderName);
796 UA_LocalizedText_clear(&foundFolderDescription);
798 UA_BrowseResult_clear(&br);
802 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//folder");
803 xmlNodeSetPtr nodeset;
805 nodeset = result->nodesetval;
807 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
808 UA_NodeId folderPathNodeId;
809 string destination, description, folder;
810 vector<xmlNodePtr> nodeFolderPath =
813 vector<xmlNodePtr> nodeDescription =
817 transform(copy.begin(), copy.end(), copy.begin(), ::toupper);
818 if(!nodeFolderPath.empty()) {
821 if(!nodeDescription.empty()) {
824 if(!nodeFolder.empty()) {
828 if(!history.empty()) {
831 if(sourceName.empty()) {
832 raiseError(
"Can not add history if no sourceName attribute is set.",
"", nodeset->nodeTab[i]->line);
834 if(!nodeFolder.empty()) {
836 if(!nodeFolderPath.empty()) {
837 if(strlen(destination.c_str()) == 0) {
838 folderNodeId = this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
841 folderNodeId = this->serverConfig.
rootFolder +
"/" + destination +
"/" + folder +
"Dir";
845 folderNodeId = this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
850 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(folderNodeId.c_str()));
853 UA_String out = UA_STRING_NULL;
854 UA_print(&temp.
folder_id, &UA_TYPES[UA_TYPES_NODEID], &out);
855 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
856 "Adding history for folder from destination and name %.*s ", (
int)out.length, out.data);
857 UA_String_clear(&out);
859 else if(copy.empty() || copy ==
"FALSE") {
862 string folderNodeId = this->serverConfig.
rootFolder +
"/" + sourceName +
"Dir";
863 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(folderNodeId.c_str()));
866 UA_String out = UA_STRING_NULL;
867 UA_print(&temp.
folder_id, &UA_TYPES[UA_TYPES_NODEID], &out);
868 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
869 "Adding history for folder from source name %.*s ", (
int)out.length, out.data);
870 UA_String_clear(&out);
874 "Can not add history if copy is true and no source name is given.",
"", nodeset->nodeTab[i]->line);
880 if(history.empty()) {
881 raiseError(
"Folder creation failed. Name is missing.",
"Skipping Folder.", nodeset->nodeTab[i]->line);
886 UA_NodeId tmpOutput = UA_NODEID_NULL;
888 if(destination.empty()) {
889 pvNodeString += this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
892 pvNodeString += this->serverConfig.
rootFolder +
"/" + destination +
"/" + folder +
"Dir";
894 UA_NodeId pvNode = UA_NODEID_STRING(1,
const_cast<char*
>(pvNodeString.c_str()));
895 UA_Server_readNodeId(this->
mappedServer, pvNode, &tmpOutput);
896 if(!UA_NodeId_isNull(&tmpOutput)) {
897 UA_NodeId_clear(&tmpOutput);
899 if(sourceName.empty() && !description.empty()) {
901 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
904 raiseError(
"Folder with source mapping failed. Target folder node id exists.",
905 std::string(
"Skipping folder: ") + sourceName, nodeset->nodeTab[i]->line);
908 if(!copy.empty() && sourceName.empty()) {
909 raiseError(std::string(
"Source 'name: ") + folder +
"' folder missing.",
"Skipping Folder.",
910 nodeset->nodeTab[i]->line);
914 if(UA_NodeId_isNull(&folderPathNodeId)) {
915 folderPathNodeId = UA_NODEID_STRING(1,
const_cast<char*
>((this->serverConfig.
rootFolder +
"Dir").c_str()));
918 if(!sourceName.empty()) {
919 if((destination.empty() && sourceName == folder) ||
920 (!destination.empty() && sourceName == destination +
"/" + folder)) {
922 "Folder creation failed. Source and Destination equal.",
"Skipping Folder.", nodeset->nodeTab[i]->line);
926 string sourceFolder = this->serverConfig.
rootFolder +
"/" + sourceName +
"Dir";
927 bool isFolderType =
false;
928 UA_NodeId sourceFolderId = UA_NODEID_STRING(1,
const_cast<char*
>(sourceFolder.c_str()));
930 UA_BrowseDescription bd;
931 bd.includeSubtypes =
false;
932 bd.nodeId = sourceFolderId;
933 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
934 bd.resultMask = UA_BROWSERESULTMASK_ALL;
935 bd.nodeClassMask = UA_NODECLASS_OBJECTTYPE;
936 bd.browseDirection = UA_BROWSEDIRECTION_BOTH;
937 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, 1000, &bd);
938 for(
size_t j = 0; j < br.referencesSize; ++j) {
939 UA_ReferenceDescription rd = br.references[j];
940 UA_NodeId folderType = UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE);
941 if(UA_NodeId_equal(&rd.nodeId.nodeId, &folderType)) {
945 UA_BrowseResult_clear(&br);
947 raiseError(std::string(
"Folder creation failed. No corresponding source folder: ") + sourceName,
948 "Skipping Folder.", nodeset->nodeTab[i]->line);
953 bool sourceAndDestinationEqual =
false;
954 if(!destination.empty()) {
955 if(sourceName == destination +
"/" + folder) sourceAndDestinationEqual =
true;
958 if(sourceName == folder) sourceAndDestinationEqual =
true;
960 if(sourceAndDestinationEqual) {
961 raiseError(std::string(
"Folder creation failed. Source and destination must be different for '") +
962 sourceName +
"' folder.",
963 "Skipping Folder.", nodeset->nodeTab[i]->line);
967 UA_LocalizedText foundFolderName;
968 UA_NodeId copyRoot =
createFolder(folderPathNodeId, folder);
969 if(UA_NodeId_isNull(©Root)) {
970 string existingDestinationFolderString;
971 if(destination.empty()) {
972 existingDestinationFolderString += this->serverConfig.
rootFolder +
"/" + folder +
"Dir";
975 existingDestinationFolderString = this->serverConfig.
rootFolder +=
976 "/" + destination +
"/" + folder +
"Dir";
978 copyRoot = UA_NODEID_STRING(1,
const_cast<char*
>(existingDestinationFolderString.c_str()));
981 if(!description.empty()) {
982 UA_Server_writeDescription(this->
mappedServer, copyRoot,
983 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
987 string existingDestinationFolderString;
988 UA_NodeId copyRoot =
createFolder(folderPathNodeId, folder);
989 if(UA_NodeId_isNull(©Root)) {
990 if(destination.empty()) {
991 existingDestinationFolderString = this->serverConfig.
rootFolder +
"/" + folder;
994 existingDestinationFolderString = this->serverConfig.
rootFolder +
"/" + destination +
"/" + folder;
996 copyRoot = UA_NODEID_STRING(1,
const_cast<char*
>(existingDestinationFolderString.c_str()));
999 if(!description.empty()) {
1000 UA_Server_writeDescription(this->
mappedServer, copyRoot,
1001 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
1004 UA_BrowseDescription bd;
1005 bd.includeSubtypes =
false;
1006 bd.nodeId = sourceFolderId;
1007 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
1008 bd.resultMask = UA_BROWSERESULTMASK_ALL;
1009 bd.nodeClassMask = UA_NODECLASS_OBJECT;
1010 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
1011 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, 1000, &bd);
1012 for(
size_t j = 0; j < br.referencesSize; ++j) {
1013 UA_ExpandedNodeId enid;
1014 enid.serverIndex = 0;
1015 enid.namespaceUri = UA_STRING_NULL;
1016 enid.nodeId = br.references[j].nodeId.nodeId;
1017 UA_Server_addReference(
1018 this->
mappedServer, copyRoot, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), enid, UA_TRUE);
1020 UA_BrowseResult_clear(&br);
1021 bd.nodeId = sourceFolderId;
1022 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
1023 bd.resultMask = UA_BROWSERESULTMASK_ALL;
1024 bd.nodeClassMask = UA_NODECLASS_VARIABLE;
1025 bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
1027 for(
size_t j = 0; j < br.referencesSize; ++j) {
1028 UA_ExpandedNodeId enid;
1029 enid.serverIndex = 0;
1030 enid.namespaceUri = UA_STRING_NULL;
1031 enid.nodeId = br.references[j].nodeId.nodeId;
1032 UA_Server_addReference(
1033 this->
mappedServer, copyRoot, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), enid, UA_TRUE);
1035 UA_BrowseResult_clear(&br);
1040 UA_NodeId retnode =
createFolder(folderPathNodeId, folder);
1042 if(copy.empty() && sourceName.empty() && !description.empty()) {
1043 UA_Server_writeDescription(this->
mappedServer, retnode,
1044 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str())));
1048 xmlXPathFreeObject(result);
1053 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//process_variable");
1054 xmlNodeSetPtr nodeset;
1056 nodeset = result->nodesetval;
1057 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1058 string sourceName, copy, destination, name, unit, description, unrollPath, history;
1059 vector<xmlNodePtr> nodeDestination =
1064 vector<xmlNodePtr> nodeDescription =
1068 transform(copy.begin(), copy.end(), copy.begin(), ::toupper);
1072 if(!history.empty()) {
1075 string targetNodeId;
1077 if(!nodeName.empty()) {
1078 if(nodeDestination.empty()) {
1080 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(targetNodeId.c_str()));
1085 targetNodeId = this->serverConfig.
rootFolder +
"/" +
1088 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(targetNodeId.c_str()));
1093 if(!sourceName.empty() && (copy.empty() || copy ==
"FALSE")) {
1095 targetNodeId = this->serverConfig.
rootFolder +
"/" + sourceName;
1096 UA_NodeId
id = UA_NODEID_STRING(1,
const_cast<char*
>(targetNodeId.c_str()));
1102 if(!nodeDestination.empty()) {
1106 if(!nodeName.empty()) {
1109 if(!nodeUnit.empty()) {
1112 if(!nodeDescription.empty()) {
1116 if(sourceName.empty()) {
1118 raiseError(std::string(
"PV mapping failed. SourceName missing for 'name' : '") + name,
1119 "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1122 raiseError(
"PV mapping failed. SourceName is missing.",
"Skipping PV mapping.", nodeset->nodeTab[i]->line);
1127 if(((destination +
"/" + name) == sourceName) && copy ==
"FALSE") {
1129 string parentSourceFolder = this->serverConfig.
rootFolder +
"/" +
1132 UA_NodeId parentSourceFolderId = UA_NODEID_STRING(1,
const_cast<char*
>(parentSourceFolder.c_str()));
1133 UA_NodeId pvNodeId = UA_NODEID_NULL;
1134 UA_BrowseDescription bd;
1135 bd.includeSubtypes =
false;
1136 bd.nodeId = parentSourceFolderId;
1137 bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
1138 bd.resultMask = UA_BROWSERESULTMASK_ALL;
1139 bd.nodeClassMask = UA_NODECLASS_VARIABLE;
1140 bd.browseDirection = UA_BROWSEDIRECTION_BOTH;
1141 UA_BrowseResult br = UA_Server_browse(this->
mappedServer, 20, &bd);
1142 for(
size_t j = 0; j < br.referencesSize; ++j) {
1143 UA_ReferenceDescription rd = br.references[j];
1147 UA_NodeId_copy(&br.references[j].nodeId.nodeId, &pvNodeId);
1150 UA_BrowseResult_clear(&br);
1151 if(UA_NodeId_isNull(&pvNodeId)) {
1153 raiseError(std::string(
"PV mapping failed. No corresponding source pv for 'name' : '") + name,
1154 "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1158 "PV mapping failed. No corresponding source pv.",
"Skipping PV mapping.", nodeset->nodeTab[i]->line);
1164 for(
auto& variable : this->variables) {
1165 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1166 if(UA_NodeId_equal(&tmpNodeId, &pvNodeId)) {
1167 variable->setEngineeringUnit(unit);
1169 UA_NodeId_clear(&tmpNodeId);
1172 if(!description.empty()) {
1174 for(
auto& variable : this->variables) {
1175 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1176 if(UA_NodeId_equal(&tmpNodeId, &pvNodeId)) {
1177 variable->setDescription(description);
1179 UA_NodeId_clear(&tmpNodeId);
1183 if(!UA_NodeId_isNull(&pvNodeId)) {
1184 UA_NodeId_clear(&pvNodeId);
1189 string parentSourceString = this->serverConfig.
rootFolder +
"/" + sourceName;
1190 UA_NodeId parentSourceId = UA_NODEID_STRING(1,
const_cast<char*
>(parentSourceString.c_str()));
1191 UA_NodeId tmpOutput = UA_NODEID_NULL;
1192 UA_Server_readNodeId(this->
mappedServer, parentSourceId, &tmpOutput);
1193 if(UA_NodeId_isNull(&tmpOutput)) {
1195 raiseError(std::string(
"PV mapping failed. Source PV not found 'name' : '") + name,
"Skipping PV mapping.",
1196 nodeset->nodeTab[i]->line);
1199 raiseError(
"PV mapping failed. Source PV not found",
"Skipping PV mapping.", nodeset->nodeTab[i]->line);
1204 UA_NodeId_clear(&tmpOutput);
1205 UA_NodeId_init(&tmpOutput);
1208 UA_NodeId createdNodeId = UA_NODEID_NULL;
1209 if(copy ==
"TRUE") {
1210 if(sourceName == (destination +
"/" + name)) {
1211 raiseError(
"PV mapping failed. Source and destination must be different if copy='true'.",
1212 std::string(
"Skipping PV ") + sourceName, nodeset->nodeTab[i]->line);
1215 UA_NodeId destinationFolderNodeId = UA_NODEID_NULL;
1216 if(destination.empty()) {
1217 destinationFolderNodeId = this->ownNodeId;
1220 if(unrollPath.empty()) {
1225 if(destination.find(
"Variables" + unrollPath) == 0) {
1226 string requestedBrowseName = destination;
1227 requestedBrowseName.erase(0, (
"Variables" + unrollPath).length());
1229 string requestedPVBrowseName = this->serverConfig.
rootFolder +
"/Variables/" + requestedBrowseName;
1230 UA_NodeId requestedPVBrowseId = UA_NODEID_STRING(1,
const_cast<char*
>(requestedPVBrowseName.c_str()));
1231 UA_NodeId tmpOutput = UA_NODEID_NULL;
1232 UA_Server_readNodeId(this->
mappedServer, requestedPVBrowseId, &tmpOutput);
1233 if(!UA_NodeId_isNull(&tmpOutput)) {
1234 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
1235 "Using legacy code. Mapped PV is used as PV mapping target");
1236 UA_NodeId_copy(&requestedPVBrowseId, &destinationFolderNodeId);
1239 destinationFolderNodeId =
1244 destinationFolderNodeId =
1253 if(!UA_NodeId_isNull(&destinationFolderNodeId)) {
1256 UA_NodeId tmpProcessVariableNodeId = processvariable->
getOwnNodeId();
1257 if(UA_NodeId_isNull(&tmpProcessVariableNodeId)) {
1258 raiseError(
"PV creation failed. PV with same name mapped.", std::string(
"Skipping PV ") + sourceName,
1259 nodeset->nodeTab[i]->line);
1263 UA_NodeId_clear(&tmpProcessVariableNodeId);
1265 this->variables.push_back(processvariable);
1268 raiseError(
"Folder creation failed.", std::string(
"Skipping PV ") + sourceName, nodeset->nodeTab[i]->line);
1271 UA_NodeId tmpPVNodeId = processvariable->
getOwnNodeId();
1272 UA_NodeId_copy(&tmpPVNodeId, &createdNodeId);
1273 UA_NodeId_clear(&tmpPVNodeId);
1279 name = sourceVarName;
1281 if(sourceVarName != name) {
1282 if(history.empty()) {
1283 raiseError(
"PV mapping failed. The pv name can't changed if copy is false.",
1284 std::string(
"Skipping PV mapping of pv ") + name);
1290 UA_ExpandedNodeId enid;
1291 enid.serverIndex = 0;
1292 enid.namespaceUri = UA_STRING_NULL;
1293 enid.nodeId = parentSourceId;
1295 UA_StatusCode addRef = UA_Server_addReference(
1296 this->
mappedServer, destinationFolder, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), enid,
true);
1297 if(sourceVarName != name) {
1298 raiseError(
"PV mapping failed. Can't create reference to original pv.",
1299 std::string(
"Skipping PV mapping of pv ") + name);
1302 UA_NodeId_copy(&parentSourceId, &createdNodeId);
1305 for(
auto& variable : this->variables) {
1306 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1307 if(UA_NodeId_equal(&tmpNodeId, &createdNodeId)) {
1308 variable->setEngineeringUnit(unit);
1310 UA_NodeId_clear(&tmpNodeId);
1313 if(!description.empty()) {
1314 for(
auto& variable : this->variables) {
1315 UA_NodeId tmpNodeId = variable->getOwnNodeId();
1316 if(UA_NodeId_equal(&tmpNodeId, &createdNodeId)) {
1317 variable->setDescription(description);
1319 UA_NodeId_clear(&tmpNodeId);
1322 UA_Server_writeDisplayName(this->
mappedServer, createdNodeId,
1323 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(name.c_str())));
1324 UA_NodeId_clear(&createdNodeId);
1327 xmlXPathFreeObject(result);
1332 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//additional_variable");
1333 xmlNodeSetPtr nodeset;
1335 nodeset = result->nodesetval;
1336 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1337 string destination, name, description, value;
1338 vector<xmlNodePtr> nodeDestination =
1341 vector<xmlNodePtr> nodeDescription =
1345 if(!nodeDestination.empty()) {
1348 if(!nodeName.empty()) {
1351 if(!nodeDescription.empty()) {
1354 if(!nodeValue.empty()) {
1359 raiseError(
"Additional variable node creation failed. Additional variable name is mandatory.",
1360 "Skipping additional variable.", nodeset->nodeTab[i]->line);
1364 UA_NodeId tmpOutput = UA_NODEID_NULL;
1365 string avNodeString;
1366 if(destination.empty()) {
1367 avNodeString = this->serverConfig.
rootFolder +
"/" + name +
"AdditionalVariable";
1370 avNodeString = this->serverConfig.
rootFolder +
"/" + destination +
"/" + name +
"AdditionalVariable";
1372 UA_NodeId avNode = UA_NODEID_STRING(1,
const_cast<char*
>(avNodeString.c_str()));
1373 UA_Server_readNodeId(this->
mappedServer, avNode, &tmpOutput);
1374 if(!UA_NodeId_isNull(&tmpOutput)) {
1375 UA_NodeId_clear(&tmpOutput);
1376 UA_NodeId_init(&tmpOutput);
1377 raiseError(
"Additional variable node creation failed. Additional variable already exists.",
1378 std::string(
"Skipping additional variable ") + name, nodeset->nodeTab[i]->line);
1382 UA_NodeId_clear(&tmpOutput);
1383 string pvNodeString;
1384 if(destination.empty()) {
1385 pvNodeString = this->serverConfig.
rootFolder +
"/" + name +
"Value";
1388 pvNodeString = this->serverConfig.
rootFolder +
"/" + destination +
"/" + name +
"Value";
1390 UA_NodeId pvNode = UA_NODEID_STRING(1,
const_cast<char*
>(pvNodeString.c_str()));
1391 UA_Server_readNodeId(this->
mappedServer, pvNode, &tmpOutput);
1392 if(!UA_NodeId_isNull(&tmpOutput)) {
1393 UA_NodeId_clear(&tmpOutput);
1394 UA_NodeId_init(&tmpOutput);
1395 raiseError(
"Additional variable node creation failed. PV with same name already exists.",
1396 std::string(
"Skipping additional variable ") + name, nodeset->nodeTab[i]->line);
1400 UA_NodeId additionalVarFolderPath = UA_NODEID_NULL;
1401 string additionalVarFolderPathNodeId;
1402 if(!destination.empty()) {
1406 additionalVarFolderPathNodeId += this->serverConfig.
rootFolder +
"Dir";
1407 additionalVarFolderPath = UA_NODEID_STRING_ALLOC(1,
const_cast<char*
>(additionalVarFolderPathNodeId.c_str()));
1409 if(UA_NodeId_isNull(&additionalVarFolderPath)) {
1410 raiseError(
"Creation of additional variable folder failed.",
"Skipping additional variable.",
1411 nodeset->nodeTab[i]->line);
1414 auto* additionalvariable =
1416 this->additionalVariables.push_back(additionalvariable);
1417 if(destination.empty()) {
1418 UA_NodeId_clear(&additionalVarFolderPath);
1422 xmlXPathFreeObject(result);
1427 return this->variables;
1430 UA_NodeId ua_uaadapter::createUAFolder(
1431 UA_NodeId basenodeid,
const std::string& folderName,
const std::string& description) {
1433 UA_StatusCode retval = UA_STATUSCODE_GOOD;
1434 UA_NodeId createdNodeId = UA_NODEID_NULL;
1436 if(UA_NodeId_equal(&
baseNodeId, &createdNodeId) == UA_TRUE) {
1437 return createdNodeId;
1441 UA_ObjectAttributes oAttr;
1442 UA_ObjectAttributes_init(&oAttr);
1444 oAttr.displayName = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(folderName.c_str()));
1445 oAttr.description = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(description.c_str()));
1447 string parentNodeIdString;
1448 if(basenodeid.identifierType == UA_NODEIDTYPE_STRING) {
1450 if(!parentNodeIdString.empty()) {
1451 parentNodeIdString.resize(parentNodeIdString.size() - 3);
1453 parentNodeIdString +=
'/' + folderName +
"Dir";
1455 else if(basenodeid.identifierType == UA_NODEIDTYPE_NUMERIC) {
1456 parentNodeIdString +=
'/' + std::to_string(basenodeid.identifier.numeric) +
"Dir";
1460 UA_NODEID_STRING(1,
const_cast<char*
>(parentNodeIdString.c_str())),
1461 basenodeid, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
1462 UA_QUALIFIEDNAME(1,
const_cast<char*
>(folderName.c_str())), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), oAttr,
1466 return createdNodeId;
1469 void ua_uaadapter::raiseError(std::string errorMesssage, std::string consequenceMessage,
const int&
line) {
1470 std::string lineMessage(
"");
1472 lineMessage = std::string(
" Mapping line number: ") + std::to_string(
line) +
".";
1474 std::string tmp[2] = {errorMesssage, consequenceMessage};
1475 if(errorMesssage.back() !=
'.') {
1476 errorMesssage +=
".";
1478 if(consequenceMessage.back() !=
'.') {
1479 consequenceMessage +=
".";
1481 if(this->mappingExceptions) {
1482 throw std::runtime_error(std::string(
"Error! ") + errorMesssage + lineMessage);
1485 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
"%s %s", errorMesssage.c_str(),
1486 (consequenceMessage + lineMessage).c_str());
1490 &
logger, UA_LOGCATEGORY_USERLAND,
"%s %s", errorMesssage.c_str(), (consequenceMessage + lineMessage).c_str());
1494 UA_StatusCode ua_uaadapter::mapSelfToNamespace() {
1495 UA_StatusCode retval = UA_STATUSCODE_GOOD;
1496 UA_NodeId createdNodeId = UA_NODEID_NULL;
1498 if(UA_NodeId_equal(&this->
baseNodeId, &createdNodeId) == UA_TRUE) {
1504 UA_ObjectAttributes oAttr;
1505 UA_ObjectAttributes_init(&oAttr);
1508 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(this->serverConfig.
rootFolder.c_str()));
1510 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(this->serverConfig.
descriptionFolder.c_str()));
1513 UA_NODEID_STRING(1, (
const_cast<char*
>((this->serverConfig.
rootFolder +
"Dir").c_str()))),
1514 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
1515 UA_QUALIFIEDNAME(1,
const_cast<char*
>(this->serverConfig.
rootFolder.c_str())),
1516 UA_NODEID_NUMERIC(CSA_NSID, UA_NS2ID_CTKMODULE), oAttr, &
ownedNodes, &createdNodeId);
1518 this->ownNodeId = createdNodeId;
1523 UA_ObjectAttributes oAttr;
1524 UA_ObjectAttributes_init(&oAttr);
1526 oAttr.displayName = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(
"ServerConfiguration"));
1528 UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en_US"),
const_cast<char*
>(
"Here adapter configurations are placed."));
1529 UA_Server_addObjectNode(this->
mappedServer, UA_NODEID_STRING(1,
const_cast<char*
>(
"ServerConfigurationDir")),
1530 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
1531 UA_QUALIFIEDNAME(1,
const_cast<char*
>(
"ServerConfiguration")),
1532 UA_NODEID_NUMERIC(CSA_NSID, UA_NS2ID_CTKMODULE), oAttr, &
ownedNodes, &createdNodeId);
1533 configNodeId = createdNodeId;
1537 UA_VariableAttributes attr = UA_VariableAttributes_default;
1538 attr.displayName = UA_LOCALIZEDTEXT(
const_cast<char*
>(
"en-US"),
const_cast<char*
>(
"logLevel"));
1539 attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
1540 attr.valueRank = -1;
1541 attr.dataType = LoggingLevelType.typeId;
1543 auto status = UA_Variant_setScalarCopy(&attr.value, &l, &LoggingLevelType);
1545 UA_NodeId currentNodeId = UA_NODEID_STRING(1,
const_cast<char*
>(
"logLevel"));
1546 UA_QualifiedName currentName = UA_QUALIFIEDNAME(1,
const_cast<char*
>(
"logLevel"));
1547 UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
1548 UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
1550 UA_DataSource logLevelDataSource;
1553 UA_Server_addDataSourceVariableNode(this->
mappedServer, currentNodeId, createdNodeId, parentReferenceNodeId,
1554 currentName, variableTypeNodeId, attr, logLevelDataSource,
this, NULL);
1555 UA_Variant_clear(&attr.value);
1558 return UA_STATUSCODE_GOOD;
1562 return this->ownNodeId;
1566 UA_NodeId lastNodeId = basenodeid;
1567 for(
const std::string& t : folderPath) {
1569 if(UA_NodeId_isNull(&lastNodeId)) {
1570 return UA_NODEID_NULL;
1577 UA_NodeId lastNodeId = UA_NODEID_NULL;
1578 for(
auto& i : this->folderVector) {
1579 if((i.folderName == folder) && (UA_NodeId_equal(&i.prevFolderNodeId, &basenodeid))) {
1580 return i.folderNodeId;
1583 return UA_NODEID_NULL;
1587 if(UA_NodeId_isNull(&basenodeid)) {
1588 return UA_NODEID_NULL;
1592 int32_t starter4Folder = 0;
1593 UA_NodeId nextNodeId = basenodeid;
1594 UA_NodeId startNodeId = basenodeid;
1595 if(UA_NodeId_isNull(&toCheckNodeId)) {
1596 bool setted =
false;
1598 for(uint32_t m = 0; m < folderPath.size(); m++) {
1599 for(
auto& i : this->folderVector) {
1601 if(!setted && (folderPath.at(m) == i.folderName) && (UA_NodeId_equal(&i.prevFolderNodeId, &nextNodeId)) &&
1602 ((m + 1) < folderPath.size())) {
1605 starter4Folder = m + 1;
1606 nextNodeId = i.folderNodeId;
1618 return toCheckNodeId;
1621 UA_NodeId prevNodeId = nextNodeId;
1623 for(uint32_t m = starter4Folder; m < folderPath.size(); m++) {
1624 prevNodeId = this->
createFolder(prevNodeId, folderPath.at(m));
1631 if(UA_NodeId_isNull(&basenodeid)) {
1632 return UA_NODEID_NULL;
1635 UA_NodeId toCheckNodeId =
existFolder(basenodeid, folderName);
1637 if(UA_NodeId_isNull(&toCheckNodeId)) {
1639 newFolder.
folderNodeId = this->createUAFolder(basenodeid, folderName, description);
1641 this->folderVector.push_back(newFolder);
1647 vector<string> mappedPvSources;
1648 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//process_variable");
1650 xmlNodeSetPtr nodeset = result->nodesetval;
1651 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1652 mappedPvSources.insert(
1656 xmlXPathFreeObject(result);
1657 return mappedPvSources;
1661 vector<string> notMappableVariablesNames;
1662 xmlXPathObjectPtr result = this->fileHandler->getNodeSet(
"//process_variable");
1665 xmlNodeSetPtr nodeset = result->nodesetval;
1666 for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1668 bool mapped =
false;
1671 if(var->getName() == mappedVar) {
1676 notMappableVariablesNames.push_back(mappedVar);
1680 xmlXPathFreeObject(result);
1683 return notMappableVariablesNames;
1687 return UA_DateTime_now();
1695 void* ,
const UA_NodeId* ,
void* nodeContext, UA_Boolean ,
1696 const UA_NumericRange* , UA_DataValue* dataValue) {
1699 UA_Variant_setScalarCopy(&dataValue->value, &l, &LoggingLevelType);
1700 dataValue->hasValue =
true;
1701 return UA_STATUSCODE_GOOD;
1705 void* ,
const UA_NodeId* ,
void* nodeContext,
const UA_NumericRange* ,
1706 const UA_DataValue* data) {
1708 const UA_Variant* var = &data->value;
1709 adapter->serverConfig.logLevel = toLogLevel(*((
UA_LoggingLevel*)var->data));
1710 adapter->logger = UA_Log_Stdout_withLevel(adapter->serverConfig.logLevel);
1711 adapter->server_config->logging = &adapter->logger;
1712 UA_LOG_INFO(adapter->server_config->logging, UA_LOGCATEGORY_USERLAND,
"Set log level to %d",
1713 adapter->serverConfig.logLevel);
1714 return UA_STATUSCODE_GOOD;