How to dynamically get valid descriptions and display names for all alarm event properties?

When interrogating the properties (CommonAlarmProperties) of an alarm event (PyAlarmEvent) object, as returned from system.alarm.queryStatus() I have noticed that the getDescription() method returns a LocalizedString object that has a readable description for some (most) properties while others return ¿enums.CommonAlarmProperties.ackTime.Desc?

Similar anomalies for getDisplayName().

Does this represent a a lack of completion by Ignition developers or a lack of understanding on my part. Is there a way to dynamically get valid descriptions
and display names for all alarm event properties?

I'm aiming to dynamically discover all available alarm event properties, with their display names and descriptions. I was aiming to do this dynamically rather than hardcode it to make it somewhat future proof (say IA adds more properties or revises descriptions in the future).

Example code:

alarms = system.alarm.queryStatus()
if len(alarms):
	event = alarms[0]
	for prop in event.getProperties():
		print "{:19s} ({:26s}): {}".format(prop.getName(), prop.getDisplayName(),prop.getDescription())
	print "Alarm Event Object:", type(event)
	print "Alarm Event Property object:", type(prop)
	print "Alarm Event Property Description object:", type(prop.getDescription())
else:
	print "Query returned no alarms"

Example Results:

>>> 
notes               (Notes                     ): Documentation about the alarm.
itemPath            (Item Path                 ): The path of the item holding the alarm, not including the alarm name.
activeDuration      (Active Duration           ): The elapsed time between the alarm becoming active and being cleared, as a formatted string. Calculated against the current time if the alarm is still active.
timeOnDelaySeconds  (Active delay (seconds)    ): The time, in seconds, before the alarm will be considered truly active after the active condition becomes true. Also known as a "rising edge time deadband".
shelvingAllowed     (Shelving Allowed          ): Whether or not this alarm can be shelved.
isActive            (Active?                   ): �enums.CommonAlarmProperties.isActive.Desc?
eventTime           (Event Time                ): �enums.CommonAlarmProperties.eventTime.Desc?
source              (Source Path               ): �enums.CommonAlarmProperties.source.Desc?
ackUserName         (Ack'ed By (Name)          ): �enums.CommonAlarmProperties.ackUserName.Desc?
enabled             (Enabled                   ): Specifies whether or not this alarm is evaluated by the system.
isAcked             (Acked?                    ): �enums.CommonAlarmProperties.isAcked.Desc?
mode                (Mode                      ): None
ackTime             (Ack Time                  ): �enums.CommonAlarmProperties.ackTime.Desc?
ackUser             (Ack'ed By                 ): �enums.CommonAlarmProperties.ackUser.Desc?
clearTime           (Clear Time                ): �enums.CommonAlarmProperties.clearTime.Desc?
deadband            (Deadband                  ): A numeric deadband for the alarm condition. The interpretation of this value depends on the deadband mode. Note that this deadband is separate from, and evaluated after, the tag's deadband.
timeOffDelaySeconds (Clear delay (seconds)     ): The time, in seconds, before the alarm will be considered truly clear after the clear condition becomes true. Also known as a "falling edge time deadband".
state               (Current State             ): �enums.CommonAlarmProperties.state.Desc?
displayPath         (Display Path              ): An optional path (separated by "/") that will be used for display and browsing purposes.
displayPathOrSource (Display Path or Source    ): �enums.CommonAlarmProperties.displayPathOrSource.Desc?
liveBranchCount     (�enums.CommonAlarmProperties.liveBranchCount?): �enums.CommonAlarmProperties.liveBranchCount.Desc?
fullItemPath        (Full Item Path            ): The full path of the item holding the alarm, not including the alarm name. An easier to read version of Source Path.
itemName            (Item Name                 ): The name of the item holding the alarm.
label               (Label                     ): An optional name that will be used for display purposes. Provides a dynamic alternative to the static "name" identifier. If left blank, the name will be used.
priority            (Priority                  ): The priority (or "severity") of this alarm.
ackNotesReqd        (Ack Notes Required        ): Select this option to require operators to provide some explanation when the alarm is acknowledged.
timestampSource     (Timestamp Source          ): Dictates whether the timestamp used on the alarm comes from the system or the value.
ackMode             (Ack Mode                  ): How acknowledgement works for the alarm: Auto - Alarm is acknowledged when cleared. Manual - Alarm acknowledged by user. Off - Alarm is always marked as 'acknowledged'.
eventValue          (Event Value               ): �enums.CommonAlarmProperties.eventValue.Desc?
isShelved           (Is Shelved?               ): �enums.CommonAlarmProperties.isShelved.Desc?
ackDuration         (Unacknowledged Duration   ): The elapsed time between the alarm becoming active and being acknowledged, as a formatted string.
activeTime          (Active Time               ): �enums.CommonAlarmProperties.activeTime.Desc?
isClear             (Clear?                    ): �enums.CommonAlarmProperties.isClear.Desc?
eventState          (Event State               ): �enums.CommonAlarmProperties.eventState.Desc?
name                (Name                      ): The identifier of the alarm. Combined with the location of the alarm, this will be the unique id of the alarm definition. For dynamic values, use Label and/or Display Path.
deadbandMode        (Deadband Mode             ): Defines how the deadband value is used. If percent, the value (0-100) will be compared against the Eng. limits defined for the value.
Alarm Event Object: <type 'com.inductiveautomation.ignition.common.alarming.PyAlarmEvent'>
Alarm Event Property object: <type 'com.inductiveautomation.ignition.common.alarming.config.CommonAlarmProperties'>
Alarm Event Property Description object: <type 'com.inductiveautomation.ignition.common.i18n.LocalizedString'>

Ignition v8.1.41

It does look like this is the generic enum description property that simply hasn't been filled in.

topAlarm = system.alarm.queryStatus()[0]
for prop in topAlarm.properties:
	if not prop.description or 'enums' in prop.description.toString():
		print '#', prop.displayName

Here are the properties with missing descriptions:

# Active?
# Event Time
# Source Path
# Ack'ed By (Name)
# Acked?
# Mode
# Ack Time
# Ack'ed By
# Clear Time
# Current State
# Display Path or Source
# Event Value
# Is Shelved?
# Active Time
# Clear?
# Event State

@Kevin.Herron, would this count as a bug (to be fixed)?

A laudable goal, but note that each module installed that interacts with alarming can add arbitrary properties, so the set of properties available to you is going to depend on if you have, for instance, alarm notification +/- each different alarm notification type (Twilio, SMS, various third parties, etc).

That said, it's not like this shouldn't work.

In this particular case, it looks like an oversight on our part. We do have bundle keys for the properties you're running into, but they're not formatted with the additional .Desc qualifier the LocalizedString is expecting:

enums.CommonAlarmProperties.isAcked=Acked?
enums.CommonAlarmProperties.isClear=Clear?
enums.CommonAlarmProperties.isActive=Active?
enums.CommonAlarmProperties.activeTime=Active Time
enums.CommonAlarmProperties.clearTime=Clear Time
enums.CommonAlarmProperties.ackTime=Ack Time
enums.CommonAlarmProperties.eventTime=Event Time
enums.CommonAlarmProperties.eventValue=Event Value
enums.CommonAlarmProperties.isShelved=Is Shelved?
enums.CommonAlarmProperties.shelveExpiration=Shelf Expiration

At least, for most of the ones on your list. Some others simply aren't documented because of either a genuine accident on our part, or because they were never intended to be end-user facing, e.g. liveBranchCount is an internal counter for alarm pipelines, not something you should ever need or want to interact with as an end user. It's hard to separate those two, because most of this stuff has been in place for literally years.

Possibly you could work around the issue with most of the keys with something like this, that directly calls out to the localization mechanism based on the key provided in the LocalizedString, and tries to get the shorter variant if the first lookup fails:

from com.inductiveautomation.ignition.common import BundleUtil

# returns None if key isn't present
i18n = lambda key: BundleUtil.get().getString(key)

def getDescription(fullKey):
    qualifier = ".Desc"
    return i18n(fullKey) or i18n(fullKey[:-len(qualifier)]) if fullKey.endsWith(qualifier) else fullKey

alarms = system.alarm.queryStatus()
if len(alarms):
    event = alarms[0]

    for prop in event.getProperties():
        print "{:19s} ({:26s}): {}".format(prop.getName(), prop.getDisplayName(), getDescription(prop.getDescription().getKey()))
    print "Alarm Event Object:", type(event)
    print "Alarm Event Property object:", type(prop)
    print "Alarm Event Property Description object:", type(prop.getDescription())
else:
    print "Query returned no alarms"

(Disclaimer: Untested, there may be one or more syntax errors, but hopefully it gets the point across.)

1 Like