Print notes of acknowledged alarms

Hello everyone, it is a pleasure to greet you.
Let me introduce myself, I'm a new developer here and I really liked the Ignition tool for its versatility.

But I come across a problem that I haven't solved for a couple of hours...

I'm trying to save notes of recognized alarms, to later "print" them in the console, but I can't find the concrete solution...

# Define a function that will retrieve alarm data in a separate thread
def getAlarms():
    # Call queryStatus to retrieve the alarm data we're looking for, and store the results in a variable.
    #   In this case, we're looking for alarm events that contain the word "Sensor" in the source path.
    #results = system.alarm.queryStatus(source=["*Sensor*"])
    results = system.alarm.queryStatus(source=["*Ramp1*"])
     
     
     
    # From this same script, define a separate function that will later interact with the
    #    GUI, allowing us to move our alarm data over to a component
    # We're also using the getDataset() function on the object returned by queryStatus,
    #   since that will provide a dataset that our table component will expect.
    def setTheTable(alarms = results.getDataset()):
         
        # Replace the property reference below with a path leading to whichever property
        #    you want to move the alarm data to.
        event.source.parent.getComponent("Table").data = alarms
     
    # The last thing we'll do in the separate thread is call invokeLater
    #   which will let our setTheTable function run in the GUI thread
    system.util.invokeLater(setTheTable)
 
# Call the getAlarms function in a separate thread, which starts the whole process     
system.util.invokeAsynchronous(getAlarms)

and I only get "basic" values in the power table:

I did that too:

I have read and reread the following post, without finding the definitive solution..
Exporting Alarm Status Table to CSV - Ignition - Inductive Automation Forum

thx...

While not the simplest solution, the script below works to create a dataset like getDataset() returns that adds an ackNotes column. Not sure if that is what you are intending to do, but hopefully you can use it as an example on how I access the data:

results = system.alarm.queryStatus(source=["*Ramp1*"])
headers = ['EventId', 'Source', 'DisplayPath', 'EventTime', 'State', 'Priority', 'ackNotes']

alarms_with_ack_note = []

for result in results:
	properties = result.getAckData().getProperties()
	for prop in properties:
		data = []
		if str(prop) == 'ackNotes':
#			print dir(result)
			data.append(result.getId())
			data.append(result.getSource())
			data.append(result.getDisplayPath())
			data.append(system.date.fromMillis(result.getActiveData().getTimestamp()))
			data.append(result.getState())
			data.append(result.getPriority())
			data.append(result.getAckData().get(prop))
			
			alarms_with_ack_note.append(data)
			
ds = system.dataset.toDataSet(headers, alarms_with_ack_note)

You will notice the commented out command with a dir function. This is really useful in attempting to understand how to manipulate objects returned to you in python. It will provide you with the different attributes the object you are working with supports, and should help you form guessing if something like event.getAckNotes() will work.

Garth

1 Like

If you want to get the information directly from the selected alarms in the alarm status table, here is a guide to doing it.

Currently, there are 23 properties that show up in the standard alarm status table model. The columns are indexed 0 through 22, and each specific column retains its specific model index no matter how the columns are arranged or rearranged in the table.

Here is a map of the columns:

# Model Index	Column Name
#     0		ackNotes
#     1		Ack Time
#     2		Ack'ed By
#     3		Active Pipeline
#     4		Clear Pipeline
#     5		Ack Pipeline
#     6		Active Time
#     7		Clear Time
#     8		Event Value
#     9		Deadband
#     10	Display Path
#     11	Acked?
#     12	Active?
#     13	Clear?
#     14	Name
#     15	Notes
#     16	Priority
#     17	Source Path
#     18	Current State
#     19	Event ID
#     20	Unacknowledged Duration
#     21	Active Duration
#     22	Label

It looks like the columns you are interested in are 0 and 2

Whenever I need to directly manipulate the data from an alarm status table, I get the table model, and I get the desired values from it in the same way that values are obtained from a basic dataset. In the alarm status table, the checkboxes are actually in a separate table from the main table, so I've developed a function that allows me to get either the main table or the checkbox table based on a passed value. Here is my function modified to suit your usage case:

