UA Ansi C Server Professional  1.3.1.232
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
Alarms & Events

Overview

The SDK allows a provider to create and emit events, also new event types can be created and instantiated. After an event is emitted by the provider, the SDK will forward it to the according clients, considering Select- and WhereClause. For condition type events and their subtypes, calls to the 'ConditionRefresh' method of the ConditionType are handled completely by the SDK.

Contents:

Areas

The methods UaServer_Events_RegisterEventNotifier and UaServer_Events_RegisterEventSource are provided for registering new event notifier and event source nodes. With the server object as root node, a hierarchy of these nodes can be set up to control which events are sent to which monitored item. EventNotifiers can be monitored for events, EventSources can not be monitored and emit their events via their parent EventNotifier. The following example illustrates how such hierarchy works:

Server -+- 'HasNotifier' --- Area1 -+- 'HasNotifier' --- DeviceA -+- 'HasEventSource' --- SensorA1
| | |
| | +- 'HasEventSource' --- SensorA2
| |
| |
| +- 'HasNotifier' --- DeviceB --- 'HasEventSource' --- SensorB
|
|
+- 'HasNotifier' --- DeviceC --- 'HasEventSource' --- SensorC

If the Server object is monitored, all events from all event notifiers will be received. To receive only events from devices A and B, the Area1 object has to be monitored as it does not include device C. If only events of sensor B shall be received, device B has to be monitored. In this example, the sensors can not be monitored, as they all are event sources.

The hierarchy of event notifiers and sources has to be configured in the SDK by the provider, then the SDK is able to forward events to the according monitored items depending on the event notifier hierarchy and the items that are monitored.

Creating and emitting events

To create a new event of a certain type, the method UaServer_Events_CreateEvent is used:

UaServer_Event *pEvent = OpcUa_Null;
OpcUa_NodeId nodeId;
OpcUa_NodeId_Initialize(&nodeId);
nodeId.Identifier.Numeric = OpcUaId_BaseEventType;
pEvent = UaServer_Events_CreateEvent(&nodeId);

For all event fields of the BaseEventType there are convenient getters and setters (e.g. UaServer_Events_SetEventId), all other event fields of an event are set with UaServer_Events_SetEventField. The local indices needed for calling this method are available in uaserver_events_types.h.

OpcUa_ByteString bsEventId;
OpcUa_DateTime time = OpcUa_DateTime_UtcNow();
OpcUa_ByteString_Initialize(&bsEventId);
UaServer_Events_CreateEventId(OpcUa_Null, &bsEventId);
UaServer_Events_SetEventId(pEvent, &bsEventId);
OpcUa_ByteString_Clear(&bsEventId);
OpcUa_NodeId_Initialize(&nodeId);
nodeId.Identifier.Numeric = OpcUaId_BaseEventType;
OpcUa_NodeId_Initialize(&nodeId);
nodeId.Identifier.Numeric = OpcUaId_Server;
UaServer_Events_SetSourceName(pEvent, "base event source name");
UaServer_Events_SetMessage(pEvent, "de", "base event message");

After all event fields are filled, the event can be fired with UaServer_Events_FireEvent. The SDK then propagates the event to all subscriptions and queues the event for the monitored items that shall receive the event, considering their ItemToMonitor and EventFilter:

OpcUa_StatusCode result;
result = UaServer_Events_FireEvent(pEvent);

Creating and emitting condition type events

For events of the ConditionType and its subtypes, the provider has to store the fired events to be able to fire them again when state changes occur. This also allows the provider to respond to methods such as Enable, Disable, AddComment etc. Creating ConditionType events works the same way as for other event types, the event just has to be stored additionally:

// global pointer to ConditionTypeEvent
UaServer_Event *g_pConditionTypeEvent;
// this function is called the first time the event should be fired
OpcUa_Void ConditionTypeEvent_Create()
{
OpcUa_NodeId nodeId;
OpcUa_NodeId_Initialize(&nodeId);
nodeId.Identifier.Numeric = OpcUaId_ConditionType;
g_pConditionTypeEvent = UaServer_Events_CreateEvent(&nodeId);
}

The event fields of the event should be filled with reasonable values before firing it. If a state change occurs and the event is fired again, only the event fields that changed have to be filled:

// this function is called if AddComment has been called on the ConditionTypeEvent
OpcUa_Void ConditionTypeEvent_AddComment(OpcUa_LocalizedText *a_pComment)
{
OpcUa_StatusCode result;
OpcUa_Variant value;
OpcUa_Variant_Initialize(&value);
// set the comment event field
value.Datatype = OpcUaType_LocalizedText;
value.Value.LocalizedText = a_pComment;
result = UaServer_Events_SetEventField(g_pConditionTypeEvent, ConditionTypeField_Comment, &value);
OpcUa_Variant_Initialize(&value);
result = UaServer_Events_FireEvent(g_pConditionTypeEvent);
}

