Alarm Acknowledgement

Hi all,

I’m having a bit of trouble understanding system.alarm.acknowledge().

The docs say that the system will ignore any alarms that have already been acknowledged. What I’m seeing is when I try to acknowledge a Cleared, Unacked alarm, it creates Active, Acknowledged alarms for it. Also when I try to Acknowledge an Active, Acknowledged alarm, it just creates more and more Active, Acknowledged alarms.

Is this expected behaviour? I would have expected that:
a) the system would not generate more acknowledged alarms if I try to acknowledge an already acknowledged alarm
b) if I acknowledge a cleared alarm, that the acknowledged result would also be a clear alarm, not active - right now I can create infinite active alarms from a single cleared, acknowledged one…

Can you explain a little bit more the setup you’re seeing this behavior with? I can’t seem to replicate it.

Hi Kevin,

I have a table with alarms in it that I can select to acknowledge.

Here’s an example of some reproduction steps:
-Select a Cleared, Unacknowledged alarm from the table
-Verify that the alarm condition is no longer active
-call system.alarm.acknowledge() on this alarm

The result:
-An Active, Acknowledged alarm is created and added to the table.

Further steps:
-Select the new Active, Acknowledged alarm in the table
-call system.alarm.acknowledge() on this alarm, say 20 times

The result:
-20 new Active, Acknowledged alarms are created and added to the table.

Attached are a few images:
In #1, I have an alarm selected (~10 rows from the bottom, UUID starts with 950ad)
In #2, I’ve acknowledged the selected alarm and selected the resulting new alarm (bottom row)
In #3, I’ve selected the new alarm in #2, then acknowledged a bunch (~20) times. The selected alarms are the result.

The code from the Acknowledge button is as follows:

table = event.source.parent.parent.getComponent("AlarmTable")
selRows = table.selectedRows
if len(selRows)>0:
	uuids = []
	for row in selRows:
		uuid =,'uuid')
	system.alarm.acknowledge(uuids, None)

What’s populating the data in that table?

Hi Kevin,

Sorry for the massive posts, but the more information the better!

This is the script that spits out the data into the table:

# Start date and end date are self explanatory
# Hide cleared and acknowledged is as well
# groups is a list of groupIDs of which we want the alarms for, or an empty list for all groups
# sites is a list of siteIDs of which we want the alarms for, or an empty list for all sites
# it doesn't make tons of sense to use the groups and sites parameters at the same time - if so, 
# then a site must be in the groups list AND the site list to be accepted
def populateAlarmsTable(startDate="2005-01-01", endDate="", hideClearedAndAckd=True, groups=[], sites=[]):
	import app
	import system
	tableHeaders = ["siteName","device","groupName","clearTime","ackUser","activeTime","alarmStatus","alarmName","ackTime","siteId","deviceId","uuid"]
	tableData = []
	states = []
	if (hideClearedAndAckd):
		states = ["ClearUnacked","ActiveUnacked","ActiveAcked"]
		states = ["ClearUnacked","ActiveUnacked","ActiveAcked", "ClearAcked"]
	sysAlarms = system.alarm.queryJournal(startDate, endDate, "PMCS_Alarms_Journal",state=states)
	for alarm in sysAlarms:
		ackData = alarm.getAckData()
		clearedData = alarm.getClearedData()
		activeData = alarm.getActiveData()
		reftable = app.siteutil.getDeviceClliFromAlarmPath(str(alarm.getSource()))
		deviceId = app.siteutil.getDeviceIdFromReftable(reftable)
		siteId = app.siteutil.getDeviceParameter(deviceId, "siteid")
		groupId = app.siteutil.getSiteParameter(siteId, "groupid")
		# If this is not in the accepted sites list, skip
		if (len(sites) != 0):
			if (siteId not in sites):
		# If this is not in the accepted groups list, skip
		if (len(groups) != 0):
			if (groupId not in groups):
		# SiteName
		tableRow.append(app.siteutil.getSiteParameter(siteId, "sitename"))
		# Device
		tableRow.append(app.siteutil.getDeviceParameter(deviceId, "name"))
		# Group
		tableRow.append(app.siteutil.getSiteParameter(siteId, "groupname"))
		# Clear Time
		if clearedData is not None:
		# Ack User
		if ackData is not None:
			tableRow.append("ack data TODO")
			tableRow.append("ack data TODO")
		# Active Time
		if activeData is not None:
			tableRow.append("Active data null")
		# Alarm Status
		# 0 - red background + warning icon = Active Unacked
		# 1 - yellow + warning = Active Acked
		# 2 - blue + info = Clear Unacked
		# 3 - clear + checkmark = Clear Acked
		state = 5
		if (str(alarm.getState()) == "Active, Unacknowledged"):
			state = 0
		elif (str(alarm.getState()) == "Active, Acknowledged"):
			state = 1		
		elif (str(alarm.getState()) == "Cleared, Unacknowledged"):
			state = 2
		elif (str(alarm.getState()) == "Cleared, Acknowledged"):
			state = 3
		# Alarm Name
		tableRow.append(app.siteutil.getAlarmName(alarm.getSource(), True))
		# Ack Time
		if ackData is not None:
			tableRow.append("ack data TODO")
		# Site ID
		# Device ID
		# UUID
	# Build the dataset to return
	ret = system.dataset.toDataSet(tableHeaders, tableData)
	return ret

Thanks for looking into this.

Right, as I suspected - you’re querying the journal.

This is pretty much working as intended. The journal captures a history of alarm-related events. If you acknowledge the same alarm event 20 times then you’ll see 20 entries in the journal telling you so.

Where you wouldn’t see 20 entries would be an alarm status table, which would continue to show the one event (as acknowledged, whether you do so 1 or 20 times) until whatever filters on your status table might cause it to stop being shown.

Fantastico, everything is making much more sense now!

Checking the status vs checking the journal fixes the main issue here, but raises another smaller one.

Querying the status gives me alarms that look really nice, where a cleared & acknowledged will have non-null data for the alarm time, clear time, ack time etc. The problem is if I want to look at historical alarms - if you imagine the table in the images above, what would be the best way to populate it with historical alarms?

If I use queryJournal, it will return multiple alarms for each condition as we’ve seen; is there a way to ‘squash’ these alarms together so that I can look at the ‘final’ alarm object which has non-null event info for cleared/acked/active state changes all in one object? Or when querying the alarms should I use their UUIDs and merge the info together manually to display it in the table?

Thanks for the help,

Or, more specifically/concisely:
Is there a way I can get results of the same form as from queryStatus(), except by specifying a date range?

I am seeing the same behavior as chache - it was caught during validation of a system we are doing for a client and the validation company is throwing a flag.

What I’m seeing is when I try to acknowledge a Cleared, Unacked alarm, it creates Active, Acknowledged alarms for it.

The issue is with cleared, acknowledged alarms. In the alarm journal, I get a “cleared, unacknowledged” event. Then, after ack’ing, I get an “active, acknowledged” event. However, the color scheme shows it as what I am assuming is cleared (green). There seems to be no way to get a cleared, acknowledged event in the journal - only active, acknowledged. When you ack a cleared alarm, is it supposed to show up as active in the journal again?

Sample sequence:

  1. Hit e-stop button. Get active, unack’ed in journal.
  2. Remove e-stop condition. Get cleared, unack’ed in journal.
  3. Ack alarm. Get active, ack’ed event in journal.

Update: I am able to get a cleared, ack’ed event in the journal if you ack the event, then clear it. If I clear, then ack, it will show up as active, ack’ed. Why is this?