Adding support for displaying and forcing TIME variables according to IEC 61131 literal format
authorlaurent
Fri, 01 Apr 2011 17:19:31 +0200
changeset 519 722714c04dcd
parent 518 343fa6867322
child 520 7ca6c3e076f7
Adding support for displaying and forcing TIME variables according to IEC 61131 literal format
dialogs/ForceVariableDialog.py
graphics/GraphicCommons.py
--- 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)