Access alarms associated data dynamically in pipeline script

Hello folks,

I’m trying to access alarm events associated data without knowing what it is. In a notification pipeline script block.

A bit like I would do

for k, v in alarm.items()

with a dictionnary.

Is there any way to figure out what data is present on an alarm ?

PyAlarmEvent implements a bunch of interfaces, including Iterable<PropertyValue>, so even something as simple as:

for pv in alarm:
    print pv.property, pv.value

Should work. No automatic unpacking, though, unfortunately.

I though I tried that…
I’ll try again, thanks Paul

Yeah so here's the issue: In the script block of the notification pipeline, it's not a PyAlarmEvent but this pretty thing: com.inductiveautomation.ignition.alarming.pipelines.blocks.ScriptableBlock$ScriptableBlockPyAlarmEvent

So i get this error when trying to iterate through it:

com.inductiveautomation.ignition.common.script.JythonExecException: TypeError: 'com.inductiveautomation.ignition.alarming.pipelines.blocks.ScriptableBlock$ScriptableBlockPyAlarmEvent' object is not iterable

1 Like

Sure… but:
image

Any of these should do the trick?

for prop in alarm.properties:
    print prop.name, prop.type, prop.defaultValue
    print alarm.get(prop)

for prop in alarm.sourceEvent().properties:
    print prop.name, prop.type, prop.defaultValue
    print alarm.get(prop)

for pv in alarm.sourceEvent().values:
    prop = pv.prop
    print prop.name, prop.type, prop.defaultValue
    print pv.value

First one resulted in an error: NoneType is not iterable
Second and third ones give me some results, but not the associated data. I’ll investigate those tomorrow, see if I can get something out of them

Ohhh, my fault.

I think you need to step from the alarm event into the active/clear/ack data:
https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.16/com/inductiveautomation/ignition/common/alarming/AlarmEvent.html#getActiveData()

Which will itself be another property set that (again, theoretically) should be directly iterable.

I’ll try this tomorrow if I can find some time between changing diapers (I’m on 4months old poop factory duty).
I do have another question though: How can I properly test pipeline scripts ? Is there some way for me to mock up a pipeline alarm event in the script console to inspect the object and play around with it ?
I did try to import a few things in the script console, but everything failed. Couldn’t figure out the right things I’d need.
I’ve been doing it directly in the pipeline, logging things and sending email notifications to output data, but that’s far from ideal…

You can send basic test events through the system via the web interface:
https://docs.inductiveautomation.com/display/DOC81/Alarm+Notification#AlarmNotification-NotificationTesting

I have a set of boolean tags with alarms configured on them, it’s much easier to just click the tags to send an alarm through the pipeline.
But it doesn’t help in inspecting the event object itself, I’d rather be able to instantiate a dummy event in the console and mess with it there.

With sufficient effort, you could generate an event on the gateway the same way the testing page does, but you won’t be able to do it from the designer - you won’t have the right classes, so you’d at best be system.util.sendMessage-ing to the gateway to describe the event.

Eh, sendMessageing things to a page that would display the info actually sounds like an improvement… Would be more readable than the logs.
Is this a terrible idea ?

You’re in unsupported territory as far as creating and firing the events goes. I’d have to look at how the test page does it to know how to properly do that.

So, here’s the simplest I could do…

context = [
	(str(p.property.name).lstrip("context_"), p.value)
	for p in event.sourceEvent().activeData if str(p.property.name).startswith('context')
]

(I prefixed them with ‘context_’ myself so I could differentiate them, but I guess I could use the type of the property instead)
I kinda feel like there should be an easier way to get unknown associated data from an event in a pipeline script :X

This works (see below), at least in Ignition v8.1.18. Posting here for the benefit of others.

def handleAlarm(event):
# Uncomment next two lines for debug output (if needed), prints event properties to wrapper.log, 
	for d in dir(event): print d
	for p in event.getProperties():	print"'event['{}']: {} {}".format(p, event[p], type(event[p]))

I found there are several properties not listed in available documentation.
Specifically projectName and pipelineName were useful to me.
Here is some sample output from the second line, modified to remove identifying details.

