ChimeraTK-ControlSystemAdapter-OPCUAAdapter  04.00.01
void_type.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) 2023-2024 Fraunhofer IOSB (Author: Florian Düwel)
18  */
19 
20 #include "void_type.h"
21 
22 #include "csa_processvariable.h"
23 #include "open62541/plugin/log_stdout.h"
24 
25 #include <functional>
26 using namespace std;
27 namespace ChimeraTK {
28  static UA_StatusCode fireVoidEvent(void_observer_data* data, const string& pv_name, const UA_Logger* logger) {
29  UA_NodeId outId;
30  UA_NodeId void_event_NodeId = UA_NODEID_STRING(1, const_cast<char*>("VoidEventType"));
31  UA_StatusCode retval = UA_Server_createEvent(data->mappedServer, void_event_NodeId, &outId);
32  if(retval != UA_STATUSCODE_GOOD) {
33  string error_message = "Creation of the void Event failed with StatusCode ";
34  error_message.append(UA_StatusCode_name(retval));
35  throw std::logic_error(error_message);
36  }
37  auto pv = data->csManager->getProcessVariable(pv_name);
38  auto eventValidity = static_cast<UA_Int32>(pv->dataValidity());
39  retval = UA_Server_writeObjectProperty_scalar(data->mappedServer, outId,
40  UA_QUALIFIEDNAME(1, const_cast<char*>("Validity")), &eventValidity, &UA_TYPES[UA_TYPES_INT32]);
41  if(retval != UA_STATUSCODE_GOOD) {
42  string error_message = "Setting void event validity failed with StatusCode ";
43  error_message.append(UA_StatusCode_name(retval));
44  throw std::logic_error(error_message);
45  }
46  UA_String eventSourceName = UA_String_fromChars(pv_name.c_str());
47  retval = UA_Server_writeObjectProperty_scalar(data->mappedServer, outId,
48  UA_QUALIFIEDNAME(0, const_cast<char*>("SourceName")), &eventSourceName, &UA_TYPES[UA_TYPES_STRING]);
49  if(retval != UA_STATUSCODE_GOOD) {
50  string error_message = "Setting void event SourceName failed with StatusCode ";
51  error_message.append(UA_StatusCode_name(retval));
52  throw std::logic_error(error_message);
53  }
54  UA_String_clear(&eventSourceName);
55  UA_LocalizedText eventMessage =
56  UA_LOCALIZEDTEXT(const_cast<char*>("en-US"), const_cast<char*>("ChimeraTK::Void written."));
57  retval = UA_Server_writeObjectProperty_scalar(data->mappedServer, outId,
58  UA_QUALIFIEDNAME(0, const_cast<char*>("Message")), &eventMessage, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
59  if(retval != UA_STATUSCODE_GOOD) {
60  string error_message = "Setting void event Message failed with StatusCode ";
61  error_message.append(UA_StatusCode_name(retval));
62  throw std::logic_error(error_message);
63  }
64  UA_UInt16 eventSeverity = 100;
65  retval = UA_Server_writeObjectProperty_scalar(data->mappedServer, outId,
66  UA_QUALIFIEDNAME(0, const_cast<char*>("Severity")), &eventSeverity, &UA_TYPES[UA_TYPES_UINT16]);
67  if(retval != UA_STATUSCODE_GOOD) {
68  string error_message = "Setting void event Severity failed with StatusCode ";
69  error_message.append(UA_StatusCode_name(retval));
70  throw std::logic_error(error_message);
71  }
72  UA_UInt16_clear(&eventSeverity);
73  UA_NodeId id;
74  UA_NodeId_init(&id);
75  id.namespaceIndex = 1;
76  id.identifierType = UA_NODEIDTYPE_STRING;
77  id.identifier.string = UA_String_fromChars((data->rootFolder + /*"/" +*/ pv_name).c_str());
78  retval = UA_Server_writeObjectProperty_scalar(
79  data->mappedServer, outId, UA_QUALIFIEDNAME(1, const_cast<char*>("cs_path")), &id, &UA_TYPES[UA_TYPES_NODEID]);
80  if(retval != UA_STATUSCODE_GOOD) {
81  string error_message = "Setting void event cs_path failed with StatusCode ";
82  error_message.append(UA_StatusCode_name(retval));
83  throw std::logic_error(error_message);
84  }
85  UA_NodeId_clear(&id);
86  UA_String eventDescription = UA_String_fromChars(pv->getDescription().c_str());
87  const UA_String null_str = UA_STRING_NULL;
88  if(UA_String_equal(&eventDescription, &null_str) != true) {
89  retval = UA_Server_writeObjectProperty_scalar(data->mappedServer, outId,
90  UA_QUALIFIEDNAME(1, const_cast<char*>("Description")), &eventDescription, &UA_TYPES[UA_TYPES_STRING]);
91  if(retval != UA_STATUSCODE_GOOD) {
92  string error_message = "Setting void event description failed with StatusCode ";
93  error_message.append(UA_StatusCode_name(retval));
94  throw std::logic_error(error_message);
95  }
96  }
97  UA_String_clear(&eventDescription);
98  UA_String eventType = UA_String_fromChars("Void");
99  retval = UA_Server_writeObjectProperty_scalar(data->mappedServer, outId,
100  UA_QUALIFIEDNAME(1, const_cast<char*>("Type")), &eventType, &UA_TYPES[UA_TYPES_STRING]);
101  if(retval != UA_STATUSCODE_GOOD) {
102  string error_message = "Setting void event Type failed with StatusCode ";
103  error_message.append(UA_StatusCode_name(retval));
104  throw std::logic_error(error_message);
105  }
106  UA_String_clear(&eventType);
107  UA_DateTime eventTime = UA_DateTime_now();
108  retval = UA_Server_writeObjectProperty_scalar(data->mappedServer, outId,
109  UA_QUALIFIEDNAME(0, const_cast<char*>("Time")), &eventTime, &UA_TYPES[UA_TYPES_DATETIME]);
110  if(retval != UA_STATUSCODE_GOOD) {
111  string error_message = "Setting void event Time failed with StatusCode ";
112  error_message.append(UA_StatusCode_name(retval));
113  throw std::logic_error(error_message);
114  }
115  UA_DateTime_clear(&eventTime);
116 
117  retval = UA_Server_triggerEvent(data->mappedServer, outId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nullptr, UA_TRUE);
118  if(retval != UA_STATUSCODE_GOOD) {
119  string error_message = "Triggering void event failed with StatusCode ";
120  error_message.append(UA_StatusCode_name(retval));
121  throw std::logic_error(error_message);
122  }
123  UA_LOG_DEBUG(logger, UA_LOGCATEGORY_USERLAND, "Fire Void Event for PV with ID %s", pv_name.c_str());
124  return retval;
125  }
126 
127  static void updateLoop(void_observer_data* data, const UA_Logger* logger) {
128  try {
129  ReadAnyGroup group;
130  std::map<TransferElementID, std::string> idToNameMap;
131  for(auto pv : data->pvs) {
132  group.add(data->csManager->getProcessArray<Void>(pv));
133  idToNameMap[data->csManager->getProcessArray<Void>(pv)->getId()] = pv;
134  //}
135  }
136  /*vector<ProcessVariable::SharedPtr> allProcessVariables = data->csManager->getAllProcessVariables();
137  for(const ProcessVariable::SharedPtr& oneProcessVariable : allProcessVariables) {
138  std::type_info const& valueType = oneProcessVariable->getValueType();
139  if(valueType == typeid(Void)) {
140  // Check if PV is writable - if not assume it is an VoidInput
141  if(!oneProcessVariable->isWriteable()) {
142  group.add(data->csManager->getProcessArray<Void>(oneProcessVariable->getName()));
143  idToNameMap[data->csManager->getProcessArray<Void>(oneProcessVariable->getName())->getId()] =
144  oneProcessVariable->getName();
145  }
146  else {
147  UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
148  "Ignoring Void input %s. Void inputs are not yet supported.", oneProcessVariable->getName().c_str());
149  }
150  }
151  }*/
152  group.finalise();
153 
154  while(data->adapter->isRunning()) {
155  // wait for next event
156  auto id = group.readAny();
157  fireVoidEvent(data, idToNameMap.at(id), logger);
158  }
159  UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Void observer thread terminated.");
160  UA_free(data);
161  }
162  catch(boost::thread_interrupted&) {
163  UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Void observer thread terminated.");
164  UA_free(data);
165  }
166  }
167 
168  void startVoidObserverThread(void_observer_data* data, const UA_Logger* logger) {
169  updateLoop(data, logger);
170  }
171 
172  /*
173  * declare the eventtype for void nodes and add it to the server
174  * */
175  UA_StatusCode addEventProperty(UA_Server* server, UA_NodeId eventNodeId, char* property_name, UA_NodeId dtype) {
176  UA_NodeId propertyNodeId;
177  UA_VariableAttributes vattr = UA_VariableAttributes_default;
178  vattr.dataType = dtype;
179  vattr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en-us"), property_name);
180  vattr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
181  UA_StatusCode retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, eventNodeId,
182  UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_QUALIFIEDNAME(1, property_name),
183  UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), vattr, nullptr, &propertyNodeId);
184  if(retval != UA_STATUSCODE_GOOD) {
185  string error_message = "Failed to add the property ";
186  error_message.append(property_name);
187  error_message.append(" with StatusCode ");
188  error_message.append(UA_StatusCode_name(retval));
189  throw std::logic_error(error_message);
190  }
191  retval = UA_Server_addReference(server, propertyNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
192  UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
193  if(retval != UA_STATUSCODE_GOOD) {
194  string error_message = "Failed to set the reference to property ";
195  error_message.append(property_name);
196  error_message.append(" mandatory with StatusCode ");
197  error_message.append(UA_StatusCode_name(retval));
198  throw std::logic_error(error_message);
199  }
200  return retval;
201  }
202 
203  UA_StatusCode add_void_event_type(UA_Server* server) {
204  UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
205  attr.displayName = UA_LOCALIZEDTEXT(const_cast<char*>("en-US"), const_cast<char*>("VoidEventType"));
206  attr.description = UA_LOCALIZEDTEXT(const_cast<char*>("en-US"), const_cast<char*>("The EventType for void nodes"));
207  UA_NodeId void_event_NodeId = UA_NODEID_STRING(1, const_cast<char*>("VoidEventType"));
208  UA_StatusCode retval = UA_Server_addObjectTypeNode(server, void_event_NodeId,
209  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
210  UA_QUALIFIEDNAME(1, const_cast<char*>("VoidEventType")), attr, nullptr, nullptr);
211  if(retval != UA_STATUSCODE_GOOD) {
212  string error_message = "Failed to add the VoidEventType with StatusCode ";
213  error_message.append(UA_StatusCode_name(retval));
214  throw std::logic_error(error_message);
215  }
216  retval |=
217  addEventProperty(server, void_event_NodeId, const_cast<char*>("cs_path"), UA_TYPES[UA_TYPES_NODEID].typeId);
218  retval |=
219  addEventProperty(server, void_event_NodeId, const_cast<char*>("Description"), UA_TYPES[UA_TYPES_STRING].typeId);
220  retval |= addEventProperty(server, void_event_NodeId, const_cast<char*>("Type"), UA_TYPES[UA_TYPES_STRING].typeId);
221  retval |=
222  addEventProperty(server, void_event_NodeId, const_cast<char*>("Validity"), UA_TYPES[UA_TYPES_INT32].typeId);
223  return retval;
224  }
225 } // namespace ChimeraTK
ChimeraTK::addEventProperty
UA_StatusCode addEventProperty(UA_Server *server, UA_NodeId eventNodeId, char *property_name, UA_NodeId dtype)
Definition: void_type.cpp:175
ChimeraTK::add_void_event_type
UA_StatusCode add_void_event_type(UA_Server *server)
Definition: void_type.cpp:203
void_type.h
csa_processvariable.h
ChimeraTK::void_observer_data
Definition: void_type.h:29
ChimeraTK::startVoidObserverThread
void startVoidObserverThread(void_observer_data *data, const UA_Logger *logger)
Definition: void_type.cpp:168
generate_open62541CCode.id
id
Definition: generate_open62541CCode.py:127
ChimeraTK
Definition: csa_additionalvariable.h:28
generate_open62541CCode.logger
logger
Definition: generate_open62541CCode.py:29