Test Results, Asynchronous Options

I’ve got a project that gets test results for an operation. These tests happen across multiple different databases that all have different formats. Because of this, my solution was to build out UDTs for each test that runs its own query to its specific database and collects the results in a standardized manner (Name, product tested, date, and overall pass). This way I don’t have to worry about each query running synchronously, and each can complete in its own time.

My main issue is with the gathering of the data. Right now, I’ve got a script (Gateway Tag Change or Message, I’ve tried both) that runs whenever any of the individual queries completes that checks the status of all the other queries. It checks all of the “test query” UDTs in a given directory and returns a unit result, and valid bit to the PLC if all the results are in. This kind of screams “race condition waiting to happen”, but I’ve been thinking on it for a handful of months on and off and I’m not getting anywhere for a system that essentially only needs to drop a new query in and the system handles it for any number of tests (theoretically, given resources).

Is there some relatively simple structure I’m just missing, or is it just not a great situation to handle asynchronously with Ignition?

I had a similar thing where I never knew which was going to be the last, but I needed to log the last event. Same problem - that makes a nasty race condition.

I came up with a solution - there may be better ones out there..
I created a Boolean array tag. Each event writes a True to it's designated spot in the array (you can write directly to the spot - you don't have to read the tag in and update the array).

Then on the array tag, I have a OnChange script that triggers, and when it's value turned True (all spots are now True), it fires the logging, and then resets all the spots to False.

^^ that's all per my memory - I did it at my last job, so more than a year ago.. I may have missed something.

2 Likes

Seems reasonable. I might change like so - a dataset with one column is the string name of the process, and the second column is the completion timestamp, or None if it was reset. If you care about keeping track of when things finished. Can also add columns if you care about getting a result object back from each process.

I have found often booleans can be replaced like so - False=None, True = a time stamp, if you care about the extra data points. Otherwise bool is fine, and a bool array is fine.

A dataset tag you would have to read/update/write, so you'd be in the same race condition.

1 Like

Ah. I tried boolean arrays earlier on in the dev process, but I didn’t realize that the structure was safe as long as you were accessing individual bits separately, so I had abandoned the idea

As far as I can tell it's safe. I tested the crap out of it pounding multiple bits with a system.tag.writeBlocking() with a list of the bits and it always told me what the last one in my list was. Maybe there is still some corner case where it fails, but I couldn't find it.

I actually had gotten somewhat fancy with it, since I was using it on a number of lines that had varying numbers of devices, and those devices could get added/removed by the line supervisor. So I actually maintained 2 arrays that were equal size, but bigger than they would ever need. I had the system that added/removed devices log/logout the device into/from it's spot in the "I am here" array. And then I had my "I am done" that they logged their done status into the same spot. Then another tag was just doing a comparison to see if all the spots that were True in the "I am here" array were True in the "I am done" array.

Fair I missed that bit in the post. Though im not too familiar with writing to bit arrays, do you have to write to the full array in systme.tag.writeBlocking? If so you are facing the same issue. Might require a separate bit per process and just a timer script to read them all to see when all true.

No, you can write to a specific bit.

1 Like

Each UDT knows when its indivually done itself? I personally probably do

# assuming isDone
PROCESSES_TO_CHECK = [‘udt1/isDone’,’udt2/isDone’, ]#etc

# on a gw timer script
def checkIfAllDone():
    results = [qV.value for qV system.tag.readBlocking(PROCESSES_TO_CHECK)]
    # all isDone booleans are True
    if all(results):
        # now you can reset/clear

My personal preference but the other method works too.

The problem there is that each script would have to change when the tests involved change. Variable tests for each line running, variable number of tests with possibility to add more / take away any in the future. This is similar to the process I had, except I browsed the tags beforehand to know what all I was reading (All in a message script)

Now I’m trying to figure if I can add some checks that will allow the array to expand / contract before processing the results, though I could just do the HERE and READY array idea.

What do you mean each script? I would only have this one gateway script that checks each UDT and determines from there. Vantage point of the gateway should be able to see everything you need.

If you mean PROCESSES_TO_CHECK changes, as long as you know how it is calculated ie which UDT’s are doing tests, you can still do that programatically and then follow the same logic on a single gw timer script.

By each script, I meant from line to line implementations of these stations. Yes, I could browse, as I had set up previously for PROCESS_TO_CHECK, but I want to get away from messaging, as I think there might be race conditions, and I don't want any timers on the gateway if I can avoid it because, like gateway tag change scripts, I’d have to make a new script for each line if we ever add any processes. I think the bits are the way to go, as long as writing to a bit in a bit array doesn’t read-change-write in the backend like some PLCs do (did?)

This will give you a race condition. I've tried it. If 2 UDTs finish at the same time, they'll both think they are the last one, and you'll end up getting 2 passes of whatever code they do upon being the last.

The key is to have 1 and only 1 thing ever check to see if all the things are done.

What do you mean 2 passes? My code would only be run once in 1 gateway timer script. I would only execute once when all tags are true. At which point presumably things would be reset after whatever is needed to be done.

Maybe I am missing something about this post I don’t understand but doing a reading of tags that would end be like [False, False, True, False] to something like [True, False, False, True] to [True, True, True, True] -Now do something and reset. If you’re worried about the gw timer running twice on all trues and it running again before the first process finished introduce a lock.

If you’re worried about order of completion then the tag indicating isDone on each individual UDT should be a timestamp or none and there could do with it whatever you want based on completion time.

Didn't catch that you were using a GW timer script. Thought you had each UDT running that.

But anyways, I hate GW timer scripts and GT Tag Change scripts for dynamic stuff. Pain to remember to change them when you add/remove something.
Also, if you want it to respond quickly, you're running a lot of tag reads for something that may actually change infrequently. I prefer event-driven approaches.

1 Like

Oh no, one gw script. I don’t like having tag change events on specific tags as they can be missed and tend to avoid them for other reasons explained on the forum.

You can set up gateway tag changes but then you’d have to more annoying to add each tag to the gateway tag change script to monitor. With a scirpt you can make the list programatically, and if you ave a specific UDT type, you can do a recursive search once for them at gw startup and make the list dynamically which woudl update each project save.

Fair enough on running more than required though. That is the trade off of a timer vs event but tag reads from the gateway scope should be very fast.

Pssst! Check out the Integration Toolkit's Bulk Script tag action...

1 Like

Very cool. So every UDT isDone tag value would have this attached, the gw script would accept a list of all these isDone values executed every 50 ms and it’d be trivial from there basically. Every new UDT instance is now fed into the function by design.