Minor changes to get better cleanup of debug and python_eval threads, accross multiple debug sessions and PLC runs.
--- a/plugger.py Sun Jan 04 17:25:22 2009 +0100
+++ b/plugger.py Sun Jan 04 17:29:12 2009 +0100
@@ -700,6 +700,8 @@
self.BuildPath = None
self.PLCEditor = None
self.PLCDebug = None
+ self.DebugThread = None
+ self.debug_break = False
# copy PluginMethods so that it can be later customized
self.PluginMethods = [dic.copy() for dic in self.PluginMethods]
@@ -1309,6 +1311,8 @@
self.logger.write_error("Build directory already clean\n")
self.ShowMethod("_showIECcode", False)
self.EnableMethod("_Clean", False)
+ # kill the builder
+ self._builder = None
self.CompareLocalAndRemotePLC()
############# Real PLC object access #############
@@ -1434,8 +1438,8 @@
"""
# This lock is used to avoid flooding wx event stack calling callafter
self.DebugThreadSlowDownLock = Semaphore(0)
- _break = False
- while not _break and self._connector is not None:
+ self.debug_break = False
+ while (not self.debug_break) and (self._connector is not None):
debug_tick, debug_vars = self._connector.GetTraceVariables()
#print debug_tick, debug_vars
self.IECdebug_lock.acquire()
@@ -1459,11 +1463,19 @@
pass
else:
wx.CallAfter(self.logger.write, "Debugger disabled\n")
- _break = True
+ self.debug_break = True
self.IECdebug_lock.release()
wx.CallAfter(self.DebugThreadSlowDownLock.release)
self.DebugThreadSlowDownLock.acquire()
+ def KillDebugThread(self):
+ self.debug_break = True
+ self.DebugThreadSlowDownLock.release()
+ self.DebugThread.join(timeout=1)
+ if self.DebugThread.isAlive():
+ self.logger.write_warning("Debug Thread couldn't be killed")
+ self.DebugThread = None
+
def _Debug(self):
"""
Start PLC (Debug Mode)
@@ -1485,6 +1497,7 @@
self.logger.write_error("Couldn't start PLC debug !\n")
self.UpdateMethodsFromPLCStatus()
+
# def _Do_Test_Debug(self):
# # debug code
# self.temporary_non_weak_callable_refs = []
@@ -1506,6 +1519,10 @@
"""
Stop PLC
"""
+ if self.DebugThread is not None:
+ self.logger.write("Stopping debug\n")
+ self.KillDebugThread()
+
if self._connector.StopPLC():
self.logger.write("Stopping PLC\n")
else:
--- a/runtime/PLCObject.py Sun Jan 04 17:25:22 2009 +0100
+++ b/runtime/PLCObject.py Sun Jan 04 17:29:12 2009 +0100
@@ -61,6 +61,10 @@
self.PLCStatus = "Empty"
self.CurrentPLCFilename=None
+ def StatusChange(self):
+ if self.statuschange is not None:
+ self.statuschange(self.PLCStatus)
+
def _GetMD5FileName(self):
return os.path.join(self.workingdir, "lasttransferedPLC.md5")
@@ -169,21 +173,30 @@
return False
def PythonThreadProc(self):
+ print "PythonThreadProc started"
+ my_globs = globals().copy()
pyfile = os.path.join(self.workingdir, "runtime.py")
if os.path.exists(pyfile):
# TODO handle exceptions in runtime.py
- execfile(pyfile)
- res = ""
- print "PythonThreadProc started"
+ # pyfile may redefine _runtime_cleanup
+ # or even call _PythonThreadProc itself.
+ execfile(pyfile, my_globs)
+ res,cmd = "None","None"
while self.PLCStatus == "Started":
+ print "_PythonIterator(", res, ")",
cmd = self._PythonIterator(res)
- #print "_PythonIterator(", res, ") -> ", cmd
+ print " -> ", cmd
+ if cmd is None:
+ break
try :
- res = eval(cmd)
+ res = str(eval(cmd,my_globs))
except Exception,e:
res = "#EXCEPTION : "+str(e)
print res
- print "PythonThreadProc finished"
+ print "PythonThreadProc interrupted"
+ if my_globs.get("_runtime_cleanup",None) is not None:
+ my_globs["_runtime_cleanup"]()
+ print "PythonThreadProc cleaned up"
def StartPLC(self, debug=False):
print "StartPLC"
@@ -193,8 +206,7 @@
if debug:
self._resumeDebug()
self.PLCStatus = "Started"
- if self.statuschange is not None:
- self.statuschange(self.PLCStatus)
+ self.StatusChange()
self.PythonThread = Thread(target=self.PythonThreadProc)
self.PythonThread.start()
return True
@@ -206,10 +218,12 @@
def _DoStopPLC(self):
self._stopPLC()
self.PLCStatus = "Stopped"
- if self.statuschange is not None:
- self.statuschange(self.PLCStatus)
+ self.PythonThread.join(timeout=1)
+ if self.PythonThread.isAlive():
+ print "Python thread couldn't be killed"
if self._FreePLC():
self.PLCStatus = "Dirty"
+ self.StatusChange()
return True
def StopPLC(self):
@@ -327,25 +341,27 @@
"""
Return a list of variables, corresponding to the list of requiered idx
"""
- tick = self._WaitDebugData()
- if tick == -1:
- res = None
- else:
- idx = ctypes.c_int()
- typename = ctypes.c_char_p()
- res = []
-
- for given_idx in self._Idxs:
- buffer=self._IterDebugData(ctypes.byref(idx), ctypes.byref(typename))
- c_type,unpack_func = self.TypeTranslator.get(typename.value, (None,None))
- if c_type is not None and given_idx == idx.value:
- res.append(unpack_func(ctypes.cast(buffer,
- ctypes.POINTER(c_type)).contents))
- else:
- print "Debug error idx : %d, expected_idx %d, type : %s"%(idx.value, given_idx,typename.value)
- res.append(None)
- self._FreeDebugData()
- return tick, res
-
-
-
+ if self.PLCStatus == "Started":
+ tick = self._WaitDebugData()
+ if tick == -1:
+ res = None
+ else:
+ idx = ctypes.c_int()
+ typename = ctypes.c_char_p()
+ res = []
+
+ for given_idx in self._Idxs:
+ buffer=self._IterDebugData(ctypes.byref(idx), ctypes.byref(typename))
+ c_type,unpack_func = self.TypeTranslator.get(typename.value, (None,None))
+ if c_type is not None and given_idx == idx.value:
+ res.append(unpack_func(ctypes.cast(buffer,
+ ctypes.POINTER(c_type)).contents))
+ else:
+ print "Debug error idx : %d, expected_idx %d, type : %s"%(idx.value, given_idx,typename.value)
+ res.append(None)
+ self._FreeDebugData()
+ return tick, res
+ return -1, None
+
+
+
--- a/targets/plc_common_main.c Sun Jan 04 17:25:22 2009 +0100
+++ b/targets/plc_common_main.c Sun Jan 04 17:29:12 2009 +0100
@@ -69,6 +69,7 @@
int res;
config_init__();
__init_debug();
+ __init_python();
%(init_calls)s
return 0;
}
@@ -79,6 +80,7 @@
{
%(cleanup_calls)s
__cleanup_debug();
+ __cleanup_python();
}
--- a/targets/plc_python.c Sun Jan 04 17:25:22 2009 +0100
+++ b/targets/plc_python.c Sun Jan 04 17:29:12 2009 +0100
@@ -29,6 +29,7 @@
#define PYTHON_LOCKED_BY_PYTHON 0
#define PYTHON_LOCKED_BY_PLC 1
#define PYTHON_MUSTWAKEUP 2
+#define PYTHON_FINISHED 4
/* Each python_eval FunctionBlock have it own state */
#define PYTHON_FB_FREE 0
@@ -55,6 +56,8 @@
void __cleanup_python()
{
+ PythonState = PYTHON_FINISHED;
+ UnBlockPythonCommands();
}
void __retrieve_python()
@@ -173,7 +176,10 @@
{
UnLockPython();
/* wait next FB to eval */
+ //printf("PythonIterator wait\n");
WaitPythonCommands();
+ /*emergency exit*/
+ if(PythonState & PYTHON_FINISHED) return NULL;
LockPython();
}
/* Mark block as processing */