I'm trying to dynamically populate a settings template from a JSON config and allow users to update panel settings:
Read from a JSON config file that contains a list of panel settings
Populate a flex repeater with label and current tag value (fields read from config)
Users can update the row's value, which just writes to a custom property on the row
When row edits are made and Save Changes is hit, I want to do the following in an onActionPerformed event:
write the edited value to a intermediate tag
write to a settings bit that operates as a one shot
(PLC receives bit, writes the intermediate tags to panel settings tags, resets bit to 0)
read the updated panel settings
write the updated values back to the rows' custom props
From what I've read, system.tag.writeBlocking() and time.sleep() are no nos in onClick events. However, I'm not familiar enough with the different approaches to implement one shot behavior via Python scripting.
I've implemented this as a single script - doesn't work. I've tried it as a staged script, where onActionPerformed() first triggers Script 1: reads edited values (system.tag.readBlocking()), writes to intermediate tags (system.tag.writeBlocking()), writes to the send settings bit (another system.tag.writeBlocking()). Then I have Script 2: reads from the live panel tags (system.tag.readBlocking()), writes the values back to the custom props that the rows read from. However, this doesn't work because I need a delay for the PLC to finish updating the panel. So it just writes back the initial values to the custom props that display to the user.
Any reason why you can't indirectly bind these tags to their associated view in the repeater? And then when reading from the config just overwrite the associated value in the entry field? Then when the PLC updates values, the embedded views will automatically update their displayed values.
If you are dead set on your current method, you need to split your logic. Ignition is an event based system, make your logic event based. Your save button should only write the values to the intermediate tags and then trigger the bit for the PLC.
Then have your PLC increment an integer when it's done with it's process. Have a Gateway Tag Change event that monitors that integer and on change, broadcasts a message to all open sessions of that specific project to read the latest data from that specific device. Make use of the message payload to pass information that can be used to filter down which sessions should care about the message.
Have a perspective Message Session Event that re-broadcasts the message within the session with system.perspective.sendMessage, and have a message handler on the page with the repeater that reads all the tags for the specified device and pushes their values into the repeater.
You could make it simpler by indirectly binding the integer to a custom property at the view level and having an 'onChange' event script do the value fetching once the value changes. You would need to do the work to be able to indirectly bind to the correct tag for the machine you are editing.
From the manual regarding actions:
Actions are not executed synchronously: sequential actions do not wait for any prior Actions to finish executing before running.
Simple answer to "why" I'm doing it the way I'm doing it: we have dozens of asset classes (think, multiple UDT types performing completely different functions...e.g. panels within a solids control or water management plant with different control planes, settings, and sensors feeding VFDs). So I basically want to multiplex settings pages based on UDT type: e.g. VFD control method, sensor configuration, custom alarm settings. Every settings page uses a single template that populates based on tag name and type for simple configuration. Right now we have hundreds of views that are bespoke per UDT "class", which makes new UDT creation hard, version control hard (if not non-existent), and efficient RAM management a pipedream. So, I made it all JSON, and dynamically loaded as an operator is navigating through their system.
To walk through the experience at a high level: a user selects their panel and the settings pages are loaded on demand as they click through a horizontal menu. In the backend, a Perspective session now loads 1 MB of JSON instead of 100 MB+ of views for UDT classes they don't even need to operate. But now the question: how do you bind a row element in a view from a Python onStartup script that reads from a JSON config file? Well, unfortunately you can't with Ignition, or else I'd have done that since it solves all my problems.
Not only that, there's not a singular tag that I'm looking to change for a Gateway scoped tag change event. It's about 20-30 tags per UDT type that are one shots to send the PLC settings updates. I could do a write to a custom prop on PLC write, then have the callback update the custom prop when done (via message handler), and tag change script on that. The tags and their behavior are out of my control and I'm limited by that with my design. Maybe one day I'll be unleashed on our PLCs, but that is not today my friend.
Independent of custom prop change, I could also writeAsync with a callback in Script 1, then do a tag read/property write directly. I would like to understand how those messages are scoped, I want a message handler that the callback thread sends directly to the Perspective session, directly to that view, not have it broadcasted globally if I can avoid it. We have multiple operators at multiple plants with functionally distinct equipment.
We are not using a tag group here. I think what my problem boils down to is:
I send PLC a 1 to TagA
PLC writes intermediate tags to PLC settings tags
PLC writes a 0 to TagA
When I implement the callback solution, writeAsync calls back after writing the 1. I would like to know when it's been set back to 0 without an explicit binding (because I can't create bindings from event scripts). Not sure how to program in that delay.