Add a debugger token to SetTraceVariables and GetTraceVariables to prevent crash an inconsistant data in case of multiple connections. Last connection now takes over existing connections's debug, and first connected IDE gets a wrning.
authorEdouard Tisserant
Thu, 31 Jan 2019 14:10:06 +0100
changeset 2485 ef327451d067
parent 2484 2318a7cde101
child 2486 44c2a4e2b84d
Add a debugger token to SetTraceVariables and GetTraceVariables to prevent crash an inconsistant data in case of multiple connections. Last connection now takes over existing connections's debug, and first connected IDE gets a wrning.
BeremizIDE.py
ProjectController.py
runtime/PLCObject.py
--- a/BeremizIDE.py	Tue Jan 29 09:14:47 2019 +0100
+++ b/BeremizIDE.py	Thu Jan 31 14:10:06 2019 +0100
@@ -209,10 +209,6 @@
     def write_error(self, s):
         self.write(s, self.red_yellow)
 
-    def writeyield(self, s):
-        self.write(s)
-        wx.GetApp().Yield()
-
     def flush(self):
         # Temporary deactivate read only mode on StyledTextCtrl for clearing
         # text. It seems that text modifications, even programmatically, are
--- a/ProjectController.py	Tue Jan 29 09:14:47 2019 +0100
+++ b/ProjectController.py	Thu Jan 31 14:10:06 2019 +0100
@@ -270,6 +270,8 @@
         self.previous_plcstate = None
         # copy StatusMethods so that it can be later customized
         self.StatusMethods = [dic.copy() for dic in self.StatusMethods]
+        self.DebugToken = None
+        self.debug_status = PlcStatus.Stopped
 
     def __del__(self):
         if self.DebugTimer:
@@ -1479,11 +1481,12 @@
         self.UpdateMethodsFromPLCStatus()
 
     def SnapshotAndResetDebugValuesBuffers(self):
-        if self._connector is not None:
-            plc_status, Traces = self._connector.GetTraceVariables()
+        debug_status = PlcStatus.Disconnected
+        if self._connector is not None and self.DebugToken is not None:
+            debug_status, Traces = self._connector.GetTraceVariables(self.DebugToken)
             # print [dict.keys() for IECPath, (dict, log, status, fvalue) in
             # self.IECdebug_datas.items()]
-            if plc_status == PlcStatus.Started:
+            if debug_status == PlcStatus.Started:
                 if len(Traces) > 0:
                     for debug_tick, debug_buff in Traces:
                         debug_vars = UnpackDebugBuffer(
@@ -1509,14 +1512,14 @@
 
         ticks, self.DebugTicks = self.DebugTicks, []
 
-        return ticks, buffers
+        return debug_status, ticks, buffers
 
     def RegisterDebugVarToConnector(self):
         self.DebugTimer = None
         Idxs = []
         self.TracedIECPath = []
         self.TracedIECTypes = []
-        if self._connector is not None:
+        if self._connector is not None and self.debug_status != PlcStatus.Broken:
             IECPathsToPop = []
             for IECPath, data_tuple in self.IECdebug_datas.iteritems():
                 WeakCallableDict, _data_log, _status, fvalue, _buffer_list = data_tuple
@@ -1545,11 +1548,12 @@
                 IdxsT = zip(*Idxs)
                 self.TracedIECPath = IdxsT[3]
                 self.TracedIECTypes = IdxsT[1]
-                self._connector.SetTraceVariablesList(zip(*IdxsT[0:3]))
+                self.DebugToken = self._connector.SetTraceVariablesList(zip(*IdxsT[0:3]))
             else:
                 self.TracedIECPath = []
                 self._connector.SetTraceVariablesList([])
-            self.SnapshotAndResetDebugValuesBuffers()
+                self.DebugToken = None
+            self.debug_status, _debug_ticks, _buffers = self.SnapshotAndResetDebugValuesBuffers()
 
     def IsPLCStarted(self):
         return self.previous_plcstate == PlcStatus.Started
@@ -1665,7 +1669,7 @@
         return self._connector.RemoteExec(script, **kwargs)
 
     def DispatchDebugValuesProc(self, event):
-        debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
+        self.debug_status, debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
         start_time = time.time()
         if len(self.TracedIECPath) == len(buffers):
             for IECPath, values in zip(self.TracedIECPath, buffers):
@@ -1676,11 +1680,15 @@
                 self.CallWeakcallables(
                     "__tick__", "NewDataAvailable", debug_ticks)
 
-        delay = time.time() - start_time
-        next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
-        if self.DispatchDebugValuesTimer is not None:
-            self.DispatchDebugValuesTimer.Start(
-                int(next_refresh * 1000), oneShot=True)
+        if self.debug_status == PlcStatus.Broken:
+            self.logger.write_warning(
+                _("Debug: token rejected - other debug took over - reconnect to recover\n"))
+        else:
+            delay = time.time() - start_time
+            next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
+            if self.DispatchDebugValuesTimer is not None:
+                self.DispatchDebugValuesTimer.Start(
+                    int(next_refresh * 1000), oneShot=True)
         event.Skip()
 
     def KillDebugThread(self):
@@ -1691,6 +1699,9 @@
         self.previous_plcstate = None
         if self.AppFrame:
             self.AppFrame.ResetGraphicViewers()
+
+        self.debug_status = PlcStatus.Started
+
         self.RegisterDebugVarToConnector()
         if self.DispatchDebugValuesTimer is not None:
             self.DispatchDebugValuesTimer.Start(
@@ -1727,7 +1738,6 @@
         if connector is not None:
             if self.StatusTimer is not None:
                 # Start the status Timer
-                wx.Yield()
                 self.StatusTimer.Start(milliseconds=500, oneShot=False)
         else:
             if self.StatusTimer is not None:
--- a/runtime/PLCObject.py	Tue Jan 29 09:14:47 2019 +0100
+++ b/runtime/PLCObject.py	Thu Jan 31 14:10:06 2019 +0100
@@ -99,6 +99,7 @@
         self.TraceThread = None
         self.TraceLock = Lock()
         self.Traces = []
+        self.DebugToken = 0
 
         self._init_blobs()
 
@@ -567,11 +568,13 @@
             pass
         return False
 
+    @RunInMain
     def SetTraceVariablesList(self, idxs):
         """
         Call ctype imported function to append
         these indexes to registred variables in PLC debugger
         """
+        self.DebugToken += 1
         if idxs:
             # suspend but dont disable
             if self._suspendDebug(False) == 0:
@@ -586,8 +589,10 @@
                     self._RegisterDebugVariable(idx, force)
                 self._TracesSwap()
                 self._resumeDebug()
+                return self.DebugToken
         else:
             self._suspendDebug(True)
+        return None
 
     def _TracesSwap(self):
         self.LastSwapTrace = time()
@@ -601,8 +606,11 @@
         return Traces
 
     @RunInMain
-    def GetTraceVariables(self):
-        return self.PLCStatus, self._TracesSwap()
+    def GetTraceVariables(self, DebugToken):
+        if (DebugToken is not None and
+            DebugToken == self.DebugToken):
+            return self.PLCStatus, self._TracesSwap()
+        return PlcStatus.Broken, []
 
     def TraceThreadProc(self):
         """