Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: Laurent@814: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor Laurent@814: #based on the plcopen standard. Laurent@814: # Laurent@814: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD Laurent@814: # Laurent@814: #See COPYING file for copyrights details. Laurent@814: # Laurent@814: #This library is free software; you can redistribute it and/or Laurent@814: #modify it under the terms of the GNU General Public Laurent@814: #License as published by the Free Software Foundation; either Laurent@814: #version 2.1 of the License, or (at your option) any later version. Laurent@814: # Laurent@814: #This library is distributed in the hope that it will be useful, Laurent@814: #but WITHOUT ANY WARRANTY; without even the implied warranty of Laurent@814: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Laurent@814: #General Public License for more details. Laurent@814: # Laurent@814: #You should have received a copy of the GNU General Public Laurent@814: #License along with this library; if not, write to the Free Software Laurent@814: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Laurent@814: Laurent@814: import datetime Laurent@1176: Laurent@1176: #------------------------------------------------------------------------------- Laurent@1176: # Date and Time conversion function Laurent@1176: #------------------------------------------------------------------------------- Laurent@1176: Laurent@1176: SECOND = 1000000 # Number of microseconds in one second Laurent@1176: MINUTE = 60 * SECOND # Number of microseconds in one minute Laurent@1176: HOUR = 60 * MINUTE # Number of microseconds in one hour Laurent@1176: DAY = 24 * HOUR # Number of microseconds in one day Laurent@1176: Laurent@1176: # Date corresponding to Epoch (1970 January the first) Laurent@1176: DATE_ORIGIN = datetime.datetime(1970, 1, 1) Laurent@1176: Laurent@1176: def get_microseconds(value): Laurent@1176: """ Laurent@1176: Function converting time duration expressed in day, second and microseconds Laurent@1176: into one expressed in microseconds Laurent@1176: @param value: Time duration to convert Laurent@1176: @return: Time duration expressed in microsecond Laurent@1176: """ Laurent@1176: return float(value.days * DAY + \ Laurent@1176: value.seconds * SECOND + \ Laurent@1176: value.microseconds) Laurent@1176: return Laurent@814: Laurent@814: def generate_time(value): Laurent@1176: """ Laurent@1176: Function converting time duration expressed in day, second and microseconds Laurent@1176: into a IEC 61131 TIME literal Laurent@1176: @param value: Time duration to convert Laurent@1176: @return: IEC 61131 TIME literal Laurent@1176: """ Laurent@1176: microseconds = get_microseconds(value) Laurent@1176: Laurent@1176: # Get absolute microseconds value and save if it was negative Laurent@814: negative = microseconds < 0 Laurent@814: microseconds = abs(microseconds) Laurent@1176: Laurent@1176: # TIME literal prefix Laurent@814: data = "T#" Laurent@814: if negative: Laurent@814: data += "-" Laurent@1176: Laurent@1176: # In TIME literal format, it isn't mandatory to indicate null values Laurent@1176: # if no greater non-null values are available. This variable is used to Laurent@1176: # inhibit formatting until a non-null value is found Laurent@1176: not_null = False Laurent@1176: Laurent@1176: for val, format in [ Laurent@1176: (int(microseconds) / DAY, "%dd"), # Days Laurent@1176: ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours Laurent@1176: ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes Laurent@1176: ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds Laurent@1176: Laurent@1176: # Add value to TIME literal if value is non-null or another non-null Laurent@1176: # value have already be found Laurent@814: if val > 0 or not_null: Laurent@814: data += format % val Laurent@1176: Laurent@1176: # Update non-null variable Laurent@814: not_null = True Laurent@1176: Laurent@1176: # In any case microseconds have to be added to TIME literal Laurent@814: data += "%gms" % (microseconds % SECOND / 1000.) Laurent@1176: Laurent@814: return data Laurent@814: Laurent@814: def generate_date(value): Laurent@1176: """ Laurent@1176: Function converting time duration expressed in day, second and microseconds Laurent@1176: into a IEC 61131 DATE literal Laurent@1176: @param value: Time duration to convert Laurent@1176: @return: IEC 61131 DATE literal Laurent@1176: """ Laurent@1176: return (DATE_ORIGIN + value).strftime("DATE#%Y-%m-%d") Laurent@814: Laurent@814: def generate_datetime(value): Laurent@1176: """ Laurent@1176: Function converting time duration expressed in day, second and microseconds Laurent@1176: into a IEC 61131 DATE_AND_TIME literal Laurent@1176: @param value: Time duration to convert Laurent@1176: @return: IEC 61131 DATE_AND_TIME literal Laurent@1176: """ Laurent@1176: return (DATE_ORIGIN + value).strftime("DT#%Y-%m-%d-%H:%M:%S.%f") Laurent@814: Laurent@814: def generate_timeofday(value): Laurent@1176: """ Laurent@1176: Function converting time duration expressed in day, second and microseconds Laurent@1176: into a IEC 61131 TIME_OF_DAY literal Laurent@1176: @param value: Time duration to convert Laurent@1176: @return: IEC 61131 TIME_OF_DAY literal Laurent@1176: """ Laurent@1176: microseconds = get_microseconds(value) Laurent@1176: Laurent@1176: # TIME_OF_DAY literal prefix Laurent@814: data = "TOD#" Laurent@1176: Laurent@1176: for val, format in [ Laurent@1176: (int(microseconds) / HOUR, "%2.2d:"), # Hours Laurent@1176: ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), # Minutes Laurent@1176: ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), # Seconds Laurent@1176: (microseconds % SECOND, "%6.6d")]: # Microseconds Laurent@1176: Laurent@1176: # Add value to TIME_OF_DAY literal Laurent@814: data += format % val Laurent@1176: Laurent@814: return data Laurent@814: Laurent@1176: # Dictionary of translation functions from value send by debugger to IEC Laurent@1176: # literal stored by type Laurent@1176: TYPE_TRANSLATOR = { Laurent@1176: "TIME": generate_time, Laurent@1176: "DATE": generate_date, Laurent@1176: "DT": generate_datetime, Laurent@1176: "TOD": generate_timeofday, Laurent@1176: "STRING": lambda v: "'%s'" % v, Laurent@1176: "WSTRING": lambda v: '"%s"' % v, Laurent@1176: "REAL": lambda v: "%.6g" % v, Laurent@1214: "LREAL": lambda v: "%.6g" % v} Laurent@814: Laurent@814: #------------------------------------------------------------------------------- Laurent@814: # Debug Data Consumer Class Laurent@814: #------------------------------------------------------------------------------- Laurent@814: Laurent@1176: """ Laurent@1176: Class that implements an element that consumes debug values Laurent@1176: Value update can be inhibited during the time the associated Debug Viewer is Laurent@1176: refreshing Laurent@1176: """ Laurent@1176: Laurent@814: class DebugDataConsumer: Laurent@814: Laurent@814: def __init__(self): Laurent@1176: """ Laurent@1176: Constructor Laurent@1176: """ Laurent@1176: # Debug value and forced flag Laurent@1176: self.Value = None Laurent@1176: self.Forced = False Laurent@1176: Laurent@1176: # Store debug value and forced flag when value update is inhibited Laurent@814: self.LastValue = None Laurent@1176: self.LastForced = False Laurent@1176: Laurent@1176: # Value IEC data type Laurent@814: self.DataType = None Laurent@1176: Laurent@1176: # Flag that value update is inhibited Laurent@814: self.Inhibited = False Laurent@814: Laurent@814: def Inhibit(self, inhibit): Laurent@1176: """ Laurent@1176: Set flag to inhibit or activate value update Laurent@1176: @param inhibit: Inhibit flag Laurent@1176: """ Laurent@1176: # Save inhibit flag Laurent@814: self.Inhibited = inhibit Laurent@1176: Laurent@1176: # When reactivated update value and forced flag with stored values Laurent@814: if not inhibit and self.LastValue is not None: Laurent@814: self.SetForced(self.LastForced) Laurent@814: self.SetValue(self.LastValue) Laurent@1176: Laurent@1176: # Reset stored values Laurent@814: self.LastValue = None Laurent@1176: self.LastForced = False Laurent@814: Laurent@814: def SetDataType(self, data_type): Laurent@1176: """ Laurent@1176: Set value IEC data type Laurent@1176: @param data_type: Value IEC data type Laurent@1176: """ Laurent@814: self.DataType = data_type Laurent@814: Laurent@1214: def NewValue(self, tick, value, forced=False, raw_bool=True): Laurent@1176: """ Laurent@1176: Function called by debug thread when a new debug value is available Laurent@1176: @param tick: PLC tick when value was captured Laurent@1176: @param value: Value captured Laurent@1176: @param forced: Forced flag, True if value is forced (default: False) Laurent@1214: @param raw_bool: Bool values must be treated rawly (default: True) Laurent@1176: """ Laurent@1176: # Translate value to IEC literal Laurent@1214: if self.DataType != "BOOL" or not raw_bool: Laurent@1214: value = TYPE_TRANSLATOR.get(self.DataType, str)(value) Laurent@1176: Laurent@1176: # Store value and forced flag when value update is inhibited Laurent@814: if self.Inhibited: Laurent@814: self.LastValue = value Laurent@814: self.LastForced = forced Laurent@1176: Laurent@1176: # Update value and forced flag in any other case Laurent@814: else: Laurent@814: self.SetForced(forced) Laurent@814: self.SetValue(value) Laurent@814: Laurent@814: def SetValue(self, value): Laurent@1176: """ Laurent@1176: Update value. Laurent@1176: May be overridden by inherited classes Laurent@1176: @param value: New value Laurent@1176: """ Laurent@814: self.Value = value Laurent@814: Laurent@814: def GetValue(self): Laurent@1176: """ Laurent@1176: Return current value Laurent@1176: @return: Current value Laurent@1176: """ Laurent@814: return self.Value Laurent@814: Laurent@814: def SetForced(self, forced): Laurent@1176: """ Laurent@1193: Update Forced flag. Laurent@1193: May be overridden by inherited classes Laurent@1176: @param forced: New forced flag Laurent@1176: """ Laurent@814: self.Forced = forced Laurent@814: Laurent@814: def IsForced(self): Laurent@1176: """ Laurent@1176: Indicate if current value is forced Laurent@1176: @return: Current forced flag Laurent@1176: """ Laurent@814: return self.Forced