# HG changeset patch # User Edouard Tisserant # Date 1716643728 -7200 # Node ID 79bdee48ced8de4f3a543757b05a03bfca887ec5 # Parent f64dce4e2f627403f6df1040f82c3f9eaa606d96 Python runtime: ensure that python thread finished before returning from StopPLC. diff -r f64dce4e2f62 -r 79bdee48ced8 runtime/PLCObject.py --- a/runtime/PLCObject.py Fri May 24 18:55:46 2024 +0200 +++ b/runtime/PLCObject.py Sat May 25 15:28:48 2024 +0200 @@ -98,6 +98,10 @@ self.Traces = [] self.DebugToken = 0 + # Event to signal when PLC is stopped. + self.PlcStopped = Event() + self.PlcStopped.set() + self._init_blobs() # First task of worker -> no @RunInMain @@ -326,7 +330,7 @@ return False - def PythonRuntimeCall(self, methodname, use_evaluator=True, reverse_order=False): + def PythonRuntimeCall(self, methodname, reverse_order=False): """ Calls init, start, stop or cleanup method provided by runtime python files, loaded when new PLC uploaded @@ -335,10 +339,7 @@ if reverse_order: methods = reversed(methods) for method in methods: - if use_evaluator: - _res, exp = self.evaluator(method) - else: - _res, exp = default_evaluator(method) + _res, exp = default_evaluator(method) if exp is not None: self.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) @@ -402,7 +403,7 @@ self.LogMessage(0, traceback.format_exc()) raise - self.PythonRuntimeCall("init", use_evaluator=False) + self.PythonRuntimeCall("init") self.PythonThreadCondLock = Lock() self.PythonThreadCmdCond = Condition(self.PythonThreadCondLock) @@ -417,7 +418,7 @@ if self.python_runtime_vars is not None: self.PythonThreadCommand("Finish") self.PythonThread.join() - self.PythonRuntimeCall("cleanup", use_evaluator=False, reverse_order=True) + self.PythonRuntimeCall("cleanup", reverse_order=True) self.python_runtime_vars = None @@ -470,6 +471,10 @@ self._PostStartPLC() self.PythonThreadLoop() self.PythonRuntimeCall("stop", reverse_order=True) + + # Signal that python runtime has stopped + self.PlcStopped.set() + elif cmd == "Finish": self.PythonThreadAcknowledge(cmd) break @@ -525,6 +530,11 @@ @RunInMain def StartPLC(self): + # Prevent accidental call to StartPLC when already Started + if self.PLCStatus != PlcStatus.Stopped: + self.LogMessage(0,_("Problem starting PLC : PLC is not Stopped")) + return + if self.PLClibraryHandle is None: if not self.LoadPLC(): self._fail(_("Problem starting PLC : can't load PLC")) @@ -538,6 +548,7 @@ self.PLCStatus = PlcStatus.Started self.StatusChange() self.PythonThreadCommand("Start") + self.PlcStopped.clear() else: self._fail(_("Problem starting PLC : error %d" % res)) @@ -546,13 +557,18 @@ if self.PLCStatus == PlcStatus.Started: self.LogMessage("PLC stopped") self._stopPLC() - self.PLCStatus = PlcStatus.Stopped - self.StatusChange() if self.TraceThread is not None: self.TraceThread.join() self.TraceThread = None - return True - return False + + # Wait for python runtime stop to complete + if self.PlcStopped.wait(timeout=5): + self.PLCStatus = PlcStatus.Stopped + self.StatusChange() + else: + self._fail(_("PLC timed out while stopping")) + + return self.PLCStatus == PlcStatus.Stopped def GetPLCstatus(self): try: