Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1571: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1571: # andrej@1571: # See COPYING file for copyrights details. andrej@1571: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. andrej@1571: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. andrej@1571: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Laurent@814: Laurent@814: import datetime Laurent@1176: andrej@1782: andrej@1782: # ------------------------------------------------------------------------------- Laurent@1176: # Date and Time conversion function andrej@1782: # ------------------------------------------------------------------------------- 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: andrej@1736: 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: """ andrej@1764: return float(value.days * DAY + andrej@1764: value.seconds * SECOND + Laurent@1176: value.microseconds) andrej@1730: return Laurent@814: andrej@1736: 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) andrej@1730: Laurent@1176: # Get absolute microseconds value and save if it was negative Laurent@814: negative = microseconds < 0 Laurent@814: microseconds = abs(microseconds) andrej@1730: Laurent@1176: # TIME literal prefix Laurent@814: data = "T#" Laurent@814: if negative: Laurent@814: data += "-" andrej@1730: 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 andrej@1730: Laurent@1176: for val, format in [ andrej@1737: (int(microseconds) / DAY, "%dd"), # Days andrej@1737: ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours andrej@1737: ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes andrej@1737: ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds andrej@1730: andrej@1730: # 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 andrej@1730: Laurent@1176: # Update non-null variable Laurent@814: not_null = True andrej@1730: andrej@1730: # In any case microseconds have to be added to TIME literal Laurent@814: data += "%gms" % (microseconds % SECOND / 1000.) andrej@1730: Laurent@814: return data Laurent@814: andrej@1736: 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: andrej@1736: 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: andrej@1736: 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) andrej@1730: Laurent@1176: # TIME_OF_DAY literal prefix Laurent@814: data = "TOD#" andrej@1730: 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 andrej@1730: Laurent@1176: # Add value to TIME_OF_DAY literal Laurent@814: data += format % val andrej@1730: Laurent@814: return data Laurent@814: andrej@1749: andrej@1730: # 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: andrej@1782: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Debug Data Consumer Class andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@1176: Laurent@814: class DebugDataConsumer: andrej@1736: """ andrej@1736: Class that implements an element that consumes debug values andrej@1736: Value update can be inhibited during the time the associated Debug Viewer is andrej@1736: refreshing andrej@1736: """ andrej@1730: 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 andrej@1730: Laurent@1176: # Store debug value and forced flag when value update is inhibited Laurent@814: self.LastValue = None Laurent@1176: self.LastForced = False andrej@1730: Laurent@1176: # Value IEC data type Laurent@814: self.DataType = None andrej@1730: Laurent@1176: # Flag that value update is inhibited Laurent@814: self.Inhibited = False andrej@1730: 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 andrej@1730: 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) andrej@1730: Laurent@1176: # Reset stored values Laurent@814: self.LastValue = None Laurent@1176: self.LastForced = False andrej@1730: 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 andrej@1730: Laurent@1365: def NewValues(self, tick, values, raw="BOOL"): 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@1220: @param raw: Data type of values not translated (default: 'BOOL') Laurent@1176: """ Laurent@1365: value, forced = values andrej@1730: Laurent@1176: # Translate value to IEC literal Laurent@1220: if self.DataType != raw: Laurent@1214: value = TYPE_TRANSLATOR.get(self.DataType, str)(value) andrej@1730: 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 andrej@1730: Laurent@1176: # Update value and forced flag in any other case Laurent@814: else: Laurent@814: self.SetForced(forced) Laurent@814: self.SetValue(value) andrej@1730: 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 andrej@1730: Laurent@814: def GetValue(self): Laurent@1176: """ Laurent@1176: Return current value Laurent@1176: @return: Current value Laurent@1176: """ Laurent@814: return self.Value andrej@1730: 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 andrej@1730: 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