Build Alarms Into a Tag

To add one or more built-in alarms to a custom tag, you will need the following parts:

  • Configuration parameters for storing alarm properties. Your tag's configuration panel should include a tab for the alarm properties.
  • Local variables and constants for handling your alarms.
  • A set of statements in the tag's Refresh module to commission the alarm, setting or updating parameters as needed.
    In the event that the tag is being deleted, decommission should be called.
  • A call to \AlarmManager\EvaluateAlarm() to reevaluate the alarm's state with the tag's current value each time that value changes.

Prior to VTScada version 11.2, there was a requirement that separate submodules be created for each alarm if the tag included more than one. This is no longer the case. The GetAlarmObjVal module is now obsolete, but may be maintained for backward-compatibility.

Configuration Parameters and Variables

Alarm records have the configuration fields shown in the following table. Not all need to be set, so select those that are relevant to your tag and application. For example, trips should not be used with an analog tag. Deadbands are not used with digital tags.

At a minimum for a properly functioning alarm, you will need Name, Priority, Function, and Setpoint.
FriendlyName, Area, Description and Disable may not be required, but are highly recommended, and are usually set using properties of the tag.

ConfigurationStruct { All Boolean flags default to FALSE }
Name

Unique name for the alarm. Typically the unique ID, which can be obtained using GetAlarmName.
If there are multiple built-in alarms in a tag, the convention is to concatenate the UniqueID with a separator and a unique integer or string per alarm. For example, Concat(Root\UniqueID, \AlarmSeparatorString, 0).

FriendlyName Display name of the alarm's source. This is only needed for alarms that are not associated with a tag, such as service alarms. Tag alarms will always show the tag's Name as the display name.
Area Area
Description Description. See also: NoLegacyAlarmDesc
Priority Priority. Must be valid to be commissioned. Must be an integer corresponding to the Alarm Priority tag values. A priority of 0 will be treated as an event and will not go on either the Active or Current lists.
Reserved  
Disable TRUE to disable the alarm.
DisableParmName Name of the tag's disable parameter. Allows you to get the operator name who made the configuration change.
OnDelay Seconds to delay before activating.
OffDelay Seconds to delay before clearing.
RearmDelay Seconds to delay before rearming after acknowledgment.
Setpoint Setpoint of alarm evaluation. The value is compared against the setpoint.
ValueLabels Array of labels to display instead of Value or Setpoint. Rarely used by tags other than digitals.
Units Phrase Key of the setpoint units.
Function Enumerated function for alarm evaluation. See: Alarm Manager Function Constants
AlarmType String identifying the type of alarm. Used for display purposes only, in accordance with the ISA18.2 standard. Examples include "Low", "High", "Test".
Trip TRUE if alarm only becomes unacked not active.
NormalTrip TRUE if alarm becomes unacked when it clears.
OffNormal TRUE if alarm only becomes active not unacked.
Deadband Setpoint deadband.
PopupEnable TRUE to enable popup display of active alarm.
SoundFile Filename relative to app path of custom sound.
Custom Number/String/Array/Dictionary/Structure containing custom data. This information is not shown anywhere on the AlarmPage by default, but could be displayed using a custom column.
AdHoc TRUE if alarm is ad hoc. Should not be set explicitly. It is an internal flag that is set if the alarm object calls AdHocAlarm() rather than Commission()
ReactivateOnValueChange If TRUE, the alarm will reactivate whenever the reported value changes.
DescPhraseID PhraseID or a ParmPhrase structure to translate the description to current language.
NormalAck TRUE to auto-ack the alarm when it clears.
Hook

The name of a module you supply, which will be called whenever any alarm event occurs for the given alarm, including events such as active / normal / acknowledge / shelve / disable / commission / etc.

VTScada will look for the callback module starting in the AlarmObject that is provided in the call to Commission(), and will search upwards until it reaches Code level. This gives you flexibility to specify your callback module in your custom alarm tag, or in a custom ancestor tag, or in the application (AppRoot).

The callback module must take a TransactionInfo structure as its only parameter. It is expected to return TRUE or Invalid to allow the transaction to be logged, or FALSE to prevent it.

An example, showing how to configure these as tag properties:

Setpoint  <:TagField("SQL_DOUBLE", "Set Point"):> = 0  
         { State or setpoint that triggers the alarm };
Function  <:TagField("SQL_VARCHAR(255)", "Function Code"):>  
         { Text string defines the  comparison between value and setpoint };
Priority  <:TagField("SQL_DOUBLE", "Priority")   :>      
         { Numeric Priority of this alarm };
Disable   <:TagField("SQL_LONGVARCHAR", "Disable"):> = FALSE 
         { TRUE to Disable the alarm };

If your alarm is to be used only for low levels or high levels, you can set the Function field as a constant, otherwise, you will need to create a configuration parameter for it and provide a selection in the user interface. See: Alarm Manager Function Constants.

If creating more than one alarm, create a set of properties for each. For example, SetpointLo and SetpointHi.

As with other tag parameters, a constant should be defined for each alarm parameter. For example, if the parameter constant before Setpoint was 7, then:

Constant #Setpoint = 8;
Constant #Function = 9;

... and so on.

An alarm configuration structure will be required to commission your alarm(s). Declare each as a protected variable:

PROTECTED AlarmCfg     { Structure of alarm configuration parameters };

Your tag may also need variables to hold any alarm status information that you care to examine during the normal running of the tag and any configuration fields that are to be set but are not being exposed as user-configurable tag properties. The AlarmStatus structure (any name may be used) is typically set immediately after the alarm is commissioned. An example is provided later in this topic.

