Imitating a mutex in the SFC module

I have a project with several SFCs that are set to RunAlways. Each SFC has a corresponding tag to control its state, which can be “on” or “off”. Each SFC waits in a starting step until its state is set to “on”, then does some work, and finally sets its state back to “off” and returns to the start to wait for another “on” command.

My issue is that some processes cannot run simultaneously. So, if SFC 1 and SFC 2 cannot run simultaneously and SFC 1 is already running when SFC 2 is set to “on”, SFC 2 should wait until SFC 1 turns back off before it starts to do anything to the system. And vice versa.

I can have a transition near the beginning of each SFC that waits until the other is off before starting to do anything. However, there’s a race condition if SFC 1 and SFC 2 are both turned on simultaneously. Then each SFC will see that the other SFC is “on” and will wait, so the two SFC’s will wait forever and will never run. I know this is very improbable, but I’d much rather it be impossible than improbable. I’ve thought about other configurations of the SFCs and the state variable, but all seem to boil down to the need to check a value and then modify a value atomically, without letting another SFC access those values at the same time. A mutex seems like the standard way to do this. Does Ignition have a similar capability? Or is there another way that I should go about this?

Thanks,
Jack

Hi Jack,

I would make one more chart to act as a queue runner.

Here is my test setup:
[attachment=2]2.PNG[/attachment][attachment=1]1.png[/attachment]

Each Action has this sort of script:

[code]def onStart(chart, step):
“”"
This will run when the step starts, before any
other action.

Arguments:
	chart: A reference to the chart's scope.
	step: A reference to this step's scope.
"""
queue = system.tag.read("[default]SFC Control/SFC1/Queue").value
if queue == 1:
	system.tag.write("[default]SFC Control/SFC1/Run",1)
	system.tag.write("[default]SFC Control/SFC1/Queue",0)</start-script>
[/code]

Each transition has this sort of expression:

{[default]SFC Control/SFC1/Run}=0

How it works: Check queue status to see if it’s ready. If so, set the run status and reset the queue status. The transition waits for the run status to be off before continuing. Repeat the operation for the next one.

Wash, Rinse, Repeat. :slight_smile:

Also, here’s a copy of this test SFC to give you a closer look.

It could be more fleshed out, but for a proof of concept, it’s okay. :wink: