.NET Based OPC UA Client/Server SDK  3.3.0.530
Alarming

The OPC UA alarm system is very flexible to cover many possible use case. The .NET SDK offers a consistent alarm handling that behaves reasonable with the default settings. Several design decision can be altered by setting the appropriate properties. But it is also possible to have a complete different implementation, if you do not use the state changing methods like Enable, Acknowledge, Suppress, etc. The following discussion will focus, however, in using the SDK provided system. Note, that conditional branches (BranchId) and SupportsFilteredRetain are not supported by the SDK neither way.

Using BindModel mechanism, the alarms should be part of the address space, so you get the alarm instances generated by the UaModeler. The alarms can be controlled by the already implemented SDK methods, e.g. Enable, Acknowledge, Suppress, etc. Those methods will not only change the state variables, e.g. EnabledState, AckedState, and SuppressedState, but will also manage the Retain property, alter other states if reasonable, and emit UA events if needed. Nearly all of those state variables are optional. So if you want to make a alarm confirmable you have to model the alarm instance in a way that it has a ConfirmedState child instance in the NodeSet. In this case, this was achieved with the UaModeler, but it can also be accomplished by manually creating the instances. The BindModel mechanism will take care that the AlarmConditionModel will have a ConfirmedState instance assigned. Once the ConfirmedState instance is present methods like Confirm will work, and the state will also be considered when calculating the Retain property.

Default behavior and configurations

In this section the default behavior of the SDK alarm model implementation will be explained. All methods mentioned here are supposed to be called by code. The client interactions are covered in a later section.

The EnabledState will only be altered by the Enable and Disable and methods. If the alarm is disabled the Retain property will be false, even if the alarm is still unacknowledged.

The ActiveState will be altered by calling the Activate and Inactivate methods directly or indirectly by the LimitAlarmModel.Evaluate method. If the alarm is active the Retain property will be true as well.

The AckedState will be altered by calling the Acknowledge and ResetAcked methods. If the alarm is not acknowledged the, Retain property will be true. The ResetAcked method will be called on Activate.

The optional ConfirmedState will be altered by calling the Confirm and ResetConfirmed methods. If the alarm is not confirmed, the Retain property will be true. The ResetConfirmed method will be called on ResetAcked. Hence, after activation the alarm will be unacknowledged and unconfirmed. The natural order is that an alarm is first acknowledge and than confirmed, but the SDK does not enforce this. It is up to the server implementation to restrict the order.

The optional OutOfServiceState will be altered by calling the RemoveFromService and PlaceInService methods.

The optional SuppressedState will be altered by calling the Suppress and Unsuppress methods.

The optional ShelvingState will be altered by calling the OneShotShelve, TimedShelve, and Unshelve methods. If the ShelvingState state machine is in the OneShotShelve state on alarm inactivation, the state machine will be set back to Unshelved. Note, The TimedShelve method will correctly transition to the TimedShelved state but it will not automatically switch back. So it is up to the application to setup a timer and call the Unshelve method.

If at least one of the states OutOfServiceState, SuppressedState or ShelvingState is active, the property SuppressedOrShelved will be set to true, otherwise to false. The states have no influence on the Retain property. It is up to the client, if it wants to show/filter those alarms or not.

The optional LatchedState will be reset by calling the Reset methods. If present it will be set to true on activation. As long as the alarm is in the LatchedState, the Retain property will be true.

The OPC UA specification defines the AckedState as mandatory. But there are cases where an explicit acknowledgment handling is not wanted. An example are warnings that are only interesting for the client as long as they are active. The SDK offers several ways to handle that. The most simple variant is the IsAckedStateIgnored property, which makes the SDK to ignore the AckedState in the Retain calculation. The AckedState stays in whatever state it is. This might confuse some clients. Another option is to set the AutoAcknowledgeMode property to OnActivate, than the AckedState will be set to acknowledged directly on activation or to OnInactivate than the alarm is unacknowledged as long as the alarm is active, but will be automatically acknowledged on inactivation.

