Query alarm journal to display alarm event data in a single row

I've been working on a report that can query the alarm journal for a given period of time and return the alarms for a given period of time to show in 1 row the active time, active value, ack user, ack notes, ack time, and clear time. The way I would like the script to work is if there was a clear or ack event that occurred during the given time period I also want to provide the prior information for that alarm (so for example provide the active information if the alarm was cleared during the time period)

I've tried following the below forum posts but still can't seem to get my script to work.

My script currently is as follows. Any suggestions?:

from com.inductiveautomation.ignition.common.alarming.config import CommonAlarmProperties
from com.inductiveautomation.ignition.common.alarming import AlarmState, AlarmStateTransition, PyAlarmEvent
from com.inductiveautomation.ignition.common.alarming.evaluation import EventProperty

# Queries the alarm journal based on the parameters provided
journalResults = system.alarm.queryJournal(includeData=True)

# Iterates through the journal results to determine the eventid, source, tag, label, priority, active, ack, and clear data
data = []
for x in journalResults:
      data.append({
            'source':x.getSource(),
            'priority':x.getPriority().getIntValue(),
            'label':x.getLabel(),
            'activeTime':x.getActiveData(),
            'value':x.get(CommonAlarmProperties.EventValue),
            'ackTime':x.getAckData(),
            'ackUser':x.get(CommonAlarmProperties.AckUserName),
            'ackNotes':x.get(CommonAlarmProperties.AckNotes),
            'clearTime':x.getClearedData()            
      })

What are you trying to do with the data? If this is a data source for a report I think you are just missing return data at the end.

I'm formatting data into a dataset, but didn't post that part because I'm confident in that piece. The reason my script does not work is because depending on the event status that is returned from system.alarm.queryJournal. So for example if I were to change my script to the following each row would print "None"

# Queries the alarm journal based on the parameters provided
journalResults = system.alarm.queryJournal(includeData=True)

# Iterates through the journal results to determine the eventid, source, tag, label, priority, active, ack, and clear data
for x in journalResults:
	if x.getLastEventState().getIntValue() != 0:
		print x.getActiveData()

There does seem to be a flaw in the documentation relating to alarm states.
The documentation clearly depicts the following integer representations of alarm states:

Original picture no longer applicable

...but try the following experiment with a filterAlarm extension function of the alarm status table:

#def filterAlarm(self, alarmEvent):
	state = alarmEvent.getLastEventState().getIntValue()
	if state == 0:
		return True
	else:
		return False

If the state number is incremented by one, the table result will follow this pattern:

#0 = Active Unacked
#1 = Cleared Unacked and Cleared Acked
#2 = Active Acked
#3 = None

In another experiment, I can place a power table underneath an alarm status table, and populate the alarm status table with the following script:

dataset = system.alarm.queryStatus().getDataset()
event.source.parent.getComponent('Power Table').data = dataset

The result seems to indicate the following which is also contradictory to the documentation:

#0 = Active Unacked
#1 = Cleared Acked
#2 = Active Acked
#3 = Active Acked

Edit: The documentation has been corrected.

getLastEventState returns an AlarmStateTransition, where getIntValue() is just an alias for Enum.ordinal(), so:

String Int
Ack 0
Active 1
Clear 2
Disabled 3
Enabled 4
Finished 5

Whereas the state field in the state model is an AlarmState, with hardcoded integer values:

String Int
ClearUnacked 0
ClearAcked 1
ActiveUnacked 2
ActiveAcked 3
Enabled 4
Disabled 5

Those values are also those used to pass in to the query functions, and they haven't changed in over a decade, so I'm not sure where the ones in the manual came from. I'll contact the documentation team.

1 Like

Thanks. Earlier, I sent them an email with a link to this post.

1 Like

So @PGriffith would you recommend I use AlarmStateTransition or AlarmState for obtaining the alarm event information I want?

Based on your post it looks like I should be able to get the information for the sourceEvent. However, when I've tried it on my end I get errors.

Each alarm event from the journal is independent. You will have to manually join them by the event ID.
Once you do, for a particular alarm event, you can get at whatever associated data it has. You will want to retrieve based on the getLastEventState (note the documentation on AlarmEvent, below):

You could "automatically" retrieve the correct EventData with dynamic dispatch, something like this:

from com.inductiveautomation.ignition.common.alarming import AlarmStateTransition

dataGetters = {
	AlarmStateTransition.Ack: lambda e: e.ackData,
	AlarmStateTransition.Active: lambda e: e.activeData,
	AlarmStateTransition.Clear: lambda e: e.clearedData,
}

stateData = dataGetters[event.lastEventState]()

I tried to do the following but got an error that says lambda() takes exactly 1 argument (0 given)

This is what I did;

from com.inductiveautomation.ignition.common.alarming import AlarmStateTransition

# Queries the alarm journal based on the parameters provided
journalResults = system.alarm.queryJournal(includeData=True)

# Iterates through the journal results to determine the eventid, source, tag, label, priority, active, ack, and clear data
for x in journalResults:
	dataGetters = {
		AlarmStateTransition.Ack: lambda e: e.ackData,
		AlarmStateTransition.Active: lambda e: e.activeData,
		AlarmStateTransition.Clear: lambda e: e.clearedData,
	}

	stateData = dataGetters[x.lastEventState]()

I'm not sure if this will get what I want. I want to iterate through the results of system.alarm.queryJournal and if the AlarmStateTransition is not Active then I'd like to query the active time. I know I can make a SQL query in the DB to get that information, just it slows down the script to minutes. Any other ideas?

Oh, right, the last line needs to be:
stateData = dataGetters[x.lastEventState](x)

That's, fundamentally, just not how the alarm journal works. The information that is logged at a particular state transition is only the event that pertains to that transition. So when an alarm goes active, it logs why it went active... but when it clears, it doesn't "know" why it was active (from the journal's perspective). So you have to, there is no way around this, group the individual events you get back from the journal back together by event ID if you want to get information about the active state from the clear, for instance. If the active event doesn't fall within the time slice you're querying for, you might not get it in the results, as well.

At least, such is my recollection of how the journal works. I could be wrong.