Trying to change the fill color of a shape based on a set of alarm conditions

Hi. I've made an icon with a set of parameters that will be embedded into a main window several times. The background of the icon is just a solid filled in circle. I'm trying to change the color of the icon based on the state of alarms for that tag.

I've tried to use a property transformation to change the fill color based on whether there are alarms active and if they've been acknowledged. I can't figure out why my I keep getting a configuration error though. Any help would be appreciated!

Here are the expressions I'm using to try to change the color.

I haven't tested this, but from what I can see, you're creating a recursive binding, which is probably your issue.

You've created a binding on the property props.style.fill that references in its property binding the same property !

This creates a loop and will probably not work. Consider changing your property binding to something else.

Personally, I'd create an expression binding with the expressions you've input in the "Map transform" on your screen, and output a different value or string depending on whether or not your conditions are met using the if() expression function.

1 Like

Hi, thanks for answering!

Do you mean something like this? I created a new value in the style property named "state". I am trying to configure the expression binding on "state" to write to the value as 1, 2 or 3.

Then tried binding the color based on the value of state but am still having issues.

I want to disclose that this is my first time designing something in Ignition so apologies if my questions or mistakes are rather rudimentary.

Hmm no. I'll give you a couple of pointers, but have you watched the videos in Inductive University to learn about Ignition ? This is a great resource to get started.

Ignition uses two main languages, the python language (this is really Jython but let's not get you lost into details) and the expression language created specifically for ignition.

When using an expression, the functions are different from Python, you can see the syntax of each function using the sigma symbol on the right.
The syntax of the if() function for the expression language is the following :
if(CONDITION,RESULT IF CONDITION IS TRUE, RESULT IF CONDITION IS FALSE).

If you need multiple conditions, then use another if() if the "RESULT IF CONDITION IS FALSE".

In the expression language, "and" is replaced by "&&".

I might be wrong, but you can't use wildcards the way you're using them. [RTP_Particle_Counter]*/... wont work. If you need a summary of a lot of tag paths, you'll have to do it differently. Have a look at system.tag.query for example or use @pturmel 's module using his tags function.

You've created a property "state" in the props.style of your component. Consider creating a custom property instead, as state is not an appropriate property to specify in style. Whenever you need something non-standard, use customs.
It's also better to always declare your custom at root-level or view-level to get rid of the nuisance of dealing with relative or absolute component paths.

Last one, when using a transform on a property, you don't have to assign the return value to the property again, this is automatic.
For example, if you create an expression binding on the custom.state property of your component, and use the following expression : if({[provider]path/to/tag}, 1, 0) then your state custom property will be equal to 1 if the tag is equal to True or 1, else it will be 0. You don't have to specify in the expression this.custom.state = 1, this is implicit whenever you create a binding on a property. (this is just an example, the if() example here makes no sense as you could just create a tag binding to get the value but just wanted to get my point accross).

Please have a look at Inductive University first. I've given you some pointers but I believe the current knowledge you have will result in development that will have to be refactored later on.

Welcome to the Ignition world, this is a great world, but it requires a bit of knowledge beforehand :slight_smile:

2 Likes

This is a great detailed reply, thank you for your time writing it out! I'll utilize this and revisit Inductive University, hopefully I can figure it out and won't have to come back to this thread.

No worries, also have a look at the documentation, it is very well detailed and contains numerous examples.

You added your state property in a place I wouldn't recommend.

Look for the "CUSTOM" grouping. Add a property there called "State". Give it some value. You always want to add your custom properties in this section so you don't cause schema errors.

Now you can use a map transform to change the color.

I'm not sure why your "material/lens" looks different from mine but here's a screenshot of the map transform binding not throwing an error.

Another thing worth noting. It is good to drive bindings on complex objects such as SVGs from custom properties on the objects because the objects will collapse in the designer every time the binding processes. Imagine a scenario where a blinking expression tag is driving a binding deep inside an embedded SVG. There are better ways to blink a color (CSS) but I have had to support systems that were done this way and I couldn't drill down enough to see what they were bound to before the whole tree collapsed again. It was difficult to troubleshoot.

For my personal learning, why not just use an expression binding on the fill property with the nested IF statements where the "true" returns are the color code?

For Example: IF(CONDITION1, "#D5D5D5", IF(CONDITION2,"#C51B86", "#FFFFFF"))

Is there a flaw in handling it this way?

Nope... but if it was me I'd use the binEnum function instead: binEnum | Ignition User Manual

Much easier to read, and then you can use that to feed a map transform.

2 Likes

Here is a different path I've tried to take to achieve my goal.
I created two custom parameters, AlarmPath and State. AlarmPath is binded to a root parameter that pulls the tag's name.

I am trying to use my State parameter to drive the fill transformations. Here's the code I am trying to achieve in the State parameter.

if(
tag("[RTP_Particle_Counter]" + {this.custom.AlarmPath} + "/Ch2_0_5um/Alarms.ActiveUnackCount") > 0,
{this.custom.State} = 1,
	if( tag("[RTP_Particle_Counter]" + {view.params.particleUDT.TagName} + "/Ch2_0_5um/Alarms.ActiveAckCount") > 0,
	{this.custom.State} = 2,
		if( tag("[RTP_Particle_Counter]" + {view.params.particleUDT.TagName} + "/Ch2_0_5um/Alarms.ClearUnackCount") > 0,
		{this.custom.State} = 3,
		{this.custom.State} = 0
)	
)
)

It's throwing me this error.

Error_ExpressionEval("If function got a null first argument")

What am I missing here? Here are screenshots of my custom value setups.



  1. That's not how expressions work. Expressions only operate via the return value, and should never have any 'side effects' (and can only have side effects if you use runScript). To wit:
  2. = is strictly for comparison, you cannot use it to assign values.
  3. Don't use the tag() function in any UI context; it's always better for maintainability and performance to use additional custom properties with indirect tag bindings.

You should extract your three indirect tag references as new custom properties.
Then add a binding on this.custom.State that uses a single expression to return the 0/1/2/3 value, as appropriate. binEnum is probably your best choice for this if your different inputs are all booleans.

The specific reason this is happening is because the tag() function doesn't immediately do a direct blocking read. So as soon as the expression evaluates once, the tag function returns a null value with an indeterminate quality code and attempts to start reading the tag based on whatever string was supplied. Only after that tag value has been delivered will the tag function return a non-null value.

?

When did this change? Historically, it stalled for a full second before delivering a null.

Ah, looks like you're (mostly) right. There are scenarios where it operates in a purely subscribed way, but it looks like that only applies to executions inside the alarming system. In most scenarios it looks like it'll wait up to 30 seconds before returning.

expressions.functions.TagFunction has good logging, at least.

Is this scope-dependent, perhaps? When I was working out the tags() function for the Integration Toolkit, I poked around a bit and only recall seeing a 1000ms stall in some snippets.

In my experience, tag() is pure hell for Perspective view startup performance.

I don't recall what the stall may be for curly-brace expression references for direct tags. (I avoid those, too.)