I'd like to share my findings on the issue of running and managing asynchronous threads in Ignition, for anyone going down the same rabbit hole, but also curious if this is a big no-no in Ignition. So far we haven't had any issues with this but I am curious if anyone else has done something similar and achieved success with it.
We were trying to create asynchronous threads that can start/stop listening to a serial port opened up on a client. We wanted to keep this in vision and have this "driver" communicate to perspective as well by writing to a UDT that perspective reads. We could have went with SFC's alternatively, or an external .NET service running on the client that talks to OPC, but these seem to be "unclean" solutions.
So to do this, we went with making asynchronous threads to open/close serial communication to the machine plugged into the local asset.
In order to close/end the serial communication, we have to shut off the asynchronous call which we can do with a timeout on no data change to end the thread, but also, we found that we can use the following to locate rogue or orphaned threads:
def get_thread(name):
# Get the Java Thread object in java.lang.Thread
from java.lang import Thread
# Get all of the currently running threads from the current java stack trace
threads = Thread.getAllStackTraces()
# Assume only one thread exists that matches name, modifiable to return multiple threads if more than 1 match
result = None
for thread in threads:
thread_name = thread.name
# Thread name matches known input thread name
if str(thread_name) == str(name):
result = thread
break
return result
Followed by application of
def stop_thread(name):
thread = get_thread(name)
thread.stop()
Using thread.stop()
is sort of frowned upon, but does it cause issues like not being garbage collected, etc.? I don't know that I have seen this solution in the forums. We have been able to shut off calls from thread generators like
def make_thread(name, target=None, callback=None, timeout=60):
import threading
thread = threading.Thread(target=target, name=name, kwargs={"timeout":timeout, "callback":callback})
thread.start()
return thread
or
def make_ign_thread(name, target=None, callback=None, timeout=60):
thread = system.util.invokeAsynchronous(function=target, description=name, kwargs={"timeout":timeout, "callback":callback}))
# Probably need to store this thread name in a DB or tag, it looks like [Thread-23]
thread_name = thread.getName()
return thread
that are no longer persistent in the current context (i.e. the script stopped but the threads continue to exist).
Anyone have experience doing this and can chime in on the pros/cons of this? Will the world be forever altered by my blasphemy? Will I regret this in the next 137 years of my existence?
Cheers,
Roger