graphics/DebugDataConsumer.py
changeset 1176 f4b434672204
parent 1173 ad09b4a755ce
child 1193 59c196884fec
equal deleted inserted replaced
1175:01842255c9ff 1176:f4b434672204
       
     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) 2007: 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 datetime
       
    26 
       
    27 #-------------------------------------------------------------------------------
       
    28 #                        Date and Time conversion function
       
    29 #-------------------------------------------------------------------------------
       
    30 
       
    31 SECOND = 1000000      # Number of microseconds in one second
       
    32 MINUTE = 60 * SECOND  # Number of microseconds in one minute
       
    33 HOUR = 60 * MINUTE    # Number of microseconds in one hour
       
    34 DAY = 24 * HOUR       # Number of microseconds in one day
       
    35 
       
    36 # Date corresponding to Epoch (1970 January the first)
       
    37 DATE_ORIGIN = datetime.datetime(1970, 1, 1)
       
    38 
       
    39 def get_microseconds(value):
       
    40     """
       
    41     Function converting time duration expressed in day, second and microseconds
       
    42     into one expressed in microseconds
       
    43     @param value: Time duration to convert
       
    44     @return: Time duration expressed in microsecond
       
    45     """
       
    46     return float(value.days * DAY + \
       
    47                  value.seconds * SECOND + \
       
    48                  value.microseconds)
       
    49     return 
       
    50 
       
    51 def generate_time(value):
       
    52     """
       
    53     Function converting time duration expressed in day, second and microseconds
       
    54     into a IEC 61131 TIME literal
       
    55     @param value: Time duration to convert
       
    56     @return: IEC 61131 TIME literal
       
    57     """
       
    58     microseconds = get_microseconds(value)
       
    59     
       
    60     # Get absolute microseconds value and save if it was negative
       
    61     negative = microseconds < 0
       
    62     microseconds = abs(microseconds)
       
    63     
       
    64     # TIME literal prefix
       
    65     data = "T#"
       
    66     if negative:
       
    67         data += "-"
       
    68     
       
    69     # In TIME literal format, it isn't mandatory to indicate null values
       
    70     # if no greater non-null values are available. This variable is used to
       
    71     # inhibit formatting until a non-null value is found
       
    72     not_null = False
       
    73     
       
    74     for val, format in [
       
    75             (int(microseconds) / DAY, "%dd"),                # Days
       
    76             ((int(microseconds) % DAY) / HOUR, "%dh"),       # Hours
       
    77             ((int(microseconds) % HOUR) / MINUTE, "%dm"),    # Minutes
       
    78             ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds
       
    79         
       
    80         # Add value to TIME literal if value is non-null or another non-null 
       
    81         # value have already be found
       
    82         if val > 0 or not_null:
       
    83             data += format % val
       
    84             
       
    85             # Update non-null variable
       
    86             not_null = True
       
    87     
       
    88     # In any case microseconds have to be added to TIME literal 
       
    89     data += "%gms" % (microseconds % SECOND / 1000.)
       
    90     
       
    91     return data
       
    92 
       
    93 def generate_date(value):
       
    94     """
       
    95     Function converting time duration expressed in day, second and microseconds
       
    96     into a IEC 61131 DATE literal
       
    97     @param value: Time duration to convert
       
    98     @return: IEC 61131 DATE literal
       
    99     """
       
   100     return (DATE_ORIGIN + value).strftime("DATE#%Y-%m-%d")
       
   101 
       
   102 def generate_datetime(value):
       
   103     """
       
   104     Function converting time duration expressed in day, second and microseconds
       
   105     into a IEC 61131 DATE_AND_TIME literal
       
   106     @param value: Time duration to convert
       
   107     @return: IEC 61131 DATE_AND_TIME literal
       
   108     """
       
   109     return (DATE_ORIGIN + value).strftime("DT#%Y-%m-%d-%H:%M:%S.%f")
       
   110 
       
   111 def generate_timeofday(value):
       
   112     """
       
   113     Function converting time duration expressed in day, second and microseconds
       
   114     into a IEC 61131 TIME_OF_DAY literal
       
   115     @param value: Time duration to convert
       
   116     @return: IEC 61131 TIME_OF_DAY literal
       
   117     """
       
   118     microseconds = get_microseconds(value)
       
   119     
       
   120     # TIME_OF_DAY literal prefix
       
   121     data = "TOD#"
       
   122     
       
   123     for val, format in [
       
   124             (int(microseconds) / HOUR, "%2.2d:"),               # Hours
       
   125             ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"),    # Minutes
       
   126             ((int(microseconds) % MINUTE) / SECOND, "%2.2d."),  # Seconds
       
   127             (microseconds % SECOND, "%6.6d")]:                  # Microseconds
       
   128         
       
   129         # Add value to TIME_OF_DAY literal
       
   130         data += format % val
       
   131     
       
   132     return data
       
   133 
       
   134 # Dictionary of translation functions from value send by debugger to IEC 
       
   135 # literal stored by type
       
   136 TYPE_TRANSLATOR = {
       
   137     "TIME": generate_time,
       
   138     "DATE": generate_date,
       
   139     "DT": generate_datetime,
       
   140     "TOD": generate_timeofday,
       
   141     "STRING": lambda v: "'%s'" % v,
       
   142     "WSTRING": lambda v: '"%s"' % v,
       
   143     "REAL": lambda v: "%.6g" % v,
       
   144     "LREAL": lambda v: "%.6g" % v,
       
   145     "BOOL": lambda v: v}
       
   146 
       
   147 #-------------------------------------------------------------------------------
       
   148 #                            Debug Data Consumer Class
       
   149 #-------------------------------------------------------------------------------
       
   150 
       
   151 """
       
   152 Class that implements an element that consumes debug values
       
   153 Value update can be inhibited during the time the associated Debug Viewer is
       
   154 refreshing
       
   155 """
       
   156 
       
   157 class DebugDataConsumer:
       
   158     
       
   159     def __init__(self):
       
   160         """
       
   161         Constructor
       
   162         """
       
   163         # Debug value and forced flag
       
   164         self.Value = None
       
   165         self.Forced = False
       
   166         
       
   167         # Store debug value and forced flag when value update is inhibited
       
   168         self.LastValue = None
       
   169         self.LastForced = False
       
   170         
       
   171         # Value IEC data type
       
   172         self.DataType = None
       
   173         
       
   174         # Flag that value update is inhibited
       
   175         self.Inhibited = False
       
   176     
       
   177     def Inhibit(self, inhibit):
       
   178         """
       
   179         Set flag to inhibit or activate value update
       
   180         @param inhibit: Inhibit flag
       
   181         """
       
   182         # Save inhibit flag
       
   183         self.Inhibited = inhibit
       
   184         
       
   185         # When reactivated update value and forced flag with stored values
       
   186         if not inhibit and self.LastValue is not None:
       
   187             self.SetForced(self.LastForced)
       
   188             self.SetValue(self.LastValue)
       
   189             
       
   190             # Reset stored values
       
   191             self.LastValue = None
       
   192             self.LastForced = False
       
   193     
       
   194     def SetDataType(self, data_type):
       
   195         """
       
   196         Set value IEC data type
       
   197         @param data_type: Value IEC data type
       
   198         """
       
   199         self.DataType = data_type
       
   200     
       
   201     def NewValue(self, tick, value, forced=False):
       
   202         """
       
   203         Function called by debug thread when a new debug value is available
       
   204         @param tick: PLC tick when value was captured
       
   205         @param value: Value captured
       
   206         @param forced: Forced flag, True if value is forced (default: False)
       
   207         """
       
   208         # Translate value to IEC literal
       
   209         value = TYPE_TRANSLATOR.get(self.DataType, str)(value)
       
   210         
       
   211         # Store value and forced flag when value update is inhibited
       
   212         if self.Inhibited:
       
   213             self.LastValue = value
       
   214             self.LastForced = forced
       
   215         
       
   216         # Update value and forced flag in any other case
       
   217         else:
       
   218             self.SetForced(forced)
       
   219             self.SetValue(value)
       
   220     
       
   221     def SetValue(self, value):
       
   222         """
       
   223         Update value.
       
   224         May be overridden by inherited classes
       
   225         @param value: New value
       
   226         """
       
   227         self.Value = value
       
   228     
       
   229     def GetValue(self):
       
   230         """
       
   231         Return current value
       
   232         @return: Current value
       
   233         """
       
   234         return self.Value
       
   235     
       
   236     def SetForced(self, forced):
       
   237         """
       
   238         Update Forced flag. May be overridden by inherited classes
       
   239         @param forced: New forced flag
       
   240         """
       
   241         self.Forced = forced
       
   242     
       
   243     def IsForced(self):
       
   244         """
       
   245         Indicate if current value is forced
       
   246         @return: Current forced flag
       
   247         """
       
   248         return self.Forced