Removed DebugThread. Take advantage of the fact that buffering is done in runtime. No need to poll for traces more than ten per second, then use simple wxTimer for that. Also removed Debug Lock since now everything happens in wx' mainloop.
authorEdouard Tisserant
Thu, 19 Apr 2018 13:02:13 +0200
changeset 1995 691d119ba20f
parent 1994 1fdc32be71b8
child 1996 4ae9c4447947
Removed DebugThread. Take advantage of the fact that buffering is done in runtime. No need to poll for traces more than ten per second, then use simple wxTimer for that. Also removed Debug Lock since now everything happens in wx' mainloop.
ProjectController.py
connectors/PYRO/__init__.py
--- a/ProjectController.py	Thu Apr 19 12:22:40 2018 +0200
+++ b/ProjectController.py	Thu Apr 19 13:02:13 2018 +0200
@@ -242,7 +242,6 @@
 
         # Setup debug information
         self.IECdebug_datas = {}
-        self.IECdebug_lock = Lock()
 
         self.DebugTimer = None
         self.ResetIECProgramsAndVariables()
@@ -258,7 +257,6 @@
         # After __init__ root confnode is not valid
         self.ProjectPath = None
         self._setBuildPath(None)
-        self.DebugThread = None
         self.debug_break = False
         self.previous_plcstate = None
         # copy ConfNodeMethods so that it can be later customized
@@ -1420,9 +1418,32 @@
         self.UpdateMethodsFromPLCStatus()
 
     def SnapshotAndResetDebugValuesBuffers(self):
+        plc_status, Traces = self._connector.GetTraceVariables()
+        # print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()]
+        if plc_status == "Started":
+            if len(Traces) > 0:
+                for debug_tick, debug_buff in Traces:
+                    debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes)
+                    if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath):
+                        for IECPath, values_buffer, value in izip(
+                                self.TracedIECPath,
+                                self.DebugValuesBuffers,
+                                debug_vars):
+                            IECdebug_data = self.IECdebug_datas.get(IECPath, None)
+                            if IECdebug_data is not None and value is not None:
+                                forced = IECdebug_data[2:4] == ["Forced", value]
+                                if not IECdebug_data[4] and len(values_buffer) > 0:
+                                    values_buffer[-1] = (value, forced)
+                                else:
+                                    values_buffer.append((value, forced))
+                        self.DebugTicks.append(debug_tick)
+
+
         buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers,
                                             [list() for dummy in xrange(len(self.TracedIECPath))])
+
         ticks, self.DebugTicks = self.DebugTicks, []
+
         return ticks, buffers
 
     def RegisterDebugVarToConnector(self):
@@ -1431,7 +1452,6 @@
         self.TracedIECPath = []
         self.TracedIECTypes = []
         if self._connector is not None:
-            self.IECdebug_lock.acquire()
             IECPathsToPop = []
             for IECPath, data_tuple in self.IECdebug_datas.iteritems():
                 WeakCallableDict, _data_log, _status, fvalue, _buffer_list = data_tuple
@@ -1462,7 +1482,6 @@
                 self.TracedIECPath = []
                 self._connector.SetTraceVariablesList([])
             self.SnapshotAndResetDebugValuesBuffers()
-            self.IECdebug_lock.release()
 
     def IsPLCStarted(self):
         return self.previous_plcstate == "Started"
@@ -1494,7 +1513,6 @@
         if IECPath != "__tick__" and IECPath not in self._IECPathToIdx:
             return None
 
-        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:
@@ -1510,14 +1528,11 @@
 
         IECdebug_data[0][callableobj] = buffer_list
 
-        self.IECdebug_lock.release()
-
         self.ReArmDebugRegisterTimer()
 
         return IECdebug_data[1]
 
     def UnsubscribeDebugIECVariable(self, IECPath, callableobj):
-        self.IECdebug_lock.acquire()
         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
         if IECdebug_data is not None:
             IECdebug_data[0].pop(callableobj, None)
@@ -1528,14 +1543,11 @@
                     lambda x, y: x | y,
                     IECdebug_data[0].itervalues(),
                     False)
-        self.IECdebug_lock.release()
 
         self.ReArmDebugRegisterTimer()
 
     def UnsubscribeAllDebugIECVariable(self):
-        self.IECdebug_lock.acquire()
         self.IECdebug_datas = {}
-        self.IECdebug_lock.release()
 
         self.ReArmDebugRegisterTimer()
 
@@ -1543,14 +1555,12 @@
         if IECPath not in self.IECdebug_datas:
             return
 
-        self.IECdebug_lock.acquire()
 
         # If no entry exist, create a new one with a fresh WeakKeyDictionary
         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
         IECdebug_data[2] = "Forced"
         IECdebug_data[3] = fvalue
 
-        self.IECdebug_lock.release()
 
         self.ReArmDebugRegisterTimer()
 
@@ -1558,14 +1568,12 @@
         if IECPath not in self.IECdebug_datas:
             return
 
-        self.IECdebug_lock.acquire()
 
         # If no entry exist, create a new one with a fresh WeakKeyDictionary
         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
         IECdebug_data[2] = "Registered"
         IECdebug_data[3] = None
 
-        self.IECdebug_lock.release()
 
         self.ReArmDebugRegisterTimer()
 
@@ -1590,51 +1598,9 @@
             return -1, "No runtime connected!"
         return self._connector.RemoteExec(script, **kwargs)
 
