Step 1: Create a Subscription with a Monitored Item
A client can subscribe to different types of monitored items (see OPC UA Subscription Concept). In Lesson 2: Create DataMonitoredItem a subscription containing a data monitored item was created. The next two steps explain how to create an event monitored item.
The monitored item is created in SampleSubscription::createMonitoredItem. We remove the code which creates data monitored items from configuration and create a single monitored item for the EventNotifier instead:
Replace these lines
OpcUa_UInt32 size, i;
ServiceSettings serviceSettings;
UaNodeIdArray lstNodeIds = m_pConfiguration->getNodesToMonitor();
size = lstNodeIds.length();
for (i = 0; i < size; i++)
{
itemsToCreate[i].ItemToMonitor.AttributeId = OpcUa_Attributes_Value;
OpcUa_NodeId_CopyTo(&lstNodeIds[i], &itemsToCreate[i].ItemToMonitor.NodeId);
itemsToCreate[i].RequestedParameters.ClientHandle = i;
itemsToCreate[i].RequestedParameters.SamplingInterval = 500;
itemsToCreate[i].RequestedParameters.QueueSize = 1;
itemsToCreate[i].RequestedParameters.DiscardOldest = OpcUa_True;
}
with the following code
OpcUa_UInt32 i;
ServiceSettings serviceSettings;
itemsToCreate[0].ItemToMonitor.AttributeId = OpcUa_Attributes_EventNotifier;
itemsToCreate[0].ItemToMonitor.NodeId.Identifier.Numeric = OpcUaId_Server;
itemsToCreate[0].RequestedParameters.ClientHandle = 0;
itemsToCreate[0].RequestedParameters.SamplingInterval = 0;
itemsToCreate[0].RequestedParameters.QueueSize = 0;
itemsToCreate[0].RequestedParameters.DiscardOldest = OpcUa_True;
This code creates a data monitored item for the EventNotifier. To actually monitor events, it is necessary to set the EventFilter for this monitored item. This will be explained in the next step.
Step 2: Set an Event Filter for an Event Monitored Item
Event filters can be used to restrict the events to be recieved. This step explains how to set an event filter to receive only events of type ControllerEventType and how to specify the event fields to include in the notification message.
We read the event type to filter from the configuration file. Add the following code to configuration.h
...
public:
...
...
private:
...
...
Add the method getEventTypeToFilter to configuration.cpp:
UaNodeId Configuration::getEventTypeToFilter()
const
{
return m_eventTypeToFilter;
}
We extend the method Configuration::loadConfiguration to read the event type for filtering from the configuration file.
Extend Configuration::updateNamespaceIndexes:
...
for (i = 0; i < m_nodesToMonitor.length(); i++)
{
m_nodesToMonitor[i].NamespaceIndex = mappingTable[m_nodesToMonitor[i].NamespaceIndex];
}
...
The event filter and the event fields have to be added to SampleSubscription::createMonitoredItems
...
itemsToCreate.create(1);
itemsToCreate[0].ItemToMonitor.AttributeId = OpcUa_Attributes_EventNotifier;
itemsToCreate[0].ItemToMonitor.NodeId.Identifier.Numeric = OpcUaId_Server;
itemsToCreate[0].RequestedParameters.ClientHandle = 0;
itemsToCreate[0].RequestedParameters.SamplingInterval = 0;
itemsToCreate[0].RequestedParameters.QueueSize = 0;
itemsToCreate[0].RequestedParameters.DiscardOldest = OpcUa_True;
eventFilter.
detachFilter(itemsToCreate[0].RequestedParameters.Filter);
printf("\nAdding monitored items to subscription ...\n");
...
Step 3: Receive Event Notifications via UaSubscriptionCallback::newEvents()
To actually receive events, we have to modify SampleSubscription::newEvents. Replace the existing Method with the following code:
void SampleSubscription::newEvents(
OpcUa_UInt32 clientSubscriptionHandle,
{
OpcUa_UInt32 i = 0;
printf("-- Event newEvents -----------------------------------------\n");
printf("clientSubscriptionHandle %d \n", clientSubscriptionHandle);
for ( i=0; i<eventFieldList.length(); i++ )
{
UaVariant message = eventFieldList[i].EventFields[0];
UaVariant sourceName = eventFieldList[i].EventFields[1];
UaVariant severity = eventFieldList[i].EventFields[2];
printf("Event[%d] Message = %s SourceName = %s Severity = %s\n",
i,
}
printf("------------------------------------------------------------\n");
}
See the documentation for UaSubscriptionCallback::newEvents. Event fields are set in Step 2.
Update main:
Replace
{
pMyClient->registerNodes();
pMyClient->writeRegistered();
printf("\n*************************************************************************\n");
printf("* Press Enter to create a subscription\n");
printf("* Once you see DataChange Notifications - kill and restart the server.\n");
printf("* You will see the client reconnect and recover the subscription.\n");
printf("* Press Enter again to stop the subscription\n");
printf("*************************************************************************\n");
getchar();
status = pMyClient->subscribe();
{
getchar();
status = pMyClient->unsubscribe();
}
pMyClient->writeRegistered();
pMyClient->unregisterNodes();
with
{
printf("\n*************************************************************************\n");
printf("* Press Enter to create a subscription\n");
printf("* Press Enter again to stop the subscription\n");
printf("*************************************************************************\n");
getchar();
status = pMyClient->subscribe();
{
getchar();
status = pMyClient->unsubscribe();
}
Step 4: Call Methods to Trigger Events on Demoserver
This step explains how to call a method on an object. To test the event monitored item set up in the previous steps, we will call a method that triggers an event that matches our filter criterium: Start/Stop an AirConditionerController object.
The method and object to call is read from the configuration file. Add the following code to configuration.h
...
public:
...
...
private:
...
Add the methods Configuration::getMethodsToCall and Configuration::getObjectsToCall to configuration.cpp.
{
return m_methodsToCall;
}
{
return m_objectToCall;
}
Extend Configuration::loadConfiguration to read the method to call and the object to call the method on from the configuration.
value = pSettings->
value(
"size", (OpcUa_UInt32)0);
for (i=0; i<size;i++)
{
}
Extend Configuration::updateNamespaceIndexes
for (i = 0; i < m_methodsToCall.length(); i++)
{
m_methodsToCall[i].NamespaceIndex = mappingTable[m_methodsToCall[i].NamespaceIndex];
}
for (i = 0; i < m_objectToCall.length(); i++)
{
m_objectToCall[i].NamespaceIndex = mappingTable[m_objectToCall[i].NamespaceIndex];
}
To actually call a method, we add the method callMethods and the helper method callMethodInternal to the class SampleClient.
Add the following code to sampleclient.h:
public:
...
...
private:
UaStatus browseInternal(
const UaNodeId& nodeToBrowse, OpcUa_UInt32 maxReferencesToReturn);
UaStatus connectInternal(
const UaString& serverUrl, SessionSecurityInfo& sessionSecurityInfo);
UaStatus findSecureEndpoint(SessionSecurityInfo& sessionSecurityInfo);
UaStatus checkServerCertificateTrust(SessionSecurityInfo& sessionSecurityInfo);
void printCertificateData(
const UaByteString& serverCertificate);
int userAcceptCertificate();
Implement the method callMethods
{
UaNodeIdArray objectNodeIds = m_pConfiguration->getObjectsToCall();
UaNodeIdArray methodNodeIds = m_pConfiguration->getMethodsToCall();
for (OpcUa_UInt32 i = 0; i < methodNodeIds.length(); i++)
{
UaStatus stat = callMethodInternal(objectNodeIds[i], methodNodeIds[i]);
{
result = stat;
}
}
return result;
}
The method reads the NodeIds of the method to call and the object on which to call the method from the configuration and calls the helper method callMethodInternal.
Add the helper method callMethodInternal to sampleclient.cpp:
{
ServiceSettings serviceSettings;
CallIn callRequest;
CallOut callResult;
callRequest.methodId = methodNodeId;
callRequest.objectId = objectNodeId;
result = m_pSession->
call(
serviceSettings,
callRequest,
callResult);
if (result.isGood())
{
if (callResult.callResult.isGood())
{
printf("Call succeeded\n");
}
else
{
printf("Call failed with status %s\n", callResult.callResult.toString().toUtf8());
}
}
else
{
printf("Call failed with status %s\n", result.toString().toUtf8());
}
return result;
}
The method takes the node ids of the method and the object as input parameters and calls UaSession::call.
Extend main to call the method:
...
status = pMyClient->subscribe();
{
pMyClient->callMethods();
getchar();
status = pMyClient->unsubscribe();
}
...