--- a/ProjectController.py Fri Jun 11 11:56:07 2021 +0200
+++ b/ProjectController.py Mon Jun 14 16:48:39 2021 +0200
@@ -40,6 +40,7 @@
from datetime import datetime
from weakref import WeakKeyDictionary
from functools import reduce
+from itertools import izip from distutils.dir_util import copy_tree
from six.moves import xrange
@@ -1512,8 +1513,8 @@
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 zip(
+ if debug_vars is not None: + for IECPath, values_buffer, value in izip( @@ -1606,8 +1607,8 @@
WeakKeyDictionary(), # Callables
[], # Data storage [(tick, data),...]
"Registered", # Variable status
- buffer_list] # Forced value
self.IECdebug_datas[IECPath] = IECdebug_data
IECdebug_data[4] |= buffer_list
@@ -1681,27 +1682,28 @@
return self._connector.RemoteExec(script, **kwargs)
def DispatchDebugValuesProc(self, event):
- self.debug_status, debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
- 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)
+ self.debug_status, debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers() if self.debug_status == PlcStatus.Broken:
self.logger.write_warning(
_("Debug: token rejected - other debug took over - reconnect to recover\n"))
- 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)
+ 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) + delay = time.time() - start_time + next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) + if self.DispatchDebugValuesTimer is not None: + res = self.DispatchDebugValuesTimer.Start( + int(next_refresh * 1000), oneShot=True) def KillDebugThread(self):
if self.DispatchDebugValuesTimer is not None:
--- a/controls/DebugVariablePanel/DebugVariableItem.py Fri Jun 11 11:56:07 2021 +0200
+++ b/controls/DebugVariablePanel/DebugVariableItem.py Mon Jun 14 16:48:39 2021 +0200
@@ -26,8 +26,9 @@
from __future__ import absolute_import
from datetime import timedelta
from graphics.DebugDataConsumer import DebugDataConsumer, TYPE_TRANSLATOR
+from controls.DebugVariablePanel.RingBuffer import RingBuffer # -------------------------------------------------------------------------------
# Constant for calculate CRC for string variables
@@ -142,8 +143,8 @@
@return: Data as numpy.array([(tick, value, forced),...])
# Return immediately if data empty or none
- if self.Data is None or len(self.Data) == 0:
+ if self.Data is None or self.Data.count == 0: # Find nearest data outside given range indexes
start_idx = (self.GetNearestData(start_tick, -1)
@@ -154,7 +155,7 @@
# Return data between indexes
- return self.Data[start_idx:end_idx]
+ return self.Data.view[start_idx:end_idx] def GetRawValue(self, index):
@@ -195,8 +196,8 @@
# Return value range for data in given tick range
- data[numpy.argmin(values), 1],
- data[numpy.argmax(values), 1])
+ data[np.argmin(values), 1], + data[np.argmax(values), 1]) @@ -207,7 +208,7 @@
if self.StoreData and self.IsNumVariable():
# Init table storing data
- self.Data = numpy.array([]).reshape(0, 3)
+ self.Data = RingBuffer(3) # Init table storing raw data if variable is strin
@@ -294,7 +295,7 @@
[float(tick), num_value, extra_value])
# Add New data to stored data table
- self.Data = numpy.append(self.Data, data_values, axis=0)
+ self.Data.append(data_values) # Signal to debug variable panel to refresh
self.Parent.HasNewData = True
@@ -337,7 +338,7 @@
if tick is not None and self.Data is not None:
# Return current value and forced flag if data empty
- if len(self.Data) == 0:
+ if self.Data.count == 0: return self.Value, self.IsForced()
# Get index of nearest data from tick given
@@ -345,9 +346,9 @@
# Get value and forced flag at given index
- self.RawData[int(self.Data[idx, 2])] \
+ self.RawData[int(self.Data.view[idx, 2])] \ if self.VariableType in ["STRING", "WSTRING"] \
- else self.Data[idx, 1:3]
+ else self.Data.view[idx, 1:3] if self.VariableType in ["TIME", "TOD", "DT", "DATE"]:
value = timedelta(seconds=value)
@@ -380,10 +381,10 @@
- ticks = self.Data[:, 0]
+ ticks = self.Data.view[:, 0] # Get nearest data from tick
- idx = numpy.argmin(abs(ticks - tick))
+ idx = np.searchsorted(ticks, tick) # Adjust data index according to constraint
if adjust < 0 and ticks[idx] > tick and idx > 0 or \
--- a/controls/DebugVariablePanel/DebugVariablePanel.py Fri Jun 11 11:56:07 2021 +0200
+++ b/controls/DebugVariablePanel/DebugVariablePanel.py Mon Jun 14 16:48:39 2021 +0200
@@ -26,7 +26,7 @@
from __future__ import absolute_import
from __future__ import division
from functools import reduce
@@ -43,6 +43,7 @@
from controls.DebugVariablePanel.DebugVariableItem import DebugVariableItem
from controls.DebugVariablePanel.DebugVariableTextViewer import DebugVariableTextViewer
from controls.DebugVariablePanel.DebugVariableGraphicViewer import *
+from controls.DebugVariablePanel.RingBuffer import RingBuffer MILLISECOND = 1000000 # Number of nanosecond in a millisecond
@@ -205,7 +206,7 @@
main_sizer = wx.BoxSizer(wx.VERTICAL)
- self.Ticks = numpy.array([]) # List of tick received
+ self.Ticks = RingBuffer() # List of tick received self.StartTick = 0 # Tick starting range of data displayed
self.Fixed = False # Flag that range of data is fixed
self.CursorTick = None # Tick of cursor for displaying values
@@ -344,11 +345,11 @@
# Save tick as start tick for range if data is still empty
- if len(self.Ticks) == 0:
+ if self.Ticks.count == 0: self.StartTick = ticks[0]
# Add tick to list of ticks received
- self.Ticks = numpy.append(self.Ticks, ticks)
+ self.Ticks.append(ticks) # Update start tick for range if range follow ticks received
if not self.Fixed or tick < self.StartTick + self.CurrentRange:
@@ -357,7 +358,7 @@
# Force refresh if graph is fixed because range of data received
# is too small to fill data range selected
- self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
+ self.Ticks.view[-1] - self.Ticks.view[0] < self.CurrentRange: @@ -385,17 +386,16 @@
def MoveCursorTick(self, move):
if self.CursorTick is not None:
- cursor_tick = max(self.Ticks[0],
- min(self.CursorTick + move, self.Ticks[-1]))
- cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick))
- if self.Ticks[cursor_tick_idx] == self.CursorTick:
+ cursor_tick = max(self.Ticks.view[0], + min(self.CursorTick + move, self.Ticks.view[-1])) + cursor_tick_idx = np.searchsorted(self.Ticks.view, cursor_tick) + if self.Ticks.view[cursor_tick_idx] == self.CursorTick: min(cursor_tick_idx + abs(move) // move,
- self.CursorTick = self.Ticks[cursor_tick_idx]
+ self.CursorTick = self.Ticks.view[cursor_tick_idx] - self.Ticks[numpy.argmin(
- numpy.abs(self.Ticks - self.CursorTick + self.CurrentRange))],
+ self.Ticks.view[np.searchsorted(self.Ticks.view, self.CursorTick + self.CurrentRange)], min(self.StartTick, self.CursorTick))
self.RefreshCanvasPosition()
@@ -547,8 +547,8 @@
if self.CursorTick is not None:
- elif len(self.Ticks) > 0:
+ elif self.Ticks.count > 0: + tick = self.Ticks.view[-1] @@ -604,16 +604,16 @@
self.RefreshGraphicsSizer()
def SetCanvasPosition(self, tick):
- tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange))
- self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))]
+ tick = max(self.Ticks.view[0], min(tick, self.Ticks.view[-1] - self.CurrentRange)) + self.StartTick = self.Ticks.view[np.searchsorted(self.Ticks.view, tick)] self.RefreshCanvasPosition()
def RefreshCanvasPosition(self):
- if len(self.Ticks) > 0:
- pos = int(self.StartTick - self.Ticks[0])
- range = int(self.Ticks[-1] - self.Ticks[0])
+ if len(self.Ticks.view) > 0: + pos = int(self.StartTick - self.Ticks.view[0]) + range = int(self.Ticks.view[-1] - self.Ticks.view[0]) @@ -626,23 +626,23 @@
if new_range_idx != current_range_idx:
self.CanvasRange.SetSelection(new_range_idx)
self.CurrentRange = self.RANGE_VALUES[new_range_idx][1] / self.Ticktime
- if len(self.Ticks) > 0:
+ if self.Ticks.count > 0: tick = self.StartTick + self.CurrentRange / 2.
new_start_tick = min(tick - (tick - self.StartTick) * self.CurrentRange / current_range,
- self.Ticks[-1] - self.CurrentRange)
- self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))]
- self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange
+ self.Ticks.view[-1] - self.CurrentRange) + self.StartTick = self.Ticks.view[np.searchsorted(self.Ticks.view, - new_start_tick)] + self.Fixed = new_start_tick < self.Ticks.view[-1] - self.CurrentRange - if len(self.Ticks) > 0:
- if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
+ if self.Ticks.count > 0: + if self.Fixed and self.Ticks.view[-1] - self.Ticks.view[0] < self.CurrentRange: - self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
+ self.StartTick = min(self.StartTick, self.Ticks.view[-1] - self.CurrentRange) - self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
+ self.StartTick = max(self.Ticks.view[0], self.Ticks.view[-1] - self.CurrentRange) def OnRangeChanged(self, event):
@@ -654,8 +654,8 @@
def OnCurrentButton(self, event):
- if len(self.Ticks) > 0:
- self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
+ if self.Ticks.count > 0: + self.StartTick = max(self.Ticks.view[0], self.Ticks.view[-1] - self.CurrentRange) @@ -695,8 +695,8 @@
def OnPositionChanging(self, event):
- if len(self.Ticks) > 0:
- self.StartTick = self.Ticks[0] + event.GetPosition()
+ if self.Ticks.count > 0: + self.StartTick = self.Ticks.view[0] + event.GetPosition() @@ -908,7 +908,7 @@
def ResetGraphicsValues(self):
- self.Ticks = numpy.array([])
+ self.Ticks = RingBuffer() for panel in self.GraphicPanels:
--- a/runtime/typemapping.py Fri Jun 11 11:56:07 2021 +0200
+++ b/runtime/typemapping.py Mon Jun 14 16:48:39 2021 +0200
@@ -92,7 +92,7 @@
buffoffset += sizeof(c_type) if iectype != "STRING" else len(value)+1
if buffoffset and buffoffset == buffsize: