# HG changeset patch
# User Laurent Bessard
# Date 1381746524 -7200
# Node ID debc97102b2361a695dc419e71cde9297d6dab70
# Parent  e9e17d3b284998ff30f631809a1bd4a65972ae63
Added support for optimizing debug, preventing to filling buffers with only the last value for debug data consumers that only show the current state

diff -r e9e17d3b2849 -r debc97102b23 ProjectController.py
--- a/ProjectController.py	Mon Oct 14 10:49:04 2013 +0200
+++ b/ProjectController.py	Mon Oct 14 12:28:44 2013 +0200
@@ -1202,7 +1202,7 @@
             self.IECdebug_lock.acquire()
             IECPathsToPop = []
             for IECPath,data_tuple in self.IECdebug_datas.iteritems():
-                WeakCallableDict, data_log, status, fvalue = data_tuple
+                WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple
                 if len(WeakCallableDict) == 0:
                     # Callable Dict is empty.
                     # This variable is not needed anymore!
@@ -1268,10 +1268,13 @@
                     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)
+        else:
+            IECdebug_data[4] |= buffer_list
+        
+        IECdebug_data[0][callableobj]=(buffer_list, args, kwargs)
 
         self.IECdebug_lock.release()
         
@@ -1286,6 +1289,12 @@
             IECdebug_data[0].pop(callableobj,None)
             if len(IECdebug_data[0]) == 0:
                 self.IECdebug_datas.pop(IECPath)
+            else:
+                IECdebug_data[4] = reduce(
+                    lambda x, y: x|y,
+                    [buffer_list for buffer_list,args,kwargs 
+                     in IECdebug_data[0].itervalues()],
+                    False)
         self.IECdebug_lock.release()
 
         self.ReArmDebugRegisterTimer()
@@ -1330,16 +1339,15 @@
     def CallWeakcallables(self, IECPath, function_name, *cargs):
         data_tuple = self.IECdebug_datas.get(IECPath, None)
         if data_tuple is not None:
-            WeakCallableDict, data_log, status, fvalue = data_tuple
+            WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple
             #data_log.append((debug_tick, value))
-            for weakcallable,(args,kwargs) in WeakCallableDict.iteritems():
+            for weakcallable,(buffer_list,args,kwargs) in WeakCallableDict.iteritems():
                 function = getattr(weakcallable, function_name, None)
                 if function is not None:
-                    if status == "Forced" and cargs[1] == fvalue:
-                        function(*(cargs + (True,) + args), **kwargs)
+                    if buffer_list:
+                        function(*(cargs + args), **kwargs)
                     else:
-                        function(*(cargs + args), **kwargs)
-                # This will block thread if more than one call is waiting
+                        function(*(tuple([lst[-1] for lst in cargs]) + args), **kwargs)
 
     def GetTicktime(self):
         return self._Ticktime
@@ -1365,13 +1373,19 @@
             #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.DebugValuesBuffers):
+                if (len(debug_vars) == len(self.DebugValuesBuffers) and
+                    len(debug_vars) == len(self.TracedIECPath)):
                     if debug_getvar_retry > DEBUG_RETRIES_WARN:
                         self.logger.write(_("... debugger recovered\n"))
                     debug_getvar_retry = 0
-                    for values_buffer, value in zip(self.DebugValuesBuffers, debug_vars):
-                        if value is not None:
-                            values_buffer.append(value)
+                    for IECPath, values_buffer, value in zip(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)
                 self.IECdebug_lock.release()
                 if debug_getvar_retry == DEBUG_RETRIES_WARN:
diff -r e9e17d3b2849 -r debc97102b23 controls/DebugVariablePanel/DebugVariableItem.py
--- a/controls/DebugVariablePanel/DebugVariableItem.py	Mon Oct 14 10:49:04 2013 +0200
+++ b/controls/DebugVariablePanel/DebugVariableItem.py	Mon Oct 14 12:28:44 2013 +0200
@@ -232,14 +232,14 @@
         return (self.Parent.IsNumType(self.VariableType) or 
                 self.VariableType in ["STRING", "WSTRING"])
     