alarmStatusTable = system.gui.getParentWindow(event).getComponentForPath('Root Container.Alarm Status Table') #This will need to be changed if the alarm status table has a custom name or if the table is not directly in the root container
def getTable(tableType):#Retrieves a specified jTable instance from the alarmStatusTable
	def tableSearch(tableType, alarmStatusTable):
		if alarmStatusTable.componentCount > 0:
			for component in alarmStatusTable.getComponents():
				if 'AlarmStatusTable$1' in str(component.__class__) and tableType == 'main':
					return component
				elif 'JideTable' in str(component.__class__) and tableType == 'checkbox':
					return component
				else:
					table = tableSearch(tableType, component)
					if table is not None:
						return table
		return None
	return tableSearch(tableType, alarmStatusTable)
mainTable = getTable('main')
checkboxTable = getTable('checkbox')
mainModel = mainTable.getModel()
checkboxModel = checkboxTable.getModel()
headers = ['Event ID', 'Ack Notes', 'Acked By']
data = []
for row in range(checkboxModel.rowCount):
	if checkboxModel.getValueAt(row, 0):#This will only be true if the checkbox is selected
		dataRow = []#add any of the columns listed in the provided column model map in whatever order is desired
		dataRow.append(mainModel.getValueAt(row, 19)) #19 = Event ID Column
		dataRow.append(mainModel.getValueAt(row, 0)) #0 = Ack Notes Column
		dataRow.append(mainModel.getValueAt(row, 2)) #2 = Ack'd by Column
		data.append(dataRow)
ds = system.dataset.toDataSet(headers, data)

Here is the result:

If you do want to pull in the values using queryStatus for purposes of building a proper dataset, here is a script that will pull in all 23 columns:

state = ['ActiveUnacked', 'ActiveAcked', 'ClearUnacked']
alarms = system.alarm.queryStatus(state = state) #Optional: Filtering the result
headers = ['ackNotes', 'Ack Time', "Ack'ed By", 'Active Pipeline', 'Clear Pipeline', 'Ack Pipeline', 'Active Time', 'Clear Time', 'Event Value', 'Deadband', 'Display Path', 'Acked?', 'Active?', 'Clear?', 'Name', 'Notes', 'Priority', 'Source Path', 'Current State', 'Event ID', 'Unacknowledged Duration', 'Active Duration', 'Label']
data = []
for alarm in alarms:
	ackNotes = alarm.getOrElse('ackNotes', None)
	ackTime = alarm.getOrElse('eventTime', None) if alarm.acked else None
	ackedBy = alarm.getOrElse('ackUser', None)
	activePipeline = alarm.getOrElse('activePipeline', None)
	clearedPipeline = alarm.getOrElse('clearPipeline', None)
	ackPipeline = alarm.getOrElse('ackPipeline', None)
	activeTime = alarm.getOrElse('activeTime', None)
	clearTime = alarm.getOrElse('clearTime', None)
	eventValue = alarm.getOrElse('eventValue', None)
	deadBand = alarm.getOrElse('deadband', None)
	displayPath = alarm.displayPath									#This also Works:.getDisplayPath() AND This also works: .getOrElse('displayPath', None)
	acked = alarm.acked 											#This also works: .getOrElse('isAcked', None)
	active = alarm.getOrElse('isActive', None) 						#In my experimentation, .active didn't work
	clear = alarm.cleared 											#This also works: .getOrElse('isClear', None)
	name = alarm.name												#This also works.getName() AND This also works: .getOrElse('name', None)
	notes = alarm.notes 											#This also works: .getNotes() AND This also works: .getOrElse('notes', None)
	priority = alarm.priority										#This also works: .getPriority() AND This also works:.getOrElse('priority', None)
	sourcePath = alarm.source										#This also works: getSource() AND This also works: .getOrElse('source', None)
	currentState = alarm.state										#This also works: .getState() #The following DID NOT return the expected result: .getOrElse('eventState', None)
	eventID = alarm.id												#This also works: .getID()
	unacknowledgedDuration = alarm.getOrElse('ackDuration', None)
	activeDuration = alarm.getOrElse('activeDuration', None)
	label = alarm.label												#This also works: .getLabel()
	data.append([ackNotes, ackTime, ackedBy, activePipeline, clearedPipeline, ackPipeline, activeTime, clearTime, eventValue, deadBand, displayPath, acked, active, clear, name, notes, priority, sourcePath, currentState, eventID, unacknowledgedDuration, activeDuration, label])
