How Bacnet Alarms work

How Bacnet Alarms work

Bacnet Alarms are usually created and sent when a bacnet object's state changes, this usually implies the "STATUS_FLAG" property value changes. 

Obs: how alarms are generated and sent is a local matter (implementation decided by the device vendor). 
There are two workflows/mechanisms in bacnet for Alarms: 

  • Alarm Registration  

  • Alarm Summary 

1. Alarm Registration in Bacnet

 

Alarm Registration and Reporting is a push mechanism (the same principle as in COV). 

The client/connector registers for alarms to the server/device, and then it waits and listens for alarms. When the alarm is created the server/device sends it to all the registered clients. 

So the server/device is the one pushing the information (alarm) to the client/connector in real-time as soon as the alarm is generated.

 

A bacnet point (form a bacnet device) can support two types of reporting - generating and sending the Alarms to devices that have registered for Alarms about that point: 

  •  intrinsec reporting: where the actual point in charge of monitoring it's own state, and when this changes, it generates the Alarm 

  • algorithmic reporting: where another bacnet object called Event Enrollment Object monitors the state of the initial bacnet point and when this changes it generates the Alarm

OBS: what type of reporting a bacnet point supports is a local matter(implementation decided by the device vendor).

How this works: to easily explain the workflow we will consider Alarms about (referint to) an Analog Value Object, the same workflow/explanations apply to al other types of objects 

There are 3 types of objects, on the bacnet device/server, involved in registering and sending a Bacnet Alarms:

  • Notification Class Object (NC)

  • Event Enrollment Objects (EE)

  • Analog Value Object (AV). 

Notification Class Object:  

  • this object contains the list of recipients (registered devices/connectors) that will receive an Alarm  

  • the list is being kept in the property "RECIPIEN_LIST" 


Analog Value Object:  

  • this object can be of any type that supports reporting; we chose this type just as an example  

  • if the object support intrisec reporting, it has a property "NOTIFICATION_CLASS" that references a Notification Class Object  

  • the referenced Notification Class Object has the list (in the "RECIPIEN_LIST" property) of devices/connectors that will receive the Alarms

  • when the "STATUS_FLAG" property changes, the object will generate an Alarm that will be sent to all the devices/connectors in the "RECIPIEN_LIST" property of the Notification Class Object referenced by the "NOTIFICATION_CLASS" property 


Event Enrollment Object:  

  • if the Analog Value Object supports algorithmic reporting  

  • this object monitors the "STATUS_FLAG" property of the Analog Value Object and if that property changes, it will generate the Alarm  

  • the object that it monitors is referenced by the property "OBJECT_PROPERTY_REFERENCE"  

  • this object has a property called "NOTIFICATION_CLASS" that references a Notification Class Object  

  • the referenced Notification Class Object has the list (in the "RECIPIEN_LIST" property) of devices/connectors that will receive the Alarms 

  • when the "STATUS_FLAG" changes for the object referenced by the property "OBJECT_PROPERTY_REFERENCE", the Event Enrollment object will send an Alarm to each device in the "RECIPIEN_LIST" property of the Notification Class Object referenced by the property "NOTIFICATION_CLASS" 

 

Here is a setup example:

  • we have a bacnet device DEV1 that has: NC1, NC2 (Notification Class Objects), AV1 (Analog Value Object) and EE1 (Event Enrollment Object)  

  • NC1 has the property "RECIPIEN_LIST"=[11,12,13]  - this means it references the bacnet devices DEV11, DEV12, DEV13  

  • NC2 has the property "RECIPIEN_LIST"=[21,22]  - this means it references the bacnet devices DEV21, DEV22 

  • AV1 has the property "NOTIFICATION_CLASS"=1  - this means it references NC1  

  • EE1 has the property "OBJECT_PROPERTY_REFERENCE"=AV1 - this means it references and monitors the status of AV1  

  • EE1 has the property "NOTIFICATION_CLASS"=2 - this means it references NC1 

  • AV1 support both types of reporting: intrinsec and algorithmic

 

What happens when AV1 status changes: 

  • AV1 generates an alarm, because it monitors it's own state 

  • AV1 looks at its property "NOTIFICATION_CLASS"=1, takes NC1, looks at the property "RECIPIEN_LIST"=[11,12,13] of NC1 

  • the alarm is sent to DEV11, DEV12, DEV13 

  • EE1 generates an alarm, because it monitors AV1 

  • EE1 looks at its property "NOTIFICATION_CLASS"=2, takes NC2, looks at the property "RECIPIEN_LIST"=[21,22] of NC2 

  • the alarm is sent to DEV21, DEV22

 

