I've made a modified version of the alarm summary script I found on here at one time to do just that. I literally made the change yesterday to it, although mine has a lot of other stuff I'm doing too, so let me see if I can clean it up, but essentially it's a Gateway script that runs like once a second and keeps track of all the alarms and dumps that in a global variable, then in each folder I have an "area" tag that parses that global tag and finds all the alarm stats for tags starting with the same starting path.
Here's my Gateway.AlarmSummary
script:
logger = system.util.getLogger(system.util.getProjectName() + '.' + __name__)
alarmSummaryGlobal = {}
def getAllAlarms():
'''
Description:
Query all alarms using system function 'system.alarm.queryStatus' and store in
a global vaiable dictionary, 'alarmSummaryGlobal'. This function calls 'system.alarm.queryStatus'
only one time vs calling it several times per instance, which can cause higher than normal CPU usage
when several instances are used.
This function is typically called from a gateway event, like a timer event.
The frequency of this event can vary depending on the specific project needs, but has been
tested using a one second delay with very good results.
Args:
None
Returns:
None
History:
Ver. Date Author Comment
---------------------------------------------------------------------------------------
1.0 2022-10-20 James Landwerlen Initial
1.1 2022-10-29 James Landwerlen Added shelving
1.2 2023-02-13 Michael Flagler Split out alarm styles
'''
global alarmSummaryGlobal
global testInt
alarmSummary = {}
state = ["ActiveUnacked", "ActiveAcked", "ClearUnacked"]
# Get an Alarm Query Result for all alarms, including alarms that are shelved
alarms = system.alarm.queryStatus(state=state, includeShelved=True)
for alarm in alarms:
# Iterate through all alarms and place into the global dictionary 'alarmSummaryGlobal' to be called later
# on by each UDT instance
source = alarm.source
# Build a typical tag path [MyTagProvider]MyTag
tagPath = '[' + alarm.source.getPathComponent('prov') +']' + alarm.source.getPathComponent('tag')
name = alarm.name
state = alarm.state.name()
priority = alarm.priority.name()
# If the alarm is shelved, flag as shelved and set state/priority to Shelved so the counts
# will be ignored later on
if alarm.isShelved():
state = priority = 'Shelved'
alarmSummary[source] = {'tagPath':tagPath, 'name':name, 'state':state, 'priority':priority, 'shelved':alarm.isShelved()}
alarmSummaryGlobal = alarmSummary
#testInt += 1
#logger.warn(str(testInt))
def getUDTInstances(tp, alarmPath):
'''
Desc:
This function is called from each UDT instance using `runScript` in the element `summary` and
returns a json with alarm information specific to the path.
The argument `tp` is populated automatically in each UDT instance using a value change script
in the element `TagProvider`.
Args:
tp (str): Tag Provider
alarmPath (str): Path to the UDT instance parent folder. Root will be blank.
Returns:
str: JSON encoded string of UDT instances
History:
Ver. Date Author Comment
---------------------------------------------------------------------------------------
1.0 2022-10-20 James Landwerlen Initial
1.1 2022-10-29 James Landwerlen Added shelving
1.2 2023-02-13 Michael Flagler Split out alarm styles
1.3 2024-07-24 Michael Flagler Added priorityUnack
'''
global alarmSummaryGlobal
#global testInt
#logger.warn(str(testInt))
alarmPath = '[' + tp + ']' + alarmPath
alarmPriorities = {'': -2, 'Shelved':-1, 'Diagnostic':0, 'Low':1, 'Medium':2, 'High':3, 'Critical':4}
statePriorities = {'': -2, 'Shelved':-1, 'ClearAcked':0, 'ClearUnacked':1, 'ActiveAcked':2, 'ActiveUnacked':3}
priorityCounts = {'Shelved':0, 'Diagnostic': 0, 'Low': 0, 'Medium': 0, 'High': 0, 'Critical': 0}
stateCounts = {'Shelved':0, 'ClearAcked':0, 'ClearUnacked':0, 'ActiveAcked':0, 'ActiveUnacked':0}
priority = ''
priorityUnack = ''
state = ''
styleClass = ''
hasUnackAlarm = False
resultDict = {'counts': priorityCounts, 'priority': '', 'state': '', 'class': '', 'hasUnackAlarm': ''}
alarmPrioritiesList = []
highestPriorityUnack = -2
#logger.warn(str(len(alarmSummaryGlobal)))
for alarm in alarmSummaryGlobal:
tagPath = alarmSummaryGlobal[alarm]['tagPath']
# If alarm found within the summary then get counts and priorities
# The tagPath must match exactly starting from the left, this prevents partial matches
if tagPath.startswith(alarmPath):
# Create a list of alarm priorities, so we can later find the highest priority as well as state for that priority
alarmPriority = alarmSummaryGlobal[alarm]['priority']
alarmState = alarmSummaryGlobal[alarm]['state']
alarmPrioritiesList.append([alarmPriorities[alarmPriority], statePriorities[alarmState]])
shelved = alarmSummaryGlobal[alarm]['shelved']
if not shelved and (alarmState in ['ClearedUnacked', 'ActiveUnacked']):
highestPriorityUnack = max(highestPriorityUnack, alarmPriorities[alarmPriority])
hasUnackAlarm = True
priorityCounts[alarmPriority] += 1
stateCounts[alarmState] += 1
if alarmPrioritiesList:
# Get max priority/state combination from list of alarms
# Here, we're comparing the priority first, and if the same, comparing the state to determine highest/max
alarmListMax = max(alarmPrioritiesList, key=lambda item: (item[0], item[1]))
priority = alarmPriorities.keys()[alarmPriorities.values().index(alarmListMax[0])]
state = statePriorities.keys()[statePriorities.values().index(alarmListMax[1])]
priorityUnack = alarmPriorities.keys()[alarmPriorities.values().index(highestPriorityUnack)]
styleClass = getAlarmStyle(str(priority), str(state))
#Take the results and place in a dictionary
resultDict = {
'priorityCounts': priorityCounts, # A dicionary holding counts for each priority
'stateCounts': stateCounts, # A dicionary holding counts for each state
'priority': priority, # The highest priority
'state': state, # The most important state
'class': styleClass, # The style class for indication purposes
'hasUnackAlarm': hasUnackAlarm, # At least one unacknowledged alarm exists
'priorityUnack': priorityUnack # Highest priority of all unacked alarms
}
json = system.util.jsonEncode(resultDict, 0)
return json
def getAlarmStyle(priority, state):
'''
Description:
This function is called from each UDT instance using `runScript` in the element `summary` and
returns a json with alarm information specific to the path.
Args:
priority (str): Priority name (Shelved, Diagnostic, Low, Medium, High, Critical)
state (str): State name (Shelved, ClearAcked, ClearUnacked, ActiveAcked, ActiveUnacked)
History:
Ver. Date Author Comment
---------------------------------------------------------------------------------------
1.0 2022-10-20 James Landwerlen Initial
1.1 2022-10-29 James Landwerlen Added shelving
1.2 2023-02-13 Michael Flagler Split out alarm styles
'''
#These styles were not part of the resource, but any style can be added and will be selected based on the state and priority
styleStates = { 'DiagnosticActiveUnacked': 'common/navigation/diagnostic-unacked',
'DiagnosticActiveAcked': 'common/navigation/diagnostic-acked',
'LowActiveUnacked': 'common/navigation/low-unacked',
'LowActiveAcked': 'common/navigation/low-acked',
'MediumActiveUnacked': 'common/navigation/medium-unacked',
'MediumActiveAcked': 'common/navigation/medium-acked',
'HighActiveUnacked': 'common/navigation/high-unacked',
'HighActiveAcked': 'common/navigation/high-acked',
'CriticalActiveUnacked': 'common/navigation/critical-unacked',
'CriticalActiveAcked': 'common/navigation/critical-acked'
}
return styleStates.get(priority + state)
Which I have a gateway timer script running at 1000ms:
Gateway.AlarmSummary.getAllAlarms()
And I've attached the export of my UDT for the AlarmSummary tag which will give both the highest priority alarm, and the highest unacked alarm priority, plus a bunch of other nice stats at that folder level the tag is in or any subfolder.
AlarmSummaryUDT.json (6.0 KB)