Aktualizr
C++ SOTA Client
common.h
1 #ifndef OPCUABRIDGE_COMMON_H_
2 #define OPCUABRIDGE_COMMON_H_
3 
4 #ifdef OPCUABRIDGE_ENABLE_SERIALIZATION
5 #include "boostarch.h"
6 #include "utility.h"
7 #endif
8 
9 #include <functional>
10 
11 #include <json/json.h>
12 #include <open62541.h>
13 
14 #include <algorithm>
15 #include <string>
16 #include <vector>
17 
18 #include "opcuabridgeconfig.h"
19 
20 namespace boost {
21 namespace filesystem {
22 class path;
23 } // namespace filesystem
24 } // namespace boost
25 
26 #define INITSERVERNODESET_FUNCTION_DEFINITION(TYPE) \
27  void InitServerNodeset(UA_Server *server) { \
28  opcuabridge::internal::AddDataSourceVariable<TYPE>(server, node_id_, this); \
29  }
30 
31 #define INITSERVERNODESET_BIN_FUNCTION_DEFINITION(TYPE, BINDATA) \
32  void InitServerNodeset(UA_Server *server) { \
33  opcuabridge::internal::AddDataSourceVariable<TYPE>(server, node_id_, this); \
34  opcuabridge::internal::AddDataSourceVariable<opcuabridge::MessageBinaryData>( \
35  server, bin_node_id_, BINDATA, UA_NODEID_STRING(kNSindex, const_cast<char *>(node_id_))); \
36  }
37 
38 #define INITSERVERNODESET_FILE_FUNCTION_DEFINITION(TYPE) \
39  void InitServerNodeset(UA_Server *server) { \
40  opcuabridge::internal::AddDataSourceVariable<TYPE>(server, node_id_, this); \
41  opcuabridge::internal::AddDataSourceVariable<opcuabridge::MessageFileData>( \
42  server, bin_node_id_, this, UA_NODEID_STRING(kNSindex, const_cast<char *>(node_id_))); \
43  }
44 
45 #define CLIENTWRITE_FUNCTION_DEFINITION() \
46  UA_StatusCode ClientWrite(UA_Client *client) { return opcuabridge::internal::ClientWrite(client, node_id_, this); }
47 
48 #define CLIENTWRITE_BIN_FUNCTION_DEFINITION(BINDATA) \
49  UA_StatusCode ClientWrite(UA_Client *client) { \
50  return opcuabridge::internal::ClientWrite(client, node_id_, this, BINDATA); \
51  }
52 
53 #define CLIENTWRITE_FILE_FUNCTION_DEFINITION() \
54  UA_StatusCode ClientWriteFile(UA_Client *client, const boost::filesystem::path &f) { \
55  return opcuabridge::internal::ClientWriteFile(client, node_id_, this, f); \
56  }
57 
58 #define CLIENTREAD_FUNCTION_DEFINITION() \
59  UA_StatusCode ClientRead(UA_Client *client) { return opcuabridge::internal::ClientRead(client, node_id_, this); }
60 
61 #define CLIENTREAD_BIN_FUNCTION_DEFINITION(BINDATA) \
62  UA_StatusCode ClientRead(UA_Client *client) { \
63  return opcuabridge::internal::ClientRead(client, node_id_, this, BINDATA); \
64  }
65 
66 #define READ_FUNCTION_FRIEND_DECLARATION(TYPE) \
67  friend UA_StatusCode read<TYPE>(UA_Server *, const UA_NodeId *, void *, const UA_NodeId *, void *, UA_Boolean, \
68  const UA_NumericRange *, UA_DataValue *);
69 
70 #define WRITE_FUNCTION_FRIEND_DECLARATION(TYPE) \
71  friend UA_StatusCode write<TYPE>(UA_Server *, const UA_NodeId *, void *, const UA_NodeId *, void *, \
72  const UA_NumericRange *, const UA_DataValue *);
73 
74 #define INTERNAL_FUNCTIONS_FRIEND_DECLARATION(TYPE) \
75  friend UA_StatusCode opcuabridge::internal::ClientWrite<TYPE>(UA_Client *, const char *, TYPE *); \
76  friend UA_StatusCode opcuabridge::internal::ClientWrite<TYPE>(UA_Client *, const char *, TYPE *, BinaryDataType *); \
77  friend UA_StatusCode opcuabridge::internal::ClientRead<TYPE>(UA_Client *, const char *, TYPE *); \
78  friend UA_StatusCode opcuabridge::internal::ClientRead<TYPE>(UA_Client *, const char *, TYPE *, BinaryDataType *); \
79  friend UA_StatusCode opcuabridge::internal::ClientWriteFile<TYPE>(UA_Client *, const char *, TYPE *, \
80  const boost::filesystem::path &);
81 
82 #define WRAPMESSAGE_FUCTION_DEFINITION(TYPE) \
83  static std::string wrapMessage(TYPE *obj) { \
84  Json::Value value = obj->wrapMessage(); \
85  Json::FastWriter fw; \
86  return fw.write(value); \
87  }
88 
89 namespace opcuabridge {
90 namespace internal {
91 /**
92  * Workaround for cppcoreguidelines-pro-bounds-pointer-arithmetic
93  * TODO: Replace with something that is memory-safe
94  */
95 void parseJson(const char *msg, size_t len, Json::Value *value);
96 } // namespace internal
97 } // namespace opcuabridge
98 
99 #define UNWRAPMESSAGE_FUCTION_DEFINITION(TYPE) \
100  static void unwrapMessage(TYPE *obj, const char *msg, size_t len) { \
101  Json::Value value; \
102  opcuabridge::internal::parseJson(msg, len, &value); \
103  obj->unwrapMessage(value); \
104  }
105 
106 #define DEFINE_SERIALIZE_METHOD() \
107  template <typename Archive> \
108  inline void serialize(Archive &ar, const unsigned int version)
109 
110 #define SERIALIZE_FIELD(AR, XML_TAGNAME, FIELD) \
111  { utility::make_serialize_field(AR, FIELD)(AR, XML_TAGNAME, FIELD); }
112 
113 #define SERIALIZE_FUNCTION_FRIEND_DECLARATION friend class boost::serialization::access;
114 
115 namespace opcuabridge {
116 
117 extern const UA_UInt16 kNSindex;
118 
119 extern const char *kLocale;
120 
121 extern void BoostLogOpcua(UA_LogLevel /*level*/, UA_LogCategory /*category*/, const char * /*msg*/, va_list /*args*/);
122 
123 enum HashFunction { HASH_FUN_SHA224, HASH_FUN_SHA256, HASH_FUN_SHA384 };
124 
125 enum SignatureMethod { SIG_METHOD_RSASSA_PSS, SIG_METHOD_ED25519 };
126 
129  virtual std::string getFullFilePath() const = 0;
130  virtual ~MessageFileData() = default;
131 };
132 typedef std::vector<uint8_t> BinaryDataType;
133 
134 template <typename T>
136  typedef std::function<void(T *)> type;
137 };
138 
139 template <typename T>
141  typedef std::function<void(T *)> type;
142 };
143 
144 template <typename T>
145 UA_StatusCode read(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId,
146  void *nodeContext, UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
147  UA_DataValue *dataValue) {
148  auto *obj = static_cast<T *>(nodeContext);
149 
150  if (obj->on_before_read_cb_) {
151  obj->on_before_read_cb_(obj);
152  }
153 
154  std::string msg = T::wrapMessage(obj);
155 
156  UA_Variant_setArrayCopy(&dataValue->value, msg.c_str(), msg.size(), &UA_TYPES[UA_TYPES_BYTE]);
157  dataValue->hasValue = true;
158 
159  return UA_STATUSCODE_GOOD;
160 }
161 
162 template <>
163 UA_StatusCode read<MessageBinaryData>(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
164  const UA_NodeId *nodeId, void *nodeContext, UA_Boolean sourceTimeStamp,
165  const UA_NumericRange *range, UA_DataValue *dataValue);
166 
167 template <>
168 UA_StatusCode read<MessageFileData>(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
169  const UA_NodeId *nodeId, void *nodeContext, UA_Boolean sourceTimeStamp,
170  const UA_NumericRange *range, UA_DataValue *dataValue);
171 
172 template <typename T>
173 UA_StatusCode write(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId,
174  void *nodeContext, const UA_NumericRange *range, const UA_DataValue *data) {
175  auto *obj = static_cast<T *>(nodeContext);
176 
177  if (!UA_Variant_isEmpty(&data->value) && UA_Variant_hasArrayType(&data->value, &UA_TYPES[UA_TYPES_BYTE])) {
178  T::unwrapMessage(obj, static_cast<const char *>(data->value.data), data->value.arrayLength);
179  }
180  if (obj->on_after_write_cb_) {
181  obj->on_after_write_cb_(obj);
182  }
183  return UA_STATUSCODE_GOOD;
184 }
185 
186 template <>
187 UA_StatusCode write<MessageBinaryData>(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
188  const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range,
189  const UA_DataValue *data);
190 
191 template <>
192 UA_StatusCode write<MessageFileData>(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
193  const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range,
194  const UA_DataValue *data);
195 
196 namespace internal {
197 
198 template <typename MessageT>
199 inline void AddDataSourceVariable(UA_Server *server, const char *node_id, void *node_context,
200  const UA_NodeId parent_node_id = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)) {
201  UA_VariableAttributes attr = UA_VariableAttributes_default;
202  attr.displayName = UA_LOCALIZEDTEXT(const_cast<char *>(kLocale), const_cast<char *>(node_id));
203  attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
204  UA_DataSource dataSource;
205  dataSource.read = &opcuabridge::read<MessageT>;
206  dataSource.write = &opcuabridge::write<MessageT>;
207  UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(kNSindex, const_cast<char *>(node_id)), parent_node_id,
208  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
209  UA_QUALIFIEDNAME(kNSindex, const_cast<char *>(node_id)), UA_NODEID_NULL, attr,
210  dataSource, node_context, nullptr);
211 }
212 
213 template <typename MessageT>
214 inline UA_StatusCode ClientWrite(UA_Client *client, const char *node_id, MessageT *obj) {
215  UA_Variant *val = UA_Variant_new();
216  std::string msg = MessageT::wrapMessage(obj);
217  UA_Variant_setArray(val, const_cast<char *>(msg.c_str()), msg.size(), &UA_TYPES[UA_TYPES_BYTE]);
218  val->storageType = UA_VARIANT_DATA_NODELETE;
219  UA_StatusCode retval =
220  UA_Client_writeValueAttribute(client, UA_NODEID_STRING(kNSindex, const_cast<char *>(node_id)), val);
221  UA_Variant_delete(val);
222  return retval;
223 }
224 
225 template <typename MessageT>
226 inline UA_StatusCode ClientWrite(UA_Client *client, const char *node_id, MessageT *obj, BinaryDataType *bin_data) {
227  // write binary child node
228  UA_Variant *bin_val = UA_Variant_new();
229  UA_Variant_setArray(bin_val, &(*bin_data)[0], bin_data->size(), &UA_TYPES[UA_TYPES_BYTE]);
230  bin_val->storageType = UA_VARIANT_DATA_NODELETE;
231  UA_StatusCode retval =
232  UA_Client_writeValueAttribute(client, UA_NODEID_STRING(kNSindex, const_cast<char *>(obj->bin_node_id_)), bin_val);
233  UA_Variant_delete(bin_val);
234 
235  if (retval == UA_STATUSCODE_GOOD) {
236  UA_Variant *val = UA_Variant_new();
237  std::string msg = MessageT::wrapMessage(obj);
238  UA_Variant_setArray(val, const_cast<char *>(msg.c_str()), msg.size(), &UA_TYPES[UA_TYPES_BYTE]);
239  val->storageType = UA_VARIANT_DATA_NODELETE;
240  retval = UA_Client_writeValueAttribute(client, UA_NODEID_STRING(kNSindex, const_cast<char *>(node_id)), val);
241  UA_Variant_delete(val);
242  }
243  return retval;
244 }
245 
246 UA_StatusCode ClientWriteFile(UA_Client * /*client*/, const char * /*node_id*/,
247  const boost::filesystem::path & /*file_path*/,
248  std::size_t block_size = OPCUABRIDGE_FILEDATA_WRITE_BLOCK_SIZE);
249 
250 template <typename MessageT>
251 inline UA_StatusCode ClientWriteFile(UA_Client *client, const char *node_id, MessageT *obj,
252  const boost::filesystem::path &file_path) {
253  UA_StatusCode retval = ClientWrite<MessageT>(client, node_id, obj);
254  if (retval == UA_STATUSCODE_GOOD) {
255  retval = ClientWriteFile(client, obj->bin_node_id_, file_path);
256  }
257  return retval;
258 }
259 
260 template <typename MessageT>
261 inline UA_StatusCode ClientRead(UA_Client *client, const char *node_id, MessageT *obj) {
262  UA_Variant *val = UA_Variant_new();
263  UA_StatusCode retval =
264  UA_Client_readValueAttribute(client, UA_NODEID_STRING(kNSindex, const_cast<char *>(node_id)), val);
265  if (retval == UA_STATUSCODE_GOOD && UA_Variant_hasArrayType(val, &UA_TYPES[UA_TYPES_BYTE])) {
266  MessageT::unwrapMessage(obj, static_cast<const char *>(val->data), val->arrayLength);
267  }
268  UA_Variant_delete(val);
269  return retval;
270 }
271 
272 template <typename MessageT>
273 inline UA_StatusCode ClientRead(UA_Client *client, const char *node_id, MessageT *obj, BinaryDataType *bin_data) {
274  UA_Variant *val = UA_Variant_new();
275  UA_StatusCode retval =
276  UA_Client_readValueAttribute(client, UA_NODEID_STRING(kNSindex, const_cast<char *>(node_id)), val);
277  if (retval == UA_STATUSCODE_GOOD && UA_Variant_hasArrayType(val, &UA_TYPES[UA_TYPES_BYTE])) {
278  MessageT::unwrapMessage(obj, static_cast<const char *>(val->data), val->arrayLength);
279  // read binary child node
280  UA_Variant *bin_val = UA_Variant_new();
281  retval = UA_Client_readValueAttribute(
282  client, UA_NODEID_STRING(kNSindex, const_cast<char *>(obj->bin_node_id_)), bin_val);
283  if (retval == UA_STATUSCODE_GOOD && UA_Variant_hasArrayType(bin_val, &UA_TYPES[UA_TYPES_BYTE])) {
284  bin_data->resize(bin_val->arrayLength);
285  const auto *src = static_cast<const unsigned char *>(bin_val->data);
286  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
287  std::copy(src, src + bin_val->arrayLength, bin_data->begin());
288  }
289  UA_Variant_delete(bin_val);
290  }
291  UA_Variant_delete(val);
292  return retval;
293 }
294 } // namespace internal
295 
296 namespace convert_to {
297 
298 template <typename T>
299 inline Json::Value jsonArray(const std::vector<T> &v) {
300  Json::Value jsonArray;
301  jsonArray.resize(static_cast<unsigned int>(v.size()));
302  for (const auto &i : v) {
303  jsonArray.append(i.wrapMessage());
304  }
305  return jsonArray;
306 }
307 
308 template <>
309 inline Json::Value jsonArray<int>(const std::vector<int> &v) {
310  Json::Value jsonArray;
311  jsonArray.resize(static_cast<unsigned int>(v.size()));
312  for (const auto &item : v) {
313  jsonArray.append(static_cast<Json::Value::Int>(item));
314  }
315  return jsonArray;
316 }
317 
318 template <>
319 inline Json::Value jsonArray<std::size_t>(const std::vector<std::size_t> &v) {
320  Json::Value jsonArray;
321  jsonArray.resize(static_cast<unsigned int>(v.size()));
322  for (const auto &item : v) {
323  jsonArray.append(static_cast<Json::Value::UInt>(item));
324  }
325  return jsonArray;
326 }
327 
328 template <typename T>
329 inline std::vector<T> stdVector(const Json::Value &v) {
330  std::vector<T> stdv;
331  for (const auto &i : v) {
332  T item;
333  item.unwrapMessage(i);
334  stdv.push_back(item);
335  }
336  return stdv;
337 }
338 
339 template <>
340 inline std::vector<int> stdVector(const Json::Value &v) {
341  std::vector<int> stdv;
342  stdv.reserve(v.size());
343  for (const auto &item : v) {
344  stdv.push_back(item.asInt());
345  }
346  return stdv;
347 }
348 
349 } // namespace convert_to
350 
351 } // namespace opcuabridge
352 
353 #endif // OPCUABRIDGE_COMMON_H_
Definition: common.h:20
General data structures.
Definition: types.cc:6