ua_subscription_events.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2018 (c) Ari Breitkreuz, fortiss GmbH
  6. */
  7. #include "ua_server_internal.h"
  8. #include "ua_subscription.h"
  9. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  10. typedef struct Events_nodeListElement {
  11. LIST_ENTRY(Events_nodeListElement) listEntry;
  12. UA_NodeId nodeId;
  13. } Events_nodeListElement;
  14. struct getNodesHandle {
  15. UA_Server *server;
  16. LIST_HEAD(Events_nodeList, Events_nodeListElement) nodes;
  17. };
  18. /* generates a unique event id */
  19. static UA_StatusCode
  20. UA_Event_generateEventId(UA_Server *server, UA_ByteString *generatedId) {
  21. /* EventId is a ByteString, which is basically just a string
  22. * We will use a 16-Byte ByteString as an identifier */
  23. generatedId->length = 16;
  24. generatedId->data = (UA_Byte *) UA_malloc(16 * sizeof(UA_Byte));
  25. if(!generatedId->data) {
  26. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND,
  27. "Server unable to allocate memory for EventId data.");
  28. return UA_STATUSCODE_BADOUTOFMEMORY;
  29. }
  30. /* GUIDs are unique, have a size of 16 byte and already have
  31. * a generator so use that.
  32. * Make sure GUIDs really do have 16 byte, in case someone may
  33. * have changed that struct */
  34. UA_assert(sizeof(UA_Guid) == 16);
  35. UA_Guid tmpGuid = UA_Guid_random();
  36. memcpy(generatedId->data, &tmpGuid, 16);
  37. return UA_STATUSCODE_GOOD;
  38. }
  39. static UA_StatusCode
  40. findAllSubtypesNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
  41. UA_NodeId referenceTypeId, void *handle) {
  42. /* only subtypes of hasSubtype */
  43. UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  44. if(isInverse || !UA_NodeId_equal(&referenceTypeId, &hasSubtypeId))
  45. return UA_STATUSCODE_GOOD;
  46. Events_nodeListElement *entry = (Events_nodeListElement *) UA_malloc(sizeof(Events_nodeListElement));
  47. if(!entry)
  48. return UA_STATUSCODE_BADOUTOFMEMORY;
  49. UA_StatusCode retval = UA_NodeId_copy(&parentId, &entry->nodeId);
  50. if(retval != UA_STATUSCODE_GOOD) {
  51. UA_free(entry);
  52. return retval;
  53. }
  54. LIST_INSERT_HEAD(&((struct getNodesHandle *) handle)->nodes, entry, listEntry);
  55. /* recursion */
  56. UA_Server_forEachChildNodeCall(((struct getNodesHandle *) handle)->server,
  57. parentId, findAllSubtypesNodeIteratorCallback, handle);
  58. return UA_STATUSCODE_GOOD;
  59. }
  60. UA_StatusCode UA_EXPORT
  61. UA_Server_createEvent(UA_Server *server, const UA_NodeId eventType, UA_NodeId *outNodeId) {
  62. if (!outNodeId) {
  63. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND, "outNodeId cannot be NULL!");
  64. return UA_STATUSCODE_BADINVALIDARGUMENT;
  65. }
  66. /* make sure the eventType is a subtype of BaseEventType */
  67. UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  68. UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
  69. if (!isNodeInTree(&server->config.nodestore, &eventType, &baseEventTypeId, &hasSubtypeId, 1)) {
  70. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND, "Event type must be a subtype of BaseEventType!");
  71. return UA_STATUSCODE_BADINVALIDARGUMENT;
  72. }
  73. UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
  74. oAttr.displayName.locale = UA_STRING_NULL;
  75. oAttr.displayName.text = UA_STRING_NULL;
  76. oAttr.description.locale = UA_STRING_NULL;
  77. oAttr.description.text = UA_STRING_NULL;
  78. UA_QualifiedName name;
  79. UA_QualifiedName_init(&name);
  80. /* create an ObjectNode which represents the event */
  81. UA_StatusCode retval =
  82. UA_Server_addObjectNode(server,
  83. UA_NODEID_NULL, /* the user may not have control over the nodeId */
  84. UA_NODEID_NULL, /* an event does not have a parent */
  85. UA_NODEID_NULL, /* an event does not have any references */
  86. name, /* an event does not have a name */
  87. eventType, /* the type of the event */
  88. oAttr, /* default attributes are fine */
  89. NULL, /* no node context */
  90. outNodeId);
  91. if (retval != UA_STATUSCODE_GOOD) {
  92. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
  93. "Adding event failed. StatusCode %s", UA_StatusCode_name(retval));
  94. return retval;
  95. }
  96. /* find the eventType variableNode */
  97. name = UA_QUALIFIEDNAME(0, "EventType");
  98. UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *outNodeId, 1, &name);
  99. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  100. UA_BrowsePathResult_deleteMembers(&bpr);
  101. return bpr.statusCode;
  102. }
  103. UA_Variant value;
  104. UA_Variant_init(&value);
  105. UA_Variant_setScalarCopy(&value, &eventType, &UA_TYPES[UA_TYPES_NODEID]);
  106. UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  107. UA_Variant_deleteMembers(&value);
  108. UA_BrowsePathResult_deleteMembers(&bpr);
  109. /* the object is not put in any queues until it is triggered */
  110. return retval;
  111. }
  112. static UA_Boolean
  113. isValidEvent(UA_Server *server, const UA_NodeId *validEventParent, const UA_NodeId *eventId) {
  114. /* find the eventType variableNode */
  115. UA_QualifiedName findName = UA_QUALIFIEDNAME(0, "EventType");
  116. UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *eventId, 1, &findName);
  117. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  118. UA_BrowsePathResult_deleteMembers(&bpr);
  119. return UA_FALSE;
  120. }
  121. UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  122. UA_Boolean tmp = isNodeInTree(&server->config.nodestore, &bpr.targets[0].targetId.nodeId,
  123. validEventParent, &hasSubtypeId, 1);
  124. UA_BrowsePathResult_deleteMembers(&bpr);
  125. return tmp;
  126. }
  127. /* static UA_StatusCode */
  128. /* whereClausesApply(UA_Server *server, const UA_ContentFilter whereClause, */
  129. /* UA_EventFieldList *efl, UA_Boolean *result) { */
  130. /* /\* if the where clauses aren't specified leave everything as is *\/ */
  131. /* if(whereClause.elementsSize == 0) { */
  132. /* *result = UA_TRUE; */
  133. /* return UA_STATUSCODE_GOOD; */
  134. /* } */
  135. /* /\* where clauses were specified *\/ */
  136. /* UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND, */
  137. /* "Where clauses are not supported by the server."); */
  138. /* *result = UA_TRUE; */
  139. /* return UA_STATUSCODE_BADNOTSUPPORTED; */
  140. /* } */
  141. /* Part 4: 7.4.4.5 SimpleAttributeOperand
  142. * The clause can point to any attribute of nodes. Either a child of the event
  143. * node and also the event type. */
  144. static UA_StatusCode
  145. resolveSimpleAttributeOperand(UA_Server *server, UA_Session *session, const UA_NodeId *origin,
  146. const UA_SimpleAttributeOperand *sao, UA_Variant *value) {
  147. /* Prepare the ReadValueId */
  148. UA_ReadValueId rvi;
  149. UA_ReadValueId_init(&rvi);
  150. rvi.indexRange = sao->indexRange;
  151. rvi.attributeId = sao->attributeId;
  152. /* If this list (browsePath) is empty the Node is the instance of the
  153. * TypeDefinition. */
  154. if(sao->browsePathSize == 0) {
  155. rvi.nodeId = sao->typeDefinitionId;
  156. UA_DataValue v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
  157. if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
  158. *value = v.value;
  159. return v.status;
  160. }
  161. /* Resolve the browse path */
  162. UA_BrowsePathResult bpr =
  163. UA_Server_browseSimplifiedBrowsePath(server, *origin, sao->browsePathSize, sao->browsePath);
  164. if(bpr.targetsSize == 0 && bpr.statusCode == UA_STATUSCODE_GOOD)
  165. bpr.statusCode = UA_STATUSCODE_BADNOTFOUND;
  166. if(bpr.statusCode != UA_STATUSCODE_GOOD) {
  167. UA_StatusCode retval = bpr.statusCode;
  168. UA_BrowsePathResult_deleteMembers(&bpr);
  169. return retval;
  170. }
  171. /* Read the first matching element. Move the value to the output. */
  172. rvi.nodeId = bpr.targets[0].targetId.nodeId;
  173. UA_DataValue v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
  174. if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
  175. *value = v.value;
  176. UA_BrowsePathResult_deleteMembers(&bpr);
  177. return v.status;
  178. }
  179. /* filters the given event with the given filter and writes the results into a notification */
  180. static UA_StatusCode
  181. UA_Server_filterEvent(UA_Server *server, UA_Session *session,
  182. const UA_NodeId *eventNode, UA_EventFilter *filter,
  183. UA_EventNotification *notification) {
  184. if (filter->selectClausesSize == 0)
  185. return UA_STATUSCODE_BADEVENTFILTERINVALID;
  186. /* setup */
  187. UA_EventFieldList_init(&notification->fields);
  188. /* EventFilterResult isn't being used currently
  189. UA_EventFilterResult_init(&notification->result); */
  190. notification->fields.eventFieldsSize = filter->selectClausesSize;
  191. notification->fields.eventFields = (UA_Variant *) UA_Array_new(notification->fields.eventFieldsSize,
  192. &UA_TYPES[UA_TYPES_VARIANT]);
  193. if (!notification->fields.eventFields) {
  194. /* EventFilterResult currently isn't being used
  195. UA_EventFiterResult_deleteMembers(&notification->result); */
  196. return UA_STATUSCODE_BADOUTOFMEMORY;
  197. }
  198. /* EventFilterResult currently isn't being used
  199. notification->result.selectClauseResultsSize = filter->selectClausesSize;
  200. notification->result.selectClauseResults = (UA_StatusCode *)
  201. UA_Array_new(filter->selectClausesSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  202. if (!notification->result->selectClauseResults) {
  203. UA_EventFieldList_deleteMembers(&notification->fields);
  204. UA_EventFilterResult_deleteMembers(&notification->result);
  205. return UA_STATUSCODE_BADOUTOFMEMORY;
  206. }
  207. */
  208. /* ================ apply the filter ===================== */
  209. /* check if the browsePath is BaseEventType, in which case nothing more needs to be checked */
  210. UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
  211. /* iterate over the selectClauses */
  212. for(size_t i = 0; i < filter->selectClausesSize; i++) {
  213. if(!UA_NodeId_equal(&filter->selectClauses[i].typeDefinitionId, &baseEventTypeId) &&
  214. !isValidEvent(server, &filter->selectClauses[0].typeDefinitionId, eventNode)) {
  215. UA_Variant_init(&notification->fields.eventFields[i]);
  216. /* EventFilterResult currently isn't being used
  217. notification->result.selectClauseResults[i] = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; */
  218. continue;
  219. }
  220. /* TODO: Put the result into the selectClausResults */
  221. resolveSimpleAttributeOperand(server, session, eventNode,
  222. &filter->selectClauses[i],
  223. &notification->fields.eventFields[i]);
  224. }
  225. /* UA_Boolean whereClauseResult = UA_TRUE; */
  226. /* return whereClausesApply(server, filter->whereClause, &notification->fields, &whereClauseResult); */
  227. return UA_STATUSCODE_GOOD;
  228. }
  229. static UA_StatusCode
  230. eventSetConstants(UA_Server *server, const UA_NodeId *event,
  231. const UA_NodeId *origin, UA_ByteString *outEventId) {
  232. /* set the source */
  233. UA_QualifiedName name = UA_QUALIFIEDNAME(0, "SourceNode");
  234. UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
  235. if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  236. UA_StatusCode tmp = bpr.statusCode;
  237. UA_BrowsePathResult_deleteMembers(&bpr);
  238. return tmp;
  239. }
  240. UA_Variant value;
  241. UA_Variant_init(&value);
  242. UA_Variant_setScalarCopy(&value, origin, &UA_TYPES[UA_TYPES_NODEID]);
  243. UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  244. UA_Variant_deleteMembers(&value);
  245. UA_BrowsePathResult_deleteMembers(&bpr);
  246. /* set the receive time */
  247. name = UA_QUALIFIEDNAME(0, "ReceiveTime");
  248. bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
  249. if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  250. UA_StatusCode tmp = bpr.statusCode;
  251. UA_BrowsePathResult_deleteMembers(&bpr);
  252. return tmp;
  253. }
  254. UA_DateTime time = UA_DateTime_now();
  255. UA_Variant_setScalar(&value, &time, &UA_TYPES[UA_TYPES_DATETIME]);
  256. UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  257. UA_BrowsePathResult_deleteMembers(&bpr);
  258. /* set the eventId attribute */
  259. UA_ByteString eventId;
  260. UA_ByteString_init(&eventId);
  261. UA_StatusCode retval = UA_Event_generateEventId(server, &eventId);
  262. if(retval != UA_STATUSCODE_GOOD)
  263. return retval;
  264. if (outEventId) {
  265. UA_ByteString_copy(&eventId, outEventId);
  266. }
  267. name = UA_QUALIFIEDNAME(0, "EventId");
  268. bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
  269. if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  270. UA_StatusCode tmp = bpr.statusCode;
  271. UA_BrowsePathResult_deleteMembers(&bpr);
  272. return tmp;
  273. }
  274. UA_Variant_init(&value);
  275. UA_Variant_setScalar(&value, &eventId, &UA_TYPES[UA_TYPES_BYTESTRING]);
  276. UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  277. UA_ByteString_deleteMembers(&eventId);
  278. UA_BrowsePathResult_deleteMembers(&bpr);
  279. return UA_STATUSCODE_GOOD;
  280. }
  281. /* insert each node into the list (passed as handle) */
  282. static UA_StatusCode
  283. getParentsNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
  284. UA_NodeId referenceTypeId, void *handle) {
  285. /* parents have an inverse reference */
  286. if(!isInverse)
  287. return UA_STATUSCODE_GOOD;
  288. Events_nodeListElement *entry = (Events_nodeListElement *) UA_malloc(sizeof(Events_nodeListElement));
  289. if (!entry) {
  290. return UA_STATUSCODE_BADOUTOFMEMORY;
  291. }
  292. UA_StatusCode retval = UA_NodeId_copy(&parentId, &entry->nodeId);
  293. if(retval != UA_STATUSCODE_GOOD) {
  294. UA_free(entry);
  295. return retval;
  296. }
  297. LIST_INSERT_HEAD(&((struct getNodesHandle *) handle)->nodes, entry, listEntry);
  298. /* recursion */
  299. UA_Server_forEachChildNodeCall(((struct getNodesHandle *) handle)->server,
  300. parentId, getParentsNodeIteratorCallback, handle);
  301. return UA_STATUSCODE_GOOD;
  302. }
  303. /* Filters an event according to the filter specified by mon and then adds it to
  304. * mons notification queue */
  305. static UA_StatusCode
  306. UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event,
  307. UA_MonitoredItem *mon) {
  308. UA_Notification *notification = (UA_Notification *) UA_malloc(sizeof(UA_Notification));
  309. if(!notification)
  310. return UA_STATUSCODE_BADOUTOFMEMORY;
  311. /* Get the session */
  312. UA_Subscription *sub = mon->subscription;
  313. UA_Session *session = sub->session;
  314. /* Apply the filter */
  315. UA_StatusCode retval = UA_Server_filterEvent(server, session, event,
  316. &mon->filter.eventFilter,
  317. &notification->data.event);
  318. if(retval != UA_STATUSCODE_GOOD) {
  319. UA_free(notification);
  320. return retval;
  321. }
  322. /* Enqueue the notification */
  323. notification->mon = mon;
  324. UA_Notification_enqueue(server, mon->subscription, mon, notification);
  325. return UA_STATUSCODE_GOOD;
  326. }
  327. /* make sure the origin is in the ObjectsFolder (TODO: or in the ViewsFolder) */
  328. static const UA_NodeId objectsFolderId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}};
  329. static const UA_NodeId references[2] =
  330. {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}},
  331. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}};
  332. UA_StatusCode UA_EXPORT
  333. UA_Server_triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_NodeId origin,
  334. UA_ByteString *outEventId, const UA_Boolean deleteEventNode) {
  335. if(!isNodeInTree(&server->config.nodestore, &origin, &objectsFolderId, references, 2)) {
  336. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
  337. "Node for event must be in ObjectsFolder!");
  338. return UA_STATUSCODE_BADINVALIDARGUMENT;
  339. }
  340. UA_StatusCode retval = eventSetConstants(server, &eventNodeId, &origin, outEventId);
  341. if(retval != UA_STATUSCODE_GOOD)
  342. return retval;
  343. /* get an array with all parents */
  344. struct getNodesHandle parentHandle;
  345. parentHandle.server = server;
  346. LIST_INIT(&parentHandle.nodes);
  347. retval = getParentsNodeIteratorCallback(origin, UA_TRUE, UA_NODEID_NULL, &parentHandle);
  348. if(retval != UA_STATUSCODE_GOOD)
  349. return retval;
  350. /* add the event to each node's monitored items */
  351. Events_nodeListElement *parentIter, *tmp_parentIter;
  352. LIST_FOREACH_SAFE(parentIter, &parentHandle.nodes, listEntry, tmp_parentIter) {
  353. const UA_ObjectNode *node = (const UA_ObjectNode *) UA_Nodestore_get(server, &parentIter->nodeId);
  354. /* SLIST_FOREACH */
  355. for (UA_MonitoredItem *monIter = node->monitoredItemQueue; monIter != NULL; monIter = monIter->next) {
  356. retval = UA_Event_addEventToMonitoredItem(server, &eventNodeId, monIter);
  357. if (retval != UA_STATUSCODE_GOOD) {
  358. UA_Nodestore_release(server, (const UA_Node *) node);
  359. return retval;
  360. }
  361. }
  362. UA_Nodestore_release(server, (const UA_Node *) node);
  363. LIST_REMOVE(parentIter, listEntry);
  364. UA_NodeId_deleteMembers(&parentIter->nodeId);
  365. UA_free(parentIter);
  366. }
  367. /* delete the node representation of the event */
  368. if (deleteEventNode) {
  369. retval = UA_Server_deleteNode(server, eventNodeId, UA_TRUE);
  370. if (retval != UA_STATUSCODE_GOOD) {
  371. UA_LOG_WARNING(server->config.logger,
  372. UA_LOGCATEGORY_SERVER,
  373. "Attempt to remove event using deleteNode failed. StatusCode %s",
  374. UA_StatusCode_name(retval));
  375. return retval;
  376. }
  377. }
  378. return UA_STATUSCODE_GOOD;
  379. }
  380. #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */