Alarm Tables, Audio Alerting, Issues with Shelving

Hi all,

I have several questions. To save time and put the main issue up front: I’d like to customize my Alarm Status table with a noisemaker that goes if any alarms are currently unacknowledged or unshelved. I’m trying to do this by having custom Ack/Shelve buttons that increment/decrement a counter. Any simple, functional solution is welcome. However, in attempting to solve the problem, I’ve come up with more questions than answers, which are below:

So I am creating a page with an Alarm Status Table. When an alarm goes active, a counter is incremented, and if that counter is positive and greater than zero, a Sound Player Component will beep every five seconds.

When the alarm is acknowledged, the counter is decremented, such that if all alarms are acknowledged, the beeping stops. We’d also like for if all current alarms are acknowledged OR shelved, for the beeping to stop.

Here’s where things get hairy: I had to create my own acknowledge button, since the alarm computer is a public terminal in our control room, but we want accountability as far as who acknowledges the alarms. The code I ended up using for the button is (with some elisions):

alrmLst = event.source.parent.getComponent('Alarm Status Table')
selAlrm = alrmLst.selectedAlarms

info = ['', '']
prompt = ['User Name', 'Password']
boxes = [system.gui.inputBox, system.gui.passwordBox,system.gui.messageBox]
	
for i in range( len(info) ):
	info[i] = boxes[i]('Please Type ' + prompt[i], '')
	if ((info[i] is None) or (info[i] == "")):
		boxes[-1]('Invalid ' + prompt[i])
		return()

ackMsg = system.gui.inputBox('Please type Acknowledgement Information','')
for i,row in enumerate(range(selAlrm.rowCount)):
	uuid = selAlrm.getValueAt(row,'EventId')
	system.util.sendMessage(project = 'AlarmTest',messageHandler = 'publicAcknowledge',payload = {'uuid':uuid,'ackMsg':ackMsg,'user':info[0]}, scope = 'G')
  1. I’m aware that the way of requesting credentials is incredibly hacked together. I more than welcome a cleaner way of doing this. A combined input/passwordBox that had one field for uname and one for password would be ideal.
  2. alrmLst does not seem to function as a normal dataset. I have to cast it to a PyDataSet to get any functionality out of it.
  3. I do not recall how I ended up using system.util.sendMessage to acknowledge instead of system.alarm.acknowledge. If I had to guess it was a copy/paste job from a different forum post. It’s odd to me that I can’t find this method mentioned anywhere in the documentation though.

I’m attempting to set up something similar with a custom ‘Shelve’ button but I’m having strange issues:

alrmLst = event.source.parent.getComponent(‘Alarm Status Table’)
selAlrm = alrmLst.selectedAlarms
data = selAlrm.data
if selAlrm.rowCount == 0:
system.gui.messageBox(“Please select an alarm to shelve”)
return()

selAlrm = system.dataset.toPyDataSet(selAlrm)

for row in range(selAlrm.rowCount):
sourcePath = selAlrm[row][‘DisplayPath’]
system.alarm.shelve(path = sourcePath, timeoutMinutes = 5)

I get:

13:52:09.429 [AWT-EventQueue-2] ERROR com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter - <HTML>Error executing script for event:&nbsp;<code><b>actionPerformed</b></code><BR>on component:&nbsp;<code><b>Button 1</b></code>.
com.inductiveautomation.ignition.common.script.JythonExecException: Traceback (most recent call last):
  File "<event:actionPerformed>", line 43, in <module>
  File "<event:actionPerformed>", line 38, in main
	at com.inductiveautomation.ignition.common.TypeUtilities.coerce(TypeUtilities.java:1299)

	at com.inductiveautomation.ignition.common.script.builtin.PyArgumentMap.coerce(PyArgumentMap.java:108)

	at com.inductiveautomation.ignition.common.script.builtin.PyArgumentMap.interpretPyArgs(PyArgumentMap.java:88)

	at com.inductiveautomation.ignition.common.script.builtin.PyArgumentMap.interpretPyArgs(PyArgumentMap.java:36)

	at com.inductiveautomation.ignition.common.script.builtin.AbstractAlarmUtilities.shelve(AbstractAlarmUtilities.java:94)

	at sun.reflect.GeneratedMethodAccessor575.invoke(Unknown Source)

	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

	at java.lang.reflect.Method.invoke(Unknown Source)


java.lang.ClassCastException: java.lang.ClassCastException: Cannot coerce value 'OpsPLC/Alarms/TestAlarms/Test 1/TestAlarm1' into type: class [Ljava.lang.String;

This is obviously and clearly a string, so I don’t see what the issue is.

Disclaimer: I haven’t read through all of the rest of the message (I wonder if the system tags that give you alarm unack counts might be easier to use than keeping your own counter), but;
system.alarm.shelve expects a list of paths, not a single string path; that’s what the [L in [Ljava.lang.String is (poorly) indicating.
system.alarm.shelve(path = [sourcePath], timeoutMinutes = 5) should work.

PGriffith - Thanks, that will be a big help, I’m going to put that to work right now.

I’ll dig through the documentation when I get a chance but if you could share where to find those system tags I’d appreciate it.

edit: Gateway/Alarming, got it, thanks

1 Like

Trying to bring things back to a more simple place,

I followed the code in the 7.9 manual for system.alarm.shelve,

table = event.source.parent.getComponent('Alarm Status Table')
rows = table.selectedRows
data = table.data
if len(rows)>0:
   sourcePaths = [str(data.getValueAt(r,'Source')) for r in rows]
   system.alarm.shelve(path=sourcePaths,timeoutMinutes=5)

Line 2 throws an error:
AttributeError: ‘com.inductiveautomation.factorypmi.application.com’ object has no attribute ‘selectedRows’

If I change selectedRows to selectedAlarms, line three throws:
AttributeError: ‘com.inductiveautomation.factorypmi.application.com’ object has no attribute ‘data’

What’s the reasoning for this table behaving so unlike other tables? How am I supposed to operate with it? It seems odd that the instructions for how to shelve and acknowledge alarms don’t work with an Alarm Status Table.

PGriffith,

Finally got the shelving to work, of course, it doesn’t affect the number of ‘unacked’ alarms. Before I go off and create a counter for shelved alarms to be subtracted from the total unacked, is there a more sane, pre-built way of doing this?