ChimeraTK-ControlSystemAdapter-OPCUAAdapter 04.00.05
Loading...
Searching...
No Matches
node_historizing.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 "node_historizing.h"
21
22#include "open62541/plugin/log_stdout.h"
23
24namespace ChimeraTK {
25 typedef struct {
26 UA_Server* server;
27 string history;
28 vector<UA_NodeId>* historizing_nodes;
29 vector<string>* historizing_setup;
31
32 UA_StatusCode get_child_variables(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void* handle) {
33 if(isInverse) return UA_STATUSCODE_GOOD;
34 auto* handler = (HandleFolderVariables*)handle;
35 UA_NodeId organizes = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
36 // get variables of nested folders
37 if(UA_NodeId_equal(&referenceTypeId, &organizes)) {
38 UA_Server_forEachChildNodeCall(handler->server, childId, get_child_variables, handler);
39 }
40 UA_NodeClass outNodeClass;
41 UA_NodeClass_init(&outNodeClass);
42 UA_Server_readNodeClass(handler->server, childId, &outNodeClass);
43 if(outNodeClass == UA_NODECLASS_VARIABLE) {
44 UA_NodeId* temp = UA_NodeId_new();
45 UA_NodeId_copy(&childId, temp);
46 handler->historizing_nodes->insert(handler->historizing_nodes->end(), *temp);
47 handler->historizing_setup->insert(handler->historizing_setup->end(), handler->history);
48 }
49 return UA_STATUSCODE_GOOD;
50 }
51
52 void add_folder_historizing(vector<UA_NodeId>* historizing_nodes, vector<string>* historizing_setup,
53 vector<AdapterFolderHistorySetup> historyfolders, UA_Server* mappedServer, UA_ServerConfig* server_config) {
54 for(size_t i = 0; i < historyfolders.size(); i++) {
55 UA_NodeId* temp = UA_NodeId_new();
56 UA_StatusCode retval = UA_Server_readNodeId(mappedServer, historyfolders[i].folder_id, temp);
57 UA_NodeId_clear(temp);
58 if(retval == UA_STATUSCODE_GOOD) {
60 handle.server = mappedServer;
61 handle.history = historyfolders[i].folder_historizing;
62 handle.historizing_nodes = historizing_nodes;
63 handle.historizing_setup = historizing_setup;
64 UA_Server_forEachChildNodeCall(mappedServer, historyfolders[i].folder_id, get_child_variables, &handle);
65 }
66 else {
67 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
68 "Warning! Folder is not mapped in the server. StatusCode: %s ", UA_StatusCode_name(retval));
69 }
70 }
71 }
72
73 void add_variable_historizing(vector<UA_NodeId>* historizing_nodes, vector<string>* historizing_setup,
74 vector<AdapterPVHistorySetup> historyvariables, UA_Server* mappedServer, UA_ServerConfig* server_config) {
75 for(size_t i = 0; i < historyvariables.size(); i++) {
76 UA_NodeId* temp = UA_NodeId_new();
77 UA_NodeId id = historyvariables[i].variable_id;
78 UA_StatusCode retval = UA_Server_readNodeId(mappedServer, id, temp);
79 if(retval == UA_STATUSCODE_GOOD) {
80 UA_String out = UA_STRING_NULL;
81 UA_print(&id, &UA_TYPES[UA_TYPES_NODEID], &out);
82 UA_LOG_DEBUG(
83 server_config->logging, UA_LOGCATEGORY_USERLAND, "Add history for node %.*s ", (int)out.length, out.data);
84 UA_String_clear(&out);
85 historizing_nodes->insert(historizing_nodes->end(), historyvariables[i].variable_id);
86 historizing_setup->insert(historizing_setup->end(), historyvariables[i].variable_historizing);
87 }
88 else {
89 UA_String out = UA_STRING_NULL;
90 UA_print(&id, &UA_TYPES[UA_TYPES_NODEID], &out);
91 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
92 "Warning! Node %.*s has a history configuration but is not mapped to the server. StatusCode: %s ",
93 (int)out.length, out.data, UA_StatusCode_name(retval));
94 UA_String_clear(&out);
95 }
96 UA_NodeId_clear(temp);
97 }
98 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND, "Added %ld nodes to the historizing.",
99 historizing_nodes->size());
100 }
101
102 void set_variable_access_level_historizing(UA_NodeId id, UA_Server* mappedServer) {
103 UA_Byte temp;
104 UA_Byte_init(&temp);
105 UA_Server_readAccessLevel(mappedServer, id, &temp);
106 if(temp & UA_ACCESSLEVELMASK_READ) {
107 temp |= UA_ACCESSLEVELMASK_HISTORYREAD;
108 UA_Server_writeAccessLevel(mappedServer, id, temp);
109 }
110 UA_Server_writeHistorizing(mappedServer, id, true);
111 UA_Byte_clear(&temp);
112 }
113
115 vector<UA_NodeId>& historizing_nodes, vector<string>& historizing_setup, UA_ServerConfig* server_config) {
116 bool repeat = true;
117 /*size_t position = 0;*/
118 while(repeat) {
119 repeat = false;
120 for(size_t i = /*position*/ 0; i < historizing_nodes.size(); i++) {
121 for(size_t j = i + 1; j < historizing_nodes.size(); j++) {
122 if(UA_NodeId_equal(reinterpret_cast<UA_NodeId*>(&historizing_nodes[i]),
123 reinterpret_cast<UA_NodeId*>(&historizing_nodes[j]))) {
124 UA_String out = UA_STRING_NULL;
125 UA_print(reinterpret_cast<UA_NodeId*>(&historizing_nodes[j]), &UA_TYPES[UA_TYPES_NODEID], &out);
126 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_USERLAND,
127 "Node %.*s has multiple history settings. The first folder/process_variable setting in the mapping "
128 "file is considered if multiple exists/overlap. "
129 "process_variable settings are preferred over folder settings.",
130 (int)out.length, out.data);
131 UA_String_clear(&out);
132 UA_NodeId_clear(reinterpret_cast<UA_NodeId*>(&historizing_nodes[j]));
133 historizing_nodes.erase(historizing_nodes.begin() + j);
134 historizing_setup.erase(historizing_setup.begin() + j);
135 repeat = true;
136 /*position = i;*/
137 break;
138 }
139 }
140 }
141 }
142 }
143
144 void remove_nodes_with_incomplete_historizing_setup(vector<UA_NodeId>& historizing_nodes,
145 vector<string>& historizing_setup, UA_ServerConfig* server_config, vector<AdapterHistorySetup> history) {
146 bool repeat = true;
147 size_t position = 0;
148 while(repeat) {
149 repeat = false;
150 for(size_t i = position; i < historizing_nodes.size(); i++) {
151 bool found = false;
152 for(auto& j : history) {
153 if(historizing_setup[i] == j.name) {
154 found = true;
155 break;
156 }
157 }
158 if(!found) {
159 UA_String out = UA_STRING_NULL;
160 UA_print(reinterpret_cast<UA_NodeId*>(&historizing_nodes[i]), &UA_TYPES[UA_TYPES_NODEID], &out);
161 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
162 "Warning! Remove node %.*s from historizing because the setup %s is missing.", (int)out.length, out.data,
163 historizing_setup[i].c_str());
164 UA_String_clear(&out);
165 // UA_NodeId_clear(&historizing_nodes[j]);
166 historizing_nodes.erase(historizing_nodes.begin() + i);
167 historizing_setup.erase(historizing_setup.begin() + i);
168 repeat = true;
169 position = i;
170 break;
171 }
172 }
173 }
174 }
175
176 UA_HistoryDataGathering add_historizing_nodes(vector<UA_NodeId>& historizing_nodes, vector<string>& historizing_setup,
177 UA_Server* mappedServer, UA_ServerConfig* server_config, vector<AdapterHistorySetup> history,
178 vector<AdapterFolderHistorySetup> historyfolders, vector<AdapterPVHistorySetup> historyvariables) {
179 add_variable_historizing(&historizing_nodes, &historizing_setup, historyvariables, mappedServer, server_config);
180 add_folder_historizing(&historizing_nodes, &historizing_setup, historyfolders, mappedServer, server_config);
181 check_historizing_nodes(historizing_nodes, historizing_setup, server_config);
182 // search nodes with incomplete history config
183 remove_nodes_with_incomplete_historizing_setup(historizing_nodes, historizing_setup, server_config, history);
184 UA_HistoryDataGathering gathering = UA_HistoryDataGathering_Default(historizing_nodes.size());
185 server_config->historyDatabase = UA_HistoryDatabase_default(gathering);
186 for(size_t i = 0; i < historizing_nodes.size(); i++) {
187 UA_HistorizingNodeIdSettings setting;
188 setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_POLL;
190 for(auto& j : history) {
191 if(historizing_setup[i] == j.name) {
192 hist = j;
193 }
194 }
195 set_variable_access_level_historizing(historizing_nodes[i], mappedServer);
196 setting.historizingBackend = UA_HistoryDataBackend_Memory_Circular(historizing_nodes.size(), hist.buffer_length);
197 setting.maxHistoryDataResponseSize = hist.entries_per_response;
198 setting.pollingInterval = hist.interval;
199 UA_StatusCode retval = gathering.registerNodeId(mappedServer, gathering.context, &historizing_nodes[i], setting);
200 if(retval != UA_STATUSCODE_GOOD) {
201 UA_String out = UA_STRING_NULL;
202 UA_print(&historizing_nodes[i], &UA_TYPES[UA_TYPES_NODEID], &out);
203 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
204 "Failed to add historizing for Node %.*s with StatusCode %s", (int)out.length, out.data,
205 UA_StatusCode_name(retval));
206 UA_String_clear(&out);
207 }
208 retval = gathering.startPoll(mappedServer, gathering.context, &historizing_nodes[i]);
209 if(retval != UA_STATUSCODE_GOOD) {
210 UA_String out = UA_STRING_NULL;
211 UA_print(&historizing_nodes[i], &UA_TYPES[UA_TYPES_NODEID], &out);
212 UA_LOG_WARNING(server_config->logging, UA_LOGCATEGORY_USERLAND,
213 "Failed to start the poll for Node %.*s with StatusCode %s", (int)out.length, out.data,
214 UA_StatusCode_name(retval));
215 UA_String_clear(&out);
216 }
217 }
218 return gathering;
219 }
220
221 void clear_history(UA_HistoryDataGathering gathering, vector<UA_NodeId>& historizing_nodes,
222 vector<string>& historizing_setup, UA_Server* mappedServer, vector<AdapterFolderHistorySetup> historyfolders,
223 vector<AdapterPVHistorySetup> historyvariables, UA_ServerConfig* server_config) {
224 // stop the poll for all historizing variables
225 for(auto& historizing_node : historizing_nodes) {
226 UA_StatusCode retval = gathering.stopPoll(mappedServer, gathering.context, &historizing_node);
227 UA_LOG_INFO(server_config->logging, UA_LOGCATEGORY_SERVER,
228 "Stopping polling thread used by the historizing -> %s", UA_StatusCode_name(retval));
229 }
230 // clear the list of valid historizing nodes
231 historizing_nodes.clear();
232 historizing_setup.clear();
233 // clear the nodeis lists (variables + server from the xml config)
234 for(auto& historyfolder : historyfolders) {
235 UA_NodeId_clear(&historyfolder.folder_id);
236 }
237 for(auto& historyvariable : historyvariables) {
238 UA_NodeId_clear(&historyvariable.variable_id);
239 }
240 }
241} // namespace ChimeraTK
UA_StatusCode get_child_variables(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle)
void add_variable_historizing(vector< UA_NodeId > *historizing_nodes, vector< string > *historizing_setup, vector< AdapterPVHistorySetup > historyvariables, UA_Server *mappedServer, UA_ServerConfig *server_config)
void add_folder_historizing(vector< UA_NodeId > *historizing_nodes, vector< string > *historizing_setup, vector< AdapterFolderHistorySetup > historyfolders, UA_Server *mappedServer, UA_ServerConfig *server_config)
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)
void check_historizing_nodes(vector< UA_NodeId > &historizing_nodes, vector< string > &historizing_setup, UA_ServerConfig *server_config)
This assumes both lists have the same size.
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)
void set_variable_access_level_historizing(UA_NodeId id, UA_Server *mappedServer)
void remove_nodes_with_incomplete_historizing_setup(vector< UA_NodeId > &historizing_nodes, vector< string > &historizing_setup, UA_ServerConfig *server_config, vector< AdapterHistorySetup > history)
vector< UA_NodeId > * historizing_nodes