ChimeraTK-ControlSystemAdapter-OPCUAAdapter  04.00.01
ua_adapter.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ChimeraTKs ControlSystem-OPC-UA-Adapter.
3  *
4  * ChimeraTKs ControlSystem-OPC-UA-Adapter is free software: you can
5  * redistribute it and/or modify it under the terms of the Lesser GNU
6  * General Public License as published by the Free Software Foundation,
7  * either version 3 of the License, or (at your option) any later version.
8  *
9  * ChimeraTKs ControlSystem-OPC-UA-Adapter is distributed in the hope
10  * that it will be useful, but WITHOUT ANY WARRANTY; without even the
11  * implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  * See the Lesser GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Foobar. If not, see https://www.gnu.org/licenses/lgpl.html
16  *
17  * Copyright (c) 2016 Chris Iatrou <Chris_Paul.Iatrou@tu-dresden.de>
18  * Copyright (c) 2016 Julian Rahm <Julian.Rahm@tu-dresden.de>
19  * Copyright (c) 2018-2023 Andreas Ebner <Andreas.Ebner@iosb.fraunhofer.de>
20  */
21 #include "void_type.h"
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>
28 
29 extern "C" {
30 #include "csa_namespace.h"
31 #include "loggingLevel_type.h"
32 #include "unistd.h"
33 
34 #include <dirent.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 }
38 
39 #include "csa_additionalvariable.h"
40 #include "csa_config.h"
41 #include "csa_processvariable.h"
42 #include "ua_adapter.h"
43 #include "ua_map_types.h"
44 #include "xml_file_handler.h"
45 
46 #include <functional>
47 #include <regex>
48 #include <string>
49 
50 // These defines allow the use of an define as a string
51 #define xstr(a) str(a)
52 #define str(a) #a
53 
54 using namespace std;
55 
56 namespace ChimeraTK {
57  ua_uaadapter::ua_uaadapter(const string& configFile) : ua_mapped_class() {
58  this->mappingExceptions = UA_FALSE;
59  this->fileHandler = std::make_shared<xml_file_handler>(configFile);
60  this->readConfig();
61  this->constructServer();
62  this->mapSelfToNamespace();
63  }
64 
66  UA_Server_delete(this->mappedServer);
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);
72  }
73  UA_NodeId_clear(&this->ownNodeId);
74  UA_NodeId_clear(&this->configNodeId);
75  }
76 
77  static string cleanUri(string s) {
78  // replace multiple with a single space
79  std::regex r1("\\s+");
80  s = std::regex_replace(s, r1, " ");
81  s = std::regex_replace(s, r1, "_");
82  // remove all not printable basic ASCII character
83  std::regex r2("[^ -~]+");
84  s = std::regex_replace(s, r2, "");
85  return s;
86  }
87 
88  static UA_ByteString loadFile(const char* const path) {
89  UA_ByteString fileContents = UA_STRING_NULL;
90 
91  /* Open the file */
92  FILE* fp = fopen(path, "rb");
93  if(!fp) {
94  errno = 0; /* We read errno also from the tcp layer... */
95  return fileContents;
96  }
97 
98  /* Get the file length, allocate the data and read */
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);
106  }
107  else {
108  fileContents.length = 0;
109  }
110  fclose(fp);
111 
112  return fileContents;
113  }
114 
115  void ua_uaadapter::fillBuildInfo(UA_ServerConfig* config) const {
116  /*get hostname */
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);
122 
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"));
132  std::string
133  versionString =
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*>(""));
141 
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()));
150  }
151 
152  void ua_uaadapter::constructServer() {
153  auto config = (UA_ServerConfig*)UA_calloc(1, sizeof(UA_ServerConfig));
154  config->logging = &logger;
155  if(!this->serverConfig.enableSecurity) {
156  UA_ServerConfig_setMinimal(config, this->serverConfig.opcuaPort, nullptr);
157  }
158  config->eventLoop->logger = &logger;
159  if(this->serverConfig.enableSecurity) {
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;
168 
169  /* Load certificate and private key */
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.");
176  }
177 
178  /* Load trust list */
179  struct dirent* entry = nullptr;
180  DIR* dp = nullptr;
181  dp = opendir(this->serverConfig.allowListFolder.c_str());
182  if(dp != nullptr) {
183  while((entry = readdir(dp))) {
184  if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue;
185  trustListSize++;
186  trustList = (UA_ByteString*)UA_realloc(trustList, sizeof(UA_ByteString) * trustListSize);
187  char sbuf[1024];
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);
191  }
192  }
193  closedir(dp);
194 
195  /* Load Issuer list */
196  dp = nullptr;
197  dp = opendir(this->serverConfig.issuerListFolder.c_str());
198  if(dp != nullptr) {
199  while((entry = readdir(dp))) {
200  if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue;
201  issuerListSize++;
202  issuerList = (UA_ByteString*)UA_realloc(issuerList, sizeof(UA_ByteString) * issuerListSize);
203  char sbuf[1024];
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);
207  }
208  }
209  closedir(dp);
210 
211  /* Load Block list */
212  dp = nullptr;
213  dp = opendir(this->serverConfig.blockListFolder.c_str());
214  if(dp != nullptr) {
215  while((entry = readdir(dp))) {
216  if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue;
217  blockListSize++;
218  blockList = (UA_ByteString*)UA_realloc(blockList, sizeof(UA_ByteString) * blockListSize);
219  char sbuf[1024];
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);
223  }
224  }
225  closedir(dp);
226 
227  // setup encrypted endpoints
228  UA_StatusCode retval = UA_ServerConfig_setDefaultWithSecurityPolicies(config, this->serverConfig.opcuaPort,
229  &certificate, &privateKey, trustList, trustListSize, issuerList, issuerListSize, blockList, blockListSize);
230 
231  if(retval != UA_STATUSCODE_GOOD) {
232  throw std::runtime_error("Failed setting up server endpoints.");
233  }
234 
235  if(!this->serverConfig.unsecure) {
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;
239 
240  UA_EndpointDescription_clear(ep);
241  // Move the last to this position
242  if(i + 1 < config->endpointsSize) {
243  config->endpoints[i] = config->endpoints[config->endpointsSize - 1];
244  i--;
245  }
246  config->endpointsSize--;
247  }
248  // Delete the entire array if the last Endpoint was removed
249  if(config->endpointsSize == 0) {
250  UA_free(config->endpoints);
251  config->endpoints = nullptr;
252  }
253  }
254 
255  UA_ByteString_clear(&certificate);
256  UA_ByteString_clear(&privateKey);
257  for(size_t i = 0; i < trustListSize; i++) UA_ByteString_clear(&trustList[i]);
258  }
259  fillBuildInfo(config);
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);
263  }
264 
265  this->mappedServer = UA_Server_newWithConfig(config);
266  this->server_config = UA_Server_getConfig(this->mappedServer);
267 
268  // Add custom tpye to server
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();
273 
274  // Username/Password handling
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()));
278  UA_AccessControl_default(this->server_config, !this->serverConfig.UsernamePasswordLogin,
279  &this->server_config->securityPolicies[this->server_config->securityPoliciesSize - 1].policyUri, 1,
280  usernamePasswordLogins);
281 
282  this->baseNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
284  UA_free(config);
285  UA_free(types);
286  UA_String_clear(&usernamePasswordLogins->password);
287  UA_String_clear(&usernamePasswordLogins->username);
288  delete usernamePasswordLogins;
289  }
290 
292  string xpath = "//config";
293  xmlXPathObjectPtr result = this->fileHandler->getNodeSet(xpath);
294  string placeHolder;
295  if(result) {
296  xmlNodeSetPtr nodeset = result->nodesetval;
297  if(nodeset->nodeNr > 1) {
298  throw std::runtime_error("To many <config>-Tags in config file");
299  }
300 
301  /*
302  * result = this->fileHandler->getNodeSet(xpath + "//process_variable_hierarchy");
303  if(result) {
304  xmlNodeSetPtr nodeset = result->nodesetval;
305  vector<xmlNodePtr> nodeVectorUnrollPathPV =
306  xml_file_handler::getNodesByName(nodeset->nodeTab[0]->children, "unroll");
307  for(auto nodeUnrollPath : nodeVectorUnrollPathPV) {
308  string unrollSepEnabled = xml_file_handler::getContentFromNode(nodeUnrollPath);
309  transform(unrollSepEnabled.begin(), unrollSepEnabled.end(), unrollSepEnabled.begin(), ::toupper);
310  if(unrollSepEnabled == "TRUE") {
311  this->pvSeperator += xml_file_handler::getAttributeValueFromNode(nodeUnrollPath, "pathSep");
312  }
313  }
314  */
315  vector<xmlNodePtr> mappingExceptionsVector =
316  xml_file_handler::getNodesByName(nodeset->nodeTab[0]->children, "mapping_exceptions");
317  if(mappingExceptionsVector.empty()) {
318  this->mappingExceptions = UA_FALSE;
319  }
320  else {
321  placeHolder = xml_file_handler::getContentFromNode(mappingExceptionsVector[0]);
322  transform(placeHolder.begin(), placeHolder.end(), placeHolder.begin(), ::toupper);
323  if(placeHolder == "TRUE") {
324  this->mappingExceptions = UA_TRUE;
325  }
326  else {
327  this->mappingExceptions = UA_FALSE;
328  }
329  }
330 
331  xmlXPathObjectPtr sub_result = this->fileHandler->getNodeSet(xpath + "//server");
332  if(result) {
333  xmlNodeSetPtr nodeset = sub_result->nodesetval;
334  if(nodeset->nodeNr > 1) {
335  throw std::runtime_error("To many <server>-Tags in config file");
336  }
337 
338  placeHolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "logLevel");
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;
343  }
344  else if(placeHolder.compare("DEBUG") == 0) {
345  this->serverConfig.logLevel = UA_LOGLEVEL_DEBUG;
346  }
347  else if(placeHolder.compare("INFO") == 0) {
348  this->serverConfig.logLevel = UA_LOGLEVEL_INFO;
349  }
350  else if(placeHolder.compare("WARNING") == 0) {
351  this->serverConfig.logLevel = UA_LOGLEVEL_WARNING;
352  }
353  else if(placeHolder.compare("ERROR") == 0) {
354  this->serverConfig.logLevel = UA_LOGLEVEL_ERROR;
355  }
356  else if(placeHolder.compare("FATAL") == 0) {
357  this->serverConfig.logLevel = UA_LOGLEVEL_FATAL;
358  }
359  else {
360  // See default set in ServerConfig
361  // Use UA_Log_Stdout because the logger does not yet exist, but log level INFO is used so
362  // writing a warning is ok here.
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());
365  }
366  }
367  else {
368  // Use UA_Log_Stdout because the logger does not yet exist, but log level INFO is used so
369  // writing a warning is ok here.
370  UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
371  "No 'logLevel'-Attribute set in config file. Use default logging level: info");
372  }
373  logger = UA_Log_Stdout_withLevel(this->serverConfig.logLevel);
374 
375  string opcuaPort = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "port");
376  if(!opcuaPort.empty()) {
377  this->serverConfig.opcuaPort = std::stoi(opcuaPort);
378  }
379  else {
380  UA_LOG_WARNING(
381  &logger, UA_LOGCATEGORY_USERLAND, "No 'port'-Attribute in config file is set. Use default Port: 16664");
382  }
383 
384  placeHolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "applicationName");
385  if(!placeHolder.empty()) {
386  this->serverConfig.applicationName = placeHolder;
387  if(this->serverConfig.rootFolder.empty()) {
388  this->serverConfig.rootFolder = placeHolder;
389  }
390  }
391  else {
392  string applicationName;
393  try {
394  string applicationName = ApplicationBase::getInstance().getName();
395  this->serverConfig.applicationName = applicationName;
396  }
397  catch(ChimeraTK::logic_error) {
398  }
399  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
400  "No 'applicationName'-Attribute is set in config file. Use default application-name.");
401  }
402 
403  // if no root folder name is set, use application name
404  if(this->serverConfig.rootFolder.empty()) {
405  this->serverConfig.rootFolder = this->serverConfig.applicationName;
406  }
407  xmlXPathFreeObject(sub_result);
408  }
409  else {
410  logger = UA_Log_Stdout_withLevel(this->serverConfig.logLevel);
411  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
412  "No <server>-Tag in config file. Use default port 16664 and application name configuration.");
413  this->serverConfig.rootFolder = this->serverConfig.applicationName;
414  }
415 
416  // check if historizing is configured
417  vector<xmlNodePtr> historizing = xml_file_handler::getNodesByName(nodeset->nodeTab[0]->children, "historizing");
418  if(!historizing.empty()) {
419  xmlXPathObjectPtr sub_sub_result = this->fileHandler->getNodeSet("//historizing");
420  if(sub_sub_result) {
421  xmlNodeSetPtr node_set = sub_sub_result->nodesetval;
422  for(int32_t i = 0; i < node_set->nodeNr; i++) {
423  vector<xmlNodePtr> nodeVectorhistorizingSetUp =
424  xml_file_handler::getNodesByName(node_set->nodeTab[i]->children, "setup");
425  string val;
426  for(auto& nodeHistorizingPath : nodeVectorhistorizingSetUp) {
427  string history_name = xml_file_handler::getAttributeValueFromNode(nodeHistorizingPath, "name");
428  AdapterHistorySetup temp;
429  char* c;
430  bool incomplete = false;
431  if(!history_name.empty()) {
432  temp.name = history_name;
433  }
434  else {
435  raiseError("Missing history parameter 'name'.",
436  "History configuration is not added to the list of available history configurations",
437  nodeHistorizingPath->line);
438  incomplete = true;
439  }
440  string history_buffer_length =
441  xml_file_handler::getAttributeValueFromNode(nodeHistorizingPath, "buffer_length");
442  if(!history_buffer_length.empty()) {
443  sscanf(history_buffer_length.c_str(), "%zu", &temp.buffer_length);
444  }
445  else {
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);
450  incomplete = true;
451  }
452  string history_entries_per_response =
453  xml_file_handler::getAttributeValueFromNode(nodeHistorizingPath, "entries_per_response");
454  if(!history_entries_per_response.empty()) {
455  sscanf(history_entries_per_response.c_str(), "%zu", &temp.entries_per_response);
456  }
457  else {
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);
462  incomplete = true;
463  }
464  string history_interval = xml_file_handler::getAttributeValueFromNode(nodeHistorizingPath, "interval");
465  if(!history_interval.empty()) {
466  sscanf(history_interval.c_str(), "%zu", &temp.interval);
467  }
468  else {
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);
473  incomplete = true;
474  }
475 
476  // check if a configuration is redefined
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) {
480  existing = true;
481  }
482  }
483  if(!existing) {
484  if(!incomplete) {
485  this->serverConfig.history.insert(this->serverConfig.history.end(), temp);
486  }
487  }
488  else {
489  raiseError("Redefinition of history configuration.",
490  "History configuration " + history_name +
491  " is not added to the list of available history configurations");
492  }
493  }
494  }
495  }
496  }
497 
498  placeHolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "rootFolder");
499  if(!placeHolder.empty()) {
500  this->serverConfig.rootFolder = placeHolder;
501  }
502 
503  placeHolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "description");
504  if(!placeHolder.empty()) {
505  this->serverConfig.descriptionFolder = placeHolder;
506  }
507  xmlXPathFreeObject(result);
508  }
509  else {
510  logger = UA_Log_Stdout_withLevel(this->serverConfig.logLevel);
511  }
512  result = this->fileHandler->getNodeSet(xpath + "//login");
513  if(result) {
514  xmlNodeSetPtr nodeset = result->nodesetval;
515  if(nodeset->nodeNr > 1) {
516  throw std::runtime_error("To many <login>-Tags in config file");
517  }
518  this->serverConfig.UsernamePasswordLogin = UA_TRUE;
519  placeHolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "password");
520  if(!placeHolder.empty()) {
521  this->serverConfig.password = placeHolder;
522  }
523  else {
524  throw std::runtime_error("<login>-Tag requires username");
525  }
526 
527  placeHolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "username");
528  if(!placeHolder.empty()) {
529  this->serverConfig.username = placeHolder;
530  }
531  else {
532  throw std::runtime_error("<login>-Tag requires password");
533  }
534 
535  xmlXPathFreeObject(result);
536  }
537  else {
538  this->serverConfig.UsernamePasswordLogin = UA_FALSE;
539  }
540 
541  result = this->fileHandler->getNodeSet(xpath + "//security");
542  if(result) {
543  xmlNodeSetPtr nodeset = result->nodesetval;
544  if(nodeset->nodeNr > 1) {
545  throw std::runtime_error("To many <security>-Tags in config file");
546  }
547  this->serverConfig.enableSecurity = true;
548  string unsecure = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "unsecure");
549  if(!unsecure.empty()) {
550  transform(unsecure.begin(), unsecure.end(), unsecure.begin(), ::toupper);
551  if(unsecure.compare("TRUE") == 0) {
552  this->serverConfig.unsecure = true;
553  }
554  else {
555  this->serverConfig.unsecure = false;
556  }
557  }
558  else {
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");
562  }
563  string certPath = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "certificate");
564  if(!certPath.empty()) {
565  this->serverConfig.certPath = certPath;
566  }
567  else {
568  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
569  "Invalid security configuration. No 'certificate'-Attribute in config file is set.");
570  }
571  string keyPath = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "privatekey");
572  if(!keyPath.empty()) {
573  this->serverConfig.keyPath = keyPath;
574  }
575  else {
576  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
577  "Invalid security configuration. No 'privatekey'-Attribute in config file is set.");
578  }
579  string allowListFolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "trustlist");
580  if(!allowListFolder.empty()) {
581  this->serverConfig.allowListFolder = allowListFolder;
582  }
583  else {
584  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
585  "Invalid security configuration. No 'trustlist'-Attribute in config file is set.");
586  }
587  string blockListFolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "blocklist");
588  if(!blockListFolder.empty()) {
589  this->serverConfig.blockListFolder = blockListFolder;
590  }
591  else {
592  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND, "No 'blockListFolder'-Attribute in config file is set.");
593  }
594  string issuerListFolder = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[0], "issuerlist");
595  if(!issuerListFolder.empty()) {
596  this->serverConfig.issuerListFolder = issuerListFolder;
597  }
598  else {
599  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND, "No 'issuerListFolder'-Attribute in config file is set.");
600  }
601  xmlXPathFreeObject(result);
602  }
603  else {
604  UA_LOG_WARNING(&logger, UA_LOGCATEGORY_USERLAND,
605  "No <security>-Tag in config file. Use default configuration with unsecure endpoint.");
606  this->serverConfig.enableSecurity = false;
607  }
608  result = this->fileHandler->getNodeSet(xpath + "//process_variable_hierarchy");
609  if(result) {
610  xmlNodeSetPtr nodeset = result->nodesetval;
611  vector<xmlNodePtr> nodeVectorUnrollPathPV =
612  xml_file_handler::getNodesByName(nodeset->nodeTab[0]->children, "unroll");
613  for(auto nodeUnrollPath : nodeVectorUnrollPathPV) {
614  string unrollSepEnabled = xml_file_handler::getContentFromNode(nodeUnrollPath);
615  transform(unrollSepEnabled.begin(), unrollSepEnabled.end(), unrollSepEnabled.begin(), ::toupper);
616  if(unrollSepEnabled == "TRUE") {
617  this->pvSeperator += xml_file_handler::getAttributeValueFromNode(nodeUnrollPath, "pathSep");
618  }
619  }
620  xmlXPathFreeObject(result);
621  }
622  else {
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 = "/";
626  }
627 
628  xmlXPathObjectPtr result_exclude = this->fileHandler->getNodeSet("//exclude");
629  xmlNodeSetPtr nodeset;
630  if(result_exclude) {
631  nodeset = result_exclude->nodesetval;
632  for(int32_t i = 0; i < nodeset->nodeNr; i++) {
633  string exclude_string = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "sourceName");
634  if(!exclude_string.empty()) {
635  this->exclude.insert(this->exclude.begin(), "/" + exclude_string);
636  }
637  }
638  }
639  xmlXPathFreeObject(result_exclude);
640 
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++) {
646  string folder =
647  xml_file_handler::getAttributeValueFromNode(nodeset_folder_with_history->nodeTab[i], "sourceName");
648  string history =
649  xml_file_handler::getAttributeValueFromNode(nodeset_folder_with_history->nodeTab[i], "history");
650  if(!folder.empty() && !history.empty()) {
651  this->folder_with_history.insert(this->folder_with_history.begin(), "/" + folder);
652  }
653  }
654  }
655  xmlXPathFreeObject(folder_with_his);
656  }
657 
659  return this->serverConfig;
660  }
661 
662  void ua_uaadapter::applyMapping(const boost::shared_ptr<ControlSystemPVManager>& csManager) {
663  // build folder structure
664  this->buildFolderStructure(csManager);
665  // start explicit mapping
666  this->explicitVarMapping(csManager);
667  // add configured additional variables
668  this->addAdditionalVariables();
669  }
670 
672  if(this->mappedServer == nullptr) {
673  UA_LOG_DEBUG(server_config->logging, UA_LOGCATEGORY_USERLAND, "No server mapped");
674  return;
675  }
676 
677  /*for(auto & variable : this->variables){
678  check_void_type(variable);
679  }*/
680 
681  vector<UA_NodeId> historizing_nodes;
682  vector<string> historizing_setup;
683  UA_HistoryDataGathering gathering =
684  add_historizing_nodes(historizing_nodes, historizing_setup, this->mappedServer, this->server_config,
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");
688  UA_Server_run_startup(this->mappedServer);
689  this->running = true;
690  while(this->running) {
691  UA_Server_run_iterate(this->mappedServer, true);
692  }
693  clear_history(gathering, historizing_nodes, historizing_setup, this->mappedServer,
694  this->serverConfig.historyfolders, this->serverConfig.historyvariables, this->server_config);
695 
696  UA_Server_run_shutdown(this->mappedServer);
697  UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND, "Stopped the server worker thread");
698  }
699 
700  UA_NodeId ua_uaadapter::enrollFolderPathFromString(const string& path, const string& seperator) {
701  vector<string> varPathVector;
702  if(!seperator.empty()) {
703  vector<string> newPathVector = xml_file_handler::parseVariablePath(path, seperator);
704  varPathVector.insert(varPathVector.end(), newPathVector.begin(), newPathVector.end());
705  }
706  if(!varPathVector.empty()) { // last element is the variable name itself
707  varPathVector.pop_back();
708  return this->createFolderPath(this->ownNodeId, varPathVector);
709  }
710  return UA_NODEID_NULL;
711  }
712 
714  const std::string& varName, const boost::shared_ptr<ControlSystemPVManager>& csManager) {
715  UA_NodeId folderPathNodeId = enrollFolderPathFromString(varName, this->pvSeperator);
716  ua_processvariable* processvariable;
717  if(!UA_NodeId_isNull(&folderPathNodeId)) {
718  processvariable =
719  new ua_processvariable(this->mappedServer, folderPathNodeId, varName.substr(1, varName.size() - 1), csManager,
720  server_config->logging, xml_file_handler::parseVariablePath(varName, this->pvSeperator).back());
721  }
722  else {
723  processvariable = new ua_processvariable(this->mappedServer, this->ownNodeId,
724  varName.substr(1, varName.size() - 1), csManager, server_config->logging);
725  }
726  this->variables.push_back(processvariable);
727  UA_NodeId tmpNodeId = processvariable->getOwnNodeId();
728  UA_Server_writeDisplayName(this->mappedServer, tmpNodeId,
729  UA_LOCALIZEDTEXT(const_cast<char*>("en_US"),
730  const_cast<char*>(xml_file_handler::parseVariablePath(varName, this->pvSeperator).back().c_str())));
731  UA_NodeId_clear(&tmpNodeId);
732  }
733 
735  const boost::shared_ptr<ControlSystemPVManager>& csManager, UA_NodeId layer, UA_NodeId target) {
736  // copy pv's of current layer
737  UA_BrowseDescription bd;
738  bd.includeSubtypes = false;
739  bd.nodeId = layer;
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];
747  // get unit, desc, ..
748  UA_LocalizedText foundPVName;
749  string foundPVNameCPP;
750  UA_Server_readDisplayName(this->mappedServer, rd.nodeId.nodeId, &foundPVName);
751  UASTRING_TO_CPPSTRING(foundPVName.text, foundPVNameCPP)
752 
753  string pvSourceNameid;
754  UA_String foundPVSourceName;
755  string foundPVSourceNameCPP;
756  UA_Variant value;
757  UA_STRING_TO_CPPSTRING_COPY(&rd.nodeId.nodeId.identifier.string, &pvSourceNameid)
758  UA_Server_readValue(
759  this->mappedServer, UA_NODEID_STRING(1, const_cast<char*>((pvSourceNameid + "/Name").c_str())), &value);
760  foundPVSourceName = *((UA_String*)value.data);
761  UASTRING_TO_CPPSTRING(foundPVSourceName, foundPVSourceNameCPP)
762  string varName = xml_file_handler::parseVariablePath(foundPVNameCPP, "/").back();
763  auto* processvariable = new ua_processvariable(
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);
768  }
769  UA_BrowseResult_clear(&br);
770  // copy folders of current layer
771  bd.includeSubtypes = false;
772  bd.nodeId = layer;
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);
786  UASTRING_TO_CPPSTRING(foundFolderName.text, foundFolderNameCPP)
787  if(result != UA_STATUSCODE_GOOD) {
788  // error handling
789  continue;
790  }
791  UA_NodeId newRootFolder = createFolder(target, foundFolderNameCPP);
792  if(foundFolderDescription.text.length > 0)
793  UA_Server_writeDescription(this->mappedServer, newRootFolder, foundFolderDescription);
794  deepCopyHierarchicalLayer(csManager, rd.nodeId.nodeId, newRootFolder);
795  UA_LocalizedText_clear(&foundFolderName);
796  UA_LocalizedText_clear(&foundFolderDescription);
797  }
798  UA_BrowseResult_clear(&br);
799  }
800 
801  void ua_uaadapter::buildFolderStructure(const boost::shared_ptr<ControlSystemPVManager>& csManager) {
802  xmlXPathObjectPtr result = this->fileHandler->getNodeSet("//folder");
803  xmlNodeSetPtr nodeset;
804  if(result) {
805  nodeset = result->nodesetval;
806 
807  for(int32_t i = 0; i < nodeset->nodeNr; i++) {
808  UA_NodeId folderPathNodeId;
809  string destination, description, folder;
810  vector<xmlNodePtr> nodeFolderPath =
811  xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "destination");
812  vector<xmlNodePtr> nodeFolder = xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "name");
813  vector<xmlNodePtr> nodeDescription =
814  xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "description");
815  string sourceName = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "sourceName");
816  string copy = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "copy");
817  transform(copy.begin(), copy.end(), copy.begin(), ::toupper);
818  if(!nodeFolderPath.empty()) {
819  destination = xml_file_handler::getContentFromNode(nodeFolderPath[0]);
820  }
821  if(!nodeDescription.empty()) {
822  description = xml_file_handler::getContentFromNode(nodeDescription[0]);
823  }
824  if(!nodeFolder.empty()) {
825  folder = xml_file_handler::getContentFromNode(nodeFolder[0]);
826  }
827  string history = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "history");
828  if(!history.empty()) {
829  // After discussion we only allow history if source name is set.
830  // Else the exclude logic would become more complex!
831  if(sourceName.empty()) {
832  raiseError("Can not add history if no sourceName attribute is set.", "", nodeset->nodeTab[i]->line);
833  }
834  if(!nodeFolder.empty()) {
835  string folderNodeId;
836  if(!nodeFolderPath.empty()) {
837  if(strlen(destination.c_str()) == 0) {
838  folderNodeId = this->serverConfig.rootFolder + "/" + folder + "Dir";
839  }
840  else {
841  folderNodeId = this->serverConfig.rootFolder + "/" + destination + "/" + folder + "Dir";
842  }
843  }
844  else {
845  folderNodeId = this->serverConfig.rootFolder + "/" + folder + "Dir";
846  }
847 
849  temp.folder_historizing = history;
850  UA_NodeId id = UA_NODEID_STRING(1, const_cast<char*>(folderNodeId.c_str()));
851  UA_NodeId_copy(&id, &temp.folder_id);
852  this->serverConfig.historyfolders.insert(this->serverConfig.historyfolders.end(), temp);
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);
858  }
859  else if(copy.empty() || copy == "FALSE") {
861  temp.folder_historizing = history;
862  string folderNodeId = this->serverConfig.rootFolder + "/" + sourceName + "Dir";
863  UA_NodeId id = UA_NODEID_STRING(1, const_cast<char*>(folderNodeId.c_str()));
864  UA_NodeId_copy(&id, &temp.folder_id);
865  this->serverConfig.historyfolders.insert(this->serverConfig.historyfolders.end(), temp);
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);
871  }
872  else {
873  raiseError(
874  "Can not add history if copy is true and no source name is given.", "", nodeset->nodeTab[i]->line);
875  }
876  }
877 
878  if(folder.empty()) {
879  // only if no history is set raise error/warning
880  if(history.empty()) {
881  raiseError("Folder creation failed. Name is missing.", "Skipping Folder.", nodeset->nodeTab[i]->line);
882  }
883  continue;
884  }
885  // check if a pv with the requested node id exists
886  UA_NodeId tmpOutput = UA_NODEID_NULL;
887  string pvNodeString;
888  if(destination.empty()) {
889  pvNodeString += this->serverConfig.rootFolder + "/" + folder + "Dir";
890  }
891  else {
892  pvNodeString += this->serverConfig.rootFolder + "/" + destination + "/" + folder + "Dir";
893  }
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);
898  // set folder description
899  if(sourceName.empty() && !description.empty()) {
900  UA_Server_writeDescription(this->mappedServer, pvNode,
901  UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(description.c_str())));
902  continue;
903  }
904  raiseError("Folder with source mapping failed. Target folder node id exists.",
905  std::string("Skipping folder: ") + sourceName, nodeset->nodeTab[i]->line);
906  continue;
907  }
908  if(!copy.empty() && sourceName.empty()) {
909  raiseError(std::string("Source 'name: ") + folder + "' folder missing.", "Skipping Folder.",
910  nodeset->nodeTab[i]->line);
911  continue;
912  }
913  folderPathNodeId = enrollFolderPathFromString(destination + "/replacePart", "/");
914  if(UA_NodeId_isNull(&folderPathNodeId)) {
915  folderPathNodeId = UA_NODEID_STRING(1, const_cast<char*>((this->serverConfig.rootFolder + "Dir").c_str()));
916  }
917  // check if source name is set -> map complete hierarchical structure to the destination
918  if(!sourceName.empty()) {
919  if((destination.empty() && sourceName == folder) ||
920  (!destination.empty() && sourceName == destination + "/" + folder)) {
921  raiseError(
922  "Folder creation failed. Source and Destination equal.", "Skipping Folder.", nodeset->nodeTab[i]->line);
923  continue;
924  }
925  // check if the src is a folder
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()));
929 
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)) {
942  isFolderType = true;
943  }
944  }
945  UA_BrowseResult_clear(&br);
946  if(!isFolderType) {
947  raiseError(std::string("Folder creation failed. No corresponding source folder: ") + sourceName,
948  "Skipping Folder.", nodeset->nodeTab[i]->line);
949  continue;
950  }
951  // enroll path destination -> copy / link the complete tree to this place
952  if(copy == "TRUE") {
953  bool sourceAndDestinationEqual = false;
954  if(!destination.empty()) {
955  if(sourceName == destination + "/" + folder) sourceAndDestinationEqual = true;
956  }
957  else {
958  if(sourceName == folder) sourceAndDestinationEqual = true;
959  }
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);
964  continue;
965  }
966  // folder structure must be copied, pv nodes must be added
967  UA_LocalizedText foundFolderName;
968  UA_NodeId copyRoot = createFolder(folderPathNodeId, folder);
969  if(UA_NodeId_isNull(&copyRoot)) {
970  string existingDestinationFolderString;
971  if(destination.empty()) {
972  existingDestinationFolderString += this->serverConfig.rootFolder + "/" + folder + "Dir";
973  }
974  else {
975  existingDestinationFolderString = this->serverConfig.rootFolder +=
976  "/" + destination + "/" + folder + "Dir";
977  }
978  copyRoot = UA_NODEID_STRING(1, const_cast<char*>(existingDestinationFolderString.c_str()));
979  }
980  deepCopyHierarchicalLayer(csManager, sourceFolderId, copyRoot);
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())));
984  }
985  }
986  else {
987  string existingDestinationFolderString;
988  UA_NodeId copyRoot = createFolder(folderPathNodeId, folder);
989  if(UA_NodeId_isNull(&copyRoot)) {
990  if(destination.empty()) {
991  existingDestinationFolderString = this->serverConfig.rootFolder + "/" + folder;
992  }
993  else {
994  existingDestinationFolderString = this->serverConfig.rootFolder + "/" + destination + "/" + folder;
995  }
996  copyRoot = UA_NODEID_STRING(1, const_cast<char*>(existingDestinationFolderString.c_str()));
997  }
998  else {
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())));
1002  }
1003  }
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);
1019  }
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;
1026  br = UA_Server_browse(this->mappedServer, 1000, &bd);
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);
1034  }
1035  UA_BrowseResult_clear(&br);
1036  }
1037  continue;
1038  }
1039 
1040  UA_NodeId retnode = createFolder(folderPathNodeId, folder);
1041  // set folder description
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())));
1045  }
1046  }
1047 
1048  xmlXPathFreeObject(result);
1049  }
1050  }
1051 
1052  void ua_uaadapter::explicitVarMapping(const boost::shared_ptr<ControlSystemPVManager>& csManager) {
1053  xmlXPathObjectPtr result = this->fileHandler->getNodeSet("//process_variable");
1054  xmlNodeSetPtr nodeset;
1055  if(result) {
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 =
1060  xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "destination");
1061  vector<xmlNodePtr> nodeName = xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "name");
1062 
1063  vector<xmlNodePtr> nodeUnit = xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "unit");
1064  vector<xmlNodePtr> nodeDescription =
1065  xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "description");
1066 
1067  copy = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "copy");
1068  transform(copy.begin(), copy.end(), copy.begin(), ::toupper);
1069 
1070  sourceName = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "sourceName");
1071  history = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "history");
1072  if(!history.empty()) {
1073  AdapterPVHistorySetup temp;
1074  temp.variable_historizing = history;
1075  string targetNodeId;
1076  // get the name of the variable
1077  if(!nodeName.empty()) {
1078  if(nodeDestination.empty()) {
1079  targetNodeId = this->serverConfig.rootFolder + "/" + xml_file_handler::getContentFromNode(nodeName[0]);
1080  UA_NodeId id = UA_NODEID_STRING(1, const_cast<char*>(targetNodeId.c_str()));
1081  UA_NodeId_copy(&id, &temp.variable_id);
1082  this->serverConfig.historyvariables.insert(this->serverConfig.historyvariables.end(), temp);
1083  }
1084  else {
1085  targetNodeId = this->serverConfig.rootFolder + "/" +
1086  xml_file_handler::getContentFromNode(nodeDestination[0]) + "/" +
1088  UA_NodeId id = UA_NODEID_STRING(1, const_cast<char*>(targetNodeId.c_str()));
1089  UA_NodeId_copy(&id, &temp.variable_id);
1090  this->serverConfig.historyvariables.insert(this->serverConfig.historyvariables.end(), temp);
1091  }
1092  }
1093  if(!sourceName.empty() && (copy.empty() || copy == "FALSE")) {
1094  name = sourceName;
1095  targetNodeId = this->serverConfig.rootFolder + "/" + sourceName;
1096  UA_NodeId id = UA_NODEID_STRING(1, const_cast<char*>(targetNodeId.c_str()));
1097  UA_NodeId_copy(&id, &temp.variable_id);
1098  this->serverConfig.historyvariables.insert(this->serverConfig.historyvariables.end(), temp);
1099  }
1100  }
1101 
1102  if(!nodeDestination.empty()) {
1103  destination = xml_file_handler::getContentFromNode(nodeDestination[0]);
1104  unrollPath = xml_file_handler::getAttributeValueFromNode(nodeDestination[0], "unrollPath");
1105  }
1106  if(!nodeName.empty()) {
1107  name = xml_file_handler::getContentFromNode(nodeName[0]);
1108  }
1109  if(!nodeUnit.empty()) {
1110  unit = xml_file_handler::getContentFromNode(nodeUnit[0]);
1111  }
1112  if(!nodeDescription.empty()) {
1113  description = xml_file_handler::getContentFromNode(nodeDescription[0]);
1114  }
1115 
1116  if(sourceName.empty()) {
1117  if(!name.empty()) {
1118  raiseError(std::string("PV mapping failed. SourceName missing for 'name' : '") + name,
1119  "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1120  }
1121  else {
1122  raiseError("PV mapping failed. SourceName is missing.", "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1123  }
1124  continue;
1125  }
1126  // check if the pv still exists -> update requested
1127  if(((destination + "/" + name) == sourceName) && copy == "FALSE") {
1128  // check if the source var exists
1129  string parentSourceFolder = this->serverConfig.rootFolder + "/" +
1130  (sourceName.substr(
1131  0, sourceName.length() - xml_file_handler::parseVariablePath(sourceName, "/").back().length() - 1));
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];
1144  string name;
1145  UASTRING_TO_CPPSTRING(rd.displayName.text, name)
1146  if(name == xml_file_handler::parseVariablePath(sourceName, "/").back()) {
1147  UA_NodeId_copy(&br.references[j].nodeId.nodeId, &pvNodeId);
1148  }
1149  }
1150  UA_BrowseResult_clear(&br);
1151  if(UA_NodeId_isNull(&pvNodeId)) {
1152  if(!name.empty()) {
1153  raiseError(std::string("PV mapping failed. No corresponding source pv for 'name' : '") + name,
1154  "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1155  }
1156  else {
1157  raiseError(
1158  "PV mapping failed. No corresponding source pv.", "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1159  }
1160  continue;
1161  }
1162  if(!unit.empty()) {
1163  // update unit
1164  for(auto& variable : this->variables) {
1165  UA_NodeId tmpNodeId = variable->getOwnNodeId();
1166  if(UA_NodeId_equal(&tmpNodeId, &pvNodeId)) {
1167  variable->setEngineeringUnit(unit);
1168  }
1169  UA_NodeId_clear(&tmpNodeId);
1170  }
1171  }
1172  if(!description.empty()) {
1173  // update description
1174  for(auto& variable : this->variables) {
1175  UA_NodeId tmpNodeId = variable->getOwnNodeId();
1176  if(UA_NodeId_equal(&tmpNodeId, &pvNodeId)) {
1177  variable->setDescription(description);
1178  }
1179  UA_NodeId_clear(&tmpNodeId);
1180  }
1181  }
1182 
1183  if(!UA_NodeId_isNull(&pvNodeId)) {
1184  UA_NodeId_clear(&pvNodeId);
1185  }
1186  continue;
1187  }
1188  // check if the source pv exists
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)) {
1194  if(!name.empty()) {
1195  raiseError(std::string("PV mapping failed. Source PV not found 'name' : '") + name, "Skipping PV mapping.",
1196  nodeset->nodeTab[i]->line);
1197  }
1198  else {
1199  raiseError("PV mapping failed. Source PV not found", "Skipping PV mapping.", nodeset->nodeTab[i]->line);
1200  }
1201  continue;
1202  }
1203  else {
1204  UA_NodeId_clear(&tmpOutput);
1205  UA_NodeId_init(&tmpOutput);
1206  }
1207  // check the pv copy attribute -> copy of pv requested; false -> reference to original pv requested
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);
1213  continue;
1214  }
1215  UA_NodeId destinationFolderNodeId = UA_NODEID_NULL;
1216  if(destination.empty()) {
1217  destinationFolderNodeId = this->ownNodeId;
1218  }
1219  else {
1220  if(unrollPath.empty()) {
1221  destinationFolderNodeId = enrollFolderPathFromString(destination + "/removedPart", "/");
1222  }
1223  else {
1224  // legacy code to make old mapping possible
1225  if(destination.find("Variables" + unrollPath) == 0) {
1226  string requestedBrowseName = destination;
1227  requestedBrowseName.erase(0, ("Variables" + unrollPath).length());
1228  // check if the requested path still exists in the Variables folder
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);
1237  }
1238  else {
1239  destinationFolderNodeId =
1240  enrollFolderPathFromString(destination + unrollPath + "removedPart", unrollPath);
1241  }
1242  }
1243  else {
1244  destinationFolderNodeId =
1245  enrollFolderPathFromString(destination + unrollPath + "removedPart", unrollPath);
1246  }
1247  }
1248  }
1249  if(name.empty()) {
1250  name = sourceName;
1251  }
1252  ua_processvariable* processvariable;
1253  if(!UA_NodeId_isNull(&destinationFolderNodeId)) {
1254  processvariable = new ua_processvariable(
1255  this->mappedServer, destinationFolderNodeId, sourceName, csManager, server_config->logging, name);
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);
1260  continue;
1261  }
1262  else {
1263  UA_NodeId_clear(&tmpProcessVariableNodeId);
1264  }
1265  this->variables.push_back(processvariable);
1266  }
1267  else {
1268  raiseError("Folder creation failed.", std::string("Skipping PV ") + sourceName, nodeset->nodeTab[i]->line);
1269  continue;
1270  }
1271  UA_NodeId tmpPVNodeId = processvariable->getOwnNodeId();
1272  UA_NodeId_copy(&tmpPVNodeId, &createdNodeId);
1273  UA_NodeId_clear(&tmpPVNodeId);
1274  }
1275  else {
1276  // get node id of the source node
1277  string sourceVarName = xml_file_handler::parseVariablePath(sourceName, "/").back();
1278  if(name.empty()) {
1279  name = sourceVarName;
1280  }
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);
1285  }
1286  continue;
1287  }
1288  // create destination folder
1289  UA_NodeId destinationFolder = enrollFolderPathFromString(destination + "/removedpart", "/");
1290  UA_ExpandedNodeId enid;
1291  enid.serverIndex = 0;
1292  enid.namespaceUri = UA_STRING_NULL;
1293  enid.nodeId = parentSourceId;
1294  // add reference to the source node
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);
1300  continue;
1301  }
1302  UA_NodeId_copy(&parentSourceId, &createdNodeId);
1303  }
1304  if(!unit.empty()) {
1305  for(auto& variable : this->variables) {
1306  UA_NodeId tmpNodeId = variable->getOwnNodeId();
1307  if(UA_NodeId_equal(&tmpNodeId, &createdNodeId)) {
1308  variable->setEngineeringUnit(unit);
1309  }
1310  UA_NodeId_clear(&tmpNodeId);
1311  }
1312  }
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);
1318  }
1319  UA_NodeId_clear(&tmpNodeId);
1320  }
1321  }
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);
1325  }
1326 
1327  xmlXPathFreeObject(result);
1328  }
1329  }
1330 
1332  xmlXPathObjectPtr result = this->fileHandler->getNodeSet("//additional_variable");
1333  xmlNodeSetPtr nodeset;
1334  if(result) {
1335  nodeset = result->nodesetval;
1336  for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1337  string destination, name, description, value;
1338  vector<xmlNodePtr> nodeDestination =
1339  xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "destination");
1340  vector<xmlNodePtr> nodeName = xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "name");
1341  vector<xmlNodePtr> nodeDescription =
1342  xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "description");
1343  vector<xmlNodePtr> nodeValue = xml_file_handler::getNodesByName(nodeset->nodeTab[i]->children, "value");
1344 
1345  if(!nodeDestination.empty()) {
1346  destination = xml_file_handler::getContentFromNode(nodeDestination[0]);
1347  }
1348  if(!nodeName.empty()) {
1349  name = xml_file_handler::getContentFromNode(nodeName[0]);
1350  }
1351  if(!nodeDescription.empty()) {
1352  description = xml_file_handler::getContentFromNode(nodeDescription[0]);
1353  }
1354  if(!nodeValue.empty()) {
1355  value = xml_file_handler::getContentFromNode(nodeValue[0]);
1356  }
1357  // check if name is empty
1358  if(name.empty()) {
1359  raiseError("Additional variable node creation failed. Additional variable name is mandatory.",
1360  "Skipping additional variable.", nodeset->nodeTab[i]->line);
1361  continue;
1362  }
1363  // check if the av node still exists
1364  UA_NodeId tmpOutput = UA_NODEID_NULL;
1365  string avNodeString;
1366  if(destination.empty()) {
1367  avNodeString = this->serverConfig.rootFolder + "/" + name + "AdditionalVariable";
1368  }
1369  else {
1370  avNodeString = this->serverConfig.rootFolder + "/" + destination + "/" + name + "AdditionalVariable";
1371  }
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);
1379  continue;
1380  }
1381  // check if pv with same name exists in the target folder
1382  UA_NodeId_clear(&tmpOutput);
1383  string pvNodeString;
1384  if(destination.empty()) {
1385  pvNodeString = this->serverConfig.rootFolder + "/" + name + "Value";
1386  }
1387  else {
1388  pvNodeString = this->serverConfig.rootFolder + "/" + destination + "/" + name + "Value";
1389  }
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);
1397  continue;
1398  }
1399 
1400  UA_NodeId additionalVarFolderPath = UA_NODEID_NULL;
1401  string additionalVarFolderPathNodeId;
1402  if(!destination.empty()) {
1403  additionalVarFolderPath = enrollFolderPathFromString(destination + "/removePart", "/");
1404  }
1405  else {
1406  additionalVarFolderPathNodeId += this->serverConfig.rootFolder + "Dir";
1407  additionalVarFolderPath = UA_NODEID_STRING_ALLOC(1, const_cast<char*>(additionalVarFolderPathNodeId.c_str()));
1408  }
1409  if(UA_NodeId_isNull(&additionalVarFolderPath)) {
1410  raiseError("Creation of additional variable folder failed.", "Skipping additional variable.",
1411  nodeset->nodeTab[i]->line);
1412  continue;
1413  }
1414  auto* additionalvariable =
1415  new ua_additionalvariable(this->mappedServer, additionalVarFolderPath, name, value, description);
1416  this->additionalVariables.push_back(additionalvariable);
1417  if(destination.empty()) {
1418  UA_NodeId_clear(&additionalVarFolderPath);
1419  }
1420  }
1421 
1422  xmlXPathFreeObject(result);
1423  }
1424  }
1425 
1426  vector<ua_processvariable*> ua_uaadapter::getVariables() {
1427  return this->variables;
1428  }
1429 
1430  UA_NodeId ua_uaadapter::createUAFolder(
1431  UA_NodeId basenodeid, const std::string& folderName, const std::string& description) {
1432  // FIXME: Check if folder name a possible name or should it be escaped (?!"§%-:, etc)
1433  UA_StatusCode retval = UA_STATUSCODE_GOOD;
1434  UA_NodeId createdNodeId = UA_NODEID_NULL;
1435 
1436  if(UA_NodeId_equal(&baseNodeId, &createdNodeId) == UA_TRUE) {
1437  return createdNodeId; // Something went wrong (initializer should set this!)
1438  }
1439 
1440  // Create our toplevel instance
1441  UA_ObjectAttributes oAttr;
1442  UA_ObjectAttributes_init(&oAttr);
1443  // Classcast to prevent Warnings
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()));
1446 
1447  string parentNodeIdString;
1448  if(basenodeid.identifierType == UA_NODEIDTYPE_STRING) {
1449  UASTRING_TO_CPPSTRING(basenodeid.identifier.string, parentNodeIdString)
1450  if(!parentNodeIdString.empty()) {
1451  parentNodeIdString.resize(parentNodeIdString.size() - 3);
1452  }
1453  parentNodeIdString += '/' + folderName + "Dir";
1454  }
1455  else if(basenodeid.identifierType == UA_NODEIDTYPE_NUMERIC) {
1456  parentNodeIdString += '/' + std::to_string(basenodeid.identifier.numeric) + "Dir";
1457  }
1458 
1459  UA_Server_addObjectNode(this->mappedServer,
1460  UA_NODEID_STRING(1, const_cast<char*>(parentNodeIdString.c_str())), // UA_NODEID_NUMERIC(1,0)
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,
1463  &this->ownedNodes, &createdNodeId);
1464 
1465  ua_mapInstantiatedNodes(createdNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), &this->ownedNodes);
1466  return createdNodeId;
1467  }
1468 
1469  void ua_uaadapter::raiseError(std::string errorMesssage, std::string consequenceMessage, const int& line) {
1470  std::string lineMessage("");
1471  if(line > 0) {
1472  lineMessage = std::string(" Mapping line number: ") + std::to_string(line) + ".";
1473  }
1474  std::string tmp[2] = {errorMesssage, consequenceMessage};
1475  if(errorMesssage.back() != '.') {
1476  errorMesssage += ".";
1477  }
1478  if(consequenceMessage.back() != '.') {
1479  consequenceMessage += ".";
1480  }
1481  if(this->mappingExceptions) {
1482  throw std::runtime_error(std::string("Error! ") + errorMesssage + lineMessage);
1483  }
1484  if(server_config) {
1485  UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND, "%s %s", errorMesssage.c_str(),
1486  (consequenceMessage + lineMessage).c_str());
1487  }
1488  else {
1489  UA_LOG_WARNING(
1490  &logger, UA_LOGCATEGORY_USERLAND, "%s %s", errorMesssage.c_str(), (consequenceMessage + lineMessage).c_str());
1491  }
1492  }
1493 
1494  UA_StatusCode ua_uaadapter::mapSelfToNamespace() {
1495  UA_StatusCode retval = UA_STATUSCODE_GOOD;
1496  UA_NodeId createdNodeId = UA_NODEID_NULL;
1497 
1498  if(UA_NodeId_equal(&this->baseNodeId, &createdNodeId) == UA_TRUE) {
1499  return 0;
1500  }
1501 
1502  {
1503  // Create our toplevel instance
1504  UA_ObjectAttributes oAttr;
1505  UA_ObjectAttributes_init(&oAttr);
1506  // Classcast to prevent Warnings
1507  oAttr.displayName =
1508  UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(this->serverConfig.rootFolder.c_str()));
1509  oAttr.description =
1510  UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(this->serverConfig.descriptionFolder.c_str()));
1511 
1512  UA_Server_addObjectNode(this->mappedServer,
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);
1517 
1518  this->ownNodeId = createdNodeId;
1519  ua_mapInstantiatedNodes(this->ownNodeId, UA_NODEID_NUMERIC(CSA_NSID, UA_NS2ID_CTKMODULE), &ownedNodes);
1520  }
1521  {
1522  // Create our toplevel instance for configuration
1523  UA_ObjectAttributes oAttr;
1524  UA_ObjectAttributes_init(&oAttr);
1525  // Classcast to prevent Warnings
1526  oAttr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("ServerConfiguration"));
1527  oAttr.description =
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;
1534  ua_mapInstantiatedNodes(configNodeId, UA_NODEID_NUMERIC(CSA_NSID, UA_NS2ID_CTKMODULE), &ownedNodes);
1535 
1536  // create log level node
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);
1544 
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);
1549 
1550  UA_DataSource logLevelDataSource;
1551  logLevelDataSource.read = readLogLevel;
1552  logLevelDataSource.write = writeLogLevel;
1553  UA_Server_addDataSourceVariableNode(this->mappedServer, currentNodeId, createdNodeId, parentReferenceNodeId,
1554  currentName, variableTypeNodeId, attr, logLevelDataSource, this, NULL);
1555  UA_Variant_clear(&attr.value);
1556  }
1557 
1558  return UA_STATUSCODE_GOOD;
1559  }
1560 
1562  return this->ownNodeId;
1563  }
1564 
1565  UA_NodeId ua_uaadapter::existFolderPath(UA_NodeId basenodeid, const std::vector<string>& folderPath) {
1566  UA_NodeId lastNodeId = basenodeid;
1567  for(const std::string& t : folderPath) {
1568  lastNodeId = this->existFolder(lastNodeId, t);
1569  if(UA_NodeId_isNull(&lastNodeId)) {
1570  return UA_NODEID_NULL;
1571  }
1572  }
1573  return lastNodeId;
1574  }
1575 
1576  UA_NodeId ua_uaadapter::existFolder(UA_NodeId basenodeid, const string& folder) {
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;
1581  }
1582  }
1583  return UA_NODEID_NULL;
1584  }
1585 
1586  UA_NodeId ua_uaadapter::createFolderPath(UA_NodeId basenodeid, std::vector<string> folderPath) {
1587  if(UA_NodeId_isNull(&basenodeid)) {
1588  return UA_NODEID_NULL;
1589  }
1590  // Check if path exist
1591  UA_NodeId toCheckNodeId = existFolderPath(basenodeid, folderPath);
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;
1597  // Check if path exist partly
1598  for(uint32_t m = 0; m < folderPath.size(); m++) {
1599  for(auto& i : this->folderVector) {
1600  // get correct folder NodeId from first folderPath element
1601  if(!setted && (folderPath.at(m) == i.folderName) && (UA_NodeId_equal(&i.prevFolderNodeId, &nextNodeId)) &&
1602  ((m + 1) < folderPath.size())) {
1603  // remember on witch position the folder still exist
1604  setted = true;
1605  starter4Folder = m + 1;
1606  nextNodeId = i.folderNodeId;
1607  }
1608  if(setted) {
1609  break;
1610  }
1611  }
1612  if(!setted) break;
1613  setted = false;
1614  }
1615  }
1616  else {
1617  // Path exist nothing to do
1618  return toCheckNodeId;
1619  }
1620 
1621  UA_NodeId prevNodeId = nextNodeId;
1622  // use the remembered position to start the loop
1623  for(uint32_t m = starter4Folder; m < folderPath.size(); m++) {
1624  prevNodeId = this->createFolder(prevNodeId, folderPath.at(m));
1625  }
1626  // return last created folder UA_NodeId
1627  return prevNodeId;
1628  }
1629 
1630  UA_NodeId ua_uaadapter::createFolder(UA_NodeId basenodeid, const string& folderName, const string& description) {
1631  if(UA_NodeId_isNull(&basenodeid)) {
1632  return UA_NODEID_NULL;
1633  }
1634  // Check if path exist
1635  UA_NodeId toCheckNodeId = existFolder(basenodeid, folderName);
1636  FolderInfo newFolder;
1637  if(UA_NodeId_isNull(&toCheckNodeId)) {
1638  newFolder.folderName = folderName;
1639  newFolder.folderNodeId = this->createUAFolder(basenodeid, folderName, description);
1640  newFolder.prevFolderNodeId = basenodeid;
1641  this->folderVector.push_back(newFolder);
1642  }
1643  return newFolder.folderNodeId;
1644  }
1645 
1647  vector<string> mappedPvSources;
1648  xmlXPathObjectPtr result = this->fileHandler->getNodeSet("//process_variable");
1649  if(result) {
1650  xmlNodeSetPtr nodeset = result->nodesetval;
1651  for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1652  mappedPvSources.insert(
1653  mappedPvSources.begin(), xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "sourceName"));
1654  }
1655  }
1656  xmlXPathFreeObject(result);
1657  return mappedPvSources;
1658  }
1659 
1661  vector<string> notMappableVariablesNames;
1662  xmlXPathObjectPtr result = this->fileHandler->getNodeSet("//process_variable");
1663 
1664  if(result) {
1665  xmlNodeSetPtr nodeset = result->nodesetval;
1666  for(int32_t i = 0; i < nodeset->nodeNr; i++) {
1667  // for(auto var:this->variables) {
1668  bool mapped = false;
1669  string mappedVar = xml_file_handler::getAttributeValueFromNode(nodeset->nodeTab[i], "sourceName");
1670  for(auto var : this->getVariables()) {
1671  if(var->getName() == mappedVar) {
1672  mapped = true;
1673  }
1674  }
1675  if(!mapped) {
1676  notMappableVariablesNames.push_back(mappedVar);
1677  }
1678  }
1679 
1680  xmlXPathFreeObject(result);
1681  }
1682 
1683  return notMappableVariablesNames;
1684  }
1685 
1687  return UA_DateTime_now();
1688  }
1689 
1691  return this->mappedServer;
1692  }
1693 
1694  UA_StatusCode ua_uaadapter::readLogLevel(UA_Server* /*server*/, const UA_NodeId* /*sessionId*/,
1695  void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext, UA_Boolean /*sourceTimeStamp*/,
1696  const UA_NumericRange* /*range*/, UA_DataValue* dataValue) {
1697  auto adapter = (ua_uaadapter*)nodeContext;
1698  UA_LoggingLevel l = toLoggingLevel(adapter->serverConfig.logLevel);
1699  UA_Variant_setScalarCopy(&dataValue->value, &l, &LoggingLevelType);
1700  dataValue->hasValue = true;
1701  return UA_STATUSCODE_GOOD;
1702  }
1703 
1704  UA_StatusCode ua_uaadapter::writeLogLevel(UA_Server* /*server*/, const UA_NodeId* /*sessionId*/,
1705  void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext, const UA_NumericRange* /*range*/,
1706  const UA_DataValue* data) {
1707  auto adapter = (ua_uaadapter*)nodeContext;
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;
1715  }
1716 
1717 } // namespace ChimeraTK
ChimeraTK::ua_uaadapter::getAllMappedPvSourceNames
vector< string > getAllMappedPvSourceNames()
Definition: ua_adapter.cpp:1646
ChimeraTK::ServerConfig::password
string password
Definition: ua_adapter.h:40
ChimeraTK::ServerConfig::rootFolder
string rootFolder
Definition: ua_adapter.h:37
ChimeraTK::ServerConfig::descriptionFolder
string descriptionFolder
Definition: ua_adapter.h:38
ChimeraTK::ua_uaadapter
This class provide the opcua server and manage the variable mapping.
Definition: ua_adapter.h:100
ChimeraTK::ServerConfig::historyvariables
vector< AdapterPVHistorySetup > historyvariables
Definition: ua_adapter.h:54
ChimeraTK::AdapterHistorySetup::name
string name
Definition: node_historizing.h:46
ChimeraTK::ua_uaadapter::writeLogLevel
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.
Definition: ua_adapter.cpp:1704
ChimeraTK::ua_mapInstantiatedNodes
UA_StatusCode ua_mapInstantiatedNodes(UA_NodeId objectId, UA_NodeId definitionId, void *handle)
Node function and proxy mapping for new nodes.
Definition: ua_map_types.cpp:30
ChimeraTK::ua_uaadapter::existFolder
UA_NodeId existFolder(UA_NodeId basenodeid, const string &folderName)
Check if a folder exist in opcua server.
Definition: ua_adapter.cpp:1576
ChimeraTK::AdapterFolderHistorySetup
Definition: node_historizing.h:35
ChimeraTK::ua_mapped_class::mappedServer
UA_Server * mappedServer
Definition: ua_mapped_class.h:39
ChimeraTK::ua_uaadapter::createFolder
UA_NodeId createFolder(UA_NodeId basenodeid, const string &folderName, const string &description="")
Creates a folder in the given parent node.
Definition: ua_adapter.cpp:1630
ChimeraTK::ServerConfig::allowListFolder
string allowListFolder
Definition: ua_adapter.h:49
UA_LoggingLevel
UA_LoggingLevel
Definition: loggingLevel_type.h:15
ChimeraTK::ServerConfig::UsernamePasswordLogin
UA_Boolean UsernamePasswordLogin
Definition: ua_adapter.h:39
ChimeraTK::ServerConfig::issuerListFolder
string issuerListFolder
Definition: ua_adapter.h:51
ChimeraTK::ua_uaadapter::enrollFolderPathFromString
UA_NodeId enrollFolderPathFromString(const string &path, const string &seperator)
Create folder structure based on the given path.
Definition: ua_adapter.cpp:700
ChimeraTK::ua_mapped_class
This class mapped all inforamtion into the opca server.
Definition: ua_mapped_class.h:33
ChimeraTK::ua_mapped_class::baseNodeId
UA_NodeId baseNodeId
Definition: ua_mapped_class.h:36
ChimeraTK::ua_uaadapter::get_server_config
ServerConfig get_server_config()
get ServerConfig.
Definition: ua_adapter.cpp:658
csa_namespace.h
ChimeraTK::AdapterPVHistorySetup
Definition: node_historizing.h:40
ChimeraTK::ServerConfig::username
string username
Definition: ua_adapter.h:41
ChimeraTK::ServerConfig::blockListFolder
string blockListFolder
Definition: ua_adapter.h:50
ChimeraTK::ServerConfig::historyfolders
vector< AdapterFolderHistorySetup > historyfolders
Definition: ua_adapter.h:53
ChimeraTK::ServerConfig::keyPath
string keyPath
Definition: ua_adapter.h:48
ChimeraTK::FolderInfo
This struct represents a folder in OPCUA with its own node id and with his parent and child node id....
Definition: ua_adapter.h:65
xstr
#define xstr(a)
Definition: ua_adapter.cpp:51
ChimeraTK::ua_uaadapter::applyMapping
void applyMapping(const boost::shared_ptr< ControlSystemPVManager > &csManager)
Read mapping file and apply the contained folders, additional variables and pv mappings.
Definition: ua_adapter.cpp:662
ChimeraTK::ua_processvariable::getOwnNodeId
UA_NodeId getOwnNodeId()
Get node id of this processvariable instance.
Definition: csa_processvariable.cpp:729
xml_file_handler.h
ChimeraTK::add_void_event_type
UA_StatusCode add_void_event_type(UA_Server *server)
Definition: void_type.cpp:203
ChimeraTK::ua_uaadapter::existFolderPath
UA_NodeId existFolderPath(UA_NodeId basenodeid, const vector< string > &folderPath)
Check if a folder path exist in opcua server.
Definition: ua_adapter.cpp:1565
ChimeraTK::ua_uaadapter::explicitVarMapping
void explicitVarMapping(const boost::shared_ptr< ControlSystemPVManager > &csManager)
Read mapping file and apply contained PV mappings.
Definition: ua_adapter.cpp:1052
ChimeraTK::ua_uaadapter::buildFolderStructure
void buildFolderStructure(const boost::shared_ptr< ControlSystemPVManager > &csManager)
Read mapping file and and add contained folders to the server.
Definition: ua_adapter.cpp:801
ChimeraTK::ua_uaadapter::implicitVarMapping
void implicitVarMapping(const std::string &varName, const boost::shared_ptr< ControlSystemPVManager > &csManager)
Start implicit mapping process.
Definition: ua_adapter.cpp:713
ChimeraTK::ServerConfig::unsecure
bool unsecure
Definition: ua_adapter.h:45
void_type.h
ChimeraTK::ua_uaadapter::folder_with_history
vector< string > folder_with_history
Definition: ua_adapter.h:161
ChimeraTK::clear_history
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)
Definition: node_historizing.cpp:218
ua_adapter.h
ChimeraTK::xml_file_handler::parseVariablePath
static std::vector< std::string > parseVariablePath(const std::string &variablePath, const std::string &seperator="/")
This methode splitt a given string bey the given seperators.
Definition: xml_file_handler.cpp:110
ChimeraTK::add_historizing_nodes
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)
Definition: node_historizing.cpp:173
ChimeraTK::ServerConfig::logLevel
UA_LogLevel logLevel
Definition: ua_adapter.h:46
csa_processvariable.h
ChimeraTK::ServerConfig
This struct represents a server config. If the hole config file is prased, all information will be st...
Definition: ua_adapter.h:36
ChimeraTK::ua_uaadapter::workerThread
void workerThread()
Create and start a thread for the opcua server instance.
Definition: ua_adapter.cpp:671
ChimeraTK::ServerConfig::history
vector< AdapterHistorySetup > history
Definition: ua_adapter.h:52
ChimeraTK::xml_file_handler::getNodesByName
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.
Definition: xml_file_handler.cpp:44
ChimeraTK::ua_uaadapter::getSourceTimeStamp
UA_DateTime getSourceTimeStamp()
Return the timestamp of the node.
Definition: ua_adapter.cpp:1686
csManager
boost::shared_ptr< ChimeraTK::ControlSystemPVManager > csManager
Definition: csa_opcua_application.cpp:59
csa_additionalvariable.h
ChimeraTK::AdapterFolderHistorySetup::folder_id
UA_NodeId folder_id
Definition: node_historizing.h:37
ChimeraTK::ua_uaadapter::getOwnNodeId
UA_NodeId getOwnNodeId()
Methode that returns the node id of the instanced class.
Definition: ua_adapter.cpp:1561
UA_LOGGINGLEVEL_INFO
@ UA_LOGGINGLEVEL_INFO
Definition: loggingLevel_type.h:18
ChimeraTK::AdapterPVHistorySetup::variable_historizing
string variable_historizing
Definition: node_historizing.h:41
ChimeraTK::ua_uaadapter::running
bool running
Definition: ua_adapter.h:158
ChimeraTK::FolderInfo::folderNodeId
UA_NodeId folderNodeId
NodeId from the folder from opcua server.
Definition: ua_adapter.h:71
csa_namespace_init
UA_StatusCode csa_namespace_init(UA_Server *server)
Definition: csa_namespace.c:101
ChimeraTK::AdapterHistorySetup::buffer_length
size_t buffer_length
Definition: node_historizing.h:47
ChimeraTK::ServerConfig::applicationName
string applicationName
Definition: ua_adapter.h:42
ChimeraTK::ServerConfig::opcuaPort
uint16_t opcuaPort
Definition: ua_adapter.h:43
ChimeraTK::ua_uaadapter::addAdditionalVariables
void addAdditionalVariables()
Read mapping file and add contained additional variables to the server.
Definition: ua_adapter.cpp:1331
ChimeraTK::ua_uaadapter::readLogLevel
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.
Definition: ua_adapter.cpp:1694
ChimeraTK::FolderInfo::prevFolderNodeId
UA_NodeId prevFolderNodeId
NodeId from the parent folder.
Definition: ua_adapter.h:75
ChimeraTK::ServerConfig::enableSecurity
bool enableSecurity
Definition: ua_adapter.h:44
ChimeraTK::ua_uaadapter::getAllNotMappableVariablesNames
vector< string > getAllNotMappableVariablesNames()
Methode to get all names from all potential VarableNodes from XML-Mappingfile which could not allocat...
Definition: ua_adapter.cpp:1660
ChimeraTK::ua_uaadapter::exclude
vector< string > exclude
Definition: ua_adapter.h:160
generate_open62541CCode.line
line
Definition: generate_open62541CCode.py:126
ChimeraTK::xml_file_handler::getContentFromNode
static std::string getContentFromNode(xmlNode *node)
This methode returns the value between a xml tag.
Definition: xml_file_handler.cpp:131
ChimeraTK::ua_uaadapter::readConfig
void readConfig()
This Methode reads the config-tag form the given <variableMap.xml>.
Definition: ua_adapter.cpp:291
ChimeraTK::ua_uaadapter::getVariables
vector< ua_processvariable * > getVariables()
Methode that returns all <ua_processvariable> of the class.
Definition: ua_adapter.cpp:1426
UASTRING_TO_CPPSTRING
#define UASTRING_TO_CPPSTRING(_p_uastring, _p_cppstring)
Definition: ua_typeconversion.h:53
ChimeraTK::AdapterFolderHistorySetup::folder_historizing
string folder_historizing
Definition: node_historizing.h:36
ChimeraTK::ua_uaadapter::createFolderPath
UA_NodeId createFolderPath(UA_NodeId basenodeid, vector< string > folderPathVector)
Create a path of folders in the given parent node.
Definition: ua_adapter.cpp:1586
ChimeraTK::FolderInfo::folderName
string folderName
Name of the folder.
Definition: ua_adapter.h:69
ChimeraTK::AdapterHistorySetup::entries_per_response
size_t entries_per_response
Definition: node_historizing.h:48
ChimeraTK::ua_uaadapter::fillBuildInfo
void fillBuildInfo(UA_ServerConfig *config) const
Fill server build information.
Definition: ua_adapter.cpp:115
ChimeraTK::ua_mapped_class::ownedNodes
nodePairList ownedNodes
Definition: ua_mapped_class.h:35
ChimeraTK::AdapterPVHistorySetup::variable_id
UA_NodeId variable_id
Definition: node_historizing.h:42
ChimeraTK::ua_uaadapter::~ua_uaadapter
virtual ~ua_uaadapter()
Destrructor of the class.
Definition: ua_adapter.cpp:65
loggingLevel_type.h
ChimeraTK::ua_additionalvariable
This class represent a additional variable from <variableMap.xml> in the information model of a OPC U...
Definition: csa_additionalvariable.h:36
ua_map_types.h
ChimeraTK::ua_uaadapter::deepCopyHierarchicalLayer
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.
Definition: ua_adapter.cpp:734
ChimeraTK::ua_uaadapter::getMappedServer
UA_Server * getMappedServer()
Return the OPC UA Server instance.
Definition: ua_adapter.cpp:1690
ChimeraTK
Definition: csa_additionalvariable.h:28
ChimeraTK::AdapterHistorySetup
Definition: node_historizing.h:45
ChimeraTK::xml_file_handler::getAttributeValueFromNode
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.
Definition: xml_file_handler.cpp:122
ChimeraTK::AdapterHistorySetup::interval
size_t interval
Definition: node_historizing.h:49
UA_STRING_TO_CPPSTRING_COPY
#define UA_STRING_TO_CPPSTRING_COPY(_p_uastring, _p_cppstring)
Definition: ua_typeconversion.h:28
generate_open62541CCode.logger
logger
Definition: generate_open62541CCode.py:29
ChimeraTK::ServerConfig::certPath
string certPath
Definition: ua_adapter.h:47
ChimeraTK::ua_processvariable
This class represent a processvariable of the controlsystemadapter in the information model of a OPC ...
Definition: csa_processvariable.h:49