ChimeraTK-ControlSystemAdapter-OPCUAAdapter  04.00.01
csa_opcua_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) 2023 Andreas Ebner <Andreas.Ebner@iosb.fraunhofer.de>
20  */
21 
22 extern "C" {
23 #include "unistd.h"
24 }
25 
26 #include "csa_opcua_adapter.h"
27 #include "csa_processvariable.h"
28 #include "ua_adapter.h"
29 #include "void_type.h"
30 
31 #include <iostream>
32 #include <utility>
33 
34 namespace ChimeraTK {
35  csa_opcua_adapter::csa_opcua_adapter(boost::shared_ptr<ControlSystemPVManager> csManager, string configFile) {
36  this->csManager = std::move(csManager);
37 
38  // Create new server adapter
39  adapter = std::make_shared<ua_uaadapter>(std::move(configFile));
40  UA_LOG_INFO(this->getLogger(), UA_LOGCATEGORY_USERLAND, "Start the mapping of %s", configFile.c_str());
41  UA_LOG_INFO(this->getLogger(), UA_LOGCATEGORY_USERLAND, "Create the adapter");
42 
43  // Pre-Processing - get list of PV's to skip excluding
44  vector<string> mappedPvSources = adapter->getAllMappedPvSourceNames();
45  if(mappedPvSources.size() > 0) {
46  std::stringstream ss;
47  for(const auto& f : mappedPvSources) {
48  ss << "\n\t\t - " << f;
49  }
50  UA_LOG_INFO(this->getLogger(), UA_LOGCATEGORY_USERLAND, "List of mapped process variables: %s", ss.str().c_str());
51  }
52 
53  vector<ProcessVariable::SharedPtr> allProcessVariables = this->csManager->getAllProcessVariables();
54 
55  // start implicit var mapping
56  bool skip_var = false;
57  for(const ProcessVariable::SharedPtr& oneProcessVariable : allProcessVariables) {
58  std::type_info const& valueType = oneProcessVariable->getValueType();
59  if(valueType == typeid(Void)) {
60  /*skip_var = true;*/
61  UA_LOG_DEBUG(this->getLogger(), UA_LOGCATEGORY_USERLAND, "Skip Variable: Variable: %s has a void type",
62  oneProcessVariable->getName().c_str());
63  continue;
64  }
65  for(auto e : this->adapter->exclude) {
66  string suffix_1 = "/*", suffix_2 = "*";
67  if(e.rfind(suffix_1) == e.size() - suffix_1.size()) {
68  if(oneProcessVariable->getName().rfind(e.substr(0, e.size() - 1)) == 0) {
69  bool pv_used = false;
70  for(const auto& ele : mappedPvSources) {
71  if(ele == oneProcessVariable->getName().substr(1, oneProcessVariable->getName().size() - 1)) {
72  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
73  "Skip exclude node - Used in pv-mapping (directory match) PV: %s",
74  oneProcessVariable->getName().c_str());
75  pv_used = true;
76  break;
77  }
78  }
79  if(!pv_used) {
80  for(auto folder : this->adapter->folder_with_history) {
81  if(oneProcessVariable->getName().substr(0, folder.size()) == folder) {
82  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
83  "Skip exclude node - Used in folder history setting: %s", oneProcessVariable->getName().c_str());
84  pv_used = true;
85  break;
86  }
87  }
88  }
89  if(!pv_used) {
90  UA_LOG_INFO(this->getLogger(), UA_LOGCATEGORY_USERLAND, "Directory var exclude (/*) from mapping %s",
91  oneProcessVariable->getName().c_str());
92  skip_var = true;
93  }
94  }
95  }
96  else if(e.rfind(suffix_2) == e.size() - suffix_2.size()) {
97  if(oneProcessVariable->getName().rfind(e.substr(0, e.size() - 1), 0) == 0) {
98  bool pv_used = false;
99  for(const auto& ele : mappedPvSources) {
100  if(ele == oneProcessVariable->getName().substr(1, oneProcessVariable->getName().size() - 1)) {
101  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
102  "Skip exclude node - Used in pv-mapping (greedy match) PV: %s",
103  oneProcessVariable->getName().c_str());
104  pv_used = true;
105  break;
106  }
107  }
108  for(auto folder : this->adapter->folder_with_history) {
109  if(oneProcessVariable->getName().substr(1, folder.size() - 1) == folder) {
110  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
111  "Skip exclude node - Used in folder history setting: %s", oneProcessVariable->getName().c_str());
112  pv_used = true;
113  break;
114  }
115  }
116  if(!pv_used) {
117  UA_LOG_INFO(this->getLogger(), UA_LOGCATEGORY_USERLAND, "Greedy var exclude (*) from mapping %s",
118  oneProcessVariable->getName().c_str());
119  skip_var = true;
120  }
121  }
122  }
123  else if(oneProcessVariable->getName() == e) {
124  // check if this node is mapped later
125  if(std::find(mappedPvSources.begin(), mappedPvSources.end(), e.substr(1, e.size() - 1)) !=
126  mappedPvSources.end()) {
127  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
128  "Skip exclude node - Used in pv-mapping (direct match) PV: %s", oneProcessVariable->getName().c_str());
129  }
130  else {
131  UA_LOG_INFO(this->getLogger(), UA_LOGCATEGORY_USERLAND, "Direct exclude var (direct match) from mapping %s",
132  oneProcessVariable->getName().c_str());
133  skip_var = true;
134  }
135  for(auto folder : this->adapter->folder_with_history) {
136  if(oneProcessVariable->getName().substr(1, folder.size() - 1) == folder) {
137  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
138  "Skip exclude node - Used in folder history setting: %s", oneProcessVariable->getName().c_str());
139  skip_var = false;
140  break;
141  }
142  }
143  }
144  }
145  if(!skip_var) {
146  adapter->implicitVarMapping(oneProcessVariable->getName(), this->csManager);
147  }
148  else {
149  // variable is skipped and therefore unused
150  this->unusedVariables.insert(oneProcessVariable->getName());
151  skip_var = false;
152  }
153  }
154 
155  adapter->applyMapping(this->csManager);
156  vector<string> allNotMappedVariables = adapter->getAllNotMappableVariablesNames();
157  if(!allNotMappedVariables.empty()) {
158  std::stringstream ss;
159  for(const string& var : allNotMappedVariables) {
160  ss << "\t" << var << endl;
161  }
162  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
163  "The following VariableNodes cant be mapped, because they are not member in PV-Manager: \n%s",
164  ss.str().c_str());
165  }
166  }
167 
169  this->stop();
170  }
171 
172  boost::shared_ptr<ControlSystemPVManager> const& csa_opcua_adapter::getControlSystemManager() const {
173  return this->csManager;
174  }
175 
176  std::shared_ptr<ua_uaadapter> csa_opcua_adapter::getUAAdapter() {
177  return adapter;
178  }
179 
181  if(!this->adapter_thread.joinable()) {
182  this->adapter_thread = std::thread(&ua_uaadapter::workerThread, this->adapter.get());
183  }
184  // start void observer loop
185  void_observer_data* data = (void_observer_data*)UA_calloc(1, sizeof(void_observer_data));
186  vector<ProcessVariable::SharedPtr> allProcessVariables = csManager->getAllProcessVariables();
187  for(const ProcessVariable::SharedPtr& oneProcessVariable : allProcessVariables) {
188  std::type_info const& valueType = oneProcessVariable->getValueType();
189  if(valueType == typeid(Void)) {
190  // Check if PV is writable - if not assume it is an VoidInput
191  if(!oneProcessVariable->isWriteable()) {
192  data->pvs.insert(data->pvs.end(), oneProcessVariable->getName());
193  UA_LOG_INFO(this->getLogger(), UA_LOGCATEGORY_USERLAND, "Adding variable %s to void thread.",
194  oneProcessVariable->getName().c_str());
195  }
196  else {
197  UA_LOG_WARNING(this->getLogger(), UA_LOGCATEGORY_USERLAND,
198  "Ignoring Void input %s. Void inputs are not yet supported.", oneProcessVariable->getName().c_str());
199  }
200  }
201  }
202  if(!data->pvs.empty()) {
203  auto conf = adapter->get_server_config();
204  data->rootFolder = conf.rootFolder;
205  data->adapter = this;
206  data->mappedServer = adapter->getMappedServer();
207  data->csManager = csManager;
208  this->observer_thread = std::thread(&startVoidObserverThread, data, this->getLogger());
209  this->observer_thread.detach();
210  }
211  else {
212  UA_free(data);
213  }
214  }
215 
217  if(this->adapter_thread.joinable()) {
218  adapter->running = false;
219  this->adapter_thread.join();
220  }
221  if(this->observer_thread.joinable()) {
222  this->observer_thread.join();
223  }
224  }
225 
227  return this->adapter_thread.joinable();
228  }
229  const set<string>& csa_opcua_adapter::getUnusedVariables() const {
230  return unusedVariables;
231  }
232 
233  const UA_Logger* csa_opcua_adapter::getLogger() {
234  return adapter->server_config->logging;
235  }
236 } // namespace ChimeraTK
ChimeraTK::void_observer_data::rootFolder
string rootFolder
Definition: void_type.h:32
csa_opcua_adapter.h
ChimeraTK::csa_opcua_adapter::stop
void stop()
Stop all objects in single threads for this case only the opc ua server.
Definition: csa_opcua_adapter.cpp:216
ChimeraTK::csa_opcua_adapter::getUnusedVariables
const set< string > & getUnusedVariables() const
Definition: csa_opcua_adapter.cpp:229
ChimeraTK::csa_opcua_adapter::getLogger
const UA_Logger * getLogger()
Get the adapter logger.
Definition: csa_opcua_adapter.cpp:233
ChimeraTK::void_observer_data::mappedServer
UA_Server * mappedServer
Definition: void_type.h:31
ChimeraTK::csa_opcua_adapter::isRunning
bool isRunning()
Checks if the opcua server is still running and return the suitable bool value.
Definition: csa_opcua_adapter.cpp:226
void_type.h
ua_adapter.h
csa_processvariable.h
ChimeraTK::csa_opcua_adapter::csa_opcua_adapter
csa_opcua_adapter(boost::shared_ptr< ControlSystemPVManager > csManager, string configFile)
Constructor for ControlSystemAdapter-OPC-UA-Adapter.
Definition: csa_opcua_adapter.cpp:35
ChimeraTK::ua_uaadapter::workerThread
void workerThread()
Create and start a thread for the opcua server instance.
Definition: ua_adapter.cpp:671
ChimeraTK::csa_opcua_adapter::~csa_opcua_adapter
~csa_opcua_adapter()
Destructor to stop the running thread, hence it stops the OPC UA server.
Definition: csa_opcua_adapter.cpp:168
csManager
boost::shared_ptr< ChimeraTK::ControlSystemPVManager > csManager
Definition: csa_opcua_application.cpp:59
ChimeraTK::csa_opcua_adapter::getUAAdapter
std::shared_ptr< ua_uaadapter > getUAAdapter()
Return the uaadapter, hence the OPC UA server.
Definition: csa_opcua_adapter.cpp:176
ChimeraTK::void_observer_data::pvs
vector< std::string > pvs
Definition: void_type.h:34
ChimeraTK::void_observer_data
Definition: void_type.h:29
ChimeraTK::csa_opcua_adapter::start
void start()
Start all objects in single threads for this case only the opc ua server.
Definition: csa_opcua_adapter.cpp:180
ChimeraTK::void_observer_data::adapter
csa_opcua_adapter * adapter
Definition: void_type.h:33
ChimeraTK::startVoidObserverThread
void startVoidObserverThread(void_observer_data *data, const UA_Logger *logger)
Definition: void_type.cpp:168
ChimeraTK::void_observer_data::csManager
boost::shared_ptr< ControlSystemPVManager > csManager
Definition: void_type.h:30
ChimeraTK
Definition: csa_additionalvariable.h:28
ChimeraTK::csa_opcua_adapter::getControlSystemManager
boost::shared_ptr< ControlSystemPVManager > const & getControlSystemManager() const
Return the ControlsystemPVManager of the class.
Definition: csa_opcua_adapter.cpp:172