In this example we extend the ControllerType defined in Lesson 2 by adding an instance of an alarm: We add a new Object called StateCondition having the TypeDefinition OffNodemalAlarmType (see Figure 6.1).
Figure 6.1: New StateCondition Object
Open the UaModeler project created in Lesson 2. Add a new Object called “StateCondition” as a child of ControllerType and choose “OffNormalAlarmType” as TypeDefinition (see Figure 6.2).
Figure 6.2: Add Alarm Object
Then regenerate the source files, export the model as xml file and replace the files BAIdentifiers.cs and buildingautomation.xml in your project.
After adding the controller object, we create an instance of the OffNormalAlarmModel. This class is used to store all data related to the OffNormalAlarmType. With LinkModelToNode the SDK maps the data defined in the instance to the nodes in the address space.
SetChildUserData(
new NodeId(block.Name, InstanceNamespaceIndex),
new QualifiedName(yourorganisation.BA.BrowseNames.StartWithSetPoint, TypeNamespaceIndex),
new SystemFunction() { Address = block.Address, Function = 3 });
NodeId alarmId =
new NodeId(block.Name +
"." + yourorganisation.BA.BrowseNames.StateCondition, InstanceNamespaceIndex);
OffNormalAlarmModel alarm = new OffNormalAlarmModel();
alarm.NodeId = alarmId;
alarm.EventType = ObjectTypeIds.OffNormalAlarmType;
alarm.SourceNode =
new NodeId(block.Name, InstanceNamespaceIndex);
alarm.SourceName = block.Name;
alarm.ConditionName = "StateCondition";
alarm.ConditionClassId = ObjectTypeIds.ProcessConditionClassType;
alarm.ConditionClassName = BrowseNames.ProcessConditionClassType;
alarm.Retain = false;
alarm.EnabledState.Value = ConditionStateNames.Enabled;
alarm.EnabledState.Id = true;
alarm.AckedState.Value = ConditionStateNames.Acknowledged;
alarm.AckedState.Id = true;
alarm.ActiveState.Value = ConditionStateNames.Inactive;
alarm.ActiveState.Id = false;
alarm.SuppressedOrShelved = false;
LinkModelToNode(alarmId, alarm, null, null, 500);
foreach (BlockProperty property in block.Properties)
We change the state of the alarm in the BlockStateChanged EventHandler.
NodeId alarmId =
new NodeId(blockName +
"." + yourorganisation.BA.BrowseNames.StateCondition, InstanceNamespaceIndex);
OffNormalAlarmModel alarm = (OffNormalAlarmModel)GetNodeUserData(alarmId);
lock (alarm)
{
alarm.LastSeverity.Value = alarm.Severity;
alarm.LastSeverity.SourceTimestamp = alarm.Time;
if (state == 0)
{
alarm.Retain = true;
alarm.AckedState.Value = ConditionStateNames.Unacknowledged;
alarm.AckedState.Id = false;
alarm.Activate(
Server.DefaultRequestContext,
true);
}
else
{
alarm.Activate(
Server.DefaultRequestContext,
false);
if (alarm.AckedState.Id == true)
{
alarm.Retain = false;
}
}
e = alarm.CreateEvent(
Server.FilterManager,
true);
}
ReportEvent(alarm.SourceNode, e);
Finally, we have to add handling for Acknowledge calls by clients. Therefore, we need to override the Acknowledge method defined at the BaseNodeManager.
RequestContext context,
AcknowledgeableConditionModel model,
byte[] eventId,
LocalizedText comment)
{
GenericEvent e = null;
lock (model)
{
StatusCode error = model.Acknowledge(context, eventId, comment);
if (error.IsBad())
{
return error;
}
e = model.CreateEvent(
Server.FilterManager,
true);
}
ReportEvent(model.SourceNode, e);
return StatusCodes.Good;
}
To test the alarm, open a new Event View in UaExpert and change to the “Alarms” tab (see Figure 6.3). The alarm can be triggered by calling the methods “Start” and “Stop”. Right click on an Alarm to acknowledge, confirm, or add a comment.
Figure 6.3: Alarms in UaExpert