High Performance OPC UA Server SDK
1.4.1.263
|
This lesson explains how to add own event types and fire events.
Files used in this lesson:
The server from the previous lesson is extended by a new event type and an event notifier. However it does not require knowledge about methods and can also be done based on Lesson 2.
In this lesson the new nodes are created manually in code to accomplish a better understanding. This is feasible for a very small number of event types and event notifiers, but for a higher quantity of nodes it is recommended to use an engineering tool and load the namespace as shown in Sensor Model Server.
For customers not interrested in the eventing functionality, but a minimum of resource usage it is also possible to disable events completely by setting the cmake option UASERVER_SUPPORT_EVENTS to OFF.
All event types are direct or indirect subtypes of the abstract BaseEventType which is below the EventTypes folder. There are already a lot of event types defined by the OPC UA Specification, which can be used in many use-cases. But to show how new event types are created, this lesson will add a new event type, it is called NewValueEventType and will be fired when the value attribute of the variables created in Lesson 2 is written.
Creating the new event type is implemented in custom_provider_event.c in the function custom_provider_create_eventtypes() and is rather straightforward, create a new node, that is a subtype of BaseEventType and of nodeclass Objecttype:
The only nodeclass specific attribute is IsAbstract, which is set to false as instances of this event will be fired. Also the node handle for this the new event type is saved in a global variable for later use.
When firing the event, we also want to attach custom data to the event, so we also need to add a new event field to the newly created event. This field is called NewValue and is intended for the new value, that was written to the value attribute. It is implemented by adding a new property to the event type:
What is important here is the browsename, it is used to identify the event field, both by clients when subscribing for events and by the server when setting the value of the field. The datatype is set to UINT32 as this is the datatype of the variables the event is used for.
For the example another event type is added, the OutOfRangeEventType. It is a subtype of the NewValueEventType and fired when a new value is written, but the value is out of range of the EURange property of the node. Creating the new event type works similar to creating the NewValueEventType, the main difference is the parent of the new node which is not the BaseEventType, but the NewValueEventType:
The OutOfRangeEventType has no own event field, but inherits the NewValue event field from the NewValueEventType. Both created event types (like every other event type) also inherit the event fields from the BaseEventType, this will be explained in more detail when firing an event.
When a client wants to receive events, it needs to subscribe to an event notifier. These are nodes of nodeclass Object, which are hierarchically organized, where the top level node of this hierarchy is the Server object (ns=0;i=2253). An extended description and example regarding this hierarchy can be found in the OPC UA Specification, Part 3, Section HasNotifier.
For the example an event notifier called MyEventNotifier is created inside the CustomNodes folder. The attribute EventNotifier is set to indicate the node can be used to subscribe to events, the node is also added in the event notifier hierarchy directly below the Server object, this is implemented in the function custom_provider_create_eventnotifier():
Each event also requires a source node, the source nodes are connected to the event notifier with reference from type HasEventSource. In this example all nodes of class Variable are possible event sources for MyEventNotifier, so references from MyEventNotifier to each of these variables are added:
In cases where there is no distinct source node for an event, it is also possible to use the event notifier itself as the source node.
The event fields of the new event types and the created event notifiers need to be registered at the SDK. This can be done for each field/notifier individually by using uaserver_eventfield_register and uaserver_eventnotifier_register or by using the *_register_all
functions to register all fields/notifiers from a given namespace in a single function call. The latter is especially useful when loading the addressspace from a file created by an engineering tool. In this case all event fields/notifiers must still be registered, but it can be achieved with few lines of code, independent of the amount of nodes in the addressspace.
In the example there is a function for initializing the event functionality, custom_provider_event_init()
, which is called at provider initialization. It call the functions custom_provider_create_eventtypes()
and custom_provider_create_eventnotifier()
explained above and calls the *_register_all
functions to register the event fields/notifiers. Furthermore, the handle for the NewValue event field is looked up and saved in a global variable for later use:
The counterpart is the custom_provider_event_clear()
function, which invalidates the handles saved in global variables and unregisters event fields/notifiers:
The number of event fields and event notifiers is limited by the settings subscription.num_eventfields and subscription.num_eventnotifiers. These settings determine the total number of event fields/notifiers from all namespaces including namespace zero.
Firing events is implemented in the function custom_provider_event_notifiy_value_change():
This function is called whenever a value is written to the custom store implemented in custom_provider_store.c by custom_store_attach_value()
:
Every time a new value is written to a node this function will fire a new event for the respective node. The function receives the new value and whether it is out of range of the EURange, depending on this information a different type of Event is created and fired.
At the beginning of the function the event structure is created, this requires the event type and the event source. As event type one of the global variables set in custom_provider_create_eventtypes() above is used, depending if the value is out of range. It would also be possible to look up the node handle dynamically, but doing the lookup only once in the server lifetime is more efficient. As source node the node handle provided by the caller is used.
Each event has multiple event fields based on its event type, these fields are inherited from supertypes and each event type can have its own fields. As every event is inherited from the BaseEventType, every event has its fields. Most of these event fields are mandatory and thus initialized at event creation with reasonable values. So the application is not required to set any of these event fields, but can use convenience functions from event to overwrite the default values. In the example only in case the value is out of range, the severity is set to a rather high value of 700, else the rather low severity of 200 is kept:
The event has one event field, that is inherited from BaseEventType, the NewValue event field. To set this field first a ua_variant must be prepared with the value, next the variant must be copied into the event structure. Event fields are identified SDK internally by unique handles, to set the value of the NewValue event field, there are multiple alternatives:
To set the event field there are add and overwrite functions. When first setting the event field, the add function should be used, when overwriting an already set field, the overwrite function must be used.
The event can now be fired and is deleted afterwards:
After building the example and connecting with UAExpert the Event View can be opened by clicking Document->Add->Event View->Add. In the top most window event notifiers can be added via drag and drop, for the example either the Server or MyEventNotifier object.
The event fields to subscribe can be changed by clicking the little arrow on the left of the event notifier, selecting the desired fields like SimpleEvents->NewValueEventType->NewValue and clicking Apply.
When now writing values to Variable1, Variable2 or Variable3 events are fired and can be viewed in the middle window. Depending on the value written different types of events with different severity will appear. The bottom window shows the values of the subscribed event fields including the NewValue field.