laurent@469: # -*- coding: utf-8 -*- laurent@469: laurent@469: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor laurent@469: #based on the plcopen standard. laurent@469: # laurent@469: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD laurent@469: # laurent@469: #See COPYING file for copyrights details. laurent@469: # laurent@469: #This library is free software; you can redistribute it and/or laurent@469: #modify it under the terms of the GNU General Public laurent@469: #License as published by the Free Software Foundation; either laurent@469: #version 2.1 of the License, or (at your option) any later version. laurent@469: # laurent@469: #This library is distributed in the hope that it will be useful, laurent@469: #but WITHOUT ANY WARRANTY; without even the implied warranty of laurent@469: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU laurent@469: #General Public License for more details. laurent@469: # laurent@469: #You should have received a copy of the GNU General Public laurent@469: #License along with this library; if not, write to the Free Software laurent@469: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA laurent@469: laurent@519: import re Edouard@516: import datetime laurent@469: Laurent@714: import wx Laurent@714: laurent@469: #------------------------------------------------------------------------------- Laurent@714: # Helpers laurent@469: #------------------------------------------------------------------------------- laurent@469: laurent@469: LOCATIONDATATYPES = {"X" : ["BOOL"], laurent@469: "B" : ["SINT", "USINT", "BYTE", "STRING"], laurent@469: "W" : ["INT", "UINT", "WORD", "WSTRING"], laurent@469: "D" : ["DINT", "UDINT", "REAL", "DWORD"], laurent@469: "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} laurent@469: laurent@475: def gen_get_function(f): laurent@475: def get_function(v): laurent@475: try: laurent@475: return f(v) laurent@475: except: laurent@475: return None laurent@475: return get_function laurent@469: edouard@476: getinteger = gen_get_function(int) edouard@476: getfloat = gen_get_function(float) edouard@476: getstring = gen_get_function(str) laurent@469: laurent@519: SECOND = 1000000 laurent@519: MINUTE = 60 * SECOND laurent@519: HOUR = 60 * MINUTE laurent@519: DAY = 24 * HOUR laurent@525: laurent@564: IEC_TIME_MODEL = re.compile("(?:(?:T|TIME)#)?(-)?(?:(%(float)s)D_?)?(?:(%(float)s)H_?)?(?:(%(float)s)M(?!S)_?)?(?:(%(float)s)S_?)?(?:(%(float)s)MS)?" % {"float": "[0-9]+(?:\.[0-9]+)?"}) laurent@525: IEC_DATE_MODEL = re.compile("(?:(?:D|DATE)#)?([0-9]{4})-([0-9]{2})-([0-9]{2})") laurent@525: IEC_DATETIME_MODEL = re.compile("(?:(?:DT|DATE_AND_TIME)#)?([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)") laurent@525: IEC_TIMEOFDAY_MODEL = re.compile("(?:(?:TOD|TIME_OF_DAY)#)?([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)") laurent@519: laurent@519: def gettime(v): laurent@519: result = IEC_TIME_MODEL.match(v.upper()) laurent@519: if result is not None: laurent@519: negative, days, hours, minutes, seconds, milliseconds = result.groups() laurent@519: microseconds = 0 laurent@519: not_null = False laurent@519: for value, factor in [(days, DAY), laurent@519: (hours, HOUR), laurent@519: (minutes, MINUTE), laurent@519: (seconds, SECOND), laurent@519: (milliseconds, 1000)]: laurent@519: if value is not None: laurent@519: microseconds += float(value) * factor laurent@519: not_null = True laurent@519: if not not_null: laurent@519: return None laurent@519: if negative is not None: laurent@519: microseconds = -microseconds laurent@519: return datetime.timedelta(microseconds=microseconds) laurent@519: laurent@519: else: Edouard@516: return None Edouard@516: laurent@525: def getdate(v): laurent@525: result = IEC_DATE_MODEL.match(v.upper()) laurent@525: if result is not None: laurent@525: year, month, day = result.groups() laurent@525: try: laurent@525: date = datetime.datetime(int(year), int(month), int(day)) laurent@525: except ValueError, e: laurent@525: return None laurent@525: base_date = datetime.datetime(1970, 1, 1) laurent@525: return date - base_date laurent@525: else: laurent@525: return None laurent@525: laurent@525: def getdatetime(v): laurent@525: result = IEC_DATETIME_MODEL.match(v.upper()) laurent@525: if result is not None: laurent@525: year, month, day, hours, minutes, seconds = result.groups() laurent@525: try: laurent@525: date = datetime.datetime(int(year), int(month), int(day), int(hours), int(minutes), int(float(seconds)), int((float(second) * SECOND) % SECOND)) laurent@525: except ValueError, e: laurent@525: return None laurent@525: base_date = datetime.datetime(1970, 1, 1) laurent@525: return date - base_date laurent@525: else: laurent@525: return None laurent@525: laurent@525: def gettimeofday(v): laurent@525: result = IEC_TIMEOFDAY_MODEL.match(v.upper()) laurent@525: if result is not None: laurent@525: hours, minutes, seconds = result.groups() laurent@525: microseconds = 0 laurent@525: for value, factor in [(hours, HOUR), laurent@525: (minutes, MINUTE), laurent@525: (seconds, SECOND)]: laurent@525: microseconds += float(value) * factor laurent@525: return datetime.timedelta(microseconds=microseconds) laurent@525: else: laurent@525: return None Edouard@516: laurent@536: GetTypeValue = {"BOOL": lambda x: {"TRUE": True, "FALSE": False, "0": False, "1": True}.get(x.upper(), None), laurent@475: "SINT": getinteger, laurent@475: "INT": getinteger, laurent@475: "DINT": getinteger, laurent@475: "LINT": getinteger, laurent@475: "USINT": getinteger, laurent@475: "UINT": getinteger, laurent@475: "UDINT": getinteger, laurent@475: "ULINT": getinteger, laurent@475: "BYTE": getinteger, laurent@475: "WORD": getinteger, laurent@475: "DWORD": getinteger, laurent@475: "LWORD": getinteger, laurent@475: "REAL": getfloat, laurent@475: "LREAL": getfloat, laurent@475: "STRING": getstring, Edouard@516: "WSTRING": getstring, laurent@525: "TIME": gettime, laurent@525: "DATE": getdate, laurent@525: "DT": getdatetime, laurent@525: "TOD": gettimeofday} laurent@469: Laurent@714: #------------------------------------------------------------------------------- Laurent@714: # Force Variable Dialog Laurent@714: #------------------------------------------------------------------------------- laurent@469: laurent@469: class ForceVariableDialog(wx.TextEntryDialog): laurent@469: laurent@479: def __init__(self, parent, iec_type, defaultValue=""): laurent@469: wx.TextEntryDialog.__init__(self, parent, message = _("Forcing Variable Value"), Laurent@747: caption = _("Please enter value for a \"%s\" variable:") % iec_type, defaultValue = defaultValue, laurent@469: style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition) laurent@469: laurent@469: self.IEC_Type = iec_type Laurent@714: Laurent@714: self.Bind(wx.EVT_BUTTON, self.OnOK, Laurent@714: self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) Laurent@714: laurent@469: def OnOK(self, event): Laurent@714: message = None laurent@469: value = self.GetSizer().GetItem(1).GetWindow().GetValue() laurent@469: if value == "": Laurent@714: message = _("You must type a value!") laurent@475: elif GetTypeValue[self.IEC_Type](value) is None: Laurent@714: message = _("Invalid value \"%s\" for \"%s\" variable!") % (value, self.IEC_Type) Laurent@714: if message is not None: Laurent@714: dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) Laurent@714: dialog.ShowModal() Laurent@714: dialog.Destroy() laurent@469: else: laurent@469: self.EndModal(wx.ID_OK) Laurent@732: event.Skip() laurent@469: laurent@469: def GetValue(self): Laurent@714: return GetTypeValue[self.IEC_Type](wx.TextEntryDialog.GetValue(self))