OBS: EE1 may reuse the alarm generated by AV1 or it may generate a new alarm; how this is implemented is a local matter (implementation decided by the device vendor). In any case both alarms have the same content and the recipients are unaffected.

2. Alarm Registration in FIN

 

When a bacnet connector is opened, it will iterate through each object that is bound to a point in DB Builder and try to register for Alarms for that object.

When a new bacnet object is bound to a point in DB Builder, while the connector is opened, the connector will not register for Alarms for that object, the user must do this manually.

The user can make the bacnet connector register for Alarms for all the bounded bacnet objects by calling the function 'bacnetNotificationsSubscribeConn(conn)'. The function returns a Grid with all the bacnet objects, all the Event Enrollment Objects referencing those bacnet objects, all the Notification Class Objects referenced by the bacnet objects and the EEs and the result of adding the connector to the "RECIPIEN_LIST" of those NCs.

The user can make the bacnet connector register for Alarms for a specific bounded objects by calling the function 'bacnetNotificationsSubscribePoint(conn, point)'. The function returns a Grid with the bacnet object, all the Event Enrollment Objects referencing this bacnet object, all the Notification Class Objects referenced by the bacnet object and the EEs and the result of adding the connector to the "RECIPIEN_LIST" of those NCs.

 

How a bacnet connector registers for bacnet Alarms for a sigle bacnet object, for example AV1, following theses steps:

  • reads the property "NOTIFICATION_CLASS" of that bacnet object, to test if the object suppots intrinsec reporting 

  • if the property exists and has a valid value, for example 3, then the connector tries to read the property "RECIPIEN_LIST" of the Notification Class Object NC3 from the device 

  • if the Notification Class Object NC3 exists, and has the property with a valid list of recipients, then the connector tests if it is in that list 

  • if the connector is not in that list it will add itself to the list (in the "RECIPIEN_LIST" property of the Notification Class Object NC3) 

  • now the connector tests for algorithmic reporting 

  • loops through all the objects in the device, and for each Event Enrollment Object it will read its property "OBJECT_PROPERTY_REFERENCE" 

  • tests if the property value is equal to the object identifier of the initial bacnet object, for our example: 1 from AV1 

  • if it finds such an Event Enrollment Object, for example EE2, then it continues with  

  • reads the property "NOTIFICATION_CLASS" of that bacnet object EE2 

  • if the property exists and has a valid value, for example 5, then the connector tries to read the property "RECIPIEN_LIST" of the Notification Class Object NC5 from the device 

  • if the Notification Class Object NC5 exists, and has the property with a valid list of recipients, then the connector tests if it is in that list 

  • if the connector is not in that list it will add itself to the list (in the "RECIPIEN_LIST" property of the Notification Class Object NC5)

 

Previously we stated that the "RECIPIEN_LIST" property of a Notification Class Object contains a list of bacnet device that will receive the Alarms.

So for a bacnet connector to add itself to a recepient list it must add a bacnet device.

The bacnet ext record has a tag "bacnetDeviceRef" that references a bacnet device record. You can find this bacnet device record by using this query in Folio 'readById(read(bacnetDeviceRef and ext)→bacnetDeviceRef)'.

This is the bacnet device, that every bacnet connector will use to add itself to a recipient list.

 

After the connector has successfully registered itself, it will receive Alarms from the device. 

To see how the connector processed these Alarms please read 5. Processing/handling BACnet Alarms in FIN.

3. Alarm Summary in Bacnet

 

Alarm Summary is a poll mechanism (similar to polling point values).

When a bacnet object generates an Alarm it is stored on the bacnet device. A bacnet device will store ONLY the last Alarm generated by/for each bacnet object - this represents the current state of alarm for that point.

A connector/client can request the Alarm Summary from the server/device and receive the last Alarm for each bacnet object on the device. It is a polling mechanism because the client asks for the information.
A connector/client can periodically request the Alarm Summary from the server/device to retieve the latest Alarms, but it is not encouraged.

It is better to register for Alarms and let the server/device send the information as soon as an Alarm is generated.

4. Alarm Summary in FIN

 

When a bacnet connector is opened it will request the Alarm Summary from the device, to receive the lated Alarms and verify if any new Alarms were generated while the connector was closed.

