Perspective - Creating dynamic icon based on alarm priority

Hello!

I'm new to Ignition, and I'm working on my first project with it. I'm attempting to use Perspective to design an HMI for a simple project. As part of this HMI, I want each device/sensor on the HMI to display a special indicator in the event of an alarm. I'd like this indicator to dynamically change based on the priority of the associated alarm, as seen in The High Performance HMI Handbook:

I've created and uploaded the desired icons to my gateway, and I can use them in the designer without issue:

image

I have this view which serves as a motor faceplate, with a parameter for motor name. I tried using an expression binding to dynamically change the path of the icon based on the highest priority alarm on the given motor. The following expression does not work:

EDIT
This is the case statement format provided kindly by @ michael.flagler. Unfortunately, it still does not work, and returns a 'Bad_NotFound' error.

case(true,
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",4,4,0,1,0), "alarmIcons/Alarm-Priority-1",
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",3,3,0,1,0), "alarmIcons/Alarm-Priority-2",
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",2,2,0,1,0), "alarmIcons/Alarm-Priority-3",
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",1,1,0,1,0), "alarmIcons/Alarm-Priority-4",
    0)

This expression structure seems to work in other expressions I've tried elsewhere, and I've confirmed that I can dynamically change the icon path with an expression binding through other methods. For example, this expression works properly if I manually assign the parameter.

if({view.params.highestAlarm}=4,'alarmIcons/Alarm-Priority-4','alarmIcons/Alarm-Priority-3')

I'm not quite sure why this isn't working, or if there is a better way to go about achieving the desired functionality. I've considered stacking all the icons in an embedded view and just setting visibility based on alarm priority, but to the best of my knowledge, my issue lies with how I'm trying to get the alarm priorities.

I'd appreciate it if anyone could give me some guidance on how to achieve this.

Thanks for your time!

Please edit your post and add your code using the 'Preformatted text' formating (CTRL+E).
Have you tried instead of writting your tag path like this:

'[default]TestBench_Controller_Tags/Motors/{view.params.motorName}/*'

Writing it like this?

'[default]TestBench_Controller_Tags/Motors/'+ {view.params.motorName} + '/*'
1 Like

You are probably going to need an individual property using indirect binding for each tag, and then use those properties rather than a direct tag read.
Yeah... don't do this. I was mistaken on this.

As @leonardo.godoi mentioned, you won't be able to use the parameter directly inside quotes and it will need to be concatenated with the other strings, also, it will probably be cleaner using a case statement like this:

case(true,
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",4,4,0,1,0), "alarmIcons/Alarm-Priority-1",
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",3,3,0,1,0), "alarmIcons/Alarm-Priority-2",
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",2,2,0,1,0), "alarmIcons/Alarm-Priority-3",
    isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*","*","*",1,1,0,1,0), "alarmIcons/Alarm-Priority-4",
    0)

Thanks for your response. I've edited my post as you described. Unfortunately, changing the string structure like this gives me the same error.

I'm not quite sure what you mean by this. Are you saying that on each tag for which I have an associated alarm, I need to configure a custom property which contains the highest alarm priority on that tag, then bind my expression to that property? I'd appreciate any clarification you can give on how to go about this. Thanks!

I mis-read things. I'll edit my post out.

1 Like

What happens if you just check for a true using isAlarmActiveFiltered? Do you get something back?

Testing this, it seems like the issue lies somewhere with the minimum and maximum priorities in this function call. Right now, I have a priority 1 alarm configured on the desired motor, which should be active. When setting the priority range from 0-4 in this function, the expression evaluates successfully.

if(isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*", "*", "*", 0,4,0,1,0), 'alarmIcons/Alarm-Priority-4', 'alarmIcons/blue-circle')

When I attempt to close the range, for example from 2-4, the expression returns the same 'Bad_NotFound'

if(isAlarmActiveFiltered("[default]Testbench_Controller_Tags/Motors/" + {view.params.motorName} + "/*", "*", "*", 2,4,0,1,0), 'alarmIcons/Alarm-Priority-4', 'alarmIcons/blue-circle')

The expression also throws an error if the configured alarm is shelved. This behavior seems really strange, since it seems that an error is thrown any time there is no active tag that fits the criteria. I would expect it to simply return false, rather than throwing an error like this.

Have I made a configuration error somewhere, or is there more at play here?

I was looking for something similar, in order to implement an alarm indicator/border for my faceplates. Here's what I came up with:

def transform(self, value, quality, timestamp):
	tag = "*" + self.view.params.tagPath + "*"
	tag = tag.replace("[edge]","")
	alarms = system.alarm.queryStatus(source=[tag], state=[2,3])
	for i in alarms:
		priority = str(i["Priority"])
		if priority == "Critical":
			value = 4
			break
		elif priority == "High":
			value = 3
			break
		elif priority == "Medium":
			value = 2
			break
		elif priority == "Low":
			value = 1
			break
return value

I haven't tried to set this up with multiple faceplates on a single view but, I expect performance to take a hit. Although, I did add a 1 second clock to the expression binding for this script.

Does anyone have a take on doing it this way?

I also read that there is a method that would get the priority out of the AlarmEvent object, which would look something like the following but, it wasn't working out for some reason. Not sure what I'm doing wrong, the manual is a bit light on getting properties out of the parent object.

alarms = system.alarm.queryStatus(source=[tag], state=[2,3])
priorities = alarms.getPriority()

What is the update rate of isAlarmActiveFiltered in an expression (Perspective)?
Trying to use this to show alarm borders on devices but getting inconsistent results.

A little bit disappointed in the alarm attributes. It would be really good if tag attributes like "ActiveUnackCount" could be used at a UDT / device level.
Ie sum all the Active Unack for all the tags within that UDT.