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 re
Laurent@814: import datetime
Laurent@814: 
Laurent@814: import wx
Laurent@814: 
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: #                                Helpers
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: 
Laurent@814: LOCATIONDATATYPES = {"X" : ["BOOL"],
Laurent@814:                      "B" : ["SINT", "USINT", "BYTE", "STRING"],
Laurent@814:                      "W" : ["INT", "UINT", "WORD", "WSTRING"],
Laurent@814:                      "D" : ["DINT", "UDINT", "REAL", "DWORD"],
Laurent@814:                      "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} 
Laurent@814: 
Laurent@814: def gen_get_function(f):
Laurent@814:     def get_function(v):
Laurent@814:         try:
Laurent@814:             return f(v)
Laurent@814:         except:
Laurent@814:             return None
Laurent@814:     return get_function
Laurent@814: 
Laurent@878: def gen_get_string(delimiter):
Laurent@878:     STRING_MODEL = re.compile("%(delimiter)s([^%(delimiter)s]*)%(delimiter)s$" % {"delimiter": delimiter})
Laurent@878:     def get_string(v):
Laurent@878:         result = STRING_MODEL.match(v)
Laurent@878:         if result is not None:
Laurent@878:             return result.group(1)
Laurent@878:         return None
Laurent@878:     return get_string
Laurent@878: 
Laurent@814: getinteger = gen_get_function(int)
Laurent@814: getfloat = gen_get_function(float)
Laurent@878: getstring = gen_get_string("'")
Laurent@878: getwstring = gen_get_string('"')
Laurent@814: 
Laurent@814: SECOND = 1000000
Laurent@814: MINUTE = 60 * SECOND
Laurent@814: HOUR = 60 * MINUTE
Laurent@814: DAY = 24 * HOUR
Laurent@814: 
Laurent@878: 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@878: IEC_DATE_MODEL = re.compile("(?:(?:D|DATE)#)?([0-9]{4})-([0-9]{2})-([0-9]{2})$")
Laurent@878: 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@878: IEC_TIMEOFDAY_MODEL = re.compile("(?:(?:TOD|TIME_OF_DAY)#)?([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)$")
Laurent@814: 
Laurent@814: def gettime(v):    
Laurent@814:     result = IEC_TIME_MODEL.match(v.upper())
Laurent@814:     if result is not None:
Laurent@814:         negative, days, hours, minutes, seconds, milliseconds = result.groups()
Laurent@814:         microseconds = 0
Laurent@814:         not_null = False
Laurent@814:         for value, factor in [(days, DAY),
Laurent@814:                               (hours, HOUR),
Laurent@814:                               (minutes, MINUTE),
Laurent@814:                               (seconds, SECOND),
Laurent@814:                               (milliseconds, 1000)]:
Laurent@814:             if value is not None:
Laurent@814:                 microseconds += float(value) * factor
Laurent@814:                 not_null = True
Laurent@814:         if not not_null:
Laurent@814:             return None
Laurent@814:         if negative is not None:
Laurent@814:             microseconds = -microseconds
Laurent@814:         return datetime.timedelta(microseconds=microseconds)
Laurent@814:     
Laurent@814:     else: 
Laurent@814:         return None
Laurent@814: 
Laurent@814: def getdate(v):
Laurent@814:     result = IEC_DATE_MODEL.match(v.upper())
Laurent@814:     if result is not None:
Laurent@814:         year, month, day = result.groups()
Laurent@814:         try:
Laurent@814:             date = datetime.datetime(int(year), int(month), int(day))
Laurent@814:         except ValueError, e:
Laurent@814:             return None
Laurent@814:         base_date = datetime.datetime(1970, 1, 1)
Laurent@814:         return date - base_date
Laurent@814:     else: 
Laurent@814:         return None
Laurent@814: 
Laurent@814: def getdatetime(v):
Laurent@814:     result = IEC_DATETIME_MODEL.match(v.upper())
Laurent@814:     if result is not None:
Laurent@814:         year, month, day, hours, minutes, seconds = result.groups()
Laurent@814:         try:
Laurent@941:             date = datetime.datetime(int(year), int(month), int(day), int(hours), int(minutes), int(float(seconds)), int((float(seconds) * SECOND) % SECOND))
Laurent@814:         except ValueError, e:
Laurent@814:             return None
Laurent@814:         base_date = datetime.datetime(1970, 1, 1)
Laurent@814:         return date - base_date
Laurent@814:     else: 
Laurent@814:         return None
Laurent@814: 
Laurent@814: def gettimeofday(v):
Laurent@814:     result = IEC_TIMEOFDAY_MODEL.match(v.upper())
Laurent@814:     if result is not None:
Laurent@814:         hours, minutes, seconds = result.groups()
Laurent@814:         microseconds = 0
Laurent@814:         for value, factor in [(hours, HOUR),
Laurent@814:                               (minutes, MINUTE),
Laurent@814:                               (seconds, SECOND)]:
Laurent@814:             microseconds += float(value) * factor
Laurent@814:         return datetime.timedelta(microseconds=microseconds)
Laurent@814:     else:
Laurent@814:         return None
Laurent@814: 
Laurent@814: GetTypeValue = {"BOOL": lambda x: {"TRUE": True, "FALSE": False, "0": False, "1": True}.get(x.upper(), None),
Laurent@814:                 "SINT": getinteger,
Laurent@814:                 "INT": getinteger,
Laurent@814:                 "DINT": getinteger,
Laurent@814:                 "LINT": getinteger,
Laurent@814:                 "USINT": getinteger,
Laurent@814:                 "UINT": getinteger,
Laurent@814:                 "UDINT": getinteger,
Laurent@814:                 "ULINT": getinteger,
Laurent@814:                 "BYTE": getinteger,
Laurent@814:                 "WORD": getinteger,
Laurent@814:                 "DWORD": getinteger,
Laurent@814:                 "LWORD": getinteger,
Laurent@814:                 "REAL": getfloat,
Laurent@814:                 "LREAL": getfloat,
Laurent@814:                 "STRING": getstring,
Laurent@878:                 "WSTRING": getwstring,
Laurent@814:                 "TIME": gettime,
Laurent@814:                 "DATE": getdate,
Laurent@814:                 "DT": getdatetime,
Laurent@814:                 "TOD": gettimeofday}
Laurent@814: 
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: #                            Force Variable Dialog
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: 
Laurent@814: class ForceVariableDialog(wx.TextEntryDialog):
Laurent@814: 
Laurent@814:     def __init__(self, parent, iec_type, defaultValue=""):
Laurent@814:         wx.TextEntryDialog.__init__(self, parent, message = _("Forcing Variable Value"), 
Laurent@814:                 caption = _("Please enter value for a \"%s\" variable:") % iec_type, defaultValue = defaultValue, 
Laurent@814:                 style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition)
Laurent@814:         
Laurent@814:         self.IEC_Type = iec_type 
Laurent@814:         
Laurent@814:         self.Bind(wx.EVT_BUTTON, self.OnOK, 
Laurent@814:               self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton())
Laurent@814:         
Laurent@814:     def OnOK(self, event):
Laurent@814:         message = None
Laurent@814:         value = self.GetSizer().GetItem(1).GetWindow().GetValue()
Laurent@814:         if value == "":
Laurent@814:             message = _("You must type a value!")
Laurent@814:         elif GetTypeValue[self.IEC_Type](value) is None:
Laurent@814:             message = _("Invalid value \"%s\" for \"%s\" variable!") % (value, self.IEC_Type)
Laurent@814:         if message is not None:
Laurent@814:             dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR)
Laurent@814:             dialog.ShowModal()
Laurent@814:             dialog.Destroy()
Laurent@814:         else:
Laurent@814:             self.EndModal(wx.ID_OK)
Laurent@814:         event.Skip()
Laurent@814: 
Laurent@814:     def GetValue(self):
Laurent@814:         return GetTypeValue[self.IEC_Type](wx.TextEntryDialog.GetValue(self))