Triggering on SFC Stop

Is there a best practices way to trigger on the end of a one-shot SFC? I can think of a few hacky ways to do it, like putting a memory tag with handmade status signals and using an async script call to monitor that tag and take action at various states, but I would prefer to use something built-in if it exists!

Isn't that what an onStop event script is for?
https://docs.inductiveautomation.com/display/DOC81/Chart+Properties#ChartProperties-chart_event_scripts

It would work for many things, but I think not for the case I am trying to solve.

I want to run an otherwise normally used SFC under a specific condition so that I can monitor the system response after the completion of the SFC. So what I have done is written a script triggered by a button that will run the SFC, wait until the SFC terminates (I currently do this by cross checking the SFC chart_id to see when it disappears -- this has a lag in it that is likely tied to the fleetingness of completed SFC charts in the monitoring widget), start a timer, wait for the timer to complete, and then run a query to calculate the results of the test (it is a vacuum stability test).

I had to make the workaround to use a cross-check against the running SFCs, but I don't love this solution due to that odd delay while the SFC is complete but still hanging around in the active list. I was hoping there was a way to catch/pull triggers from within a script instead of pushing a script from inside the SFC onStop trigger.

With some doing, you could obtain an instance of the ChartManagerService from the GatewayContext, and add a ChartObserver?
https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.24/com/inductiveautomation/sfc/api/ChartManagerService.html#addChartObserver(com.inductiveautomation.sfc.ChartObserver)

https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.24/com/inductiveautomation/ignition/gateway/model/GatewayContext.html#getModuleServicesManager()

https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.24/com/inductiveautomation/ignition/gateway/model/ModuleManager.html#resolveClass(java.lang.String)

The ChartObserver will get lifecycle events for every chart as it transitions:
https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.24/com/inductiveautomation/sfc/ChartObserver.html

1 Like

That does seem like I could get in there to manipulate the charts at script level -- it is more detail and effort than I can afford for this particular task but I will keep this in my back pocket for if it comes down to it, thank you.

I hope that sometime down the line that system.sfc has some methods for doing this sort of thing a bit more natively. I was a bit surprised to find that SFC's are not obviously accessible in object form inside of scripts, rather functionally through system.sfc.startChart. I would love to see something that looks like

def my_cancel_callback():
    raise Exception("Someone cancelled my chart!")
def switch_toggled(state):
    print(state)

chart = system.sfc.getChart("my_project", "toggle_switch") #project, chart_name; just like startChart
chart.set_params({"tag_path": "[default]coil0", "target_state": False})
chart.onCancel(my_cancel_callback)
chart.onStop(switch_toggled, [False], {}) # this could also take *args, **kwargs implicitly
chart.start()

Currently, you can get the params through as part of the arguments for system.sfc.startChart, but I guess you can't easily register callbacks against the triggers from outside of the SFC designer itself, which is a bummer!

Attaching jython callbacks to platform java objects is a great way to leak memory, destabilize, and then crash your gateway. What you want looks nice, but is fraught with pitfalls.

Ah, bummer. I'm used to python, but C python where you can definitely do this. I don't fully understand the differences between standard python and jython, not to mention the differences between 2.7 and 3.8+ (I haven't used 2.7 since grad school).

I suppose the functionality could also be added in functional form, it just seems like since the SFCs are very object-like they should be interactive in scripts like other objects.

It is not that it is jython, but that jython code in the Ignition gateway can be replaced by scripting restarts when projects are edited or when modules are added/removed. A jython callback attached to a java object will hold a reference to that jython code through such a restart, leaking the entire jython interpreter. When called, the callback will run in that discarded environment, including any old code you might have replaced. If the callback is a one-time thing, soon to be called and then released, it is tolerable. But listeners tend to be open-ended, and therefore disastrous for this behavior. Long-lived background threads are even worse.

If the gateway was restarted whenever project edits were saved, it'd be just like python and you could use all the jython callbacks you want. /:

2 Likes

Ah I see. That does add a whole layer of complication to the system doesn't it! Wipes out an entire wing of python functionality. But I understand why it's mission critical.

Would it be possible to dynamically set the onStop/onCancel/onAbort before the chart was called, but without permanently adjusting the SFC on disk? In that case I could generate a script that would otherwise be legal to put in the SFC itself, but attach it before the SFC was launched within the script that is calling startChart(). Or is this impossible because the SFC is not actually a holistic object but an objective representation of a series of functions? Like, how are the parameters that are passed to the SFC tracked and can entire functions be tracked similarly? I saw that there is a way using a ChartObserver (or something similar) to edit variables during runtime, but not full functions.

You've exhausted my knowledge of the SFC module. (I avoid SFCs like the plague. I despise the whole SFC paradigm, in Ignition or in PLCs.)

Oh! Well thank you for sharing what you had, it was helpful.

While I have your attention, may I ask how you prefer to go about generating what I want to call a "recipe" for a process? I currently try to think of them as "parameterized" SFCs, effectively a program for some device that Ignition is controlling.

An example might be (parameters in parens) something like close valve (A), close valve (B), activate vacuum pump (PmpA), open valve (A), await pressure requirement (P1) via sensor loop, close valve (A), set MFC (B) to flow (FB1), open valve (B), await pressure requirement (P2), adjust MFC (B) to flow (FB2), open valve (A), wait for (T1) seconds... etc.

As is this makes perfect sense to do with SFCs, but I am open to superior alternatives!

Not to put words in Phil's mouth, but I'd guess he goes for a purely-programmatic state machine (if such logic can't be done in the PLC, where it potentially belongs).

1 Like

That would make sense -- harder to pawn off work on process engineers though!