dataset = system.dataset.toDataSet(headers, data)
2 Likes

I'm still confused, none of the possible solutions work for me :confused:

I tried removing columns to see if it solves the error by bringing only specific info, but nothing,
I think that what I am doing is not much and even so it gives me an error of nonetype or the following..

Unable to convert row 4, column 3 to type class java.lang.Boolean

I'm sorry if it's very basic, but I can't understand what's going on

How do you know it isn't working? What are you doing with the dataset that the above scripts return?

Have you tried assigning the dataset to a table's data property to visually inspect what is being returned?

At this point I'm imagining that the problem is not actually with the dataset but the conversion of the dataset to CSV, since that step is not covered by either of the above examples. If so, what is your current approach? Can you share your conversion script?

Hello

I am looking to print what is in the alarm panel, currently I have the code in a button called "print in console"

My approach is to review what is on the panel, who "acknowledged" the alarm and what note they added to it, that

Okay, so when somebody pushes the button at the bottom of the screen, you want to print any acknowledgement notes and the person who made the notes to the console and specifically from the data that is currently displayed in the above Alarm Status table? Do you need any additional data such as the event ID or Display Path?

Exactly, for now I'm looking to see the acknowledged alarms and the comment given. It would be very helpful to see them in the console, because that way I could pass them to a power table and save the columns that I want.

I also do not understand why the "ack notes" do not appear in the database provided in the gateway

Configuration of GW:
image

Postgres ERD:

alarm_events table:

alarm_event_data:

Many thanks..

This script, when fired from a button's Action Performed event handler, will get the table model from the alarm status table and will subsequently find and print any notes to the console along with the name of the user who made them:

alarmStatusTable = system.gui.getParentWindow(event).getComponentForPath('Root Container.Alarm Status Table') #This will need to be changed if the alarm status table has a custom name or if the table is not directly in the root container
def tableSearch(alarmStatusTable):
	if alarmStatusTable.componentCount > 0:
		for component in alarmStatusTable.getComponents():
			if 'AlarmStatusTable$1' in str(component.__class__):
				return component
			else:
				table = tableSearch(component)
				if table is not None:
					return table
	return None
table = tableSearch(alarmStatusTable)
tableModel = table.getModel()
for row in range(tableModel.rowCount):
	if len(str(tableModel.getValueAt(row, 0))) > 0:
		consoleMessage = 'User: ' + str(tableModel.getValueAt(row, 2)) +'\n'
		consoleMessage += 'Notes: ' + str(tableModel.getValueAt(row, 0)) +'\n'
		print consoleMessage

image

Output:
image

1 Like

this code work fine for me:

Many thanks!

with the purpose of not writing another post.. :bulb:

What is the best way to handle already acknowledged alarms by modifying the previously created ack_note?

I have this sql code that joins the stringvalue of the same alarm ID, but I am not sure how to do the above...

im looking for edit OR ADD ack note and/or responsible that previous created last ack note.
For example:

1.- operator acknowledges high temperature on event_1 alarm and writes "lack of air compression" in the ack note.
Later...
2.- Supervisor reviews event_1 and writes "air compression solved".

could be used comment panel for editing/multiple ack notes on same event_1 ?

You're welcome

That's fine, but as a matter of etiquette, a seperate post is generally preferred because it makes it easier for subsequent users with the same question to find the information they are looking for.

IA has specifically designed the actual acknowledgement notes to be uneditable. Ways of subverting this have been explored in other forum posts, but I would say the best way would be to store supervisor notes separately. Then, create a custom way of accessing or displaying the notes in the gui.