event['branchDepth']		<type 'int'>
event['notes']				<type 'unicode'>
event['itemPath']			<type 'unicode'>
event['activeDuration']		<type 'unicode'>
event['timeOnDelaySeconds']	<type 'float'>
event['shelvingAllowed']	<type 'bool'>
event['isActive']			<type 'bool'>
event['eventTime']			<type 'java.util.Date'>
event['source']				<type 'com.inductiveautomation.ignition.common.QualifiedPath'>
event['ackUserName']		<type 'unicode'>
event['enabled']			<type 'bool'>
event['isAcked']: 			<type 'instancemethod'>
event['mode']				<type 'com.inductiveautomation.ignition.common.alarming.config.AlarmMode'>
event['ackTime']			<type 'NoneType'>
event['ackUser']			<type 'NoneType'>
event['clearTime']			<type 'java.util.Date'>
event['deadband']			<type 'float'>
event['timeOffDelaySeconds']	<type 'float'>
event['state']				<type 'com.inductiveautomation.ignition.common.alarming.AlarmState'>
event['pipelineVersion']	<type 'long'>
event['displayPath']		<type 'com.inductiveautomation.ignition.common.StringPath'>
event['displayPathOrSource']	<type 'unicode'>
event['pipelineTransitionCount']	<type 'int'>
event['pipelineName']		<type 'unicode'>
event['projectName']		<type 'unicode'>
event['fullItemPath']			<type 'unicode'>
event['itemName']		<type 'unicode'>
event['label']	<type 'unicode'>
event['priority']	<type 'com.inductiveautomation.ignition.common.alarming.AlarmPriority'>
event['isJournaled']	<type 'bool'>
event['ackNotesReqd']	<type 'bool'>
event['timestampSource']	<type 'com.inductiveautomation.ignition.common.sqltags.model.types.TimestampSource'>
event['ackMode']	<type 'com.inductiveautomation.ignition.common.sqltags.model.types.AlertAckMode'>
event['eventValue']	<type 'bool'>
event['isShelved']	<bound method com.inductiveautomation.ignition.alarming.pipelines.blocks.ScriptableBlock$ScriptableBlockPyAlarmEvent.isShelved of Event[name=Alarm, source=prov:default:/tag:_Test-Tyler/TestBool:/alm:Alarm, priority=Low, desc=null]> <type 'instancemethod'>
event['ackDuration']:	<type 'unicode'>
event['activeTime']:	<type 'java.util.Date'>
event['isClear']	<type 'bool'>
event['eventState']	<type 'com.inductiveautomation.ignition.common.alarming.AlarmStateTransition'>
event['name']: Alarm <type 'unicode'>
event['deadbandMode']	<type 'com.inductiveautomation.ignition.common.sqltags.model.types.DeadbandMode'>
1 Like

I don't think getProperties gets you associated data.
I found those in event.sourceEvent().activeData

1 Like

I (Ignition 8.1.42 within Alarm Pipeline script) was able to retrieve associated data via event.sourceEvent().getActiveData(). and also event.getActiveData().

.. and then accessing specifics of each PropertyValue using:

myLogger = system.util.getLogger("Test_Pipeline")
activeData = event.getActiveData()
for prop in activeData:
	myLogger.info("key={}, value={}".format(prop.getProperty().getName(), prop.getValue()))

Here is my latest attempt to identify and access alarm associated data from an event in an alarm notification pipeline script, without prior knowledge what alarm associated data might exist.

The basic approach here is to create a set from the properties found in in event.getActiveData().getProperties() and then remove any matching properties that exist in the built-in CommonAlarmProperties and AlarmModeProperties. In theory that leaves only the alarm associated data.

def handleAlarm(event):
	from com.inductiveautomation.ignition.common.alarming.config import CommonAlarmProperties, AlarmModeProperties
	associatedDataProps = list(set(event.getActiveData().getProperties()).difference(AlarmModeProperties.values()).difference(CommonAlarmProperties.values()))

	for prop in associatedDataProps:
		print prop, event.get(prop)

Seems to work. Has anyone discovered better solutions?