ProjectController.py
changeset 1363 e87e0166d0a7
parent 1330 96b242e4c59d
child 1364 e9e17d3b2849
--- a/ProjectController.py	Sat Oct 12 10:10:30 2013 +0900
+++ b/ProjectController.py	Mon Oct 14 10:31:32 2013 +0200
@@ -22,7 +22,7 @@
 from editors.FileManagementPanel import FileManagementPanel
 from editors.ProjectNodeEditor import ProjectNodeEditor
 from editors.IECCodeViewer import IECCodeViewer
-from editors.DebugViewer import DebugViewer
+from editors.DebugViewer import DebugViewer, REFRESH_PERIOD
 from dialogs import DiscoveryDialog
 from PLCControler import PLCControler
 from plcopen.structures import IEC_KEYWORDS
@@ -110,6 +110,9 @@
         self.MandatoryParams = None
         self._builder = None
         self._connector = None
+        self.DispatchDebugValuesTimer = None
+        self.DebugValuesBuffers = []
+        self.DebugTicks = []
         self.SetAppFrame(frame, logger)
         
         self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else ""))
@@ -157,15 +160,23 @@
         self.AppFrame = frame
         self.logger = logger
         self.StatusTimer = None
+        if self.DispatchDebugValuesTimer is not None:
+            self.DispatchDebugValuesTimer.Stop()
+        self.DispatchDebugValuesTimer = None
         
         if frame is not None:
             frame.LogViewer.SetLogSource(self._connector)
             
             # Timer to pull PLC status
-            ID_STATUSTIMER = wx.NewId()
-            self.StatusTimer = wx.Timer(self.AppFrame, ID_STATUSTIMER)
-            self.AppFrame.Bind(wx.EVT_TIMER, self.PullPLCStatusProc, self.StatusTimer)
-        
+            self.StatusTimer = wx.Timer(self.AppFrame, -1)
+            self.AppFrame.Bind(wx.EVT_TIMER, 
+                self.PullPLCStatusProc, self.StatusTimer)
+            
+            # Timer to dispatch debug values to consumers
+            self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1)
+            self.AppFrame.Bind(wx.EVT_TIMER, 
+                self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer)
+            
             self.RefreshConfNodesBlockLists()
 
     def ResetAppFrame(self, logger):
@@ -1177,6 +1188,12 @@
     def PullPLCStatusProc(self, event):
         self.UpdateMethodsFromPLCStatus()
         
+    def SnapshotAndResetDebugValuesBuffers(self):
+        buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, 
+            [list() for iec_path in self.TracedIECPath])
+        ticks, self.DebugTicks = self.DebugTicks, []
+        return ticks, buffers
+    
     def RegisterDebugVarToConnector(self):
         self.DebugTimer=None
         Idxs = []
@@ -1210,11 +1227,12 @@
             else:
                 self.TracedIECPath = []
                 self._connector.SetTraceVariablesList([])
+            self.SnapshotAndResetDebugValuesBuffers()
             self.IECdebug_lock.release()
     
     def IsPLCStarted(self):
         return self.previous_plcstate == "Started"
-     
+    
     def ReArmDebugRegisterTimer(self):
         if self.DebugTimer is not None:
             self.DebugTimer.cancel()
@@ -1233,7 +1251,7 @@
         Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
         return IEC_Type
         
-    def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs):
+    def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs):
         """
         Dispatching use a dictionnary linking IEC variable paths
         to a WeakKeyDictionary linking 
@@ -1250,7 +1268,8 @@
                     WeakKeyDictionary(), # Callables
                     [],                  # Data storage [(tick, data),...]
                     "Registered",        # Variable status
-                    None]                # Forced value
+                    None,
+                    buffer_list]                # Forced value
             self.IECdebug_datas[IECPath] = IECdebug_data
         
         IECdebug_data[0][callableobj]=(args, kwargs)
@@ -1347,14 +1366,14 @@
             #print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()]
             if plc_status == "Started":
                 self.IECdebug_lock.acquire()
-                if len(debug_vars) == len(self.TracedIECPath):
+                if len(debug_vars) == len(self.DebugValuesBuffers):
                     if debug_getvar_retry > DEBUG_RETRIES_WARN:
                         self.logger.write(_("... debugger recovered\n"))
                     debug_getvar_retry = 0
-                    for IECPath,value in zip(self.TracedIECPath, debug_vars):
+                    for values_buffer, value in zip(self.DebugValuesBuffers, debug_vars):
                         if value is not None:
-                            self.CallWeakcallables(IECPath, "NewValue", debug_tick, value)
-                    self.CallWeakcallables("__tick__", "NewDataAvailable", debug_tick)
+                            values_buffer.append(value)
+                    self.DebugTicks.append(debug_tick)
                 self.IECdebug_lock.release()
                 if debug_getvar_retry == DEBUG_RETRIES_WARN:
                     self.logger.write(_("Waiting debugger to recover...\n"))
@@ -1368,6 +1387,20 @@
                 self.debug_break = True
         self.logger.write(_("Debugger disabled\n"))
         self.DebugThread = None
+        if self.DispatchDebugValuesTimer is not None:
+            self.DispatchDebugValuesTimer.Stop()
+
+    def DispatchDebugValuesProc(self, event):
+        self.IECdebug_lock.acquire()
+        debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
+        self.IECdebug_lock.release()
+        if len(self.TracedIECPath) == len(buffers):
+            for IECPath, values in zip(self.TracedIECPath, buffers):
+                if len(values) > 0:
+                    self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values)
+            if len(debug_ticks) > 0:
+                self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks)
+        event.Skip()
 
     def KillDebugThread(self):
         tmp_debugthread = self.DebugThread
@@ -1380,12 +1413,16 @@
             else:
                 self.logger.write(_("Debugger stopped.\n"))
         self.DebugThread = None
+        if self.DispatchDebugValuesTimer is not None:
+            self.DispatchDebugValuesTimer.Stop()
 
     def _connect_debug(self): 
         self.previous_plcstate = None
         if self.AppFrame:
             self.AppFrame.ResetGraphicViewers()
         self.RegisterDebugVarToConnector()
+        if self.DispatchDebugValuesTimer is not None:
+            self.DispatchDebugValuesTimer.Start(int(REFRESH_PERIOD * 1000))
         if self.DebugThread is None:
             self.DebugThread = Thread(target=self.DebugThreadProc)
             self.DebugThread.start()