controls/DebugVariablePanel/DebugVariableItem.py
changeset 1193 59c196884fec
parent 1192 d8783c0c7d80
child 1214 2ef048b5383c
equal deleted inserted replaced
1192:d8783c0c7d80 1193:59c196884fec
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import numpy
       
    26 import binascii
       
    27 
       
    28 from graphics.DebugDataConsumer import DebugDataConsumer, TYPE_TRANSLATOR
       
    29 
       
    30 #-------------------------------------------------------------------------------
       
    31 #                 Constant for calculate CRC for string variables
       
    32 #-------------------------------------------------------------------------------
       
    33 
       
    34 STRING_CRC_SIZE = 8
       
    35 STRING_CRC_MASK = 2 ** STRING_CRC_SIZE - 1
       
    36 
       
    37 #-------------------------------------------------------------------------------
       
    38 #                          Debug Variable Item Class
       
    39 #-------------------------------------------------------------------------------
       
    40 
       
    41 """
       
    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
       
    44 """
       
    45 
       
    46 class DebugVariableItem(DebugDataConsumer):
       
    47     
       
    48     def __init__(self, parent, variable, store_data=False):
       
    49         """
       
    50         Constructor
       
    51         @param parent: Reference to debug variable panel
       
    52         @param variable: Path of variable to debug
       
    53         """
       
    54         DebugDataConsumer.__init__(self)
       
    55         
       
    56         self.Parent = parent
       
    57         self.Variable = variable
       
    58         self.StoreData = store_data
       
    59         
       
    60         # Get Variable data type
       
    61         self.RefreshVariableType()
       
    62         
       
    63     def __del__(self):
       
    64         """
       
    65         Destructor
       
    66         """
       
    67         # Reset reference to debug variable panel
       
    68         self.Parent = None
       
    69     
       
    70     def SetVariable(self, variable):
       
    71         """
       
    72         Set path of variable 
       
    73         @param variable: Path of variable to debug
       
    74         """
       
    75         if self.Parent is not None and self.Variable != variable:
       
    76             # Store variable path
       
    77             self.Variable = variable
       
    78             # Get Variable data type
       
    79             self.RefreshVariableType()
       
    80             
       
    81             # Refresh debug variable panel
       
    82             self.Parent.RefreshView()
       
    83     
       
    84     def GetVariable(self, mask=None):
       
    85         """
       
    86         Return path of variable 
       
    87         @param mask: Mask to apply to variable path [var_name, '*',...]
       
    88         @return: String containing masked variable path
       
    89         """
       
    90         # Apply mask to variable name
       
    91         if mask is not None:
       
    92             # '#' correspond to parts that are different between all items
       
    93             
       
    94             # Extract variable path parts
       
    95             parts = self.Variable.split('.')
       
    96             # Adjust mask size to size of variable path
       
    97             mask = mask + ['*'] * max(0, len(parts) - len(mask))
       
    98             
       
    99             # Store previous mask
       
   100             last = None
       
   101             # Init masked variable path
       
   102             variable = ""
       
   103             
       
   104             for m, p in zip(mask, parts):
       
   105                 # Part is not masked, add part prefixed with '.' is previous
       
   106                 # wasn't masked
       
   107                 if m == '*':
       
   108                     variable += ('.' if last == '*' else '') + p
       
   109                 
       
   110                 # Part is mask, add '..' if first or previous wasn't masked
       
   111                 elif last is None or last == '*':
       
   112                     variable += '..'
       
   113                 
       
   114                 last = m
       
   115             
       
   116             return variable
       
   117         
       
   118         return self.Variable
       
   119     
       
   120     def RefreshVariableType(self):
       
   121         """
       
   122         Get and store variable data type
       
   123         """
       
   124         self.VariableType = self.Parent.GetDataType(self.Variable)
       
   125         # Reset data stored
       
   126         self.ResetData()
       
   127     
       
   128     def GetVariableType(self):
       
   129         """
       
   130         Return variable data type
       
   131         @return: Variable data type
       
   132         """
       
   133         return self.VariableType
       
   134     
       
   135     def GetData(self, start_tick=None, end_tick=None):
       
   136         """
       
   137         Return data stored contained in given range
       
   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)
       
   140         @return: Data as numpy.array([(tick, value, forced),...])
       
   141         """
       
   142         # Return immediately if data empty or none
       
   143         if self.Data is None or len(self.Data) == 0:
       
   144             return self.Data
       
   145         
       
   146         # Find nearest data outside given range indexes
       
   147         start_idx = (self.GetNearestData(start_tick, -1)
       
   148                      if start_tick is not None
       
   149                      else 0)
       
   150         end_idx = (self.GetNearestData(end_tick, 1)
       
   151                    if end_tick is not None
       
   152                    else len(self.Data))
       
   153         
       
   154         # Return data between indexes
       
   155         return self.Data[start_idx:end_idx]
       
   156     
       
   157     def GetRawValue(self, index):
       
   158         """
       
   159         Return raw value at given index for string variables
       
   160         @param index: Variable value index
       
   161         @return: Variable data type
       
   162         """
       
   163         if (self.VariableType in ["STRING", "WSTRING"] and
       
   164             index < len(self.RawData)):
       
   165             return self.RawData[idx][0]
       
   166         return ""
       
   167     
       
   168     def GetValueRange(self):
       
   169         """
       
   170         Return variable value range
       
   171         @return: (minimum_value, maximum_value)
       
   172         """
       
   173         return self.MinValue, self.MaxValue
       
   174     
       
   175     def ResetData(self):
       
   176         """
       
   177         Reset data stored when store data option enabled
       
   178         """
       
   179         if self.StoreData and self.IsNumVariable():
       
   180             # Init table storing data
       
   181             self.Data = numpy.array([]).reshape(0, 3)
       
   182             
       
   183             # Init table storing raw data if variable is strin
       
   184             self.RawData = ([]
       
   185                             if self.VariableType in ["STRING", "WSTRING"]
       
   186                             else None)
       
   187                 
       
   188             # Init Value range variables
       
   189             self.MinValue = None
       
   190             self.MaxValue = None
       
   191         
       
   192         else:
       
   193             self.Data = None
       
   194         
       
   195         # Init variable value
       
   196         self.Value = ""
       
   197     
       
   198     def IsNumVariable(self):
       
   199         """
       
   200         Return if variable data type is numeric. String variables are
       
   201         considered as numeric (string CRC)
       
   202         @return: True if data type is numeric
       
   203         """
       
   204         return (self.Parent.IsNumType(self.VariableType) or 
       
   205                 self.VariableType in ["STRING", "WSTRING"])
       
   206     
       
   207     def NewValue(self, tick, value, forced=False):
       
   208         """
       
   209         Function called by debug thread when a new debug value is available
       
   210         @param tick: PLC tick when value was captured
       
   211         @param value: Value captured
       
   212         @param forced: Forced flag, True if value is forced (default: False)
       
   213         """
       
   214         DebugDataConsumer.NewValue(self, tick, value, forced)
       
   215         
       
   216         if self.Data is not None:
       
   217             # String data value is CRC
       
   218             num_value = (binascii.crc32(value) & STRING_CRC_MASK
       
   219                          if self.VariableType in ["STRING", "WSTRING"]
       
   220                          else float(value))
       
   221             
       
   222             # Update variable range values
       
   223             self.MinValue = (min(self.MinValue, num_value)
       
   224                              if self.MinValue is not None
       
   225                              else num_value)
       
   226             self.MaxValue = (max(self.MaxValue, num_value)
       
   227                              if self.MaxValue is not None
       
   228                              else num_value)
       
   229             
       
   230             # Translate forced flag to float for storing in Data table
       
   231             forced_value = float(forced)
       
   232             
       
   233             # In the case of string variables, we store raw string value and
       
   234             # forced flag in raw data table. Only changes in this two values
       
   235             # are stored. Index to the corresponding raw value is stored in 
       
   236             # data third column
       
   237             if self.VariableType in ["STRING", "WSTRING"]:
       
   238                 raw_data = (value, forced_value)
       
   239                 if len(self.RawData) == 0 or self.RawData[-1] != raw_data:
       
   240                     extra_value = len(self.RawData)
       
   241                     self.RawData.append(raw_data)
       
   242                 else:
       
   243                     extra_value = len(self.RawData) - 1
       
   244             
       
   245             # In other case, data third column is forced flag
       
   246             else:
       
   247                 extra_value = forced_value
       
   248             
       
   249             # Add New data to stored data table
       
   250             self.Data = numpy.append(self.Data, 
       
   251                     [[float(tick), num_value, extra_value]], axis=0)
       
   252         
       
   253             # Signal to debug variable panel to refresh
       
   254             self.Parent.HasNewData = True
       
   255         
       
   256     def SetForced(self, forced):
       
   257         """
       
   258         Update Forced flag
       
   259         @param forced: New forced flag
       
   260         """
       
   261         # Store forced flag
       
   262         if self.Forced != forced:
       
   263             self.Forced = forced
       
   264             
       
   265             # Signal to debug variable panel to refresh
       
   266             self.Parent.HasNewData = True
       
   267     
       
   268     def SetValue(self, value):
       
   269         """
       
   270         Update value.
       
   271         @param value: New value
       
   272         """
       
   273         # Remove quote and double quote surrounding string value to get raw value
       
   274         if (self.VariableType == "STRING" and
       
   275             value.startswith("'") and value.endswith("'") or
       
   276             self.VariableType == "WSTRING" and
       
   277             value.startswith('"') and value.endswith('"')):
       
   278             value = value[1:-1]
       
   279         
       
   280         # Store variable value
       
   281         if self.Value != value:
       
   282             self.Value = value
       
   283             
       
   284             # Signal to debug variable panel to refresh
       
   285             self.Parent.HasNewData = True
       
   286     
       
   287     def GetValue(self, tick=None, raw=False):
       
   288         """
       
   289         Return current value or value and forced flag for tick given
       
   290         @return: Current value or value and forced flag
       
   291         """
       
   292         # If tick given and stored data option enabled
       
   293         if tick is not None and self.Data is not None:
       
   294             
       
   295             # Return current value and forced flag if data empty
       
   296             if len(self.Data) == 0:
       
   297                 return self.Value, self.IsForced()
       
   298             
       
   299             # Get index of nearest data from tick given
       
   300             idx = self.GetNearestData(tick, 0)
       
   301             
       
   302             # Get value and forced flag at given index
       
   303             value, forced = self.RawData[int(self.Data[idx, 2])] \
       
   304                             if self.VariableType in ["STRING", "WSTRING"] \
       
   305                             else self.Data[idx, 1:3]
       
   306             
       
   307             # Get raw value if asked
       
   308             if not raw:
       
   309                 value = TYPE_TRANSLATOR.get(
       
   310                         self.VariableType, str)(value)
       
   311             
       
   312             return value, forced
       
   313             
       
   314         # Return raw value if asked
       
   315         if not raw and self.VariableType in ["STRING", "WSTRING"]:
       
   316             return TYPE_TRANSLATOR.get(
       
   317                     self.VariableType, str)(self.Value)
       
   318         return self.Value
       
   319 
       
   320     def GetNearestData(self, tick, adjust):
       
   321         """
       
   322         Return index of nearest data from tick given
       
   323         @param tick: Tick where find nearest data
       
   324         @param adjust: Constraint for data position from tick
       
   325                        -1: older than tick
       
   326                        1:  newer than tick
       
   327                        0:  doesn't matter
       
   328         @return: Index of nearest data
       
   329         """
       
   330         # Return immediately if data is empty
       
   331         if self.Data is None:
       
   332             return None
       
   333         
       
   334         # Extract data ticks
       
   335         ticks = self.Data[:, 0]
       
   336         
       
   337         # Get nearest data from tick
       
   338         idx = numpy.argmin(abs(ticks - tick))
       
   339         
       
   340         # Adjust data index according to constraint
       
   341         if (adjust < 0 and ticks[idx] > tick and idx > 0 or
       
   342             adjust > 0 and ticks[idx] < tick and idx < len(ticks)):
       
   343             idx += adjust
       
   344         
       
   345         return idx