Adding support for displaying and forcing TIME variables according to IEC 61131 literal format
--- a/dialogs/ForceVariableDialog.py Fri Apr 01 15:33:36 2011 +0200
+++ b/dialogs/ForceVariableDialog.py Fri Apr 01 17:19:31 2011 +0200
@@ -22,6 +22,7 @@
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import wx
+import re
import datetime
#-------------------------------------------------------------------------------
@@ -46,10 +47,33 @@
getfloat = gen_get_function(float)
getstring = gen_get_function(str)
-def gettime(v):
- try:
- return datetime.timedelta(0, float(v))
- except:
+SECOND = 1000000
+MINUTE = 60 * SECOND
+HOUR = 60 * MINUTE
+DAY = 24 * HOUR
+IEC_TIME_MODEL = re.compile("(?:T|TIME)#(-)?(?:(%(float)s)D_?)?(?:(%(float)s)H_?)?(?:(%(float)s)M_?)?(?:(%(float)s)S_?)?(?:(%(float)s)MS)?" % {"float": "[0-9]+(?:\.[0-9]+)?"})
+
+def gettime(v):
+ result = IEC_TIME_MODEL.match(v.upper())
+ if result is not None:
+ negative, days, hours, minutes, seconds, milliseconds = result.groups()
+ microseconds = 0
+ not_null = False
+ for value, factor in [(days, DAY),
+ (hours, HOUR),
+ (minutes, MINUTE),
+ (seconds, SECOND),
+ (milliseconds, 1000)]:
+ if value is not None:
+ microseconds += float(value) * factor
+ not_null = True
+ if not not_null:
+ return None
+ if negative is not None:
+ microseconds = -microseconds
+ return datetime.timedelta(microseconds=microseconds)
+
+ else:
return None
--- a/graphics/GraphicCommons.py Fri Apr 01 15:33:36 2011 +0200
+++ b/graphics/GraphicCommons.py Fri Apr 01 17:19:31 2011 +0200
@@ -175,6 +175,30 @@
return dir_target
return v_base
+SECOND = 1000000
+MINUTE = 60 * SECOND
+HOUR = 60 * MINUTE
+DAY = 24 * HOUR
+
+def generate_time(value):
+ microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
+ negative = microseconds < 0
+ microseconds = abs(microseconds)
+ data = "T#"
+ not_null = False
+ if negative:
+ data += "-"
+ for val, format in [(int(microseconds) / DAY, "%dd"),
+ ((int(microseconds) % DAY) / HOUR, "%dh"),
+ ((int(microseconds) % HOUR) / MINUTE, "%dm"),
+ ((int(microseconds) % MINUTE) / SECOND, "%ds")]:
+ if val > 0 or not_null:
+ data += format % val
+ not_null = True
+ data += "%gms" % (microseconds % SECOND / 1000.)
+ return data
+
+TYPE_TRANSLATOR = {"TIME": generate_time}
#-------------------------------------------------------------------------------
# Debug Data Consumer Class
@@ -185,6 +209,7 @@
def __init__(self):
self.LastValue = None
self.Value = None
+ self.DataType = None
self.LastForced = False
self.Forced = False
self.Inhibited = False
@@ -195,15 +220,19 @@
self.SetForced(self.LastForced)
self.SetValue(self.LastValue)
self.LastValue = None
-
+
+ def SetDataType(self, data_type):
+ self.DataType = data_type
+
def NewValue(self, tick, value, forced=False):
+ value = TYPE_TRANSLATOR.get(self.DataType, lambda x:x)(value)
if self.Inhibited:
self.LastValue = value
self.LastForced = forced
else:
self.SetForced(forced)
self.SetValue(value)
-
+
def SetValue(self, value):
self.Value = value
@@ -266,6 +295,7 @@
result = self.DataProducer.SubscribeDebugIECVariable(iec_path, consumer) is not None
if result is not None and consumer != self:
self.DataConsumers[consumer] = iec_path
+ consumer.SetDataType(self.GetDataType(iec_path))
return result
def RemoveDataConsumer(self, consumer):
@@ -1619,7 +1649,7 @@
def CreateToolTip(self, pos):
if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
- if isinstance(self.Value, StringType):
+ if isinstance(self.Value, StringType) and self.Value.find("#") == -1:
computed_value = "\"%s\""%self.Value
else:
computed_value = str(self.Value)
@@ -1789,7 +1819,7 @@
if self.Value != value:
self.Value = value
if value is not None and not isinstance(value, BooleanType):
- if isinstance(value, StringType):
+ if isinstance(value, StringType) and value.find('#') == -1:
self.ComputedValue = "\"%s\""%value
else:
self.ComputedValue = str(value)
@@ -2687,19 +2717,20 @@
if self.Value is not None and not isinstance(self.Value, BooleanType) and self.Value != "undefined":
dc.SetFont(self.Parent.GetMiniFont())
dc.SetTextForeground(wx.NamedColour("purple"))
- width, height = self.ValueSize
- if self.BoundingBox[2] > width * 4 or self.BoundingBox[3] > height * 4:
- x = self.Points[0].x + width * (self.StartPoint[1][0] - 1) / 2
- y = self.Points[0].y + height * (self.StartPoint[1][1] - 1) / 2
- dc.DrawText(self.ComputedValue, x, y)
- x = self.Points[-1].x + width * (self.EndPoint[1][0] - 1) / 2
- y = self.Points[-1].y + height * (self.EndPoint[1][1] - 1) / 2
- dc.DrawText(self.ComputedValue, x, y)
- else:
- middle = len(self.Segments) / 2 + len(self.Segments) % 2 - 1
- x = (self.Points[middle].x + self.Points[middle + 1].x - width) / 2
- y = (self.Points[middle].y + self.Points[middle + 1].y - height) / 2
- dc.DrawText(self.ComputedValue, x, y)
+ if self.ValueSize is not None:
+ width, height = self.ValueSize
+ if self.BoundingBox[2] > width * 4 or self.BoundingBox[3] > height * 4:
+ x = self.Points[0].x + width * (self.StartPoint[1][0] - 1) / 2
+ y = self.Points[0].y + height * (self.StartPoint[1][1] - 1) / 2
+ dc.DrawText(self.ComputedValue, x, y)
+ x = self.Points[-1].x + width * (self.EndPoint[1][0] - 1) / 2
+ y = self.Points[-1].y + height * (self.EndPoint[1][1] - 1) / 2
+ dc.DrawText(self.ComputedValue, x, y)
+ else:
+ middle = len(self.Segments) / 2 + len(self.Segments) % 2 - 1
+ x = (self.Points[middle].x + self.Points[middle + 1].x - width) / 2
+ y = (self.Points[middle].y + self.Points[middle + 1].y - height) / 2
+ dc.DrawText(self.ComputedValue, x, y)
dc.SetFont(self.Parent.GetFont())
dc.SetTextForeground(wx.BLACK)