diff -r 02d0daed3e46 -r 112b4bc523b3 plugger.py --- a/plugger.py Thu Sep 04 16:07:14 2008 +0200 +++ b/plugger.py Fri Sep 05 16:25:57 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, Thread +from threading import Timer, Lock, Thread, Semaphore from time import localtime from datetime import datetime # import necessary stuff from PLCOpenEditor @@ -1054,62 +1054,6 @@ for v in self._VariablesList if v["type"] in DebugTypes ])} return debug_code - - def RegisterDebugVarToConnector(self): - self.DebugTimer=None - Idxs = [] - self.TracedIECPath = [] - if self._connector is not None: - self.IECdebug_lock.acquire() - 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) - # TODO - print "Unused : " + IECPath - else: - # Convert - Idx = self._IECPathToIdx.get(IECPath,None) - if Idx is not None: - Idxs.append(Idx) - self.TracedIECPath.append(IECPath) - else: - self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath) - self._connector.SetTraceVariablesList(Idxs) - self.IECdebug_lock.release() - - def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): - """ - Dispatching use a dictionnary linking IEC variable paths - to a WeakKeyDictionary linking - weakly referenced callables to optionnal args - """ - self.IECdebug_lock.acquire() - # If no entry exist, create a new one with a fresh WeakKeyDictionary - IECdebug_data = self.IECdebug_datas.get(IECPath, None) - if IECdebug_data is None: - IECdebug_data = [ - WeakKeyDictionary(), # Callables - [], # Data storage [(tick, data),...] - "Registered"] # Variable status - self.IECdebug_datas[IECPath] = IECdebug_data - - 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.start() - - return IECdebug_data[1] def Generate_plc_common_main(self): """ @@ -1337,10 +1281,85 @@ self.logger.write_error("Couldn't start PLC !\n") self.UpdateMethodsFromPLCStatus() + def RegisterDebugVarToConnector(self): + self.DebugTimer=None + Idxs = [] + self.TracedIECPath = [] + if self._connector is not None: + self.IECdebug_lock.acquire() + IECPathsToPop = [] + 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! + #print "Unused : " + IECPath + IECPathsToPop.append(IECPath) + else: + # Convert + Idx = self._IECPathToIdx.get(IECPath,None) + if Idx is not None: + Idxs.append(Idx) + self.TracedIECPath.append(IECPath) + else: + self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath) + for IECPathToPop in IECPathsToPop: + self.IECdebug_datas.pop(IECPathToPop) + + self._connector.SetTraceVariablesList(Idxs) + self.IECdebug_lock.release() + + def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): + """ + Dispatching use a dictionnary linking IEC variable paths + to a WeakKeyDictionary linking + weakly referenced callables to optionnal args + """ + self.IECdebug_lock.acquire() + # If no entry exist, create a new one with a fresh WeakKeyDictionary + IECdebug_data = self.IECdebug_datas.get(IECPath, None) + if IECdebug_data is None: + IECdebug_data = [ + WeakKeyDictionary(), # Callables + [], # Data storage [(tick, data),...] + "Registered"] # Variable status + self.IECdebug_datas[IECPath] = IECdebug_data + + 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.start() + + return IECdebug_data[1] + + def UnsubscribeDebugIECVariable(self, IECPath, callableobj): + IECdebug_data = self.IECdebug_datas.get(IECPath, None) + if IECdebug_data is None: + IECdebug_data[0].pop(callableobj,None) + + def DebugCallerFunc(self, weakcallable, value, *args, **kwargs): + # do the call + weakcallable.SetValue(value, *args, **kwargs) + # will unlock debug thread + self.DebugThreadSlowDownLock.release() + def DebugThreadProc(self): + """ + This thread waid PLC debug data, and dispatch them to subscribers + """ + # This lock is used to avoid flooding wx event stack calling callafter + self.DebugThreadSlowDownLock = Semaphore(0) while self._connector is not None: debug_tick, debug_vars = self._connector.GetTraceVariables() - print debug_tick, debug_vars + #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): @@ -1349,13 +1368,16 @@ WeakCallableDict, data_log, status = data_tuple data_log.append((debug_tick, value)) for weakcallable,(args,kwargs) in WeakCallableDict.iteritems(): - wx.CallAfter(weakcallable.SetValue, value, *args, **kwargs) + # delegate call to wx event loop + wx.CallAfter(self.DebugCallerFunc, weakcallable, value, *args, **kwargs) + # This will block thread if more than one call is waiting + self.DebugThreadSlowDownLock.acquire() elif debug_vars is not None: wx.CallAfter(self.logger.write_warning, "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath))) - elif debug_tick == -1: + #elif debug_tick == -1: #wx.CallAfter(self.logger.write, "Debugger unavailable\n") - pass + # pass else: wx.CallAfter(self.logger.write, "Debugger disabled\n") break @@ -1367,6 +1389,7 @@ if self.GetIECProgramsAndVariables() and \ self._connector.StartPLC(debug=True): self.logger.write("Starting PLC (debug mode)\n") + self.TracedIECPath = [] # TODO : laucnch PLCOpenEditor in Debug Mode self.DebugThread = Thread(target=self.DebugThreadProc) self.DebugThread.start() @@ -1379,12 +1402,13 @@ # self.temporary_non_weak_callable_refs = [] # for IEC_Path, idx in self._IECPathToIdx.iteritems(): # class tmpcls: -# def __init__(self): +# def __init__(_self): # self.buf = None -# def setbuf(self,buf): +# def setbuf(_self,buf): # self.buf = buf -# def SetValue(self, value, idx, name): -# print "debug call:", value, idx, name, self.buf +# def SetValue(_self, value, idx, name): +# self.logger.write("debug call: %s %d %s\n"%(repr(value), idx, name)) +# #self.logger.write("debug call: %s %d %s %s\n"%(repr(value), idx, name, repr(self.buf))) # a = tmpcls() # res = self.SubscribeDebugIECVariable(IEC_Path, a, idx, IEC_Path) # a.setbuf(res)