After being fired the first time, events of the ConditionType and its subtypes will be stored in a separate list by the SDK to be able to react to calls to the ConditionRefresh method. Hence, the provider doesn't have to handle calls of the ConditionRefresh method.

The event can be deleted if it's not expected to be fired again, for example when the provider shuts down:

// this function is called when the prvoder shuts down
OpcUa_Void ConditionTypeEvent_Delete()
{
UaServer_Events_DeleteEvent(&g_pConditionTypeEvent);
g_pConditionTypeEvent = OpcUa_Null;
}

Creating new event types

New event types are created with UaServer_Events_RegisterEventType. The UaServer_EventTypeDefinition passed into this method consists of following elements:

OpcUa_NodeId EventTypeId;
OpcUa_NodeId ParentEventTypeId;
OpcUa_UInt32 NoOfEventFields;
UaServer_EventField *EventFields;

The EventTypeId field defines the NodeId of the new event type, the ParentEventTypeId field has to be set to the NodeId of the new type's parent event type. Every event type has to be a subtype of the BaseEventType, so the ParentEventTypeId field has to contain the NodeId of an existing event type.

The NoOfEventFields field is set to the number of event fields of the new type, the EventFields field contains its EventField definitions. This does not include the event fields of inherited event types, only the type's new EventFields have to be declared here.

Following example illustrates how to create a new event type that is a subtype of the BaseEventType and how to register two EventFields for it:

OpcUa_NodeId eventTypeId, parentEventTypeId;
// register event type
OpcUa_NodeId_Initialize(&eventTypeId);
OpcUa_NodeId_Initialize(&parentEventTypeId);
eventTypeId.NamespaceIndex = g_UaProviderSample_uNamespaceIndex;
eventTypeId.Identifier.Numeric = 50000;
parentEventTypeId.NamespaceIndex = 0;
parentEventTypeId.Identifier.Numeric = OpcUaId_BaseEventType;
UaServer_Events_RegisterEventType(&eventTypeId, &parentEventTypeId);
// register event fields
UaServer_EventField_Initialize(&eventField);
eventField.BrowsePath.NoOfElements = 1;
eventField.BrowsePath.Elements = (OpcUa_QualifiedName*)OpcUa_Alloc(sizeof(OpcUa_QualifiedName));
OpcUa_QualifiedName_Initialize(&eventField.BrowsePath.Elements[0]);
eventField.BrowsePath.Elements[0].NamespaceIndex = g_UaProviderSample_uNamespaceIndex;
OpcUa_String_AttachReadOnly(&eventField.BrowsePath.Elements[0].Name, "MyEventField1");
g_SampleEventTypeField_MyEventField1 = UaServer_Events_RegisterEventField(&eventTypeId, &eventField);
OpcUa_String_AttachReadOnly(&eventField.BrowsePath.Elements[0].Name, "MyEventField2");
g_SampleEventTypeField_MyEventField2 = UaServer_Events_RegisterEventField(&eventTypeId, &eventField);

Now, the event type can be instantiated by its event type ID with UaServer_Events_CreateEvent and the EventFields can be set using the local indices returned by UaServer_Events_RegisterEventField.

UaServer_Event *pEvent = OpcUa_Null;
OpcUa_NodeId nodeId;
OpcUa_Variant value;
// create the event
OpcUa_NodeId_Initialize(&nodeId);
nodeId.NamespaceIndex = g_UaProviderSample_uNamespaceIndex;
nodeId.Identifier.Numeric = 50000;
pEvent = UaServer_Events_CreateEvent(&nodeId);
// set the BaseEventType EventFields
UaServer_Events_SetSourceName(pEvent, "base event source name");
...
// set the new event type's event fields
OpcUa_Variant_Initialize(&value);
value.Datatype = OpcUaType_UInt32;
value.Value.UInt32 = 10;
UaServer_Events_SetEventField(pEvent, g_SampleEventTypeField_MyEventField1, &value);
OpcUa_Variant_Clear(&value);
value.Datatype = OpcUaType_String;
OpcUa_String_Initialize(&value.Value.String);
OpcUa_String_AttachReadOnly(&value.Value.String, "MyEventField2 information");
UaServer_Events_SetEventField(pEvent, g_SampleEventTypeField_MyEventField2, &value);
OpcUa_Variant_Clear(&value);
// fire the event
// delete the event