Numeric entry with unit conversion (bidirectional) question

I’m working on numeric entry template that will automatically switch between metric and imperial, but only write imperial values to the PLC. Here are the template’s custom properties and parameters:

the key property here is the “value”. It is an indirect binding to the tag specified in the tagPath parameter. The processType parameter is an enumeration that gets passed into my conversion functions to determine the correct math to use. So for instance, we’ve standardized on using psi for pressure in imperial units and mbar in metric units. So when you pass “pressure” to my function, it multiplies or divides by 68.9476. If you pass “level” it does the math with the 0.3048 factor between feet and meters. Here are the functions for reference:

def convertToMetric(value, processType):
	# Converts an input value to metric units according to the process type.
	# Typically used for reading values FROM the PLC and converting for vizualization
	# Acceptable process types: pressure, flow, level, temp
	if processType == 'pressure':
		newValue = value * 68.9476
	elif processType == 'flow':
		newValue = value * 0.227
	elif processType == 'level':
		newValue = value * 0.3048
	elif processType == 'temp':
		newValue = (value - 32) * 0.55556
	else:
		newValue = 'Invalid process type'
	return newValue

def convertToImperial(value, processType):
	# Converts an input value to imperial units according to the process type.
	# Typically used for converting visualized values before writing TO the PLC.
	# Acceptable process types: pressure, flow, level, temp
	if processType == 'pressure':
		newValue = value / 68.9476
	elif processType == 'flow':
		newValue = value / 0.227
	elif processType == 'level':
		newValue = value / 0.3048
	elif processType == 'temp':
		newValue = (value * 1.8 ) + 32
	else:
		newValue = 'Invalid process type'
	return newValue

What I want is a numeric entry that, in metric mode, displays and accepts input in metric units, but converts those to imperial before writing to the PLC, while keeping the displayed value in metric. So it seems like a binding between my numeric entry’s value property and the custom property is out.

What I’ve done so far is first, create a script on the startup event for my numeric entry field that will read the value of the view.custom.value which is bound to the tag, and then, if necessary, do the conversion and write it to the object’s value.

That should get things initialized at least.

On my numeric entry’s value property, I have a change script attached:

It’s basically doing the other conversion script (to Imperial) if I have metric selected, and if not, just passing the raw value to it.

But, I have an issue.

If the value of the tag changes by any other means, the value in my input box will not change! I need to keep that synchronized, but without using a binding of some kind, the only other method I know is by using property change scripts. But if I put a property change script on view.custom.value as well as the value property on my object that writes to it with ITS property change script, what I’m worried about is the following sequence:

  1. Object’s property change script writes value to view.custom.value
  2. view.custom.value registers the write as a change
  3. view.custom.value executes its change script.
  4. Object’s value property is updated.
  5. Loop to step 1

Would having two property change scripts create a race condition where they are constantly executing because they are constantly recognizing their “writes” as a change even if the value doesn’t actually change? I’m only worried because I’m going to be dealing with floating point numbers and I’m afraid I’ll run into a situation where it detects a changed value because this time the floating point math had a result that was 0.00000000000000000000001 different than the last value.

Am I worried about nothing? Does simply writing to a property automatically trigger any property change scripts attached to it? If the value does actually have to be different to register as changed, does it handle floating point weirdness where you can’t get perfect precision with floating point numbers?

Edit:

I have confirmed there is a limited amount of “bouncing” that happens between the two scripts. It seems like every time I change a value, the scripting is executed 3-5 times:

The other thing I found is that when the tag changes, the conversion math is done both ways, which results in the value property being slightly different than what the user entered. For instance, I entered in 7312.5, but after the dueling scripts, the value in the numeric entry is 7312.500227234. It doesn’t change in the display until I actually click on the numeric entry.

I think what needs to happen is I only need to run my write-back on my view custom property if the source of that change was NOT my other property change script. I’m not sure how to accomplish that yet, though.

OK. I think I figured it out:

First thing I had to do was create a boolean view custom property called “userChange.” I use this as a handshake between numeric entry and the bound “value” property in my view.

In my object value property change script, everything is only if the origin is “Browser.” So the script will only execute if the change comes from someone typing a value in. If the tag value changes by some other means, it will have a different origin, and will not execute the property change script. Finally, I set my handshaking property to True

For the View custom property, It’s a little more complicated. I only want to execute this script if the Binding changes the value. The problem is, if I update it with my property change script, I will actually get TWO executions, one with an origin of Script, and then another with an origin of Binding. So I still have to differentiate if my origin is Binding, because it could just be the binding being updated after I wrote to the tag. That’s where the handshaking property comes in. I wait for my binding update, and when I get it, I check to see if the userChange property is True. If it is, don’t execute the rest of the script, and just set it to off.

I’m counting on getting that Binding update. That’s the only thing I’m not sure about now.