Over the past few days I have gone through a rabbit hole of several forums, reddit posts, and so on trying to figure out what is happening and why. Seems there is no actual answer.
The issue first occurred when I was trying to mess with propertyChange scripting on a root container, which turned into a button, and then turned into a text(all scripting was done on a custom property of boolean and integer, no difference).
I realized through output console and message boxes popping up at me while debugging the code, that EVERY time I would open the screen that had anything with a propertyChange script(filtered down to the custom property using if events.propertyName = 'custom property name'), it turns out that it fires the property change script a couple or few times before working correctly(one trigger action) once in the actual screen the propertyChange is embedded in. No matter what component, screen or type of property, it will always run the script in the following format it seems:
First trigger: when vision window opens up and empties the custom property to "none" verifired by printing to console as well as message boxes to pop up.
Second trigger: when vision window is finished opening, and custom property fills with the value I originally had in it from the binding.
Once in the vision window, the script would only fire once and only once, once I changed the property via a button I had scripted to change the value.
I'm not exactly looking for an exact solution, more of a discussion to how people overcame this(and still using propertyChange scripts) rather than completely changing the method of achieving a same result with said different method.
I had a similar issue years ago and what i remember trying (but don't remember if it was the solution) was disabling cache and saving the window after running it in preview with the designer in a state where i knew that it wouldn't trigger any propertyChange script and always checking oldValue event property. My idea was to save a known "stable" state of the window and with the cache disabled force it to always start on that same state. I can check tomorrow if those measures were what i actually ended up with.
I get where you are coming from, and doing it a different method may or does work, but propertyChange scripts can be such a useful tool that if it functions correctly, can be very powerful and make things less complicated(again, if runs correctly).
I do appreciate the different methods provided that you and the others have suggested, but needless to say:
The issue should not be an issue. It should not fire twice when scripting to a custom property change that is binded to a tag under the component properties, and 3 times when scripting to a root container's custom property change binded to a different tag. It, and emphasis on "shouldn't" run at all unless the property actually DOES change, rather than thinking it changes when the new window instance opens up, along with the root container and components.
Again, I do understand there are different ways to achieve something, but the reality is that there shouldn't be a restriction such as this to resort to those other methods, instead it should just simply work the way it is supposed to.
Due to the script running multiple times based on property changes, one of the times it runs when I open the window instance, it runs because the property was changed from whatever old value in the custom property I had, changed to none when the window instance opened, and counts towards a "change" and then populates with my old values that are SUPPOSED to be in there to begin with.
PropertyChange scripts in Vision run with the Java Swing model, because Vision is Java Swing. Swing delivers propertyChange events when the component says to, whether the new value is actually different or not. That's required by Swing.
Swing also delivers all such events to single handler, synchronously, with the expectation that the receiver will always check the property name. If you are running any code outside of the checks for the required property name, you are screwing up. The code posted in your other thread suggests you or your peers haven't been disciplined about that, and you probably need to audit every single script in your project for that problem.
Ignition (IA) doesn't have the power to dictate how Swing works.
Also, Vision windows' component properties are snapshotted when you save your project in the designer. That snapshot, with very few caveats, establish the initial conditions for the components in your windows. If you have properties changing from that to null and back again, look at your code, not elsewhere.
The code I am using at this point to debug this issue is what's shown in the picture. It is just a simple 3 lines to filter down to a custom property binded to any tag, to have this happen the way it is. I am quite new to ignition so I am learning new things everyday, this however is something that I am continuously trying to debug.
The procedure is:
Close window from designer in preview mode and/or when project is launched.
Reopen that window and without changing the property value
You have a binding on propertyChanger that is delivering a null, then delivering the result. What is the binding? (Show us.)
Lots of bindings types run synchronously, while others run in background and deliver later. If a synchronous binding, like an expression, needs a value from something that has an async binding, they will run twice--the first will get a null. In expression bindings where you expect this, you can use the coalesce() function to temporarily supply as safe substitute for the reference that is still executing.
Keep in mind that tag bindings and query bindings call out to the gateway for their result, and run asynchronously, so all such will have a null for as many milliseconds as that network round trip takes.
The tag binding is that highlighted in the tag browser window. It is on a default tag group, direct, subscribed, 88ms rate.
It only happens when bound to any tag, so yes your comment on the running null until it is synced with the tag binding is true. However, this probably shouldn't be happening in this way of window opening, as you can see, creates lots of issues.
I did try the coalesce() function in an expression on the custom property as an expression with the following:
coalesce({[default]bTesterBoolean}, 0)
This resulted in extra property changes now. See photo below.
Same procedure as before. I have spoken with iA yesterday and sent off my window to the tech, as he could not replicate this on either .38 or .44 version he has, it seems it only happens to my window. Have you tried to see if you could replicate this? It seems that this is only happening to me...and some others.
Yes, sorry. On startup, there's a round trip required to get the latest value, and you will get a null until it arrives. That null is accompanied by a "good uncertain initial value" quality code to suppress overlays, but that cannot suppress errors elsewhere the null might be processed.
The coalesce() is to be used where the another property is bound to the tag bound property.
Okay so here's where were at now:
I have bound propertyChanger2 using the coalesce({Root Container.propertyChanger}, 0), which essentially did work...to an extent. It did set it to zero, but resets it back to true and fires twice regardless whether I put in scripts when activated window, closed etc...
It just seems like it wants to keep running twice.|
So,
The solution that may seem obvious when told, may seem very quick and simple to understand/debug, however after several trials and errors with properties, indexing and property changing boolean tags it is as shown below to prevent the script from firing twice, when undesired(when opening window instance):
For tag purposesIgnition#propertyChange#script#closingremotewindow#8.1.38
propertyChange Script on root container:
if event.propertyName == 'propertyChanger':
#checks to see if event.newValue is Null when opening page
#and then skips over the first property change when Null
if event.newValue is not None:
print(event.oldValue)
print(event.newValue)
#read the index variable
indexNumVar = system.tag.readBlocking(["[Default]tempTags/nindexNum"])[0].value
#Write the index to 1 if its a zero so then later can be a button/pop up
#that will write a 2 to the index number in order to only
#allow next operation to happen once the user presses
#something to store their IP Address
if indexNumVar == 0:
system.tag.writeAsync("[Default]tempTags/nindexNum.value", 1)\
system.gui.messageBox("I have written a 1 to index")
#Button will fire this property change script due to button
#causing custom property changing via button user clicks
if indexNumVar == 2:
#store clients IP address
firstUserIPVar = system.net.getIpAddress()
#read clients IP address that is in the tag that
#was written from a button storing it there
firstUserIP = system.tag.readBlocking(["[Default]tempTags/sfirstUserIP"])[0].value
#checks if IP address matches the one that is stored in value
if firstUserIPVar == firstUserIP:
system.gui.messageBox("I have entered all if statements, IP address matches stored")
system.tag.writeAsync("[Default]tempTags/nindexNum.value", 1)
else:
system.gui.messageBox("IP Does not match and time to kick you")
system.nav.swapTo('Main Windows/MAIN')
openedWindows = system.gui.getOpenedWindowNames()
for Path in openedWindows:
if Path != 'Main Windows/MAIN' and Path != 'ScreenGroup/Footer' and Path != 'ScreenGroup/HEADER':
system.nav.closeWindow(Path)
system.tag.writeAsync("[client]stnBtnSelected.Value", 0)
For BTN to activate this script once in window(mouse released):
indexNumVar = system.tag.readBlocking(["[Default]tempTags/nindexNum"])[0].value
if indexNumVar == 1:
#Store clients Ip Address into currentUserIP
currentUserIP = system.net.getIpAddress()
#Write userIP into firstUserIP Tag
system.tag.writeAsync("[Default]tempTags/sFirstUserIP.value",currentUserIP)
#Index change to allow propertyChange script to go into final if statement
system.tag.writeAsync("[Default]tempTags/nindexNum.value", 2)
system.gui.messageBox("Access Granted")
#trigger the property change either to false or true to get a script trigger
propertyChangerVar = system.tag.readBlocking(["[Default]tempTags/bPropertyChanger"])[0].value
if propertyChangerVar == 0:
system.tag.writeAsync("[Default]tempTags/bPropertyChanger.value", 1)
else:
system.tag.writeAsync("[Default]tempTags/bPropertyChanger.value", 0)