--- 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.DispatchDebugValuesTimer = None + self.DebugValuesBuffers = [] self.SetAppFrame(frame, logger)
self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else ""))
@@ -157,15 +160,23 @@
+ if self.DispatchDebugValuesTimer is not None: + self.DispatchDebugValuesTimer.Stop() + self.DispatchDebugValuesTimer = 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, [] def RegisterDebugVarToConnector(self):
@@ -1210,11 +1227,12 @@
self._connector.SetTraceVariablesList([])
+ self.SnapshotAndResetDebugValuesBuffers() self.IECdebug_lock.release()
return self.previous_plcstate == "Started"
def ReArmDebugRegisterTimer(self):
if self.DebugTimer is not None:
@@ -1233,7 +1251,7 @@
Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
- 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
+ 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"))
- for IECPath,value in zip(self.TracedIECPath, debug_vars):
+ for values_buffer, value in zip(self.DebugValuesBuffers, debug_vars): - 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.logger.write(_("Debugger disabled\n"))
+ 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): + self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values) + if len(debug_ticks) > 0: + self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks) def KillDebugThread(self):
tmp_debugthread = self.DebugThread
@@ -1380,12 +1413,16 @@
self.logger.write(_("Debugger stopped.\n"))
+ if self.DispatchDebugValuesTimer is not None: + self.DispatchDebugValuesTimer.Stop() def _connect_debug(self):
self.previous_plcstate = None
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)
--- a/controls/DebugVariablePanel/DebugVariableGraphicPanel.py Sat Oct 12 10:10:30 2013 +0900
+++ b/controls/DebugVariablePanel/DebugVariableGraphicPanel.py Mon Oct 14 10:31:32 2013 +0200
@@ -330,7 +330,7 @@
DebugViewer.RefreshNewData(self, *args, **kwargs)
- def NewDataAvailable(self, tick, *args, **kwargs):
+ def NewDataAvailable(self, ticks, *args, **kwargs): Called by DataProducer for each tick captured or by panel to refresh
@@ -338,15 +338,15 @@
All other parameters are passed to refresh function
# Save tick as start tick for range if data is still empty
+ self.StartTick = ticks[0] # Add tick to list of ticks received
- self.Ticks = numpy.append(self.Ticks, [tick])
+ self.Ticks = numpy.append(self.Ticks, ticks) # Update start tick for range if range follow ticks received
if not self.Fixed or tick < self.StartTick + self.CurrentRange:
@@ -357,8 +357,12 @@
self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
- DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
+ self.HasNewData = False + DebugViewer.NewDataAvailable(self, ticks, *args, **kwargs) --- a/controls/DebugVariablePanel/DebugVariableItem.py Sat Oct 12 10:10:30 2013 +0900
+++ b/controls/DebugVariablePanel/DebugVariableItem.py Mon Oct 14 10:31:32 2013 +0200
@@ -232,52 +232,63 @@
return (self.Parent.IsNumType(self.VariableType) or
self.VariableType in ["STRING", "WSTRING"])
- def NewValue(self, tick, value, forced=False):
+ def NewValues(self, ticks, values, forced=False): Function called by debug thread when a new debug value is available
@param tick: PLC tick when value was captured
@param value: Value captured
@param forced: Forced flag, True if value is forced (default: False)
- DebugDataConsumer.NewValue(self, tick, value, forced, raw=None)
+ DebugDataConsumer.NewValues(self, ticks, values, forced, raw=None) if self.Data is not None:
- # String data value is CRC
- num_value = (binascii.crc32(value) & STRING_CRC_MASK
- if self.VariableType in ["STRING", "WSTRING"]
- # Update variable range values
- self.MinValue = (min(self.MinValue, num_value)
- if self.MinValue is not None
- self.MaxValue = (max(self.MaxValue, num_value)
- if self.MaxValue is not None
+ if self.VariableType in ["STRING", "WSTRING"]: + last_raw_data = (self.RawData[-1] + if len(self.RawData) > 0 else None) + last_raw_data_idx = len(self.RawData) - 1 # Translate forced flag to float for storing in Data table
forced_value = float(forced)
- # In the case of string variables, we store raw string value and
- # forced flag in raw data table. Only changes in this two values
- # are stored. Index to the corresponding raw value is stored in
- if self.VariableType in ["STRING", "WSTRING"]:
- raw_data = (value, forced_value)
- if len(self.RawData) == 0 or self.RawData[-1] != raw_data:
- extra_value = len(self.RawData)
- self.RawData.append(raw_data)
+ for tick, value in zip(ticks, values): + # String data value is CRC + num_value = (binascii.crc32(value) & STRING_CRC_MASK + if self.VariableType in ["STRING", "WSTRING"] + # Update variable range values + self.MinValue = (min(self.MinValue, num_value) + if self.MinValue is not None + self.MaxValue = (max(self.MaxValue, num_value) + if self.MaxValue is not None + # In the case of string variables, we store raw string value and + # forced flag in raw data table. Only changes in this two values + # are stored. Index to the corresponding raw value is stored in + if self.VariableType in ["STRING", "WSTRING"]: + raw_data = (value, forced_value) + if len(self.RawData) == 0 or last_raw_data != raw_data: + last_raw_data = raw_data + self.RawData.append(raw_data) + extra_value = last_raw_data_idx + # In other case, data third column is forced flag - extra_value = len(self.RawData) - 1
+ extra_value = forced_value - # In other case, data third column is forced flag
- extra_value = forced_value
+ [float(tick), num_value, extra_value]) # Add New data to stored data table
- self.Data = numpy.append(self.Data,
- [[float(tick), num_value, extra_value]], axis=0)
+ self.Data = numpy.append(self.Data, data_values, axis=0) # Signal to debug variable panel to refresh
self.Parent.HasNewData = True
--- a/editors/DebugViewer.py Sat Oct 12 10:10:30 2013 +0900
+++ b/editors/DebugViewer.py Mon Oct 14 10:31:32 2013 +0200
@@ -214,7 +214,7 @@
# Search for variable informations in project data
infos = self.DataProducer.GetInstanceInfos(iec_path)
@@ -246,7 +246,7 @@
if self.DataProducer is not None:
self.DataProducer.ReleaseDebugIECVariable(iec_path)
- def NewDataAvailable(self, tick, *args, **kwargs):
+ def NewDataAvailable(self, ticks, *args, **kwargs): Called by DataProducer for each tick captured
@param tick: PLC tick captured
--- a/editors/Viewer.py Sat Oct 12 10:10:30 2013 +0900
+++ b/editors/Viewer.py Mon Oct 14 10:31:32 2013 +0200
@@ -1053,7 +1053,7 @@
self.ElementRefreshList.append(element)
self.ElementRefreshList_lock.release()
- def RefreshNewData(self):
+ def NewDataAvailable(self, ticks, *args, **kwargs): self.ElementRefreshList_lock.acquire()
for element in self.ElementRefreshList:
@@ -1066,8 +1066,6 @@
if refresh_rect is not None:
self.RefreshRect(self.GetScrolledRect(refresh_rect), False)
- DebugViewer.RefreshNewData(self)
def SubscribeAllDataConsumers(self):
--- a/graphics/DebugDataConsumer.py Sat Oct 12 10:10:30 2013 +0900
+++ b/graphics/DebugDataConsumer.py Mon Oct 14 10:31:32 2013 +0200
@@ -197,7 +197,7 @@
self.DataType = data_type
- def NewValue(self, tick, value, forced=False, raw="BOOL"):
+ def NewValues(self, ticks, values, forced=False, raw="BOOL"): Function called by debug thread when a new debug value is available
@param tick: PLC tick when value was captured
@@ -205,6 +205,8 @@
@param forced: Forced flag, True if value is forced (default: False)
@param raw: Data type of values not translated (default: 'BOOL')
+ tick, value = ticks[-1], values[-1] # Translate value to IEC literal
value = TYPE_TRANSLATOR.get(self.DataType, str)(value)