How to avoid write to property looping

I do this bad practice all the time.
Sometimes it behaves, sometimes value is flickering between two values.

I have a:
tag value written to custom property written to numeric field on property change event written back to custom property -> written to tag via biderectional

What is the best strategy? I can't use bidirection as I have a logic when it is allowed to write back to tag.

What two values does it flicker between? A good and bad (value that shouldn't write to the tag) value? Or the 'new' value and the original value?

Are you performing any check on what is writing to the entry field value to prevent backlash from tag value coming in? (Is it from browser or binding)

Typically I have a custom property on the entry field called initial that is bound to the tag or whatever the value source is. I then bind the entry field's value to the initial property.

In the onChange event of the entry field's value property, I check the current value against the value of initial. If they match I leave it be, if they are different and meet whatever requirement I have, I write the value back to the top level/main value source.

1 Like

The new value and original value.

Isn't it Property Change Event only get triggered when previous and current value changed. So it is redundant to change if currentValue != previousValue?

I have a setpoint tag which has value of say 50.
This tag is bound to custom property (bidirection enabled)
This custom property is bound to Numeric Entry Field.
Numeric Entry field has Property Change Script. (if mode==auto, do not proceed, otherwise write currentValue to custom property)

if Numeric is modified to 100.

Value will flicker between 50 and 100. Sometimes flicker symptom happens other times it worked as expected..

I'd like to know if there is universal proper way to write back to the source without looping continuously.. i feel like there is an edge case bug where it loop between the original value and the write value.

...what if at the time (sub second) I made the write (took subsecond), the original value changed hence the looping.

If your change script is properly checking the change source this still shouldn't happen. You should see your new value for a split second then the new value from the machine. It might even be fast enough that you do not see your entered value.

The value of the entry field has 2 things that can change it's value. The user, or its binding, if it has one.

By adding the initial custom property into the binding chain before the entry field's value property, that custom property will always have the new tag value before the entry field's value property will.

If the value of the entry field's value changes, and it matches the value in initial, you can safely assume it came from the binding on initial*. (*If the user enters the exact same value we don't need to write it anyways, so that edge case is safe).

However, if the user enters a value into the field, there will be a difference between the entry field's value property and the value in the initial property.

Thus, checking the current value of the value property's change event against the value of the initial property will tell you exactly if the change came from the tag(binding) or from the user.

Then you only write the value back up to the top level if the change came from the user and is valid based on your other criteria. You can leave the value be or change it back to the same value as initial if it doesn't meet your criteria.

It's pretty much a method to prevent the binding update from triggering your script's write action by being able to consistently determine where the change came from, the user or the tag.

For what it is worth I do this style on all of my entry fields where I cannot indirectly bind the entry field value to the value source, be it tag or dataset for collecting data

In addition to all this you can also look at the value of event.origin, which also provides insight as to what caused the change. You can make sure you only write to the tag if the change origin is Browser.

3 Likes

Another possible way is to move your script into the onActionPerformed event of the entry field, and either check immediately if the value is valid or send the value upwards to a message handler that will decide if the value is valid and write it to the tag or custom property.

I'm not sure if this event catches if the user tabs or clicks away from the entry.

Oh :scream_cat: Wow.. That is reallyyy... cooolll.

I didn't know about the origin.. This certainly breaks the loop..

Another fundamental trick learned.. Thanks! :clap:

	if not self.view.custom.isAuto:
		if origin=="Browser":
			self.view.custom.tagFlowRate = currentValue.value

Also, if this is your only criteria for allowing the user to enter the new setpoint, just disable the entry field (toggle the enabled property) if the associated machine is in auto. Then the user won't be able to enter a value, and you don't have to check machine state before writing the new value. Much simpler.

2 Likes

Yes I am aware of that option and as well as onActionPerformed.

I use property change script a lot.
My guts tell me that this solution does not apply everytime.
If I find similar problem, I will update this thread.

Edit:
I re-read your post, you actually gave me two new options. a way to determine the source important take away

If you ever work in Vision, it doesn't have the event origin information, so checking equality with an "upstream" binding is the canonical way to determine if a new value in an input field is user-supplied versus binding-supplied.

4 Likes

Why not just disable the field if it's not in auto?

Alternatively, make 2 custom properties. 1 unidirectional binding and one bidirectional binding to the same tag. Bind the field value to the unidirectional tag and use a change script on that one to write the bidirectional one.

This is not something for only tag read/write use case. Certainly this is an option.

There are always instance where we want to show status of custom property at the same time being able to write to that property.

if I write to the bidirectional custom property, that data will come back on the unidirectional property, causing change script to trigger..? might as well use one bidirectional property then use "origin" filter.? - hence the looping

Don't do this. The timing will be unpredictable.

Bidirectionally bind a custom property to the source tag. Unidirectionally bind the entry field to the custom property. Only this will ensure the entry field's binding follows the custom prop binding.

Yes, do that :man_facepalming:. No need for a second custom property then.

1 Like

Well if you don't end up writing to the tag the custom property will just get overwritten by the binding the next time it refreshes.

I don't know how I missed this existing, but this is really insightful.
For example, I didn't know that, with a bidirectional binding on a numeric input, there will be 2x value change events fired: one for the Browser event, one for the Binding update. Same for when a script changes the bidi value: it will fire a value change for the Script origin and then one for the Binding origin :dizzy_face: I feel I should have known about this a long time ago.............

Thanks for mentioning it!

1 Like

Nope, only 1 event is fired: in the below case, either Binding or Browser

def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	system.perspective.print("change script executed")
	system.perspective.print("origin: %s" % origin)

image

"bidirectional" is the key part

(I edited it bold and italic for emphasis)