{ Alarm variables }
PROTECTED AlarmStatus  { Structure of alarm status information. };
{ ... other variables as required ... }

If the tag has a built-in alarm, that fact should be reflected as an icon in the Tag Browser.

To achieve this, all that is required is to add the following constant declaration. The Tag Browser will take care of the rest.

Constant BuiltInAlarm = TRUE { Flag - TRUE if this tag has a built in alarm  };

Tag Initialization

Your module should ensure that the Alarm Manager has started before attempting to commission itself. Because it is also typical for a tag to ensure that the Expression Manager has started, that is also shown in the following example.

MyTagInit [
  If \AlarmManager\Started && \ExpressionManager\Started MyTagMain;
  [
    CriticalSection(
      Root = Self();
      Refresh();
    );
  ]
]

Main State - Handle changes to the Disable parameter

Under the ISA18.2 standard, it is recommended that you provide a way for the tag to be disabled and re-enabled dynamically. This can be done with the tag's Common module (right-click menu), and by allowing the Disable parameter to be configured via a constant, expression or tag (i.e. via a PTypeToggle in the configuration panel).

Changes to this parameter outside the properties dialog are handled in your tag's main state. The configuration object must be updated and the Alarm Manager's Commission function called.

AlarmDisable = PickValid(Cast(ValueType(Disable) == \#VTypeObject
                              ? Disable\Value
                              : (Valid(Scope(Root, Disable)\Name)
                                 ? Scope(Root, Disable)\Value
                                 : Disable),
                              \#VTypeStatus),
                         AlarmDisable,
                         FALSE);
{ Enable/Disable }
If Watch(1, AlarmDisable);
[
  AlarmCfg = \AlarmManager\GetAlarmConfiguration(Root\UniqueID);
  AlarmCfg\Disable = AlarmDisable;
  \AlarmManager\Commission(Root, AlarmCfg, Value);
]

Note: You might prefer to set the 5th parameter of Commission to TRUE for these types of modification to the alarm so that the change is not added to the history list.

Refresh Submodule: Configure, Commission and Update

Alarm parameters are handled much like others in the refresh module. (Note: you are advised to use a PTypeToggle in your tag's configuration panel for the disable parameter so that it can be tied to a tag or expression. Other parameters may be configured as you see fit. The following example reflects this detail.)

\ExpressionManager\SafeRefresh(&Disable,   Parms[#Disable]);
AlarmRearmEnable = PickValid(Cast(AlarmRearmEnable, \#VTypeStatus), GetDefaultValue(&AlarmRearmEnable));

The Refresh module must call the Commission function of the Alarm Manager, handing it all of the properties you are configuring in your alarm.

If the tag contains only one alarm, then the name will be the same as the tag's unique ID value. If there are multiple alarms within the tag, then each will be the UniqueID concatenated with the alarm separator string (defaulting to :#:) followed by a digit starting with 0. Thus, when assigning the alarm name for a single alarm, the code will be:

AlarmCfg\Name            = Root\UniqueID;

When assigning the alarm name for the first of several alarms within a tag, the code will become:

AlarmLoCfg\Name = Concat(Root\UniqueID, 
                         PickValid(\AlarmSeparatorString, ":#:"), "0");

Noting that the digit must be incremented for each subsequent alarm.

The name will be used several times, therefore it makes sense to store it in a variable such as "AlarmName" or "AlarmLoName".

The following example shows slightly more than a minimal configuration. Your code will likely set additional properties in the configuration structure.

IfElse(Valid(Name), Execute(
  AlarmName               = Root\UniqueID;
  AlarmCfg                 = \AlarmManager\GetAlarmConfiguration(AlarmName);
  AlarmCfg.Name            = AlarmName;
  AlarmCfg.FriendlyName    = Name      { The tag's short name };
  AlarmCfg.Area            = Area      { The tag's area };
  AlarmCfg.DescPhraseID    = DescPhrase { Variable containing a phase key for the description};
  AlarmCfg.Priority        = Priority;
  AlarmCfg.Function        = \AlarmManager\ALM_FUNC_LESS_THAN; { a low alarm, no user-selection }
  AlarmCfg.Setpoint        = PickValid(Cast(Setpoint, \#VTypeDouble), AlarmCfg\Setpoint);
  \AlarmManager.Commission(Root, AlarmCfg, Value);
  { ... repeat for more alarms ... }
);
{ Else, the tag is being deleted... }
  IfThen(Valid(Parms[#Name]),
    \AlarmManager.Decommission(AlarmName);
  );
);

If your tag needs to know its current alarm state, you should follow this by obtaining a reference to your alarm's entry in the master database. Typically, this is required only for widgets that can indicate that state. The reference to the database entry will allow quick access the alarm's current state without having to make function calls.

AlarmStatus = \AlarmManager\GetAlarmStatus(Root\UniqueID); 

Evaluate the Alarm

The preceding should result in a commissioned alarm record when you compile your tag and generate an instance of it in your running application, setting priority to an integer. But, that is not enough to trip or activate an alarm event. You must also call the Alarm Manager's EvaluateAlarm function whenever the value of your tag changes so that the new value can be compared to the setpoint using the assigned comparison function.

If your tag links to a driver and has a NewData submodule, then place the following code there. Otherwise, it might go into the main state, if that is where new values are being calculated. Note that EvaluateAlarm cannot be called in Steady State.

If Watch(1, Value);
[
  \AlarmManager\EvaluateAlarm(Root\UniqueID, Value);

If the time is being collected from remote equipment with the value, then you should include that in the call as a UTC timestamp:

\AlarmManager\EvaluateAlarm(Root\UniqueID, Value, TimeOfValue);