It is not strictly defined in the specification when to reset the ConfirmedState. As stated above, the default behavior is to reset it together with the AckedState. This can be changed with the ConfirmedStateResetMode property.

Limit alarms

In the MachineDemoServer, an ExclusiveLevelAlarm is used. The SDK provides the method Evaluate which takes the current level and sets the alarm's ActiveState depending on the previously set Limits. If present it will respect the configured level depended Severities and Deadbands.

private void CheckTemperatureCondition()
{
TemperatureCondition.Evaluate(Temperature.Value);
}

The limits have been set before, when a glass pane is actually present:

public void SetTemperatureAlarmLimits(double min, double max)
{
using (TemperatureCondition.MergeTransitions())
{
if (!TemperatureCondition.EnabledState.Id)
{
TemperatureCondition.Enable();
}
TemperatureCondition.LowLimit = min;
TemperatureCondition.HighLimit = max;
CheckTemperatureCondition();
}
}

If the condition is still disabled, it will first be enabled. Than the limits will be set and eventually the current temperature is checked against the new values. Since Enable and the Evaluate method can cause a transition generation, where only one is desired, both calls are surrounded by a MergeTransitions scope. This will merge all alarm transitions until its end.

Client Method Calls

There are several methods defined by the specification which a client could possibly call to acknowledge, confirm, silent, etc. an alarm. The possible implementation strongly depends on the alarm system, so the alarm model does not implement the IConditionMethods, IAcknowledgeableConditionMethods, and IAlarmConditionMethods interfaces. But the BindModel mechanism will set the ConditionMethods property to the node manager. So it is enough to override the virtual methods of the node manager. Here, in the MachineDemoServer/MachineDemoServerNodeManager.Alarms.cs file.

internal partial class MachineDemoServerNodeManager : BaseNodeManager

In the example, the user is not allowed to manually Enable or Disable alarms. This should only happen by code. So the status code BadUserAccessDenied will be returned.

public override StatusCode Enable(RequestContext context, ConditionModel model)
{
return StatusCodes.BadUserAccessDenied;
}

In most cases the call can be simply forwarded to the actual alarm implementation. The alarm methods intentionally return StatusCodes instead of throwing exceptions.

public override StatusCode Acknowledge(RequestContext context, AcknowledgeableConditionModel model, byte[] eventId, LocalizedText comment)
{
Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Acknowledge");
return model.Acknowledge(eventId, comment, context.UserIdentity.UserName);
}

Most methods in the OPC UA specification have also a second method variant which takes an additional comment as argument. There are not always direct counterparts on the alarm class for those methods. But they can easily be composed by the simple methods, here Suppress, and the AddComment method. Again, the calls are enveloped in a MergeTransitions scope, so that only one transition is generated. Without the using (model.MergeTransitions()) block, two events would be reported to the clients.

public override StatusCode Suppress(RequestContext context, AlarmConditionModel model)
{
return model.Suppress();
}
public override StatusCode Suppress2(RequestContext context, AlarmConditionModel model, LocalizedText Comment)
{
using (model.MergeTransitions())
{
var status = model.Suppress();
if (status.IsGood() && !LocalizedText.IsNullOrEmpty(Comment))
{
model.AddComment(model.EventId, Comment, context.UserIdentity.UserName);
}
return status;
}
}

The UA methods for the ShelvingState are not defined on the containing alarm, but on the ShelvedStateMachineType. Therefore, every ShelvedStateMachineModel instance carries a reference to its parent alarm. Note, this will be set when calling BindModel. If you are using some other mechanism, like LinkModelToNode you have to set it on your own.

public override StatusCode OneShotShelve(RequestContext context, ShelvedStateMachineModel model)
{
var alarm = model.ParentAlarm;
if (alarm != null)
{
return alarm.OneShotShelve();
}
return StatusCodes.BadNotSupported;
}