When receiving the Alarm Summary, the connector will process each Alarm for the bacnet objects that are bound to points in DB Builder

To see how the connector processed these Alarms please read 5. Processing/handling Bacnet Alarms in Finstack.
The user can retrieve the Alarm Summary by calling the function 'bacnetGetAlarmSummary(conn)'.

This function returns a Grid with the last Alarm for each bacnet object on the device, and it will automatically process each Alarm for the bacnet objects that are bound to points in DB Builder.

5. Processing/handling Bacnet Alarms in FIN

 

Bacnet Alarms have two important properties: "notify type" and "process identified".

"notify type" can have one of two values: Alarm type or Event type . Yes, Alarms in Bacnet can be Alarm types or Event types. Event type alarms usually have a more informative role that something has happened on the device and Alarm type alarms (yes, that does sound weird) have a more warning role that something WRONG or IMPORTANT has happened on the device.  We are only interested in Alarm type alarms.

"process identifier" is a number that shall convey the identification of the process in the receiving device for which the alarm is intended (from the official documentation).

Usually this "process identified" is a number agreed upon by the manufacturer of the device to be set and send with different values on different Alarms. And the connectors/clients can process differently Alarms based on the "process identifier" value.

In Finstack the bacnet connector record can have the numeric tag 'bacnetAlarmPid'. And if this tag is set with a value V, only Alarms that have "process identifier"=V will be added in the Alarms App. If the bacnet connector record does not have the tag 'bacnetAlarmPid' set, then all Alarms are added in the Alarms App.

Also, if we want to have multiple “process identifiers” all we need to do is to create multiple tuning policies with the numeric tag 'bacnetAlarmPid' and set different values for each of them. Then, assign the policy with the process identifier we want to the BACnet connector.

 

A bacnet connector will process a bacnet Alarm like this: 

  • if the Alarm is of type Event then it will be stored in the internal 'unhandledEvents' list 

  • if the Alarm is of type Alarm then will do extra test 

  • if the 'bacnetAlarmPid' tag is set and the value is NOT equal to the Alarm "process identifier" then the Alarm will be stored in the internal 'unahndledAlarms' list

  • else the Alarm will be stored in the internal 'handledAlarms' list and will be added in the Alarms App 

 

The user can visualize the Alarms from the 'unhandledEvents' list by calling the funtion 'bacnetGetUnhandledEvents(conn)'.

The user can visualize the Alarms from the 'unhandledAlarms' list by calling the funtion 'bacnetGetUnhandledAlarms(conn)'.

The user can visualize the Alarms from the 'handledAlarms' list by calling the funtion 'bacnetGetHandledAlarms(conn)'.

The user can also visualize the handled Alarms inside the Alarms App.

6. Troubleshooting

 

  1. If you know that the device has sent and Alarm, but it does not show up in Alarms App, you should try and see if it is in any of the 'unhandledEvents', 'unhandledAlarms' or 'handledAlarms' lists by calling the functions 'bacnetGetUnhandledEvents(conn)', 'bacnetGetUnhandledAlarms(conn)' and 'bacnetGetHandledAlarms(conn)'.

The Alarms may not show up in Alarms App because the alarams may have different setting for the properties "notify type" and "process identified" - see 5. Processing/handling Bacnet Alarms in Finstack on how these properies are interpreted.

Also there are some issues with the Alarms App displaying the Alarms: I saw alarms appearing in Skyfoundry's UI but not in ours - someone needt to investigate this.

 

  1. If the connector does not receive the Alarms you can check if the the connector is properly registered to receive them, by calling the function 'bacnetGetNotificationSubscriptions(conn)'.

This function retrieves all the bacnet objects this connector KNOWS/THINKS it is registered to receive Alarms about.

 

  1. If the connector is not registered for Alarms about some bacnet objects or does not receive the Alarms try to re-register by calling either of the functions 'bacnetNotificationsSubscribePoint(conn, point)' and 'bacnetNotificationsSubscribeConn(conn)'. Check the output of the functions that the registration was successful.

 

  1. Verify on the remote device that the bacnet connector (the local bacnet device associated to the bacnet ext) is present in the "RECIPIEN_LIST" property of the appropriate Notification Class Objects.

You can verify this if you have direct access to the device by some other mean besides Finstack and inspect the bacnet objects on the device.

