ua_subscription_events.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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. UA_StatusCode
  11. UA_MonitoredItem_removeNodeEventCallback(UA_Server *server, UA_Session *session,
  12. UA_Node *node, void *data) {
  13. if (node->nodeClass != UA_NODECLASS_OBJECT)
  14. return UA_STATUSCODE_BADINVALIDARGUMENT;
  15. UA_ObjectNode *on = (UA_ObjectNode*)node;
  16. UA_MonitoredItem *remove = (UA_MonitoredItem*)data;
  17. if(!on->monitoredItemQueue)
  18. return UA_STATUSCODE_GOOD;
  19. /* Edge case that it's the first element */
  20. if(on->monitoredItemQueue == remove) {
  21. on->monitoredItemQueue = remove->next;
  22. return UA_STATUSCODE_GOOD;
  23. }
  24. UA_MonitoredItem *prev = on->monitoredItemQueue;
  25. UA_MonitoredItem *entry = prev->next;
  26. for(; entry != NULL; prev = entry, entry = entry->next) {
  27. if(entry == remove) {
  28. prev->next = entry->next;
  29. return UA_STATUSCODE_GOOD;
  30. }
  31. }
  32. return UA_STATUSCODE_BADNOTFOUND;
  33. }
  34. /* We use a 16-Byte ByteString as an identifier */
  35. static UA_StatusCode
  36. generateEventId(UA_ByteString *generatedId) {
  37. UA_StatusCode res = UA_ByteString_allocBuffer(generatedId, 16 * sizeof(UA_Byte));
  38. if(res != UA_STATUSCODE_GOOD)
  39. return res;
  40. UA_UInt32 *ids = (UA_UInt32*)generatedId->data;
  41. ids[0] = UA_UInt32_random();
  42. ids[1] = UA_UInt32_random();
  43. ids[2] = UA_UInt32_random();
  44. ids[3] = UA_UInt32_random();
  45. return UA_STATUSCODE_GOOD;
  46. }
  47. UA_StatusCode
  48. UA_Server_createEvent(UA_Server *server, const UA_NodeId eventType,
  49. UA_NodeId *outNodeId) {
  50. if(!outNodeId) {
  51. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND,
  52. "outNodeId must not be NULL. The event's NodeId must be returned "
  53. "so it can be triggered.");
  54. return UA_STATUSCODE_BADINVALIDARGUMENT;
  55. }
  56. /* Make sure the eventType is a subtype of BaseEventType */
  57. UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  58. UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
  59. if(!isNodeInTree(server->nsCtx, &eventType, &baseEventTypeId, &hasSubtypeId, 1)) {
  60. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND,
  61. "Event type must be a subtype of BaseEventType!");
  62. return UA_STATUSCODE_BADINVALIDARGUMENT;
  63. }
  64. /* Create an ObjectNode which represents the event */
  65. UA_QualifiedName name;
  66. // set a dummy name. This is not used.
  67. name = UA_QUALIFIEDNAME(0,"E");
  68. UA_NodeId newNodeId = UA_NODEID_NULL;
  69. UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
  70. UA_StatusCode retval =
  71. UA_Server_addObjectNode(server,
  72. UA_NODEID_NULL, /* Set a random unused NodeId */
  73. UA_NODEID_NULL, /* No parent */
  74. UA_NODEID_NULL, /* No parent reference */
  75. name, /* an event does not have a name */
  76. eventType, /* the type of the event */
  77. oAttr, /* default attributes are fine */
  78. NULL, /* no node context */
  79. &newNodeId);
  80. if(retval != UA_STATUSCODE_GOOD) {
  81. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND,
  82. "Adding event failed. StatusCode %s", UA_StatusCode_name(retval));
  83. return retval;
  84. }
  85. /* Find the eventType variable */
  86. name = UA_QUALIFIEDNAME(0, "EventType");
  87. UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, newNodeId, 1, &name);
  88. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  89. retval = bpr.statusCode;
  90. UA_BrowsePathResult_deleteMembers(&bpr);
  91. UA_Server_deleteNode(server, newNodeId, true);
  92. UA_NodeId_deleteMembers(&newNodeId);
  93. return retval;
  94. }
  95. /* Set the EventType */
  96. UA_Variant value;
  97. UA_Variant_init(&value);
  98. UA_Variant_setScalar(&value, (void*)(uintptr_t)&eventType, &UA_TYPES[UA_TYPES_NODEID]);
  99. retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  100. UA_BrowsePathResult_deleteMembers(&bpr);
  101. if(retval != UA_STATUSCODE_GOOD) {
  102. UA_Server_deleteNode(server, newNodeId, true);
  103. UA_NodeId_deleteMembers(&newNodeId);
  104. return retval;
  105. }
  106. *outNodeId = newNodeId;
  107. return UA_STATUSCODE_GOOD;
  108. }
  109. static UA_Boolean
  110. isValidEvent(UA_Server *server, const UA_NodeId *validEventParent,
  111. const UA_NodeId *eventId) {
  112. /* find the eventType variableNode */
  113. UA_QualifiedName findName = UA_QUALIFIEDNAME(0, "EventType");
  114. UA_BrowsePathResult bpr =
  115. UA_Server_browseSimplifiedBrowsePath(server, *eventId, 1, &findName);
  116. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  117. UA_BrowsePathResult_deleteMembers(&bpr);
  118. return false;
  119. }
  120. /* Get the EventType Property Node */
  121. UA_Variant tOutVariant;
  122. UA_Variant_init(&tOutVariant);
  123. /* Read the Value of EventType Property Node (the Value should be a NodeId) */
  124. UA_StatusCode retval =
  125. UA_Server_readValue(server, bpr.targets[0].targetId.nodeId, &tOutVariant);
  126. if(retval != UA_STATUSCODE_GOOD ||
  127. !UA_Variant_hasScalarType(&tOutVariant, &UA_TYPES[UA_TYPES_NODEID])) {
  128. UA_BrowsePathResult_deleteMembers(&bpr);
  129. return false;
  130. }
  131. const UA_NodeId *tEventType = (UA_NodeId*)tOutVariant.data;
  132. /* Make sure the EventType is not a Subtype of CondtionType
  133. * First check for filter set using UaExpert
  134. * (ConditionId Clause won't be present in Events, which are not Conditions)
  135. * Second check for Events which are Conditions or Alarms (Part 9 not supported yet) */
  136. UA_NodeId conditionTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CONDITIONTYPE);
  137. UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  138. if(UA_NodeId_equal(validEventParent, &conditionTypeId) ||
  139. isNodeInTree(server->nsCtx, tEventType, &conditionTypeId, &hasSubtypeId, 1)) {
  140. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND,
  141. "Alarms and Conditions are not supported yet!");
  142. UA_BrowsePathResult_deleteMembers(&bpr);
  143. UA_Variant_deleteMembers(&tOutVariant);
  144. return false;
  145. }
  146. /* check whether Valid Event other than Conditions */
  147. UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
  148. UA_Boolean isSubtypeOfBaseEvent = isNodeInTree(server->nsCtx, tEventType,
  149. &baseEventTypeId, &hasSubtypeId, 1);
  150. UA_BrowsePathResult_deleteMembers(&bpr);
  151. UA_Variant_deleteMembers(&tOutVariant);
  152. return isSubtypeOfBaseEvent;
  153. }
  154. /* Part 4: 7.4.4.5 SimpleAttributeOperand
  155. * The clause can point to any attribute of nodes. Either a child of the event
  156. * node and also the event type. */
  157. static UA_StatusCode
  158. resolveSimpleAttributeOperand(UA_Server *server, UA_Session *session, const UA_NodeId *origin,
  159. const UA_SimpleAttributeOperand *sao, UA_Variant *value) {
  160. /* Prepare the ReadValueId */
  161. UA_ReadValueId rvi;
  162. UA_ReadValueId_init(&rvi);
  163. rvi.indexRange = sao->indexRange;
  164. rvi.attributeId = sao->attributeId;
  165. /* If this list (browsePath) is empty the Node is the instance of the
  166. * TypeDefinition. */
  167. if(sao->browsePathSize == 0) {
  168. rvi.nodeId = sao->typeDefinitionId;
  169. UA_DataValue v = UA_Server_readWithSession(server, session, &rvi,
  170. UA_TIMESTAMPSTORETURN_NEITHER);
  171. if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
  172. *value = v.value;
  173. return v.status;
  174. }
  175. /* Resolve the browse path */
  176. UA_BrowsePathResult bpr =
  177. UA_Server_browseSimplifiedBrowsePath(server, *origin, sao->browsePathSize,
  178. sao->browsePath);
  179. if(bpr.targetsSize == 0 && bpr.statusCode == UA_STATUSCODE_GOOD)
  180. bpr.statusCode = UA_STATUSCODE_BADNOTFOUND;
  181. if(bpr.statusCode != UA_STATUSCODE_GOOD) {
  182. UA_StatusCode retval = bpr.statusCode;
  183. UA_BrowsePathResult_deleteMembers(&bpr);
  184. return retval;
  185. }
  186. /* Read the first matching element. Move the value to the output. */
  187. rvi.nodeId = bpr.targets[0].targetId.nodeId;
  188. UA_DataValue v = UA_Server_readWithSession(server, session, &rvi,
  189. UA_TIMESTAMPSTORETURN_NEITHER);
  190. if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
  191. *value = v.value;
  192. UA_BrowsePathResult_deleteMembers(&bpr);
  193. return v.status;
  194. }
  195. /* Filters the given event with the given filter and writes the results into a
  196. * notification */
  197. static UA_StatusCode
  198. UA_Server_filterEvent(UA_Server *server, UA_Session *session,
  199. const UA_NodeId *eventNode, UA_EventFilter *filter,
  200. UA_EventNotification *notification) {
  201. if (filter->selectClausesSize == 0)
  202. return UA_STATUSCODE_BADEVENTFILTERINVALID;
  203. UA_EventFieldList_init(&notification->fields);
  204. /* EventFilterResult isn't being used currently
  205. UA_EventFilterResult_init(&notification->result); */
  206. notification->fields.eventFields = (UA_Variant *)
  207. UA_Array_new(filter->selectClausesSize, &UA_TYPES[UA_TYPES_VARIANT]);
  208. if(!notification->fields.eventFields) {
  209. /* EventFilterResult currently isn't being used
  210. UA_EventFiterResult_deleteMembers(&notification->result); */
  211. return UA_STATUSCODE_BADOUTOFMEMORY;
  212. }
  213. notification->fields.eventFieldsSize = filter->selectClausesSize;
  214. /* EventFilterResult currently isn't being used
  215. notification->result.selectClauseResultsSize = filter->selectClausesSize;
  216. notification->result.selectClauseResults = (UA_StatusCode *)
  217. UA_Array_new(filter->selectClausesSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  218. if(!notification->result->selectClauseResults) {
  219. UA_EventFieldList_deleteMembers(&notification->fields);
  220. UA_EventFilterResult_deleteMembers(&notification->result);
  221. return UA_STATUSCODE_BADOUTOFMEMORY;
  222. }
  223. */
  224. /* Apply the filter */
  225. /* Check if the browsePath is BaseEventType, in which case nothing more
  226. * needs to be checked */
  227. UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
  228. for(size_t i = 0; i < filter->selectClausesSize; i++) {
  229. if(!UA_NodeId_equal(&filter->selectClauses[i].typeDefinitionId, &baseEventTypeId) &&
  230. !isValidEvent(server, &filter->selectClauses[i].typeDefinitionId, eventNode)) {
  231. UA_Variant_init(&notification->fields.eventFields[i]);
  232. /* EventFilterResult currently isn't being used
  233. notification->result.selectClauseResults[i] = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; */
  234. continue;
  235. }
  236. /* TODO: Put the result into the selectClausResults */
  237. resolveSimpleAttributeOperand(server, session, eventNode,
  238. &filter->selectClauses[i],
  239. &notification->fields.eventFields[i]);
  240. }
  241. return UA_STATUSCODE_GOOD;
  242. }
  243. static UA_StatusCode
  244. eventSetStandardFields(UA_Server *server, const UA_NodeId *event,
  245. const UA_NodeId *origin, UA_ByteString *outEventId) {
  246. /* Set the SourceNode */
  247. UA_StatusCode retval;
  248. UA_QualifiedName name = UA_QUALIFIEDNAME(0, "SourceNode");
  249. UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
  250. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  251. retval = bpr.statusCode;
  252. UA_BrowsePathResult_deleteMembers(&bpr);
  253. return retval;
  254. }
  255. UA_Variant value;
  256. UA_Variant_init(&value);
  257. UA_Variant_setScalarCopy(&value, origin, &UA_TYPES[UA_TYPES_NODEID]);
  258. retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  259. UA_Variant_deleteMembers(&value);
  260. UA_BrowsePathResult_deleteMembers(&bpr);
  261. if(retval != UA_STATUSCODE_GOOD)
  262. return retval;
  263. /* Set the ReceiveTime */
  264. name = UA_QUALIFIEDNAME(0, "ReceiveTime");
  265. bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
  266. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  267. retval = bpr.statusCode;
  268. UA_BrowsePathResult_deleteMembers(&bpr);
  269. return retval;
  270. }
  271. UA_DateTime rcvTime = UA_DateTime_now();
  272. UA_Variant_setScalar(&value, &rcvTime, &UA_TYPES[UA_TYPES_DATETIME]);
  273. retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  274. UA_BrowsePathResult_deleteMembers(&bpr);
  275. if(retval != UA_STATUSCODE_GOOD)
  276. return retval;
  277. /* Set the EventId */
  278. UA_ByteString eventId = UA_BYTESTRING_NULL;
  279. retval = generateEventId(&eventId);
  280. if(retval != UA_STATUSCODE_GOOD)
  281. return retval;
  282. name = UA_QUALIFIEDNAME(0, "EventId");
  283. bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
  284. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
  285. retval = bpr.statusCode;
  286. UA_ByteString_deleteMembers(&eventId);
  287. UA_BrowsePathResult_deleteMembers(&bpr);
  288. return retval;
  289. }
  290. UA_Variant_init(&value);
  291. UA_Variant_setScalar(&value, &eventId, &UA_TYPES[UA_TYPES_BYTESTRING]);
  292. retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
  293. UA_BrowsePathResult_deleteMembers(&bpr);
  294. if(retval != UA_STATUSCODE_GOOD) {
  295. UA_ByteString_deleteMembers(&eventId);
  296. return retval;
  297. }
  298. /* Return the EventId */
  299. if(outEventId)
  300. *outEventId = eventId;
  301. else
  302. UA_ByteString_deleteMembers(&eventId);
  303. return UA_STATUSCODE_GOOD;
  304. }
  305. /* Filters an event according to the filter specified by mon and then adds it to
  306. * mons notification queue */
  307. static UA_StatusCode
  308. UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event,
  309. UA_MonitoredItem *mon) {
  310. UA_Notification *notification = (UA_Notification *) UA_malloc(sizeof(UA_Notification));
  311. if(!notification)
  312. return UA_STATUSCODE_BADOUTOFMEMORY;
  313. /* Get the session */
  314. UA_Subscription *sub = mon->subscription;
  315. UA_Session *session = sub->session;
  316. /* Apply the filter */
  317. UA_StatusCode retval = UA_Server_filterEvent(server, session, event,
  318. &mon->filter.eventFilter,
  319. &notification->data.event);
  320. if(retval != UA_STATUSCODE_GOOD) {
  321. UA_free(notification);
  322. return retval;
  323. }
  324. /* Enqueue the notification */
  325. notification->mon = mon;
  326. UA_Notification_enqueue(server, mon->subscription, mon, notification);
  327. return UA_STATUSCODE_GOOD;
  328. }
  329. static const UA_NodeId objectsFolderId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}};
  330. static const UA_NodeId parentReferences_events[2] =
  331. {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}},
  332. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}};
  333. UA_StatusCode
  334. UA_Server_triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_NodeId origin,
  335. UA_ByteString *outEventId, const UA_Boolean deleteEventNode) {
  336. /* Check that the origin node exists */
  337. const UA_Node *originNode = UA_Nodestore_getNode(server->nsCtx, &origin);
  338. if(!originNode) {
  339. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND,
  340. "Origin node for event does not exist.");
  341. return UA_STATUSCODE_BADNOTFOUND;
  342. }
  343. UA_Nodestore_releaseNode(server->nsCtx, originNode);
  344. /* Make sure the origin is in the ObjectsFolder (TODO: or in the ViewsFolder) */
  345. if(!isNodeInTree(server->nsCtx, &origin, &objectsFolderId,
  346. parentReferences_events, 2)) {
  347. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND,
  348. "Node for event must be in ObjectsFolder!");
  349. return UA_STATUSCODE_BADINVALIDARGUMENT;
  350. }
  351. UA_StatusCode retval = eventSetStandardFields(server, &eventNodeId, &origin, outEventId);
  352. if(retval != UA_STATUSCODE_GOOD) {
  353. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  354. "Events: Could not set the standard event fields with StatusCode %s",
  355. UA_StatusCode_name(retval));
  356. return retval;
  357. }
  358. /* Get the parents */
  359. UA_ExpandedNodeId *parents = NULL;
  360. size_t parentsSize = 0;
  361. retval = browseRecursive(server, 1, &origin, 2, parentReferences_events,
  362. UA_BROWSEDIRECTION_INVERSE, true, &parentsSize, &parents);
  363. if(retval != UA_STATUSCODE_GOOD) {
  364. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  365. "Events: Could not create the list of nodes listening on the "
  366. "event with StatusCode %s", UA_StatusCode_name(retval));
  367. return retval;
  368. }
  369. /* Add the event to each node's monitored items */
  370. for(size_t i = 0; i < parentsSize; i++) {
  371. const UA_ObjectNode *node = (const UA_ObjectNode*)
  372. UA_Nodestore_getNode(server->nsCtx, &parents[i].nodeId);
  373. if(!node)
  374. continue;
  375. if(node->nodeClass != UA_NODECLASS_OBJECT) {
  376. UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)node);
  377. continue;
  378. }
  379. UA_MonitoredItem *monIter = node->monitoredItemQueue;
  380. for(; monIter != NULL; monIter = monIter->next) {
  381. retval = UA_Event_addEventToMonitoredItem(server, &eventNodeId, monIter);
  382. if(retval != UA_STATUSCODE_GOOD)
  383. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  384. "Events: Could not add the event to a listening node with StatusCode %s",
  385. UA_StatusCode_name(retval));
  386. }
  387. UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)node);
  388. }
  389. UA_Array_delete(parents, parentsSize, &UA_TYPES[UA_TYPES_NODEID]);
  390. /* Delete the node representation of the event */
  391. if(deleteEventNode) {
  392. retval = UA_Server_deleteNode(server, eventNodeId, true);
  393. if(retval != UA_STATUSCODE_GOOD) {
  394. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  395. "Attempt to remove event using deleteNode failed. StatusCode %s",
  396. UA_StatusCode_name(retval));
  397. return retval;
  398. }
  399. }
  400. return UA_STATUSCODE_GOOD;
  401. }
  402. #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */