1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
/**
* Generating events
* -----------------
* To make sense of the many things going on in a server, monitoring items can
* be useful. Though in many cases, data change does not convey enough
* information to be the optimal solution. Events can be generated at any time,
* hold a lot of information and can be filtered so the client only receives the
* specific attributes of interest.
*
* Emitting events by calling methods
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The following example will be based on the server method tutorial. We will be
* creating a method node which generates an event from the server node.
*
* The event we want to generate should be very simple. Since the `BaseEventType` is abstract,
* we will have to create our own event type. `EventTypes` are saved internally as `ObjectTypes`,
* so add the type as you would a new `ObjectType`. */
static UA_NodeId eventType;
static UA_StatusCode
addNewEventType(UA_Server *server) {
UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "SimpleEventType");
attr.description = UA_LOCALIZEDTEXT("en-US", "The simple event type we created");
return UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
UA_QUALIFIEDNAME(0, "SimpleEventType"),
attr, NULL, &eventType);
}
/**
* Setting up an event
* ^^^^^^^^^^^^^^^^^^^
* In order to set up the event, we can first use ``UA_Server_createEvent`` to
* give us a node representation of the event. All we need for this is our
* `EventType`. Once we have our event node, which is saved internally as an
* `ObjectNode`, we can define the attributes the event has the same way we
* would define the attributes of an object node. It is not necessary to define
* the attributes `EventId`, `ReceiveTime`, `SourceNode` or `EventType` since
* these are set automatically by the server. In this example, we will be
* setting the fields 'Message' and 'Severity' in addition to `Time` which is
* needed to make the example UaExpert compliant.
*/
static UA_StatusCode
setUpEvent(UA_Server *server, UA_NodeId *outId) {
UA_StatusCode retval = UA_Server_createEvent(server, eventType, outId);
if (retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"createEvent failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/* Set the Event Attributes */
/* Setting the Time is required or else the event will not show up in UAExpert! */
UA_DateTime eventTime = UA_DateTime_now();
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Time"),
&eventTime, &UA_TYPES[UA_TYPES_DATETIME]);
UA_UInt16 eventSeverity = 100;
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Severity"),
&eventSeverity, &UA_TYPES[UA_TYPES_UINT16]);
UA_LocalizedText eventMessage = UA_LOCALIZEDTEXT("en-US", "An event has been generated.");
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Message"),
&eventMessage, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
UA_String eventSourceName = UA_STRING("Server");
UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "SourceName"),
&eventSourceName, &UA_TYPES[UA_TYPES_STRING]);
return UA_STATUSCODE_GOOD;
}
/**
* Triggering an event
* ^^^^^^^^^^^^^^^^^^^
* First a node representing an event is generated using ``setUpEvent``. Once
* our event is good to go, we specify a node which emits the event - in this
* case the server node. We can use ``UA_Server_triggerEvent`` to trigger our
* event onto said node. Passing ``NULL`` as the second-last argument means we
* will not receive the `EventId`. The last boolean argument states whether the
* node should be deleted. */
static UA_StatusCode
generateEventMethodCallback(UA_Server *server,
const UA_NodeId *sessionId, void *sessionHandle,
const UA_NodeId *methodId, void *methodContext,
const UA_NodeId *objectId, void *objectContext,
size_t inputSize, const UA_Variant *input,
size_t outputSize, UA_Variant *output) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Creating event");
/* set up event */
UA_NodeId eventNodeId;
UA_StatusCode retval = setUpEvent(server, &eventNodeId);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Creating event failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
retval = UA_Server_triggerEvent(server, eventNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
NULL, UA_TRUE);
if(retval != UA_STATUSCODE_GOOD)
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Triggering event failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/**
* Now, all that is left to do is to create a method node which uses our
* callback. We do not require any input and as output we will be using the
* `EventId` we receive from ``triggerEvent``. The `EventId` is generated by the
* server internally and is a random unique ID which identifies that specific
* event.
*
* This method node will be added to a basic server setup.
*/
static void
addGenerateEventMethod(UA_Server *server) {
UA_MethodAttributes generateAttr = UA_MethodAttributes_default;
generateAttr.description = UA_LOCALIZEDTEXT("en-US","Generate an event.");
generateAttr.displayName = UA_LOCALIZEDTEXT("en-US","Generate Event");
generateAttr.executable = true;
generateAttr.userExecutable = true;
UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, 62541),
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_QUALIFIEDNAME(1, "Generate Event"),
generateAttr, &generateEventMethodCallback,
0, NULL, 0, NULL, NULL, NULL);
}
/** It follows the main server code, making use of the above definitions. */
int main(void) {
UA_Server *server = UA_Server_new();
addNewEventType(server);
addGenerateEventMethod(server);
UA_Server_runUntilInterrupt(server);
UA_Server_delete(server);
return 0;
}
|