The goal of this lesson is to show how to provide Events in the Address Space.
Content:
Adding ControllerEventType
Figure 5-1: ControllerType
Create the new ObjectType
Figure 5-1 indicates that we are going to create an additional ObjectType: the ControllerEventType. For this purpose we add the following code to NmBuildingAutomation::createTypeNodes():
...
"ControllerEventType",
UaNodeId(Ba_ControllerEventType, getNameSpaceIndex()),
m_defaultLocaleId,
OpcUa_False);
addStatus = addNodeAndReference(OpcUaId_BaseEventType, pControllerEventType, OpcUaId_HasSubtype);
UA_ASSERT(addStatus.
isGood());
"Temperature",
UaNodeId(Ba_ControllerEventType_Temperature, getNameSpaceIndex()),
defaultValue,
OpcUa_AccessLevels_CurrentRead,
m_defaultLocaleId);
addStatus = addNodeAndReference(pControllerEventType, pProperty, OpcUaId_HasProperty);
UA_ASSERT(addStatus.
isGood());
"State",
UaNodeId(Ba_ControllerEventType_State, getNameSpaceIndex()),
defaultValue,
OpcUa_AccessLevels_CurrentRead,
m_defaultLocaleId);
addStatus = addNodeAndReference(pControllerEventType, pProperty, OpcUaId_HasProperty);
UA_ASSERT(addStatus.
isGood());
return ret;
}
Our Event type is modelled as ObjectType. The two variables State and Temperature of the ControllerType corresponds with the Event fields of the ControllerEventType. The Event fields are realized as Properties. Due to this a HasProperty Reference is passed to addNodeAndReference().
Indicate that Controllers fire events
In order to indicate that the ControllerType fires ControllerEventType events we create an additional GeneratesEvent reference after creating the event type:
"State",
UaNodeId(Ba_ControllerEventType_State, getNameSpaceIndex()),
defaultValue,
Ua_AccessLevel_CurrentRead,
m_defaultLocaleId);
addStatus = addNodeAndReference(pControllerEventType, pProperty, OpcUaId_HasProperty);
UA_ASSERT(addStatus.
isGood());
addStatus = addUaReference(
UaNodeId(Ba_ControllerType, getNameSpaceIndex()),
UaNodeId(Ba_ControllerEventType, getNameSpaceIndex()),
OpcUaId_GeneratesEvent);
UA_ASSERT(addStatus.isGood());
}
The link between Controller and ControllerEventType is implemented as GeneratesEvent Reference. addUaReference() takes two type definition NodeIds representing the ObjectTypes, and the GeneratesEvent define.
Implementing ControllerEventType
Every Event type needs its own Event data class derived from BaseEventTypeData. This class contains the Event field data for an Event and serves as a base for the client-side implementation of the select clause.
Define class BaEventData
#ifndef BAEVENTDATA_H
#define BAEVENTDATA_H
#include "uaeventdata.h"
{
UA_DISABLE_COPY(ControllerEventTypeData);
public:
ControllerEventTypeData(OpcUa_Int16 nsIdx);
virtual ~ControllerEventTypeData();
OpcUa_Int16 m_nsIdx;
private:
static map<OpcUa_UInt32, OpcUa_UInt32> s_ControllerEventTypeDataFields;
};
#endif // BAEVENTDATA_H
Our class definition also declares a map as static member in order to manage registered Event fields.
Implement class BaEventData
#include "baeventdata.h"
#include "buildingautomationtypeids.h"
#include "eventmanageruanode.h"
map<OpcUa_UInt32, OpcUa_UInt32> ControllerEventTypeData::s_ControllerEventTypeDataFields;
The map must be defined.
#include "baeventdata.h"
#include "buildingautomationtypeids.h"
#include "eventmanageruanode.h"
map<OpcUa_UInt32, OpcUa_UInt32> ControllerEventTypeData::s_ControllerEventTypeDataFields;
ControllerEventTypeData::ControllerEventTypeData(OpcUa_Int16 nsIdx)
{
m_nsIdx = nsIdx;
m_EventTypeId.setNodeId(Ba_ControllerEventType, m_nsIdx);
}
ControllerEventTypeData::~ControllerEventTypeData()
{
}
The constructor takes the according NameSpace index and defines the Event type NodeId member. We leave the destructor empty.
void ControllerEventTypeData::registerEventFields()
{
s_ControllerEventTypeDataFields.clear();
}
This method registers all event type fields with the EventManagerUaNode. The handling of EventManager will be explained later.
void ControllerEventTypeData::getFieldData(OpcUa_UInt32 index,
Session* pSession, OpcUa_Variant& data)
{
map<OpcUa_UInt32, OpcUa_UInt32>::iterator it;
it = s_ControllerEventTypeDataFields.find(index);
if ( it == s_ControllerEventTypeDataFields.end() )
{
return;
}
switch(it->second)
{
case 1:
{
m_Temperature.copyTo(&data);
break;
}
case 2:
{
m_State.copyTo(&data);
break;
}
default:
{
OpcUa_Variant_Initialize(&data);
}
}
}
getFieldData() takes the index of the selected field and returns the data for this field.
Finally we have to register the event data class with the BaseEventType in order to allow a client to select the event fields:
UaStatus NmBuildingAutomation::createTypeNodes()
{
ControllerEventTypeData eventTypeData(getNameSpaceIndex());
eventTypeData.registerEventFields();
return ret;
}
Implementing handling of EventManager
NmBuildingAutomation implements all necessary functionality for the event handling by deriving from NodeManagerBase. This class implements the EventManager interface by deriving from EventManagerUaNode and it provides the necessary mapping code between a NodeManager and an EventManager:
Figure 5-2 The EventManager implementation
The only code that needs to be changed for the event handling support in the NmBuildingAutomation is to pass in OpcUa_True for the firesEvents flag of the cunstructor of the NodeManagerBase base class:
NmBuildingAutomation::NmBuildingAutomation()
{
m_pCommIf = new BaCommunicationInterface;
}
Implement Event trigger
In order to achieve this we create controller Events in ControllerObject::beginCall():
OpcUa_UInt32 callbackHandle,
const UaVariantArray& inputArguments)
{
UaVariantArray outputArguments;
UaStatusCodeArray inputArgumentResults;
UaDiagnosticInfos inputArgumentDiag;
if(pMethodHandleUaNode)
{
if(pMethod)
{
...
{
ControllerEventTypeData eventData(browseName().namespaceIndex());
OpcUa_Double value;
m_pCommIf->getControllerData(m_deviceAddress, 0, value);
eventData.m_Temperature.setDouble(value);
BaCommunicationInterface::ControllerState state;
m_pCommIf->getControllerState(m_deviceAddress, state);
eventData.m_State.setUInt32(state);
eventData.m_EventId.setByteString(eventId, OpcUa_True);
eventData.m_SourceNode.setNodeId(nodeId());
eventData.m_SourceName.setString(browseName().toString());
eventData.m_Time.setDateTime(timestamp);
eventData.m_ReceiveTime.setDateTime(timestamp);
if ( state == BaCommunicationInterface::Ba_ControllerState_Off )
{
messageText =
UaString(
"State of %1 changed to OFF").
arg(browseName().toString());
}
else
{
messageText =
UaString(
"State of %1 changed to ON").
arg(browseName().toString());
}
eventData.m_Severity.setUInt16(500);
m_pNodeManager->fireEvent(&eventData); }
callbackHandle,
inputArgumentResults,
inputArgumentDiag,
outputArguments,
ret);
ret = OpcUa_Good;
}
else
{
assert(false);
ret = OpcUa_BadInvalidArgument;
}
}
else
{
assert(false);
ret = OpcUa_BadInvalidArgument;
}
return ret;
}
Trigger and receive Events with UA Expert
- Choose menu entry Document->Add and then document type Event View. The Event View tab appears.
- Drag and drop the ServerObject item from Address Space browser window to Monitored Items window of the tab.
- Select the item in the Monitored Items window to browse Event fields.
- Trigger a State change Event by calling at first Start and then Stop as explained in Lesson 4: Adding support for Methods.
The result is shown in Figure 5-3.
Figure 5-3: Events triggered and monitored with UA Expert