-    def DebugThreadProc(self):
-        """
-        This thread waid PLC debug data, and dispatch them to subscribers
-        """
-        self.debug_break = False
-        debug_getvar_retry = 0
-        while (not self.debug_break) and (self._connector is not None):
-            plc_status, Traces = self._connector.GetTraceVariables()
-            debug_getvar_retry += 1
-            # print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()]
-            if plc_status == "Started":
-                if len(Traces) > 0:
-                    self.IECdebug_lock.acquire()
-                    for debug_tick, debug_buff in Traces:
-                        debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes)
-                        if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath):
-                            for IECPath, values_buffer, value in izip(
-                                    self.TracedIECPath,
-                                    self.DebugValuesBuffers,
-                                    debug_vars):
-                                IECdebug_data = self.IECdebug_datas.get(IECPath, None)  # FIXME get
-                                if IECdebug_data is not None and value is not None:
-                                    forced = IECdebug_data[2:4] == ["Forced", value]
-                                    if not IECdebug_data[4] and len(values_buffer) > 0:
-                                        values_buffer[-1] = (value, forced)
-                                    else:
-                                        values_buffer.append((value, forced))
-                            self.DebugTicks.append(debug_tick)
-                            debug_getvar_retry = 0
-                    self.IECdebug_lock.release()
-
-                if debug_getvar_retry != 0:
-                    # Be patient, tollerate PLC to come with fresh samples
-                    time.sleep(0.1)
-            else:
-                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()
         start_time = time.time()
         if len(self.TracedIECPath) == len(buffers):
             for IECPath, values in izip(self.TracedIECPath, buffers):
@@ -1645,22 +1611,12 @@
 
         delay = time.time() - start_time
         next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
-        if self.DispatchDebugValuesTimer is not None and self.DebugThread is not None:
+        if self.DispatchDebugValuesTimer is not None:
             self.DispatchDebugValuesTimer.Start(
                 int(next_refresh * 1000), oneShot=True)
         event.Skip()
 
     def KillDebugThread(self):
-        tmp_debugthread = self.DebugThread
-        self.debug_break = True
-        if tmp_debugthread is not None:
-            self.logger.writeyield(_("Stopping debugger...\n"))
-            tmp_debugthread.join(timeout=5)
-            if tmp_debugthread.isAlive() and self.logger:
-                self.logger.write_warning(_("Couldn't stop debugger.\n"))
-            else:
-                self.logger.write(_("Debugger stopped.\n"))
-        self.DebugThread = None
         if self.DispatchDebugValuesTimer is not None:
             self.DispatchDebugValuesTimer.Stop()
 
@@ -1672,9 +1628,6 @@
         if self.DispatchDebugValuesTimer is not None:
             self.DispatchDebugValuesTimer.Start(
                 int(REFRESH_PERIOD * 1000), oneShot=True)
-        if self.DebugThread is None:
-            self.DebugThread = Thread(target=self.DebugThreadProc)
-            self.DebugThread.start()
 
     def _Run(self):
         """
--- a/connectors/PYRO/__init__.py	Thu Apr 19 12:22:40 2018 +0200
+++ b/connectors/PYRO/__init__.py	Thu Apr 19 13:02:13 2018 +0200
@@ -139,43 +139,23 @@
         confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n"))
         return None
 
+    _special_return_funcs = {
+        "StartPLC": False,
+        "GetTraceVariables": ("Broken", None),
+        "GetPLCstatus": ("Broken", None),
+        "RemoteExec": (-1, "RemoteExec script failed!")
+    }
     class PyroProxyProxy(object):
         """
         A proxy proxy class to handle Beremiz Pyro interface specific behavior.
         And to put Pyro exception catcher in between caller and Pyro proxy
         """
-        def __init__(self):
-            # for safe use in from debug thread, must create a copy
-            self.RemotePLCObjectProxyCopy = None
-
-        def _PyroStartPLC(self, *args, **kwargs):
-            return RemotePLCObjectProxy.StartPLC(*args, **kwargs)
-        StartPLC = PyroCatcher(_PyroStartPLC, False)
-
-        def _PyroGetTraceVariables(self):
-            """
-            for use from debug thread, use a copy
-            pyro creates a new thread on server end proxy object is copied
-            """
-            if self.RemotePLCObjectProxyCopy is None:
-                self.RemotePLCObjectProxyCopy = copy.copy(RemotePLCObjectProxy)
-            return self.RemotePLCObjectProxyCopy.GetTraceVariables()
-        GetTraceVariables = PyroCatcher(_PyroGetTraceVariables, ("Broken", None))
-
-        def _PyroGetPLCstatus(self):
-            return RemotePLCObjectProxy.GetPLCstatus()
-        GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, ("Broken", None))
-
-        def _PyroRemoteExec(self, script, **kwargs):
-            return RemotePLCObjectProxy.RemoteExec(script, **kwargs)
-        RemoteExec = PyroCatcher(_PyroRemoteExec, (-1, "RemoteExec script failed!"))
-
         def __getattr__(self, attrName):
             member = self.__dict__.get(attrName, None)
             if member is None:
                 def my_local_func(*args, **kwargs):
                     return RemotePLCObjectProxy.__getattr__(attrName)(*args, **kwargs)
-                member = PyroCatcher(my_local_func, None)
+                member = PyroCatcher(my_local_func, _special_return_funcs.get(attrName, None))
                 self.__dict__[attrName] = member
             return member