ChimeraTK-ControlSystemAdapter-OPCUAAdapter 04.00.05
Loading...
Searching...
No Matches
csa_processvariable.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
22#include "csa_processvariable.h"
23
24#include "csa_config.h"
25
26extern "C" {
27#include "csa_namespace.h"
28}
29
30#include "open62541/plugin/log_stdout.h"
31#include "ua_map_types.h"
32
33#include <iostream>
34#include <utility>
35#include <vector>
36namespace ChimeraTK {
37 ua_processvariable::ua_processvariable(UA_Server* server, UA_NodeId basenodeid, const string& namePV,
38 boost::shared_ptr<ControlSystemPVManager> csManager, const UA_Logger* logger, string overwriteNodeString)
39 : namePV(namePV), nameNew(namePV), csManager(std::move(csManager)),
40 nodeStringIdOverwrite(std::move(overwriteNodeString)), array(false), ua_mapped_class(server, basenodeid) {
41 this->mapSelfToNamespace(logger);
42 }
43
45 //* Our ua_mapped_class destructor will take care of deleting our opcua footprint as long as all variables are mapped
46 // in this->ownedNodes
47 UA_NodeId_clear(&this->ownNodeId);
48 }
49
51 const UA_NodeId* /*sessionId*/, void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext,
52 UA_Boolean includeSourceTimeStamp, const UA_NumericRange* /*range*/, UA_DataValue* value) {
53 auto* thisObj = static_cast<ua_processvariable*>(nodeContext);
54
55 UA_String ua_val = UA_String_fromChars((char*)thisObj->getName().c_str());
56 UA_Variant_setScalarCopy(&value->value, &ua_val, &UA_TYPES[UA_TYPES_STRING]);
57 UA_String_clear(&ua_val);
58 value->hasValue = true;
59 if(includeSourceTimeStamp) {
60 value->sourceTimestamp = thisObj->getSourceTimeStamp();
61 value->hasSourceTimestamp = true;
62 }
63 return UA_STATUSCODE_GOOD;
64 }
65
67 return this->namePV;
68 }
69
70 // EngineeringUnit
72 const UA_NodeId* /*sessionId*/, void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext,
73 const UA_NumericRange* /*range*/, const UA_DataValue* value) {
74 auto* theClass = static_cast<ua_processvariable*>(nodeContext);
75 std::string cpps;
76 char* s;
77 s = (char*)malloc(((UA_String) * ((UA_String*)value->value.data)).length + 1);
78 memset(s, 0, ((UA_String) * ((UA_String*)value->value.data)).length + 1);
79 memcpy(s, ((UA_String) * ((UA_String*)value->value.data)).data,
80 ((UA_String) * ((UA_String*)value->value.data)).length);
81 cpps.assign(s, ((UA_String) * ((UA_String*)value->value.data)).length);
82 free(s);
83 theClass->setEngineeringUnit(cpps);
84 return UA_STATUSCODE_GOOD;
85 }
86
87 void ua_processvariable::setEngineeringUnit(string engineeringUnit) {
88 this->engineeringUnit = std::move(engineeringUnit);
89 }
90
92 const UA_NodeId* /*sessionId*/, void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext,
93 UA_Boolean includeSourceTimeStamp, const UA_NumericRange* /*range*/, UA_DataValue* value) {
94 auto* thisObj = static_cast<ua_processvariable*>(nodeContext);
95
96 UA_String ua_val = UA_String_fromChars((char*)thisObj->getEngineeringUnit().c_str());
97 UA_Variant_setScalarCopy(&value->value, &ua_val, &UA_TYPES[UA_TYPES_STRING]);
98 UA_String_clear(&ua_val);
99 value->hasValue = true;
100 if(includeSourceTimeStamp) {
101 value->sourceTimestamp = thisObj->getSourceTimeStamp();
102 value->hasSourceTimestamp = true;
103 }
104 return UA_STATUSCODE_GOOD;
105 }
106
108 if(!this->engineeringUnit.empty()) {
109 return this->engineeringUnit;
110 }
111 else {
112 this->engineeringUnit = this->csManager->getProcessVariable(this->namePV)->getUnit();
113 return this->engineeringUnit;
114 }
115 }
116
117 // Description
119 const UA_NodeId* /*sessionId*/, void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext,
120 const UA_NumericRange* /*range*/, const UA_DataValue* value) {
121 auto* theClass = static_cast<ua_processvariable*>(nodeContext);
122 std::string cpps;
123 char* s;
124 s = (char*)malloc(((UA_String) * ((UA_String*)value->value.data)).length + 1);
125 memset(s, 0, ((UA_String) * ((UA_String*)value->value.data)).length + 1);
126 memcpy(s, ((UA_String) * ((UA_String*)value->value.data)).data,
127 ((UA_String) * ((UA_String*)value->value.data)).length);
128 cpps.assign(s, ((UA_String) * ((UA_String*)value->value.data)).length);
129 free(s);
130 theClass->setDescription(cpps);
131 return UA_STATUSCODE_GOOD;
132 }
133
134 void ua_processvariable::setDescription(string description) {
135 this->description = std::move(description);
136 }
137
139 const UA_NodeId* /*sessionId*/, void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext,
140 UA_Boolean includeSourceTimeStamp, const UA_NumericRange* /*range*/, UA_DataValue* value) {
141 auto* thisObj = static_cast<ua_processvariable*>(nodeContext);
142
143 UA_String ua_val = UA_String_fromChars((char*)thisObj->getDescription().c_str());
144 UA_Variant_setScalarCopy(&value->value, &ua_val, &UA_TYPES[UA_TYPES_STRING]);
145 UA_String_clear(&ua_val);
146 value->hasValue = true;
147 if(includeSourceTimeStamp) {
148 value->sourceTimestamp = thisObj->getSourceTimeStamp();
149 value->hasSourceTimestamp = true;
150 }
151 return UA_STATUSCODE_GOOD;
152 }
153
155 const UA_NodeId* /*sessionId*/, void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext,
156 UA_Boolean includeSourceTimeStamp, const UA_NumericRange* /*range*/, UA_DataValue* value) {
157 auto* thisObj = static_cast<ua_processvariable*>(nodeContext);
158 DataValidity dv = thisObj->csManager->getProcessVariable(thisObj->namePV)->dataValidity();
159 UA_Int32 validity;
160 switch(dv) {
161 case DataValidity::ok:
162 validity = 1;
163 break;
164 case DataValidity::faulty:
165 validity = 0;
166 break;
167 default:
168 validity = -1;
169 }
170 UA_Variant_setScalarCopy(&value->value, &validity, &UA_TYPES[UA_TYPES_INT32]);
171 value->hasValue = true;
172 if(includeSourceTimeStamp) {
173 value->sourceTimestamp = thisObj->getSourceTimeStamp();
174 value->hasSourceTimestamp = true;
175 }
176 return UA_STATUSCODE_GOOD;
177 }
178
180 if(!this->description.empty()) {
181 return this->description;
182 }
183 else {
184 this->description = this->csManager->getProcessVariable(this->namePV)->getDescription();
185 return this->description;
186 }
187 }
188
189 // Type
191 const UA_NodeId* /*sessionId*/, void* /*sessionContext*/, const UA_NodeId* /*nodeId*/, void* nodeContext,
192 UA_Boolean includeSourceTimeStamp, const UA_NumericRange* /*range*/, UA_DataValue* value) {
193 auto* thisObj = static_cast<ua_processvariable*>(nodeContext);
194
195 UA_String ua_val = UA_String_fromChars((char*)thisObj->getType().c_str());
196 UA_Variant_setScalarCopy(&value->value, &ua_val, &UA_TYPES[UA_TYPES_STRING]);
197 UA_String_clear(&ua_val);
198 value->hasValue = true;
199 if(includeSourceTimeStamp) {
200 value->sourceTimestamp = thisObj->getSourceTimeStamp();
201 value->hasSourceTimestamp = true;
202 }
203 return UA_STATUSCODE_GOOD;
204 }
205
207 // Note: typeid().name() may return the name; may as well return the symbol's name from the binary though...
208 std::type_info const& valueType = this->csManager->getProcessVariable(this->namePV)->getValueType();
209 if(valueType == typeid(int8_t))
210 return "int8_t";
211 else if(valueType == typeid(uint8_t))
212 return "uint8_t";
213 else if(valueType == typeid(int16_t))
214 return "int16_t";
215 else if(valueType == typeid(uint16_t))
216 return "uint16_t";
217 else if(valueType == typeid(int32_t))
218 return "int32_t";
219 else if(valueType == typeid(uint32_t))
220 return "uint32_t";
221 else if(valueType == typeid(int64_t))
222 return "int64_t";
223 else if(valueType == typeid(uint64_t))
224 return "uint64_t";
225 else if(valueType == typeid(float))
226 return "float";
227 else if(valueType == typeid(double))
228 return "double";
229 else if(valueType == typeid(string))
230 return "string";
231 else if(valueType == typeid(Boolean))
232 return "Boolean";
233 else if(valueType == typeid(Void))
234 return "Void";
235 else
236 return "Unsupported type";
237 }
238
239 UA_StatusCode ua_processvariable::mapSelfToNamespace(const UA_Logger* logger) {
240 UA_StatusCode retval = UA_STATUSCODE_GOOD;
241 UA_NodeId createdNodeId = UA_NODEID_NULL;
242 if(!nodeStringIdOverwrite.empty()) this->nameNew = nodeStringIdOverwrite;
243
244 if(UA_NodeId_equal(&this->baseNodeId, &createdNodeId))
245 return UA_STATUSCODE_GOOD; // initializer should have set this!
246
247 UA_LocalizedText description;
248 description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"),
249 const_cast<char*>(this->csManager->getProcessVariable(this->namePV)->getDescription().c_str()));
250
251 // Create our toplevel instance
252 UA_VariableAttributes attr;
253 UA_VariableAttributes_init(&attr);
254 attr = UA_VariableAttributes_default;
255 // Allow negative sampling intervals -> used by Labview and handled by open62541-interface 1.3.3.-2
256 attr.minimumSamplingInterval = -1.;
257 attr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(this->nameNew.c_str()));
258 attr.description = description;
259 attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
260
261 if(this->csManager->getProcessVariable(this->namePV)->isWriteable()) {
262 attr.writeMask = UA_ACCESSLEVELMASK_READ ^ UA_ACCESSLEVELMASK_WRITE;
263 attr.accessLevel = UA_ACCESSLEVELMASK_READ ^ UA_ACCESSLEVELMASK_WRITE;
264 }
265
266 // Append the app and application folder names to the string nodeId, this is needed
267 // because the mapping allows one pv linked in more folder -> only pv path is not unique
268 string baseNodeIdName;
269 if(this->baseNodeId.identifierType == UA_NODEIDTYPE_STRING) {
270 UA_STRING_TO_CPPSTRING_COPY(&this->baseNodeId.identifier.string, &baseNodeIdName)
271 }
272 if(!baseNodeIdName.empty()) {
273 baseNodeIdName.resize(baseNodeIdName.size() - 3);
274 }
275
276 // check if the nodeId is used by another mapping and find next free NodeId
277 UA_NodeId result;
278 if(UA_Server_readDataType(this->mappedServer,
279 UA_NODEID_STRING(1, const_cast<char*>((baseNodeIdName + "/" + this->nameNew).c_str())),
280 &result) == UA_STATUSCODE_GOOD) {
281 return UA_STATUSCODE_BADNODEIDEXISTS;
282 }
283 retval = UA_Server_addVariableNode(this->mappedServer,
284 UA_NODEID_STRING(1, const_cast<char*>((baseNodeIdName + "/" + this->nameNew).c_str())), this->baseNodeId,
285 UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, const_cast<char*>(this->nameNew.c_str())),
286 UA_NODEID_NUMERIC(CSA_NSID, 1001), attr, (void*)this, &createdNodeId);
287 UA_NodeId_copy(&createdNodeId, &this->ownNodeId);
288 ua_mapInstantiatedNodes(createdNodeId, UA_NODEID_NUMERIC(CSA_NSID, 1001), &this->ownedNodes);
289
290 /* Use a datasource map to map any local getter/setter functions to OPC UA variables nodes */
291 UA_DataSource_Map mapDs;
292 this->addPVChildNodes(createdNodeId, baseNodeIdName, mapDs);
293
294 UA_Variant arrayDimensions;
295 UA_Variant_init(&arrayDimensions);
296
297 std::type_info const& valueType = this->csManager->getProcessVariable(this->namePV)->getValueType();
298
300 mapElem.typeTemplateId = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE);
301 mapElem.description = description;
302 // Read is possible for all elements
303 UA_Variant uaArrayDimensions;
304 UA_UInt32 arrayDims[1];
305
306 if(valueType == typeid(int8_t)) {
307 arrayDims[0] = typeSpecificSetup<int8_t>(mapElem, createdNodeId);
308 }
309 else if(valueType == typeid(uint8_t)) {
310 arrayDims[0] = typeSpecificSetup<uint8_t>(mapElem, createdNodeId);
311 }
312 else if(valueType == typeid(int16_t)) {
313 arrayDims[0] = typeSpecificSetup<int16_t>(mapElem, createdNodeId);
314 }
315 else if(valueType == typeid(uint16_t)) {
316 arrayDims[0] = typeSpecificSetup<uint16_t>(mapElem, createdNodeId);
317 }
318 else if(valueType == typeid(int32_t)) {
319 arrayDims[0] = typeSpecificSetup<int32_t>(mapElem, createdNodeId);
320 }
321 else if(valueType == typeid(uint32_t)) {
322 arrayDims[0] = typeSpecificSetup<uint32_t>(mapElem, createdNodeId);
323 }
324 else if(valueType == typeid(int64_t)) {
325 arrayDims[0] = typeSpecificSetup<int64_t>(mapElem, createdNodeId);
326 }
327 else if(valueType == typeid(uint64_t)) {
328 arrayDims[0] = typeSpecificSetup<uint64_t>(mapElem, createdNodeId);
329 }
330 else if(valueType == typeid(float)) {
331 arrayDims[0] = typeSpecificSetup<float>(mapElem, createdNodeId);
332 }
333 else if(valueType == typeid(double)) {
334 arrayDims[0] = typeSpecificSetup<double>(mapElem, createdNodeId);
335 }
336 else if(valueType == typeid(string)) {
337 arrayDims[0] = typeSpecificSetup<std::string>(mapElem, createdNodeId);
338 }
339 else if(valueType == typeid(Boolean)) {
340 arrayDims[0] = typeSpecificSetup<Boolean>(mapElem, createdNodeId);
341 }
342 else if(valueType != typeid(Void)) {
343 int status;
344 auto* demangledName = abi::__cxa_demangle(valueType.name(), nullptr, nullptr, &status);
345
346 if(status == 0) {
347 UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "Cannot proxy unknown type %s for variable %s", demangledName,
348 this->namePV.c_str());
349 free(demangledName);
350 }
351 else {
352 UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND,
353 "Cannot proxy unknown type %s (demangling failed) for variable %s", valueType.name(), this->namePV.c_str());
354 }
355 }
356
357 if(!this->csManager->getProcessVariable(this->namePV)->isWriteable()) {
358 mapElem.dataSource.write = nullptr;
359 }
360 if(this->array) {
361 UA_Server_writeValueRank(this->mappedServer, createdNodeId, UA_VALUERANK_ONE_DIMENSION);
362 UA_Variant_setArray(&uaArrayDimensions, arrayDims, 1, &UA_TYPES[UA_TYPES_UINT32]);
363 UA_Server_writeArrayDimensions(this->mappedServer, createdNodeId, uaArrayDimensions);
364 }
365 else {
366 UA_Server_writeValueRank(this->mappedServer, createdNodeId, UA_VALUERANK_SCALAR);
367 }
368
369 // add variable data source map element to the list
370 UA_NodeId_copy(&createdNodeId, &mapElem.concreteNodeId);
371 mapDs.push_back(mapElem);
372
373 UA_NodeId nodeIdVariableType = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE);
374 NODE_PAIR_PUSH(this->ownedNodes, nodeIdVariableType, createdNodeId)
375
376 for(auto i : mapDs) {
377 retval |= UA_Server_setVariableNode_dataSource(this->mappedServer, i.concreteNodeId, i.dataSource);
378 UA_NodeId_clear(&i.concreteNodeId);
379 UA_NodeId_clear(&i.typeTemplateId);
380 }
381 UA_NodeId_clear(&createdNodeId);
382 return retval;
383 }
384
385 UA_StatusCode ua_processvariable::addPVChildNodes(
386 UA_NodeId pvNodeId, const string& baseNodePath, UA_DataSource_Map& map) {
387 UA_NodeId createdNodeId = UA_NODEID_NULL;
388 UA_VariableAttributes attr;
389 UA_StatusCode addResult;
390
391 // Adding the 'name' node to the PV
392 UA_DataSource_Map_Element mapElemName;
393 mapElemName.typeTemplateId = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_NAME);
394 mapElemName.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
395 mapElemName.dataSource.read = ua_readproxy_ua_processvariable_getName;
396 mapElemName.dataSource.write = nullptr;
397
398 UA_VariableAttributes_init(&attr);
399 attr = UA_VariableAttributes_default;
400 attr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("Name"));
401 attr.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
402 attr.valueRank = UA_VALUERANK_SCALAR;
403 attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
404 UA_String opcua_node_variable_t_ns_2_i_6004_variant_DataContents = UA_STRING_ALLOC(const_cast<char*>(""));
405 UA_Variant_setScalar(
406 &attr.value, &opcua_node_variable_t_ns_2_i_6004_variant_DataContents, &UA_TYPES[UA_TYPES_STRING]);
407 addResult = UA_Server_addVariableNode(this->mappedServer,
408 UA_NODEID_STRING(1, const_cast<char*>((baseNodePath + "/" + this->nameNew + "/Name").c_str())), pvNodeId,
409 UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, const_cast<char*>("Name")),
410 UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, this, &createdNodeId);
411 if(addResult == UA_STATUSCODE_GOOD) {
412 UA_NodeId nameVariable = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_NAME);
413 NODE_PAIR_PUSH(this->ownedNodes, nameVariable, createdNodeId)
414 UA_NodeId_copy(&createdNodeId, &mapElemName.concreteNodeId);
415 map.push_back(mapElemName);
416 }
417 else {
418 UA_NodeId_clear(&createdNodeId);
419 return addResult;
420 }
421 UA_NodeId_clear(&createdNodeId);
422 createdNodeId = UA_NODEID_NULL;
423
424 // Adding the 'Description' node to the PV
425 UA_DataSource_Map_Element mapElemDesc;
426 mapElemDesc.typeTemplateId = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_DESC);
427 mapElemDesc.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
428 mapElemDesc.dataSource.read = ua_readproxy_ua_processvariable_getDescription;
429 mapElemDesc.dataSource.write = nullptr;
430
431 UA_VariableAttributes_init(&attr);
432 attr = UA_VariableAttributes_default;
433 attr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("Description"));
434 attr.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
435 attr.accessLevel = UA_ACCESSLEVELMASK_READ;
436 attr.userAccessLevel = UA_ACCESSLEVELMASK_READ;
437 attr.valueRank = UA_VALUERANK_SCALAR;
438 attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
439 UA_String opcua_node_variable_t_ns_2_i_6001_variant_DataContents = UA_STRING(const_cast<char*>(""));
440 UA_Variant_setScalar(
441 &attr.value, &opcua_node_variable_t_ns_2_i_6001_variant_DataContents, &UA_TYPES[UA_TYPES_STRING]);
442 addResult = UA_Server_addVariableNode(this->mappedServer,
443 UA_NODEID_STRING(1, const_cast<char*>((baseNodePath + "/" + this->nameNew + "/Description").c_str())), pvNodeId,
444 UA_NODEID_NUMERIC(0, 47), UA_QUALIFIEDNAME(1, const_cast<char*>("Description")), UA_NODEID_NUMERIC(0, 63), attr,
445 this, &createdNodeId);
446 if(addResult == UA_STATUSCODE_GOOD) {
447 UA_NodeId descVariable = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_DESC);
448 NODE_PAIR_PUSH(this->ownedNodes, descVariable, createdNodeId)
449 UA_NodeId_copy(&createdNodeId, &mapElemDesc.concreteNodeId);
450 map.push_back(mapElemDesc);
451 }
452 else {
453 UA_NodeId_clear(&createdNodeId);
454 return addResult;
455 }
456
457 UA_NodeId_clear(&createdNodeId);
458 createdNodeId = UA_NODEID_NULL;
459
460 // Adding the 'EngineeringUnit' node to the PV
462 mapElemEU.typeTemplateId = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_UNIT);
463 mapElemEU.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
464 mapElemEU.dataSource.read = ua_readproxy_ua_processvariable_getEngineeringUnit;
465 mapElemEU.dataSource.write = nullptr;
466 UA_VariableAttributes_init(&attr);
467 attr = UA_VariableAttributes_default;
468 attr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("EngineeringUnit"));
469 attr.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
470 attr.accessLevel = UA_ACCESSLEVELMASK_READ;
471 attr.userAccessLevel = UA_ACCESSLEVELMASK_READ;
472 attr.valueRank = UA_VALUERANK_SCALAR;
473 attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
474 UA_String defaultEngineeringUnit = UA_STRING(const_cast<char*>(""));
475 UA_Variant_setScalar(&attr.value, &defaultEngineeringUnit, &UA_TYPES[UA_TYPES_STRING]);
476 addResult = UA_Server_addVariableNode(this->mappedServer,
477 UA_NODEID_STRING(1, const_cast<char*>((baseNodePath + "/" + this->nameNew + "/EngineeringUnit").c_str())),
478 pvNodeId, UA_NODEID_NUMERIC(0, 47), UA_QUALIFIEDNAME(1, const_cast<char*>("EngineeringUnit")),
479 UA_NODEID_NUMERIC(0, 63), attr, this, &createdNodeId);
480 if(addResult == UA_STATUSCODE_GOOD) {
481 UA_NodeId engineeringunitVariable = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_UNIT);
482 NODE_PAIR_PUSH(this->ownedNodes, engineeringunitVariable, createdNodeId)
483 UA_NodeId_copy(&createdNodeId, &mapElemEU.concreteNodeId);
484 map.push_back(mapElemEU);
485 }
486 else {
487 UA_NodeId_clear(&createdNodeId);
488 return addResult;
489 }
490
491 UA_NodeId_clear(&createdNodeId);
492 createdNodeId = UA_NODEID_NULL;
493
494 // Adding the 'Type' node to the PV
495 UA_DataSource_Map_Element mapElemType;
496 mapElemType.typeTemplateId = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_TYPE);
497 mapElemType.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
498 mapElemType.dataSource.read = ua_readproxy_ua_processvariable_getType;
499 mapElemType.dataSource.write = nullptr;
500 UA_VariableAttributes_init(&attr);
501 attr = UA_VariableAttributes_default;
502 attr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("Type"));
503 attr.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("Data type used in ChimeraTK"));
504 attr.accessLevel = UA_ACCESSLEVELMASK_READ;
505 attr.userAccessLevel = UA_ACCESSLEVELMASK_READ;
506 attr.valueRank = UA_VALUERANK_SCALAR;
507 attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
508 UA_String opcua_node_variable_t_ns_2_i_6012_variant_DataContents = UA_STRING(const_cast<char*>(""));
509 UA_Variant_setScalar(
510 &attr.value, &opcua_node_variable_t_ns_2_i_6012_variant_DataContents, &UA_TYPES[UA_TYPES_STRING]);
511 UA_NodeId nodeId = UA_NODEID_NUMERIC(2, 6012);
512 UA_NodeId typeDefinition = UA_NODEID_NUMERIC(0, 63);
513 UA_NodeId parentNodeId = UA_NODEID_NUMERIC(2, 1001);
514 addResult = UA_Server_addVariableNode(this->mappedServer,
515 UA_NODEID_STRING(1, const_cast<char*>((baseNodePath + "/" + this->nameNew + "/Type").c_str())), pvNodeId,
516 UA_NODEID_NUMERIC(0, 47), UA_QUALIFIEDNAME(1, const_cast<char*>("Type")), UA_NODEID_NUMERIC(0, 63), attr, this,
517 &createdNodeId);
518 if(addResult == UA_STATUSCODE_GOOD) {
519 UA_NodeId typeVariable = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_TYPE);
520 NODE_PAIR_PUSH(this->ownedNodes, typeVariable, createdNodeId)
521 UA_NodeId_copy(&createdNodeId, &mapElemType.concreteNodeId);
522 map.push_back(mapElemType);
523 }
524 else {
525 UA_NodeId_clear(&createdNodeId);
526 return addResult;
527 }
528
529 UA_NodeId_clear(&createdNodeId);
530 createdNodeId = UA_NODEID_NULL;
531
532 // Adding the Validity node to the PV
533 UA_DataSource_Map_Element mapElemValidity;
534 mapElemValidity.typeTemplateId = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_VALIDITY);
535 mapElemValidity.description = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>(""));
536 mapElemValidity.dataSource.read = ua_readproxy_ua_processvariable_getValidity;
537 mapElemValidity.dataSource.write = nullptr;
538 UA_VariableAttributes_init(&attr);
539 attr = UA_VariableAttributes_default;
540 attr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("Validity"));
541 attr.description =
542 UA_LOCALIZEDTEXT(const_cast<char*>("en_US"), const_cast<char*>("Data validity. 0: faulty, 1: ok"));
543 attr.accessLevel = UA_ACCESSLEVELMASK_READ;
544 attr.userAccessLevel = UA_ACCESSLEVELMASK_READ;
545 attr.valueRank = UA_VALUERANK_SCALAR;
546 attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
547 UA_Int32 defaultValidity = -1;
548 UA_Variant_setScalar(&attr.value, &defaultValidity, &UA_TYPES[UA_TYPES_INT32]);
549 addResult = UA_Server_addVariableNode(this->mappedServer,
550 UA_NODEID_STRING(1, const_cast<char*>((baseNodePath + "/" + this->nameNew + "/Validity").c_str())), pvNodeId,
551 UA_NODEID_NUMERIC(0, 47), UA_QUALIFIEDNAME(1, const_cast<char*>("Validity")), UA_NODEID_NUMERIC(0, 63), attr,
552 this, &createdNodeId);
553 if(addResult == UA_STATUSCODE_GOOD) {
554 UA_NodeId vadilityVariable = UA_NODEID_NUMERIC(CSA_NSID, CSA_NSID_VARIABLE_VALIDITY);
555 NODE_PAIR_PUSH(this->ownedNodes, vadilityVariable, createdNodeId)
556 UA_NodeId_copy(&createdNodeId, &mapElemValidity.concreteNodeId);
557 map.push_back(mapElemValidity);
558 }
559 else {
560 UA_NodeId_clear(&createdNodeId);
561 return addResult;
562 }
563
564 UA_NodeId_clear(&createdNodeId);
565
566 return addResult;
567 }
568
573 auto t = this->csManager->getProcessVariable(this->namePV)->getVersionNumber().getTime();
574 auto microseconds = std::chrono::time_point_cast<std::chrono::microseconds>(t).time_since_epoch().count();
575 // For the initial value from ChimeraTK microseconds will be 0 -> use current time in this case
576 if(microseconds == 0) {
577 microseconds = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now())
578 .time_since_epoch()
579 .count();
580 }
581 return (microseconds * UA_DATETIME_USEC) + UA_DATETIME_UNIX_EPOCH;
582 }
583
585 UA_NodeId outputNode;
586 UA_NodeId_copy(&this->ownNodeId, &outputNode);
587 return outputNode;
588 }
589
590} // namespace ChimeraTK
This class mapped all inforamtion into the opca server.
This class represent a processvariable of the controlsystemadapter in the information model of a OPC ...
static UA_StatusCode ua_readproxy_ua_processvariable_getDescription(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value)
static UA_StatusCode ua_writeproxy_ua_processvariable_setDescription(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *value)
void setEngineeringUnit(string engineeringUnit)
Set engineering unit of processvariable.
string getName()
Get name of processvariable.
string getType()
Get type of processvariable.
UA_DateTime getSourceTimeStamp()
Reimplement the sourcetimestamp for every processvariable.
~ua_processvariable()
Destructor for ua_processvariable.
static UA_StatusCode ua_readproxy_ua_processvariable_getEngineeringUnit(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value)
UA_NodeId getOwnNodeId()
Get node id of this processvariable instance.
string getEngineeringUnit()
Get engineering unit of processvariable.
static UA_StatusCode ua_readproxy_ua_processvariable_getType(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value)
static UA_StatusCode ua_readproxy_ua_processvariable_getValidity(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value)
Get vadility of processvariable.
void setDescription(string description)
Get description unit of processvariable.
ua_processvariable(UA_Server *server, UA_NodeId basenodeid, const string &namePV, boost::shared_ptr< ControlSystemPVManager > csManager, const UA_Logger *logger, string overwriteNodeString="")
Constructor from ua_processvaribale for generic creation.
static UA_StatusCode ua_writeproxy_ua_processvariable_setEngineeringUnit(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *value)
static UA_StatusCode ua_readproxy_ua_processvariable_getName(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value)
string getDescription()
Get description unit of processvariable.
boost::shared_ptr< ChimeraTK::ControlSystemPVManager > csManager
std::list< UA_DataSource_Map_Element > UA_DataSource_Map
struct ChimeraTK::UA_DataSource_Map_Element_t UA_DataSource_Map_Element
UA_StatusCode ua_mapInstantiatedNodes(UA_NodeId objectId, UA_NodeId definitionId, void *handle)
Node function and proxy mapping for new nodes.
#define NODE_PAIR_PUSH(_p_listname, _p_srcId, _p_targetId)
#define UA_STRING_TO_CPPSTRING_COPY(_p_uastring, _p_cppstring)