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;
}