Or you can use the function 'bacnetReadObjectProperty(conn, obj, prop)' to read the necessary information: first the "NOTIFICATION_CLASS" property of the bacnet object or the Event Enrollment Object, then the "NOTIFICATION_CLASS" property of the Notification Class Object.

 

  1. If the connector still desn't receive Alarms turn on wireshark to verify if the Alarms are actually sent by the device.

7. Bacnet functions 

**  ** Retrieves the Alarm Summary from the device and adds them in the Alarm Extension.  **  ** Parameter:  ** - conn: Ref to or a record with a bacnetConn tag  **  ** Returns:  ** Grid with the alarms retrieved from the device.  **  Grid bacnetGetAlarmSummary(Obj conn)  **  ** Acknowledges an alarm. Need to specify the connector this alarm came from.  **  ** Parameters:  ** - conn: Ref to or a record with a bacnetConn tag  ** - alarm: Dict representing the alarm. The dict structure must comply to the alarm's format  ** and resemble the results of funtion like **  ** Returns:  ** String that shows the success of the operation.  **  Grid bacnetAcknowledgeAlarm(Obj conn, Dict alarm)  **  ** Subscribe to connector, to receive notifications for all the points from it  ** that are bound in DB builder.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  **  ** Return:  ** Grid with the localDeviceId that was subscribed, each point to receive alarms for, and  ** each Notification Class in which it was registered.  **  Grid bacnetNotificationsSubscribeConn(Obj conn)  **  ** Subscribe to connector, to receive notifications only for the provided point.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  ** - point: point from Connector bound in DB  **  ** Return:  ** Grid with the localDeviceId that was subscribed, the point to receive alarms for, and  ** each Notification Class in which it was registered.  **  Grid bacnetNotificationsSubscribePoint(Obj conn, Obj point)  **  ** Unsubscribe from connector, from receiving notifications for all the points  ** that are bound in DB builder.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  **  ** Returns:  ** Grid with the localDeviceId that was unsubscribed, each point and it's Notification Classes  ** from which it was unregistered.  **  Grid bacnetNotificationsUnscubscribeConn(Obj conn)  **  ** Unsubscribe from connector, from receiving notifications only for the provided point.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  ** - point: point from Connector bound in DB  **  ** Returns:  ** Grid with the localDeviceId that was unsubscribed, the point and it's Notification Classes  ** from which it was unregistered.  **  Grid bacnetNotificationsUnsubscribePoint(Obj conn, Obj point)  **  ** Adds the connector to the provided notification class,  ** to enable receiving alarm/event notifications.  ** Low level function for debugging mostly.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  ** - point: a point bound to a Notification Class Object or a dict ["bacnetRef":"NC1"]  **  Grid bacnetAddToNotificationClass(Obj conn, Obj point)  **  ** Removes the local device corresponding to the connector from the provided  ** notification class, to disable receiving alarm/event notifications.  ** Low level function for debugging mostly.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  ** - point: a point bound to a Notification Class Object or a dict ["bacnetRef":"NC1"]  **  Grid bacnetRemoveFromNotificationClass(Obj conn, Obj point)  **  ** Returns all the Notification Classes this current connector receives notifications from.  **  ** Paramaters:  ** - conn: the BACnet connector used to communicate to the device  **  ** Returns:  ** Grid with the locadDeviceId registered, the notification class to which it is registered,  ** and the Finstack point for whicj it is registered to receive alarms.  **  Grid bacnetGetNotificationSubscriptions(Obj conn)  **  ** Returns the handled alarms from this Bacnet connector.  ** This is more for testing purposes, to see we are receiving alarms.  ** These alarms should also be visible in the Alarm Extension.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  **  ** Examples:  ** bacnetGetHandledAlarms(@connRef)  **  Grid bacnetGetHandledAlarms(Obj conn)  **  ** Returns the unhandled alarms from this Bacnet connector  ** This is more for testing purposes, to see we are receiving alarms.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  **  ** Examples:  ** bacnetGetUnhandledAlarms(@connRef)  **  Grid bacnetGetUnhandledAlarms(Obj conn)  **  ** Returns the unhandled alarm events (type=event) of Bacnet connector  ** This is more for testing purposes, to see we are receiving alarms.  **  ** Parameters:  ** - conn: the BACnet connector used to communicate to the device  **  ** Examples:  ** bacnetGetUnhandledEvents(@connRef)  **  Grid bacnetGetUnhandledEvents(Obj conn)