--- a/connectors/PYRO/__init__.py Tue Sep 02 12:24:25 2008 +0200
+++ b/connectors/PYRO/__init__.py Wed Sep 03 17:28:17 2008 +0200
@@ -76,7 +76,7 @@
return RemotePLCObjectProxy
- def _PyroStartPLC(self):
+ def _PyroStartPLC(self, *args, **kwargs): pluginsroot._connector.GetPyroProxy() is used
rather than RemotePLCObjectProxy because
@@ -94,7 +94,7 @@
# let remote PLC time to resurect.(freeze app)
- return pluginsroot._connector.GetPyroProxy().StartPLC()
+ return pluginsroot._connector.GetPyroProxy().StartPLC(*args, **kwargs) StartPLC = PyroCatcher(_PyroStartPLC, False)
@@ -102,7 +102,10 @@
for safe use in from debug thread, must use the copy
- return RemotePLCObjectProxyCopy.GetTraceVariables()
+ if RemotePLCObjectProxyCopy.GetPLCstatus() == "Started": + return RemotePLCObjectProxyCopy.GetTraceVariables() GetTraceVariables = PyroCatcher(_PyroGetTraceVariables)
--- a/plugger.py Tue Sep 02 12:24:25 2008 +0200
+++ b/plugger.py Wed Sep 03 17:28:17 2008 +0200
@@ -600,7 +600,7 @@
ieclib_path = os.path.join(base_folder, "matiec", "lib")
# import for project creation timestamping
-from threading import Timer, Lock
+from threading import Timer, Lock, Thread from time import localtime
from datetime import datetime
# import necessary stuff from PLCOpenEditor
@@ -617,6 +617,7 @@
from discovery import DiscoveryDialog
+from weakref import WeakKeyDictionary class PluginsRoot(PlugTemplate, PLCControler):
@@ -666,10 +667,8 @@
self.IECdebug_lock = Lock()
- # Timer to prevent rapid-fire when registering many variables
- self.DebugTimer=Timer(0.5,self.RegisterDebugVarToConnector)
self.ResetIECProgramsAndVariables()
#This method are not called here... but in NewProject and OpenProject
#self._AddParamsMembers()
@@ -972,8 +971,8 @@
self._VariablesList = None
self._IECPathToIdx = None
- self._IdxToIECPath = None
+ self.TracedIECPath = [] def GetIECProgramsAndVariables(self):
Parse CSV-like file VARIABLES.csv resulting from IEC2C compiler.
@@ -989,7 +988,6 @@
- self._IdxToIECPath = {}
@@ -1023,7 +1021,6 @@
IEC_path=attrs["IEC_path"]
self._IECPathToIdx[IEC_path]=Idx
- self._IdxToIECPath[Idx]=IEC_path
self.logger.write_error("Cannot open/parse VARIABLES.csv!\n")
self.logger.write_error(traceback.format_exc())
@@ -1059,28 +1056,31 @@
def RegisterDebugVarToConnector(self):
+ self.TracedIECPath = [] if self._connector is not None:
self.IECdebug_lock.acquire()
- for IECPath,data_tuple in self.IECdebug_datas:
+ for IECPath,data_tuple in self.IECdebug_datas.iteritems(): WeakCallableDict, data_log, status = data_tuple
if len(WeakCallableDict) == 0:
# Callable Dict is empty.
# This variable is not needed anymore!
# self.IECdebug_callables.pop(IECPath)
+ print "Unused : " + IECPath Idx = self._IECPathToIdx.get(IECPath,None)
+ self.TracedIECPath.append(IECPath) self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath)
+ self._connector.SetTraceVariablesList(Idxs) self.IECdebug_lock.release()
- self._connector.TraceVariables(Idxs)
- def SubscribeDebugIECVariable(self, IECPath, callable, *args, **kwargs):
+ def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): Dispatching use a dictionnary linking IEC variable paths
to a WeakKeyDictionary linking
@@ -1096,12 +1096,20 @@
"Registered"] # Variable status
self.IECdebug_datas[IECPath] = IECdebug_data
- IECdebug_data[0][callable]=(args, kwargs)
+ IECdebug_data[0][callableobj]=(args, kwargs) self.IECdebug_lock.release()
+ if self.DebugTimer is not None: + self.DebugTimer.cancel() + # Timer to prevent rapid-fire when registering many variables + # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead + self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) # Rearm anti-rapid-fire timer
- self.DebugTimer.cancel()
+ return IECdebug_data[1] def Generate_plc_common_main(self):
@@ -1329,19 +1337,58 @@
self.logger.write_error("Couldn't start PLC !\n")
self.UpdateMethodsFromPLCStatus()
+ def DebugThreadProc(self): + while self._connector is not None: + debug_tick, debug_vars = self._connector.GetTraceVariables() + print debug_tick, debug_vars + if debug_vars is not None and \ + len(debug_vars) == len(self.TracedIECPath): + for IECPath,value in zip(self.TracedIECPath, debug_vars): + data_tuple = self.IECdebug_datas.get(IECPath, None) + if data_tuple is not None: + WeakCallableDict, data_log, status = data_tuple + data_log.append((debug_tick, value)) + for weakcallable,(args,kwargs) in WeakCallableDict.iteritems(): + wx.CallAfter(weakcallable, value, *args, **kwargs) + elif debug_vars is not None: + wx.CallAfter(self.logger.write_warning, + "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath))) + #wx.CallAfter(self.logger.write, "Debugger unavailable\n") + wx.CallAfter(self.logger.write, "Debugger disabled\n") - if self.GetIECProgramsAndVariables() and self._connector.StartPLC():
+ if self.GetIECProgramsAndVariables() and \ + self._connector.StartPLC(debug=True): self.logger.write("Starting PLC (debug mode)\n")
# TODO : laucnch PLCOpenEditor in Debug Mode
- self.logger.write_warning("Debug mode for PLCopenEditor not implemented\n")
- self.logger.write_warning("Starting alternative test GUI\n")
- # TODO : laucnch PLCOpenEditor in Debug Mode
+ self.DebugThread = Thread(target=self.DebugThreadProc) + self.DebugThread.start() self.logger.write_error("Couldn't start PLC debug !\n")
self.UpdateMethodsFromPLCStatus()
+# def _Do_Test_Debug(self): +# self.temporary_non_weak_callable_refs = [] +# for IEC_Path, idx in self._IECPathToIdx.iteritems(): +# def __call__(self, value, idx, name): +# print "debug call:", value, idx, name, self.buf +# res = self.SubscribeDebugIECVariable(IEC_Path, a, idx, IEC_Path) +# self.temporary_non_weak_callable_refs.append(a) @@ -1485,6 +1532,10 @@
"tooltip" : "Start PLC (debug mode)",
+# {"bitmap" : opjimg("Debug"), +# "name" : "Do_Test_Debug", +# "tooltip" : "Test debug mode)", +# "method" : "_Do_Test_Debug"}, {"bitmap" : opjimg("Stop"),
--- a/plugins/canfestival/cf_runtime.c Tue Sep 02 12:24:25 2008 +0200
+++ b/plugins/canfestival/cf_runtime.c Wed Sep 03 17:28:17 2008 +0200
@@ -89,7 +89,8 @@
#define NODE_OPEN(nodename)\
if(!canOpen(&nodename##Board,&nodename##_Data)){\
- printf("Cannot open " #nodename " Board (%%s,%%s)\n",nodename##Board.busname, nodename##Board.baudrate);\
+ fprintf(stderr,"Cannot open CAN intefrace %%s at speed %%s\n for CANopen node \"" #nodename "\"",nodename##Board.busname, nodename##Board.baudrate);\ @@ -100,6 +101,7 @@
#ifndef NOT_USE_DYNAMIC_LOADING
if( !LoadCanDriver("%(candriver)s") ){
fprintf(stderr, "Cannot load CAN interface library for CanFestival (%(candriver)s)\n");\
--- a/runtime/PLCObject.py Tue Sep 02 12:24:25 2008 +0200
+++ b/runtime/PLCObject.py Wed Sep 03 17:28:17 2008 +0200
@@ -40,6 +40,7 @@
class PLCObject(pyro.ObjBase):
def __init__(self, workingdir, daemon, argv):
pyro.ObjBase.__init__(self)
self.argv = [workingdir] + argv # force argv[0] to be "path" to exec...
@@ -86,8 +87,9 @@
self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables
self._ResetDebugVariables.restype = None
- self._RegisterDebugVariable = self.PLClibraryHandle.ResetDebugVariables
+ self._RegisterDebugVariable = self.PLClibraryHandle.RegisterDebugVariable self._RegisterDebugVariable.restype = None
+ self._RegisterDebugVariable.argtypes = [ctypes.c_int] self._IterDebugData = self.PLClibraryHandle.IterDebugData
self._IterDebugData.restype = ctypes.c_void_p
@@ -98,6 +100,13 @@
self._WaitDebugData = self.PLClibraryHandle.WaitDebugData
self._WaitDebugData.restype = ctypes.c_int
+ self._suspendDebug = self.PLClibraryHandle.suspendDebug + self._suspendDebug.restype = None + self._resumeDebug = self.PLClibraryHandle.resumeDebug + self._resumeDebug.restype = None print traceback.format_exc()
@@ -115,6 +124,9 @@
self._RegisterDebugVariable = lambda x:None
self._IterDebugData = lambda x,y:None
self._FreeDebugData = lambda:None
+ self._WaitDebugData = lambda:-1 + self._suspendDebug = lambda:None + self._resumeDebug = lambda:None self.PLClibraryHandle = None
# Unload library explicitely
if getattr(self,"_PLClibraryHandle",None) is not None:
@@ -151,11 +163,13 @@
+ def StartPLC(self, debug=False): if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
c_argv = ctypes.c_char_p * len(self.argv)
if self._LoadNewPLC() and self._startPLC(len(self.argv),c_argv(*self.argv)) == 0:
self.PLCStatus = "Started"
@@ -244,11 +258,13 @@
Call ctype imported function to append
these indexes to registred variables in PLC debugger
# keep a copy of requested idx
self._ResetDebugVariables()
self._RegisterDebugVariable(idx)
TypeTranslator = {"BOOL" : ctypes.c_uint8,
@@ -277,19 +293,23 @@
Return a list of variables, corresponding to the list of requiered idx
tick = self._WaitDebugData()
typename = ctypes.c_char_p()
+ for given_idx in self._Idxs: buffer=self._IterDebugData(ctypes.byref(idx), ctypes.byref(typename))
- c_type = TypeTranslator.get(s.value, None)
- res += cast(buffer, POINTER(c_type)).value
+ c_type = self.TypeTranslator.get(typename.value, None) + if c_type is not None and given_idx == idx.value: + res.append(ctypes.cast(buffer, + ctypes.POINTER(c_type)).contents.value)
+ print "Debug error idx : %d, expected_idx %d, type : %s"%(idx.value, given_idx,typename.value)
--- a/targets/Linux/plc_Linux_main.c Tue Sep 02 12:24:25 2008 +0200
+++ b/targets/Linux/plc_Linux_main.c Wed Sep 03 17:28:17 2008 +0200
@@ -86,23 +86,37 @@
+static int __debug_tick; +static pthread_mutex_t wait_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t wait_cond = PTHREAD_COND_INITIALIZER; + /* Eventually unlock debugger thread*/ + pthread_mutex_lock(&wait_mutex); + pthread_cond_broadcast(&wait_cond); + pthread_mutex_unlock(&wait_mutex); timer_delete (PLC_timer);
-pthread_mutex_t DebugLock = PTHREAD_MUTEX_INITIALIZER;
-static int __debug_tick;
/* from plc_debugger.c */
/* Wait signal from PLC thread */
- pthread_mutex_lock(&DebugLock);
+ pthread_mutex_lock(&wait_mutex); + pthread_cond_wait(&wait_cond, &wait_mutex); + pthread_mutex_unlock(&wait_mutex); @@ -112,5 +126,7 @@
/* signal debugger thread to continue*/
- pthread_mutex_unlock(&DebugLock);
+ pthread_mutex_lock(&wait_mutex); + pthread_cond_broadcast(&wait_cond); + pthread_mutex_unlock(&wait_mutex); --- a/targets/plc_common_main.c Tue Sep 02 12:24:25 2008 +0200
+++ b/targets/plc_common_main.c Wed Sep 03 17:28:17 2008 +0200
@@ -24,6 +24,9 @@
void config_run__(int tick);
void config_init__(void);
+void __init_debug(void); +void __cleanup_debug(void); * Functions and variables to export to generated C softPLC and plugins
@@ -33,7 +36,9 @@
static int init_level = 0;
-static int Debugging = 1;
+static int Debugging = 0; +static int WasDebugging = 0; * Prototypes of functions exported by plugins
@@ -52,6 +57,8 @@
if(Debugging) __publish_debug();
+ else if(WasDebugging) AbortDebug(); + WasDebugging = Debugging; @@ -66,6 +73,7 @@
@@ -75,6 +83,7 @@
@@ -164,13 +173,16 @@
+extern int WaitDebugData(); /* Prevent PLC to enter debug code */
+ /* wait next tick end to be sure*/
/* Let PLC enter debug code */
--- a/targets/plc_debug.c Tue Sep 02 12:24:25 2008 +0200
+++ b/targets/plc_debug.c Wed Sep 03 17:28:17 2008 +0200
@@ -136,13 +136,14 @@
+ subscription_cursor = subscription_table; void* IterDebugData(int* idx, const char **type_name)
if(subscription_cursor < latest_subscription){
*idx = *subscription_cursor;
- struct_plcvar* my_var = &variable_table[*subscription_cursor++];
+ struct_plcvar* my_var = &variable_table[*(subscription_cursor++)]; *type_name = __get_type_enum_name(my_var->type);
--- a/wxPopen.py Tue Sep 02 12:24:25 2008 +0200
+++ b/wxPopen.py Wed Sep 03 17:28:17 2008 +0200
@@ -28,7 +28,8 @@
import subprocess, ctypes
-from signal import SIGTERM
+ from signal import SIGTERM, SIGKILL class outputThread(threading.Thread):
@@ -140,7 +141,7 @@
if self.finish_callback is not None:
self.finish_callback(self,ecode,pid)
- def kill(self,signal=SIGTERM):
+ def kill(self,gently=True): if wx.Platform == '__WXMSW__':
@@ -149,8 +150,12 @@
ctypes.windll.kernel32.TerminateProcess(handle, -1)
ctypes.windll.kernel32.CloseHandle(handle)
- os.kill(self.Proc.pid, signal)
+ os.kill(self.Proc.pid, sig)