controls/DebugVariablePanel/DebugVariableItem.py
changeset 1730 64d8f52bc8c8
parent 1571 486f94a8032c
child 1736 7e61baa047f0
equal deleted inserted replaced
1726:d51af006fa6b 1730:64d8f52bc8c8
    42 Class that implements an element that consumes debug values for PLC variable and
    42 Class that implements an element that consumes debug values for PLC variable and
    43 stores received values for displaying them in graphic panel or table
    43 stores received values for displaying them in graphic panel or table
    44 """
    44 """
    45 
    45 
    46 class DebugVariableItem(DebugDataConsumer):
    46 class DebugVariableItem(DebugDataConsumer):
    47     
    47 
    48     def __init__(self, parent, variable, store_data=False):
    48     def __init__(self, parent, variable, store_data=False):
    49         """
    49         """
    50         Constructor
    50         Constructor
    51         @param parent: Reference to debug variable panel
    51         @param parent: Reference to debug variable panel
    52         @param variable: Path of variable to debug
    52         @param variable: Path of variable to debug
    53         """
    53         """
    54         DebugDataConsumer.__init__(self)
    54         DebugDataConsumer.__init__(self)
    55         
    55 
    56         self.Parent = parent
    56         self.Parent = parent
    57         self.Variable = variable
    57         self.Variable = variable
    58         self.StoreData = store_data
    58         self.StoreData = store_data
    59         
    59 
    60         # Get Variable data type
    60         # Get Variable data type
    61         self.RefreshVariableType()
    61         self.RefreshVariableType()
    62         
    62 
    63     def __del__(self):
    63     def __del__(self):
    64         """
    64         """
    65         Destructor
    65         Destructor
    66         """
    66         """
    67         # Reset reference to debug variable panel
    67         # Reset reference to debug variable panel
    68         self.Parent = None
    68         self.Parent = None
    69     
    69 
    70     def SetVariable(self, variable):
    70     def SetVariable(self, variable):
    71         """
    71         """
    72         Set path of variable 
    72         Set path of variable
    73         @param variable: Path of variable to debug
    73         @param variable: Path of variable to debug
    74         """
    74         """
    75         if self.Parent is not None and self.Variable != variable:
    75         if self.Parent is not None and self.Variable != variable:
    76             # Store variable path
    76             # Store variable path
    77             self.Variable = variable
    77             self.Variable = variable
    78             # Get Variable data type
    78             # Get Variable data type
    79             self.RefreshVariableType()
    79             self.RefreshVariableType()
    80             
    80 
    81             # Refresh debug variable panel
    81             # Refresh debug variable panel
    82             self.Parent.RefreshView()
    82             self.Parent.RefreshView()
    83     
    83 
    84     def GetVariable(self, mask=None):
    84     def GetVariable(self, mask=None):
    85         """
    85         """
    86         Return path of variable 
    86         Return path of variable
    87         @param mask: Mask to apply to variable path [var_name, '*',...]
    87         @param mask: Mask to apply to variable path [var_name, '*',...]
    88         @return: String containing masked variable path
    88         @return: String containing masked variable path
    89         """
    89         """
    90         # Apply mask to variable name
    90         # Apply mask to variable name
    91         if mask is not None:
    91         if mask is not None:
    92             # '#' correspond to parts that are different between all items
    92             # '#' correspond to parts that are different between all items
    93             
    93 
    94             # Extract variable path parts
    94             # Extract variable path parts
    95             parts = self.Variable.split('.')
    95             parts = self.Variable.split('.')
    96             # Adjust mask size to size of variable path
    96             # Adjust mask size to size of variable path
    97             mask = mask + ['*'] * max(0, len(parts) - len(mask))
    97             mask = mask + ['*'] * max(0, len(parts) - len(mask))
    98             
    98 
    99             # Store previous mask
    99             # Store previous mask
   100             last = None
   100             last = None
   101             # Init masked variable path
   101             # Init masked variable path
   102             variable = ""
   102             variable = ""
   103             
   103 
   104             for m, p in zip(mask, parts):
   104             for m, p in zip(mask, parts):
   105                 # Part is not masked, add part prefixed with '.' is previous
   105                 # Part is not masked, add part prefixed with '.' is previous
   106                 # wasn't masked
   106                 # wasn't masked
   107                 if m == '*':
   107                 if m == '*':
   108                     variable += ('.' if last == '*' else '') + p
   108                     variable += ('.' if last == '*' else '') + p
   109                 
   109 
   110                 # Part is mask, add '..' if first or previous wasn't masked
   110                 # Part is mask, add '..' if first or previous wasn't masked
   111                 elif last is None or last == '*':
   111                 elif last is None or last == '*':
   112                     variable += '..'
   112                     variable += '..'
   113                 
   113 
   114                 last = m
   114                 last = m
   115             
   115 
   116             return variable
   116             return variable
   117         
   117 
   118         return self.Variable
   118         return self.Variable
   119     
   119 
   120     def RefreshVariableType(self):
   120     def RefreshVariableType(self):
   121         """
   121         """
   122         Get and store variable data type
   122         Get and store variable data type
   123         """
   123         """
   124         self.VariableType = self.Parent.GetDataType(self.Variable)
   124         self.VariableType = self.Parent.GetDataType(self.Variable)
   125         # Reset data stored
   125         # Reset data stored
   126         self.ResetData()
   126         self.ResetData()
   127     
   127 
   128     def GetVariableType(self):
   128     def GetVariableType(self):
   129         """
   129         """
   130         Return variable data type
   130         Return variable data type
   131         @return: Variable data type
   131         @return: Variable data type
   132         """
   132         """
   133         return self.VariableType
   133         return self.VariableType
   134     
   134 
   135     def GetData(self, start_tick=None, end_tick=None):
   135     def GetData(self, start_tick=None, end_tick=None):
   136         """
   136         """
   137         Return data stored contained in given range
   137         Return data stored contained in given range
   138         @param start_tick: Start tick of given range (default None, first data)
   138         @param start_tick: Start tick of given range (default None, first data)
   139         @param end_tick: end tick of given range (default None, last data)
   139         @param end_tick: end tick of given range (default None, last data)
   140         @return: Data as numpy.array([(tick, value, forced),...])
   140         @return: Data as numpy.array([(tick, value, forced),...])
   141         """
   141         """
   142         # Return immediately if data empty or none
   142         # Return immediately if data empty or none
   143         if self.Data is None or len(self.Data) == 0:
   143         if self.Data is None or len(self.Data) == 0:
   144             return self.Data
   144             return self.Data
   145         
   145 
   146         # Find nearest data outside given range indexes
   146         # Find nearest data outside given range indexes
   147         start_idx = (self.GetNearestData(start_tick, -1)
   147         start_idx = (self.GetNearestData(start_tick, -1)
   148                      if start_tick is not None
   148                      if start_tick is not None
   149                      else 0)
   149                      else 0)
   150         end_idx = (self.GetNearestData(end_tick, 1)
   150         end_idx = (self.GetNearestData(end_tick, 1)
   151                    if end_tick is not None
   151                    if end_tick is not None
   152                    else len(self.Data))
   152                    else len(self.Data))
   153         
   153 
   154         # Return data between indexes
   154         # Return data between indexes
   155         return self.Data[start_idx:end_idx]
   155         return self.Data[start_idx:end_idx]
   156     
   156 
   157     def GetRawValue(self, index):
   157     def GetRawValue(self, index):
   158         """
   158         """
   159         Return raw value at given index for string variables
   159         Return raw value at given index for string variables
   160         @param index: Variable value index
   160         @param index: Variable value index
   161         @return: Variable data type
   161         @return: Variable data type
   162         """
   162         """
   163         if (self.VariableType in ["STRING", "WSTRING"] and
   163         if (self.VariableType in ["STRING", "WSTRING"] and
   164             index < len(self.RawData)):
   164             index < len(self.RawData)):
   165             return self.RawData[index][0]
   165             return self.RawData[index][0]
   166         return ""
   166         return ""
   167     
   167 
   168     def GetValueRange(self):
   168     def GetValueRange(self):
   169         """
   169         """
   170         Return variable value range
   170         Return variable value range
   171         @return: (minimum_value, maximum_value)
   171         @return: (minimum_value, maximum_value)
   172         """
   172         """
   173         return self.MinValue, self.MaxValue
   173         return self.MinValue, self.MaxValue
   174     
   174 
   175     def GetDataAndValueRange(self, start_tick, end_tick, full_range=True):
   175     def GetDataAndValueRange(self, start_tick, end_tick, full_range=True):
   176         """
   176         """
   177         Return variable data and value range for a given tick range
   177         Return variable data and value range for a given tick range
   178         @param start_tick: Start tick of given range (default None, first data)
   178         @param start_tick: Start tick of given range (default None, first data)
   179         @param end_tick: end tick of given range (default None, last data)
   179         @param end_tick: end tick of given range (default None, last data)
   180         @param full_range: Value range is calculated on whole data (False: only
   180         @param full_range: Value range is calculated on whole data (False: only
   181         calculated on data in given range)
   181         calculated on data in given range)
   182         @return: (numpy.array([(tick, value, forced),...]), 
   182         @return: (numpy.array([(tick, value, forced),...]),
   183                   min_value, max_value)
   183                   min_value, max_value)
   184         """
   184         """
   185         # Get data in given tick range
   185         # Get data in given tick range
   186         data = self.GetData(start_tick, end_tick)
   186         data = self.GetData(start_tick, end_tick)
   187         
   187 
   188         # Value range is calculated on whole data
   188         # Value range is calculated on whole data
   189         if full_range:
   189         if full_range:
   190             return data, self.MinValue, self.MaxValue
   190             return data, self.MinValue, self.MaxValue
   191         
   191 
   192         # Check that data in given range is not empty
   192         # Check that data in given range is not empty
   193         values = data[:, 1]
   193         values = data[:, 1]
   194         if len(values) > 0:
   194         if len(values) > 0:
   195             # Return value range for data in given tick range
   195             # Return value range for data in given tick range
   196             return (data,
   196             return (data,
   197                     data[numpy.argmin(values), 1],
   197                     data[numpy.argmin(values), 1],
   198                     data[numpy.argmax(values), 1])
   198                     data[numpy.argmax(values), 1])
   199         
   199 
   200         # Return default values
   200         # Return default values
   201         return data, None, None
   201         return data, None, None
   202     
   202 
   203     def ResetData(self):
   203     def ResetData(self):
   204         """
   204         """
   205         Reset data stored when store data option enabled
   205         Reset data stored when store data option enabled
   206         """
   206         """
   207         if self.StoreData and self.IsNumVariable():
   207         if self.StoreData and self.IsNumVariable():
   208             # Init table storing data
   208             # Init table storing data
   209             self.Data = numpy.array([]).reshape(0, 3)
   209             self.Data = numpy.array([]).reshape(0, 3)
   210             
   210 
   211             # Init table storing raw data if variable is strin
   211             # Init table storing raw data if variable is strin
   212             self.RawData = ([]
   212             self.RawData = ([]
   213                             if self.VariableType in ["STRING", "WSTRING"]
   213                             if self.VariableType in ["STRING", "WSTRING"]
   214                             else None)
   214                             else None)
   215                 
   215 
   216             # Init Value range variables
   216             # Init Value range variables
   217             self.MinValue = None
   217             self.MinValue = None
   218             self.MaxValue = None
   218             self.MaxValue = None
   219         
   219 
   220         else:
   220         else:
   221             self.Data = None
   221             self.Data = None
   222         
   222 
   223         # Init variable value
   223         # Init variable value
   224         self.Value = ""
   224         self.Value = ""
   225     
   225 
   226     def IsNumVariable(self):
   226     def IsNumVariable(self):
   227         """
   227         """
   228         Return if variable data type is numeric. String variables are
   228         Return if variable data type is numeric. String variables are
   229         considered as numeric (string CRC)
   229         considered as numeric (string CRC)
   230         @return: True if data type is numeric
   230         @return: True if data type is numeric
   231         """
   231         """
   232         return (self.Parent.IsNumType(self.VariableType) or 
   232         return (self.Parent.IsNumType(self.VariableType) or
   233                 self.VariableType in ["STRING", "WSTRING"])
   233                 self.VariableType in ["STRING", "WSTRING"])
   234     
   234 
   235     def NewValues(self, ticks, values):
   235     def NewValues(self, ticks, values):
   236         """
   236         """
   237         Function called by debug thread when a new debug value is available
   237         Function called by debug thread when a new debug value is available
   238         @param tick: PLC tick when value was captured
   238         @param tick: PLC tick when value was captured
   239         @param value: Value captured
   239         @param value: Value captured
   240         @param forced: Forced flag, True if value is forced (default: False)
   240         @param forced: Forced flag, True if value is forced (default: False)
   241         """
   241         """
   242         DebugDataConsumer.NewValues(self, ticks[-1], values[-1], raw=None)
   242         DebugDataConsumer.NewValues(self, ticks[-1], values[-1], raw=None)
   243         
   243 
   244         if self.Data is not None:
   244         if self.Data is not None:
   245             
   245 
   246             if self.VariableType in ["STRING", "WSTRING"]:
   246             if self.VariableType in ["STRING", "WSTRING"]:
   247                 last_raw_data = (self.RawData[-1]
   247                 last_raw_data = (self.RawData[-1]
   248                                  if len(self.RawData) > 0 else None)
   248                                  if len(self.RawData) > 0 else None)
   249                 last_raw_data_idx = len(self.RawData) - 1
   249                 last_raw_data_idx = len(self.RawData) - 1
   250             
   250 
   251             data_values = []
   251             data_values = []
   252             for tick, (value, forced) in zip(ticks, values):
   252             for tick, (value, forced) in zip(ticks, values):
   253                 # Translate forced flag to float for storing in Data table
   253                 # Translate forced flag to float for storing in Data table
   254                 forced_value = float(forced)
   254                 forced_value = float(forced)
   255                 
   255 
   256                 # String data value is CRC
   256                 # String data value is CRC
   257                 num_value = (binascii.crc32(value) & STRING_CRC_MASK
   257                 num_value = (binascii.crc32(value) & STRING_CRC_MASK
   258                              if self.VariableType in ["STRING", "WSTRING"]
   258                              if self.VariableType in ["STRING", "WSTRING"]
   259                              else float(value))
   259                              else float(value))
   260             
   260 
   261                 # Update variable range values
   261                 # Update variable range values
   262                 self.MinValue = (min(self.MinValue, num_value)
   262                 self.MinValue = (min(self.MinValue, num_value)
   263                                  if self.MinValue is not None
   263                                  if self.MinValue is not None
   264                                  else num_value)
   264                                  else num_value)
   265                 self.MaxValue = (max(self.MaxValue, num_value)
   265                 self.MaxValue = (max(self.MaxValue, num_value)
   266                                  if self.MaxValue is not None
   266                                  if self.MaxValue is not None
   267                                  else num_value)
   267                                  else num_value)
   268             
   268 
   269                 # In the case of string variables, we store raw string value and
   269                 # In the case of string variables, we store raw string value and
   270                 # forced flag in raw data table. Only changes in this two values
   270                 # forced flag in raw data table. Only changes in this two values
   271                 # are stored. Index to the corresponding raw value is stored in 
   271                 # are stored. Index to the corresponding raw value is stored in
   272                 # data third column
   272                 # data third column
   273                 if self.VariableType in ["STRING", "WSTRING"]:
   273                 if self.VariableType in ["STRING", "WSTRING"]:
   274                     raw_data = (value, forced_value)
   274                     raw_data = (value, forced_value)
   275                     if len(self.RawData) == 0 or last_raw_data != raw_data:
   275                     if len(self.RawData) == 0 or last_raw_data != raw_data:
   276                         last_raw_data_idx += 1
   276                         last_raw_data_idx += 1
   277                         last_raw_data = raw_data
   277                         last_raw_data = raw_data
   278                         self.RawData.append(raw_data)
   278                         self.RawData.append(raw_data)
   279                     extra_value = last_raw_data_idx
   279                     extra_value = last_raw_data_idx
   280                 
   280 
   281                 # In other case, data third column is forced flag
   281                 # In other case, data third column is forced flag
   282                 else:
   282                 else:
   283                     extra_value = forced_value
   283                     extra_value = forced_value
   284             
   284 
   285                 data_values.append(
   285                 data_values.append(
   286                     [float(tick), num_value, extra_value])
   286                     [float(tick), num_value, extra_value])
   287             
   287 
   288             # Add New data to stored data table
   288             # Add New data to stored data table
   289             self.Data = numpy.append(self.Data, data_values, axis=0)
   289             self.Data = numpy.append(self.Data, data_values, axis=0)
   290             
   290 
   291             # Signal to debug variable panel to refresh
   291             # Signal to debug variable panel to refresh
   292             self.Parent.HasNewData = True
   292             self.Parent.HasNewData = True
   293         
   293 
   294     def SetForced(self, forced):
   294     def SetForced(self, forced):
   295         """
   295         """
   296         Update Forced flag
   296         Update Forced flag
   297         @param forced: New forced flag
   297         @param forced: New forced flag
   298         """
   298         """
   299         # Store forced flag
   299         # Store forced flag
   300         if self.Forced != forced:
   300         if self.Forced != forced:
   301             self.Forced = forced
   301             self.Forced = forced
   302             
   302 
   303             # Signal to debug variable panel to refresh
   303             # Signal to debug variable panel to refresh
   304             self.Parent.HasNewData = True
   304             self.Parent.HasNewData = True
   305     
   305 
   306     def SetValue(self, value):
   306     def SetValue(self, value):
   307         """
   307         """
   308         Update value.
   308         Update value.
   309         @param value: New value
   309         @param value: New value
   310         """
   310         """
   312         if (self.VariableType == "STRING" and
   312         if (self.VariableType == "STRING" and
   313             value.startswith("'") and value.endswith("'") or
   313             value.startswith("'") and value.endswith("'") or
   314             self.VariableType == "WSTRING" and
   314             self.VariableType == "WSTRING" and
   315             value.startswith('"') and value.endswith('"')):
   315             value.startswith('"') and value.endswith('"')):
   316             value = value[1:-1]
   316             value = value[1:-1]
   317         
   317 
   318         # Store variable value
   318         # Store variable value
   319         if self.Value != value:
   319         if self.Value != value:
   320             self.Value = value
   320             self.Value = value
   321             
   321 
   322             # Signal to debug variable panel to refresh
   322             # Signal to debug variable panel to refresh
   323             self.Parent.HasNewData = True
   323             self.Parent.HasNewData = True
   324     
   324 
   325     def GetValue(self, tick=None, raw=False):
   325     def GetValue(self, tick=None, raw=False):
   326         """
   326         """
   327         Return current value or value and forced flag for tick given
   327         Return current value or value and forced flag for tick given
   328         @return: Current value or value and forced flag
   328         @return: Current value or value and forced flag
   329         """
   329         """
   330         # If tick given and stored data option enabled
   330         # If tick given and stored data option enabled
   331         if tick is not None and self.Data is not None:
   331         if tick is not None and self.Data is not None:
   332             
   332 
   333             # Return current value and forced flag if data empty
   333             # Return current value and forced flag if data empty
   334             if len(self.Data) == 0:
   334             if len(self.Data) == 0:
   335                 return self.Value, self.IsForced()
   335                 return self.Value, self.IsForced()
   336             
   336 
   337             # Get index of nearest data from tick given
   337             # Get index of nearest data from tick given
   338             idx = self.GetNearestData(tick, 0)
   338             idx = self.GetNearestData(tick, 0)
   339             
   339 
   340             # Get value and forced flag at given index
   340             # Get value and forced flag at given index
   341             value, forced = self.RawData[int(self.Data[idx, 2])] \
   341             value, forced = self.RawData[int(self.Data[idx, 2])] \
   342                             if self.VariableType in ["STRING", "WSTRING"] \
   342                             if self.VariableType in ["STRING", "WSTRING"] \
   343                             else self.Data[idx, 1:3]
   343                             else self.Data[idx, 1:3]
   344             
   344 
   345             # Get raw value if asked
   345             # Get raw value if asked
   346             if not raw:
   346             if not raw:
   347                 value = TYPE_TRANSLATOR.get(
   347                 value = TYPE_TRANSLATOR.get(
   348                         self.VariableType, str)(value)
   348                         self.VariableType, str)(value)
   349             
   349 
   350             return value, forced
   350             return value, forced
   351             
   351 
   352         # Return raw value if asked
   352         # Return raw value if asked
   353         if not raw and self.VariableType in ["STRING", "WSTRING"]:
   353         if not raw and self.VariableType in ["STRING", "WSTRING"]:
   354             return TYPE_TRANSLATOR.get(
   354             return TYPE_TRANSLATOR.get(
   355                     self.VariableType, str)(self.Value)
   355                     self.VariableType, str)(self.Value)
   356         return self.Value
   356         return self.Value
   366         @return: Index of nearest data
   366         @return: Index of nearest data
   367         """
   367         """
   368         # Return immediately if data is empty
   368         # Return immediately if data is empty
   369         if self.Data is None:
   369         if self.Data is None:
   370             return None
   370             return None
   371         
   371 
   372         # Extract data ticks
   372         # Extract data ticks
   373         ticks = self.Data[:, 0]
   373         ticks = self.Data[:, 0]
   374         
   374 
   375         # Get nearest data from tick
   375         # Get nearest data from tick
   376         idx = numpy.argmin(abs(ticks - tick))
   376         idx = numpy.argmin(abs(ticks - tick))
   377         
   377 
   378         # Adjust data index according to constraint
   378         # Adjust data index according to constraint
   379         if (adjust < 0 and ticks[idx] > tick and idx > 0 or
   379         if (adjust < 0 and ticks[idx] > tick and idx > 0 or
   380             adjust > 0 and ticks[idx] < tick and idx < len(ticks)):
   380             adjust > 0 and ticks[idx] < tick and idx < len(ticks)):
   381             idx += adjust
   381             idx += adjust
   382         
   382 
   383         return idx
   383         return idx