-    def NewValues(self, ticks, values, forced=False):
+    def NewValues(self, ticks, values):
         """
         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.NewValues(self, ticks, values, forced, raw=None)
+        DebugDataConsumer.NewValues(self, ticks[-1], values[-1], raw=None)
         
         if self.Data is not None:
             
@@ -247,13 +247,12 @@
                 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)
             
             data_values = []
-            for tick, value in zip(ticks, values):
-            
+            for tick, (value, forced) in zip(ticks, values):
+                # Translate forced flag to float for storing in Data table
+                forced_value = float(forced)
+                
                 # String data value is CRC
                 num_value = (binascii.crc32(value) & STRING_CRC_MASK
                              if self.VariableType in ["STRING", "WSTRING"]
diff -r e9e17d3b2849 -r debc97102b23 controls/DebugVariablePanel/DebugVariablePanel.py
--- a/controls/DebugVariablePanel/DebugVariablePanel.py	Mon Oct 14 10:49:04 2013 +0200
+++ b/controls/DebugVariablePanel/DebugVariablePanel.py	Mon Oct 14 12:28:44 2013 +0200
@@ -754,7 +754,7 @@
         if idx is None:
             idx = len(self.GraphicPanels)
         item = DebugVariableItem(self, iec_path, True)
-        result = self.AddDataConsumer(iec_path.upper(), item)
+        result = self.AddDataConsumer(iec_path.upper(), item, True)
         if result is not None or force:
             
             self.Freeze()
@@ -832,7 +832,7 @@
         if source_item is None:
             item = DebugVariableItem(self, source, True)
             if item.IsNumVariable():
-                result = self.AddDataConsumer(source.upper(), item)
+                result = self.AddDataConsumer(source.upper(), item, True)
                 if result is not None or force:
                     source_item = item
         if source_item is not None and source_item.IsNumVariable():
diff -r e9e17d3b2849 -r debc97102b23 controls/DebugVariablePanel/DebugVariableViewer.py
--- a/controls/DebugVariablePanel/DebugVariableViewer.py	Mon Oct 14 10:49:04 2013 +0200
+++ b/controls/DebugVariablePanel/DebugVariableViewer.py	Mon Oct 14 12:28:44 2013 +0200
@@ -149,7 +149,7 @@
                 self.RemoveItem(item)
             else:
                 # If it exist, resubscribe and refresh data type
-                self.ParentWindow.AddDataConsumer(iec_path.upper(), item)
+                self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True)
                 item.RefreshVariableType()
     
     def ResetItemsData(self):
diff -r e9e17d3b2849 -r debc97102b23 editors/DebugViewer.py
--- a/editors/DebugViewer.py	Mon Oct 14 10:49:04 2013 +0200
+++ b/editors/DebugViewer.py	Mon Oct 14 12:28:44 2013 +0200
@@ -106,7 +106,7 @@
             
             # Subscribe tick to new data producer
             if producer is not None:
-                producer.SubscribeDebugIECVariable("__tick__", self)
+                producer.SubscribeDebugIECVariable("__tick__", self, True)
             
             # Unsubscribe tick from old data producer
             if getattr(self, "DataProducer", None) is not None:
@@ -134,7 +134,7 @@
         # Save inhibit flag
         self.Inhibited = inhibit
     
-    def AddDataConsumer(self, iec_path, consumer):
+    def AddDataConsumer(self, iec_path, consumer, buffer_list=False):
         """
         Subscribe data consumer to DataProducer
         @param iec_path: Path in PLC of variable needed by data consumer
@@ -148,7 +148,7 @@
         
         # Subscribe data consumer to DataProducer
         result = self.DataProducer.SubscribeDebugIECVariable(
-                        iec_path, consumer)
+                        iec_path, consumer, buffer_list)
         if result is not None and consumer != self:
             
             # Store data consumer if successfully subscribed and inform
@@ -178,7 +178,7 @@
         """
         # Subscribe tick if needed
         if self.SubscribeTick and self.Debug and self.DataProducer is not None:
-            self.DataProducer.SubscribeDebugIECVariable("__tick__", self)
+            self.DataProducer.SubscribeDebugIECVariable("__tick__", self, True)
     
     def UnsubscribeAllDataConsumers(self, tick=True):
         """
diff -r e9e17d3b2849 -r debc97102b23 graphics/DebugDataConsumer.py
--- a/graphics/DebugDataConsumer.py	Mon Oct 14 10:49:04 2013 +0200
+++ b/graphics/DebugDataConsumer.py	Mon Oct 14 12:28:44 2013 +0200
@@ -197,7 +197,7 @@
         """
         self.DataType = data_type
     
-    def NewValues(self, ticks, values, forced=False, raw="BOOL"):
+    def NewValues(self, tick, values, raw="BOOL"):
         """
         Function called by debug thread when a new debug value is available
         @param tick: PLC tick when value was captured
@@ -205,7 +205,7 @@
         @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]
+        value, forced = values
         
         # Translate value to IEC literal
         if self.DataType != raw: