--- a/IDEFrame.py Mon May 27 09:24:39 2013 +0200
+++ b/IDEFrame.py Mon May 27 13:09:54 2013 +0200
@@ -2022,9 +2022,9 @@
elif isinstance(editor, GraphicViewer):
editor.ResetView(True)
else:
- editor.RegisterVariables()
+ editor.SubscribeAllDataConsumers()
elif editor.IsDebugging():
- editor.RegisterVariables()
+ editor.SubscribeAllDataConsumers()
self.DebugVariablePanel.UnregisterObsoleteData()
def AddDebugVariable(self, iec_path, force=False):
--- a/ProjectController.py Mon May 27 09:24:39 2013 +0200
+++ b/ProjectController.py Mon May 27 13:09:54 2013 +0200
@@ -21,7 +21,7 @@
from editors.FileManagementPanel import FileManagementPanel
from editors.ProjectNodeEditor import ProjectNodeEditor
from editors.IECCodeViewer import IECCodeViewer
-from graphics import DebugViewer
+from editors.DebugViewer import DebugViewer
from dialogs import DiscoveryDialog
from PLCControler import PLCControler
from plcopen.structures import IEC_KEYWORDS
--- a/controls/DebugVariablePanel.py Mon May 27 09:24:39 2013 +0200
+++ b/controls/DebugVariablePanel.py Mon May 27 13:09:54 2013 +0200
@@ -45,7 +45,8 @@
except:
USE_MPL = False
-from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
+from graphics.DebugDataConsumer import DebugDataConsumer
+from editors.DebugViewer import DebugViewer, REFRESH_PERIOD
from controls import CustomGrid, CustomTable
from dialogs.ForceVariableDialog import ForceVariableDialog
from util.BitmapLibrary import GetBitmap
@@ -1837,7 +1838,7 @@
self.Thaw()
def UnregisterObsoleteData(self):
- self.RegisterVariables()
+ self.SubscribeAllDataConsumers()
if USE_MPL:
if self.DataProducer is not None:
self.Ticktime = self.DataProducer.GetTicktime()
@@ -1872,7 +1873,7 @@
self.Thaw()
def ResetView(self):
- self.DeleteDataConsumers()
+ self.UnsubscribeAllDataConsumers()
if USE_MPL:
self.Fixed = False
for panel in self.GraphicPanels:
--- a/controls/LogViewer.py Mon May 27 09:24:39 2013 +0200
+++ b/controls/LogViewer.py Mon May 27 13:09:54 2013 +0200
@@ -29,7 +29,7 @@
import wx
from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD
-from graphics import DebugViewer, REFRESH_PERIOD
+from editors.DebugViewer import DebugViewer, REFRESH_PERIOD
from targets.typemapping import LogLevelsCount, LogLevels
from util.BitmapLibrary import GetBitmap
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/editors/DebugViewer.py Mon May 27 13:09:54 2013 +0200
@@ -0,0 +1,348 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
+#based on the plcopen standard.
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from threading import Lock, Timer
+from time import time as gettime
+
+import wx
+
+REFRESH_PERIOD = 0.1 # Minimum time between 2 refresh
+DEBUG_REFRESH_LOCK = Lock() # Common refresh lock for all debug viewers
+
+#-------------------------------------------------------------------------------
+# Debug Viewer Class
+#-------------------------------------------------------------------------------
+
+"""
+Class that implements common behavior of every viewers able to display debug
+values
+"""
+
+class DebugViewer:
+
+ def __init__(self, producer, debug, subscribe_tick=True):
+ """
+ Constructor
+ @param producer: Object receiving debug value and dispatching them to
+ consumers
+ @param debug: Flag indicating that Viewer is debugging
+ @param subscribe_tick: Flag indicating that viewer need tick value to
+ synchronize
+ """
+ self.Debug = debug
+ self.SubscribeTick = subscribe_tick
+
+ # Flag indicating that consumer value update inhibited
+ # (DebugViewer is refreshing)
+ self.Inhibited = False
+
+ # List of data consumers subscribed to DataProducer
+ self.DataConsumers = {}
+
+ # Time stamp indicating when last refresh have been initiated
+ self.LastRefreshTime = gettime()
+ # Flag indicating that DebugViewer has acquire common debug lock
+ self.HasAcquiredLock = False
+ # Lock for access to the two preceding variable
+ self.AccessLock = Lock()
+
+ # Timer to refresh Debug Viewer one last time in the case that a new
+ # value have been received during refresh was inhibited and no one
+ # after refresh was activated
+ self.LastRefreshTimer = None
+ # Lock for access to the timer
+ self.TimerAccessLock = Lock()
+
+ # Set DataProducer and subscribe tick if needed
+ self.SetDataProducer(producer)
+
+ def __del__(self):
+ """
+ Destructor
+ """
+ # Unsubscribe all data consumers
+ self.UnsubscribeAllDataConsumers()
+
+ # Delete reference to DataProducer
+ self.DataProducer = None
+
+ # Stop last refresh timer
+ if self.LastRefreshTimer is not None:
+ self.LastRefreshTimer.cancel()
+
+ # Release Common debug lock if DebugViewer has acquired it
+ if self.HasAcquiredLock:
+ DEBUG_REFRESH_LOCK.release()
+
+ def SetDataProducer(self, producer):
+ """
+ Set Data Producer
+ @param producer: Data Producer
+ """
+ # In the case that tick need to be subscribed and DebugViewer is
+ # debugging
+ if self.SubscribeTick and self.Debug:
+
+ # Subscribe tick to new data producer
+ if producer is not None:
+ producer.SubscribeDebugIECVariable("__tick__", self)
+
+ # Unsubscribe tick from old data producer
+ if getattr(self, "DataProducer", None) is not None:
+ self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
+
+ # Save new data producer
+ self.DataProducer = producer
+
+ def IsDebugging(self):
+ """
+ Get flag indicating if Debug Viewer is debugging
+ @return: Debugging flag
+ """
+ return self.Debug
+
+ def Inhibit(self, inhibit):
+ """
+ Set consumer value update inhibit flag
+ @param inhibit: Inhibit flag
+ """
+ # Inhibit every data consumers in list
+ for consumer, iec_path in self.DataConsumers.iteritems():
+ consumer.Inhibit(inhibit)
+
+ # Save inhibit flag
+ self.Inhibited = inhibit
+
+ def AddDataConsumer(self, iec_path, consumer):
+ """
+ Subscribe data consumer to DataProducer
+ @param iec_path: Path in PLC of variable needed by data consumer
+ @param consumer: Data consumer to subscribe
+ @return: List of value already received [(tick, data),...] (None if
+ subscription failed)
+ """
+ # Return immediately if no DataProducer defined
+ if self.DataProducer is None:
+ return None
+
+ # Subscribe data consumer to DataProducer
+ result = self.DataProducer.SubscribeDebugIECVariable(
+ iec_path, consumer)
+ if result is not None and consumer != self:
+
+ # Store data consumer if successfully subscribed and inform
+ # consumer of variable data type
+ self.DataConsumers[consumer] = iec_path
+ consumer.SetDataType(self.GetDataType(iec_path))
+
+ return result
+
+ def RemoveDataConsumer(self, consumer):
+ """
+ Unsubscribe data consumer from DataProducer
+ @param consumer: Data consumer to unsubscribe
+ """
+ # Remove consumer from data consumer list
+ iec_path = self.DataConsumers.pop(consumer, None)
+
+ # Unsubscribe consumer from DataProducer
+ if iec_path is not None:
+ self.DataProducer.UnsubscribeDebugIECVariable(
+ iec_path, consumer)
+
+ def SubscribeAllDataConsumers(self):
+ """
+ Called to Subscribe all data consumers contained in DebugViewer.
+ May be overridden by inherited classes.
+ """
+ # Subscribe tick if needed
+ if self.SubscribeTick and self.Debug and self.DataProducer is not None:
+ self.DataProducer.SubscribeDebugIECVariable("__tick__", self)
+
+ def UnsubscribeAllDataConsumers(self):
+ """
+ Called to Unsubscribe all data consumers.
+ """
+ if self.DataProducer is not None:
+
+ # Unsubscribe all data consumers in list
+ for consumer, iec_path in self.DataConsumers.iteritems():
+ self.DataProducer.UnsubscribeDebugIECVariable(
+ iec_path, consumer)
+
+ # Unscribe tick if needed
+ if self.SubscribeTick and self.Debug:
+ self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
+
+ self.DataConsumers = {}
+
+ def GetDataType(self, iec_path):
+ """
+ Return variable data type.
+ @param iec_path: Path in PLC of variable
+ @return: variable data type (None if not found)
+ """
+ if self.DataProducer is not None:
+
+ # Search for variable informations in project compilation files
+ data_type = self.DataProducer.GetDebugIECVariableType(
+ iec_path.upper())
+ if data_type is not None:
+ return data_type
+
+ # Search for variable informations in project data
+ infos = self.DataProducer.GetInstanceInfos(iec_path)
+ if infos is not None:
+ return infos["type"]
+
+ return None
+
+ def IsNumType(self, data_type):
+ """
+ Indicate if data type given is a numeric data type
+ @param data_type: Data type to test
+ @return: True if data type given is numeric
+ """
+ if self.DataProducer is not None:
+ return self.DataProducer.IsNumType(data_type)
+
+ return False
+
+ def ForceDataValue(self, iec_path, value):
+ """
+ Force PLC variable value
+ @param iec_path: Path in PLC of variable to force
+ @param value: Value forced
+ """
+ if self.DataProducer is not None:
+ self.DataProducer.ForceDebugIECVariable(iec_path, value)
+
+ def ReleaseDataValue(self, iec_path):
+ """
+ Release PLC variable value
+ @param iec_path: Path in PLC of variable to release
+ """
+ if self.DataProducer is not None:
+ self.DataProducer.ReleaseDebugIECVariable(iec_path)
+
+ def NewDataAvailable(self, tick, *args, **kwargs):
+ """
+ Called by DataProducer for each tick captured in
+ @param tick: PLC tick captured
+ All other parameters are passed to refresh function
+ """
+ # Stop last refresh timer
+ self.TimerAccessLock.acquire()
+ if self.LastRefreshTimer is not None:
+ self.LastRefreshTimer.cancel()
+ self.LastRefreshTimer=None
+ self.TimerAccessLock.release()
+
+ # Only try to refresh DebugViewer if it is visible on screen and not
+ # already refreshing
+ if self.IsShown() and not self.Inhibited:
+
+ # Try to get acquire common refresh lock if minimum period between
+ # two refresh has expired
+ if gettime() - self.LastRefreshTime > REFRESH_PERIOD and \
+ DEBUG_REFRESH_LOCK.acquire(False):
+ self.StartRefreshing(*args, **kwargs)
+
+ # If common lock wasn't acquired for any reason, restart last
+ # refresh timer
+ else:
+ self.StartLastRefreshTimer(*args, **kwargs)
+
+ # In the case that DebugViewer isn't visible on screen and has already
+ # acquired common refresh lock, reset DebugViewer
+ elif not self.IsShown() and self.HasAcquiredLock:
+ DebugViewer.RefreshNewData(self)
+
+ def ShouldRefresh(self, *args, **kwargs):
+ """
+ Callback function called when last refresh timer expired
+ All parameters are passed to refresh function
+ """
+ # Cancel if DebugViewer is not visible on screen
+ if self and self.IsShown():
+
+ # Try to acquire common refresh lock
+ if DEBUG_REFRESH_LOCK.acquire(False):
+ self.StartRefreshing(*args, **kwargs)
+
+ # Restart last refresh timer if common refresh lock acquired failed
+ else:
+ self.StartLastRefreshTimer(*args, **kwargs)
+
+ def StartRefreshing(self, *args, **kwargs):
+ """
+ Called to initiate a refresh of DebugViewer
+ All parameters are passed to refresh function
+ """
+ # Update last refresh time stamp and flag for common refresh
+ # lock acquired
+ self.AccessLock.acquire()
+ self.HasAcquiredLock = True
+ self.LastRefreshTime = gettime()
+ self.AccessLock.release()
+
+ # Inhibit data consumer value update
+ self.Inhibit(True)
+
+ # Initiate DebugViewer refresh
+ wx.CallAfter(self.RefreshNewData, *args, **kwargs)
+
+ def StartLastRefreshTimer(self, *args, **kwargs):
+ """
+ Called to start last refresh timer for the minimum time between 2
+ refresh
+ All parameters are passed to refresh function
+ """
+ self.TimerAccessLock.acquire()
+ self.LastRefreshTimer = Timer(
+ REFRESH_PERIOD, self.ShouldRefresh, args, kwargs)
+ self.LastRefreshTimer.start()
+ self.TimerAccessLock.release()
+
+ def RefreshNewData(self, *args, **kwargs):
+ """
+ Called to refresh DebugViewer according to values received by data
+ consumers
+ May be overridden by inherited classes
+ Can receive any parameters depending on what is needed by inherited
+ class
+ """
+ if self:
+ # Activate data consumer value update
+ self.Inhibit(False)
+
+ # Release common refresh lock if acquired and update
+ # last refresh time
+ self.AccessLock.acquire()
+ if self.HasAcquiredLock:
+ DEBUG_REFRESH_LOCK.release()
+ self.HasAcquiredLock = False
+ if gettime() - self.LastRefreshTime > REFRESH_PERIOD:
+ self.LastRefreshTime = gettime()
+ self.AccessLock.release()
--- a/editors/GraphicViewer.py Mon May 27 09:24:39 2013 +0200
+++ b/editors/GraphicViewer.py Mon May 27 13:09:54 2013 +0200
@@ -29,8 +29,9 @@
import wx.lib.plot as plot
import wx.lib.buttons
-from graphics.GraphicCommons import DebugViewer, MODE_SELECTION, MODE_MOTION
-from EditorPanel import EditorPanel
+from graphics.GraphicCommons import MODE_SELECTION, MODE_MOTION
+from editors.DebugViewer import DebugViewer
+from editors.EditorPanel import EditorPanel
from util.BitmapLibrary import GetBitmap
colours = ['blue', 'red', 'green', 'yellow', 'orange', 'purple', 'brown', 'cyan',
--- a/editors/Viewer.py Mon May 27 09:24:39 2013 +0200
+++ b/editors/Viewer.py Mon May 27 13:09:54 2013 +0200
@@ -35,6 +35,7 @@
from dialogs import *
from graphics import *
+from editors.DebugViewer import DebugViewer
from EditorPanel import EditorPanel
SCROLLBAR_UNIT = 10
@@ -375,7 +376,7 @@
manipulating graphic elements
"""
-class Viewer(EditorPanel, DebugViewer, DebugDataConsumer):
+class Viewer(EditorPanel, DebugViewer):
if wx.VERSION < (2, 6, 0):
def Bind(self, event, function, id = None):
@@ -556,7 +557,6 @@
EditorPanel.__init__(self, parent, tagname, window, controler, debug)
DebugViewer.__init__(self, controler, debug)
- DebugDataConsumer.__init__(self)
# Adding a rubberband to Viewer
self.rubberBand = RubberBand(viewer=self)
@@ -892,7 +892,7 @@
self.ToolTipElement = None
def Flush(self):
- self.DeleteDataConsumers()
+ self.UnsubscribeAllDataConsumers()
for block in self.Blocks.itervalues():
block.Flush()
@@ -1048,8 +1048,8 @@
else:
DebugViewer.RefreshNewData(self)
- def RegisterVariables(self):
- DebugViewer.RegisterVariables(self)
+ def SubscribeAllDataConsumers(self):
+ DebugViewer.SubscribeAllDataConsumers(self)
self.RefreshView()
# Refresh Viewer elements
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/graphics/DebugDataConsumer.py Mon May 27 13:09:54 2013 +0200
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
+#based on the plcopen standard.
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import datetime
+
+#-------------------------------------------------------------------------------
+# Date and Time conversion function
+#-------------------------------------------------------------------------------
+
+SECOND = 1000000 # Number of microseconds in one second
+MINUTE = 60 * SECOND # Number of microseconds in one minute
+HOUR = 60 * MINUTE # Number of microseconds in one hour
+DAY = 24 * HOUR # Number of microseconds in one day
+
+# Date corresponding to Epoch (1970 January the first)
+DATE_ORIGIN = datetime.datetime(1970, 1, 1)
+
+def get_microseconds(value):
+ """
+ Function converting time duration expressed in day, second and microseconds
+ into one expressed in microseconds
+ @param value: Time duration to convert
+ @return: Time duration expressed in microsecond
+ """
+ return float(value.days * DAY + \
+ value.seconds * SECOND + \
+ value.microseconds)
+ return
+
+def generate_time(value):
+ """
+ Function converting time duration expressed in day, second and microseconds
+ into a IEC 61131 TIME literal
+ @param value: Time duration to convert
+ @return: IEC 61131 TIME literal
+ """
+ microseconds = get_microseconds(value)
+
+ # Get absolute microseconds value and save if it was negative
+ negative = microseconds < 0
+ microseconds = abs(microseconds)
+
+ # TIME literal prefix
+ data = "T#"
+ if negative:
+ data += "-"
+
+ # In TIME literal format, it isn't mandatory to indicate null values
+ # if no greater non-null values are available. This variable is used to
+ # inhibit formatting until a non-null value is found
+ not_null = False
+
+ for val, format in [
+ (int(microseconds) / DAY, "%dd"), # Days
+ ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours
+ ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes
+ ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds
+
+ # Add value to TIME literal if value is non-null or another non-null
+ # value have already be found
+ if val > 0 or not_null:
+ data += format % val
+
+ # Update non-null variable
+ not_null = True
+
+ # In any case microseconds have to be added to TIME literal
+ data += "%gms" % (microseconds % SECOND / 1000.)
+
+ return data
+
+def generate_date(value):
+ """
+ Function converting time duration expressed in day, second and microseconds
+ into a IEC 61131 DATE literal
+ @param value: Time duration to convert
+ @return: IEC 61131 DATE literal
+ """
+ return (DATE_ORIGIN + value).strftime("DATE#%Y-%m-%d")
+
+def generate_datetime(value):
+ """
+ Function converting time duration expressed in day, second and microseconds
+ into a IEC 61131 DATE_AND_TIME literal
+ @param value: Time duration to convert
+ @return: IEC 61131 DATE_AND_TIME literal
+ """
+ return (DATE_ORIGIN + value).strftime("DT#%Y-%m-%d-%H:%M:%S.%f")
+
+def generate_timeofday(value):
+ """
+ Function converting time duration expressed in day, second and microseconds
+ into a IEC 61131 TIME_OF_DAY literal
+ @param value: Time duration to convert
+ @return: IEC 61131 TIME_OF_DAY literal
+ """
+ microseconds = get_microseconds(value)
+
+ # TIME_OF_DAY literal prefix
+ data = "TOD#"
+
+ for val, format in [
+ (int(microseconds) / HOUR, "%2.2d:"), # Hours
+ ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), # Minutes
+ ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), # Seconds
+ (microseconds % SECOND, "%6.6d")]: # Microseconds
+
+ # Add value to TIME_OF_DAY literal
+ data += format % val
+
+ return data
+
+# Dictionary of translation functions from value send by debugger to IEC
+# literal stored by type
+TYPE_TRANSLATOR = {
+ "TIME": generate_time,
+ "DATE": generate_date,
+ "DT": generate_datetime,
+ "TOD": generate_timeofday,
+ "STRING": lambda v: "'%s'" % v,
+ "WSTRING": lambda v: '"%s"' % v,
+ "REAL": lambda v: "%.6g" % v,
+ "LREAL": lambda v: "%.6g" % v,
+ "BOOL": lambda v: v}
+
+#-------------------------------------------------------------------------------
+# Debug Data Consumer Class
+#-------------------------------------------------------------------------------
+
+"""
+Class that implements an element that consumes debug values
+Value update can be inhibited during the time the associated Debug Viewer is
+refreshing
+"""
+
+class DebugDataConsumer:
+
+ def __init__(self):
+ """
+ Constructor
+ """
+ # Debug value and forced flag
+ self.Value = None
+ self.Forced = False
+
+ # Store debug value and forced flag when value update is inhibited
+ self.LastValue = None
+ self.LastForced = False
+
+ # Value IEC data type
+ self.DataType = None
+
+ # Flag that value update is inhibited
+ self.Inhibited = False
+
+ def Inhibit(self, inhibit):
+ """
+ Set flag to inhibit or activate value update
+ @param inhibit: Inhibit flag
+ """
+ # Save inhibit flag
+ self.Inhibited = inhibit
+
+ # When reactivated update value and forced flag with stored values
+ if not inhibit and self.LastValue is not None:
+ self.SetForced(self.LastForced)
+ self.SetValue(self.LastValue)
+
+ # Reset stored values
+ self.LastValue = None
+ self.LastForced = False
+
+ def SetDataType(self, data_type):
+ """
+ Set value IEC data type
+ @param data_type: Value IEC data type
+ """
+ self.DataType = data_type
+
+ def NewValue(self, tick, value, forced=False):
+ """
+ Function called by debug thread when a new debug value is available
+ @param tick: PLC tick when value was captured
+ @param value: Value captured
+ @param forced: Forced flag, True if value is forced (default: False)
+ """
+ # Translate value to IEC literal
+ value = TYPE_TRANSLATOR.get(self.DataType, str)(value)
+
+ # Store value and forced flag when value update is inhibited
+ if self.Inhibited:
+ self.LastValue = value
+ self.LastForced = forced
+
+ # Update value and forced flag in any other case
+ else:
+ self.SetForced(forced)
+ self.SetValue(value)
+
+ def SetValue(self, value):
+ """
+ Update value.
+ May be overridden by inherited classes
+ @param value: New value
+ """
+ self.Value = value
+
+ def GetValue(self):
+ """
+ Return current value
+ @return: Current value
+ """
+ return self.Value
+
+ def SetForced(self, forced):
+ """
+ Update Forced flag. May be overridden by inherited classes
+ @param forced: New forced flag
+ """
+ self.Forced = forced
+
+ def IsForced(self):
+ """
+ Indicate if current value is forced
+ @return: Current forced flag
+ """
+ return self.Forced
--- a/graphics/FBD_Objects.py Mon May 27 09:24:39 2013 +0200
+++ b/graphics/FBD_Objects.py Mon May 27 13:09:54 2013 +0200
@@ -24,7 +24,7 @@
import wx
-from GraphicCommons import *
+from graphics.GraphicCommons import *
from plcopen.structures import *
#-------------------------------------------------------------------------------
--- a/graphics/GraphicCommons.py Mon May 27 09:24:39 2013 +0200
+++ b/graphics/GraphicCommons.py Mon May 27 13:09:54 2013 +0200
@@ -23,13 +23,13 @@
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import wx
-from time import time as gettime
from math import *
from types import *
import datetime
from threading import Lock,Timer
from graphics.ToolTipProducer import ToolTipProducer
+from graphics.DebugDataConsumer import DebugDataConsumer
#-------------------------------------------------------------------------------
# Common constants
@@ -186,56 +186,6 @@
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
-
-def generate_date(value):
- base_date = datetime.datetime(1970, 1, 1)
- date = base_date + value
- return date.strftime("DATE#%Y-%m-%d")
-
-def generate_datetime(value):
- base_date = datetime.datetime(1970, 1, 1)
- date_time = base_date + value
- return date_time.strftime("DT#%Y-%m-%d-%H:%M:%S.%f")
-
-def generate_timeofday(value):
- microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
- negative = microseconds < 0
- microseconds = abs(microseconds)
- data = "TOD#"
- for val, format in [(int(microseconds) / HOUR, "%2.2d:"),
- ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"),
- ((int(microseconds) % MINUTE) / SECOND, "%2.2d."),
- (microseconds % SECOND, "%6.6d")]:
- data += format % val
- return data
-
-TYPE_TRANSLATOR = {"TIME": generate_time,
- "DATE": generate_date,
- "DT": generate_datetime,
- "TOD": generate_timeofday}
-
def MiterPen(colour, width=1, style=wx.SOLID):
pen = wx.Pen(colour, width, style)
pen.SetJoin(wx.JOIN_MITER)
@@ -243,200 +193,6 @@
return pen
#-------------------------------------------------------------------------------
-# Debug Data Consumer Class
-#-------------------------------------------------------------------------------
-
-class DebugDataConsumer:
-
- def __init__(self):
- self.LastValue = None
- self.Value = None
- self.DataType = None
- self.LastForced = False
- self.Forced = False
- self.Inhibited = False
-
- def Inhibit(self, inhibit):
- self.Inhibited = inhibit
- if not inhibit and self.LastValue is not None:
- 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
-
- def GetValue(self):
- return self.Value
-
- def SetForced(self, forced):
- self.Forced = forced
-
- def IsForced(self):
- return self.Forced
-
-#-------------------------------------------------------------------------------
-# Debug Viewer Class
-#-------------------------------------------------------------------------------
-
-REFRESH_PERIOD = 0.1
-DEBUG_REFRESH_LOCK = Lock()
-
-class DebugViewer:
-
- def __init__(self, producer, debug, register_tick=True):
- self.DataProducer = None
- self.Debug = debug
- self.RegisterTick = register_tick
- self.Inhibited = False
-
- self.DataConsumers = {}
-
- self.LastRefreshTime = gettime()
- self.HasAcquiredLock = False
- self.AccessLock = Lock()
- self.TimerAccessLock = Lock()
-
- self.LastRefreshTimer = None
-
- self.SetDataProducer(producer)
-
- def __del__(self):
- self.DataProducer = None
- self.DeleteDataConsumers()
- if self.LastRefreshTimer is not None:
- self.LastRefreshTimer.Stop()
- if self.HasAcquiredLock:
- DEBUG_REFRESH_LOCK.release()
-
- def SetDataProducer(self, producer):
- if self.RegisterTick and self.Debug:
- if producer is not None:
- producer.SubscribeDebugIECVariable("__tick__", self)
- if self.DataProducer is not None:
- self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
- self.DataProducer = producer
-
- def IsDebugging(self):
- return self.Debug
-
- def Inhibit(self, inhibit):
- for consumer, iec_path in self.DataConsumers.iteritems():
- consumer.Inhibit(inhibit)
- self.Inhibited = inhibit
-
- def AddDataConsumer(self, iec_path, consumer):
- if self.DataProducer is None:
- return None
- result = self.DataProducer.SubscribeDebugIECVariable(iec_path, consumer)
- 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):
- iec_path = self.DataConsumers.pop(consumer, None)
- if iec_path is not None:
- self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
-
- def RegisterVariables(self):
- if self.RegisterTick and self.Debug and self.DataProducer is not None:
- self.DataProducer.SubscribeDebugIECVariable("__tick__", self)
-
- def GetDataType(self, iec_path):
- if self.DataProducer is not None:
- data_type = self.DataProducer.GetDebugIECVariableType(iec_path.upper())
- if data_type is not None:
- return data_type
-
- infos = self.DataProducer.GetInstanceInfos(iec_path)
- if infos is not None:
- return infos["type"]
- return None
-
- def IsNumType(self, data_type):
- return self.DataProducer.IsNumType(data_type)
-
- def ForceDataValue(self, iec_path, value):
- if self.DataProducer is not None:
- self.DataProducer.ForceDebugIECVariable(iec_path, value)
-
- def ReleaseDataValue(self, iec_path):
- if self.DataProducer is not None:
- self.DataProducer.ReleaseDebugIECVariable(iec_path)
-
- def DeleteDataConsumers(self):
- if self.DataProducer is not None:
- for consumer, iec_path in self.DataConsumers.iteritems():
- self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
- self.DataConsumers = {}
-
- def ShouldRefresh(self):
- if self:
- wx.CallAfter(self._ShouldRefresh)
-
- def _ShouldRefresh(self):
- if self:
- if DEBUG_REFRESH_LOCK.acquire(False):
- self.AccessLock.acquire()
- self.HasAcquiredLock = True
- self.AccessLock.release()
- self.RefreshNewData()
- else:
- self.TimerAccessLock.acquire()
- self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh)
- self.LastRefreshTimer.start()
- self.TimerAccessLock.release()
-
- def NewDataAvailable(self, tick, *args, **kwargs):
- self.TimerAccessLock.acquire()
- if self.LastRefreshTimer is not None:
- self.LastRefreshTimer.cancel()
- self.LastRefreshTimer=None
- self.TimerAccessLock.release()
- if self.IsShown() and not self.Inhibited:
- if gettime() - self.LastRefreshTime > REFRESH_PERIOD and DEBUG_REFRESH_LOCK.acquire(False):
- self.AccessLock.acquire()
- self.HasAcquiredLock = True
- self.AccessLock.release()
- self.LastRefreshTime = gettime()
- self.Inhibit(True)
- wx.CallAfter(self.RefreshViewOnNewData, *args, **kwargs)
- else:
- self.TimerAccessLock.acquire()
- self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh)
- self.LastRefreshTimer.start()
- self.TimerAccessLock.release()
- elif not self.IsShown() and self.HasAcquiredLock:
- DebugViewer.RefreshNewData(self)
-
- def RefreshViewOnNewData(self, *args, **kwargs):
- if self:
- self.RefreshNewData(*args, **kwargs)
-
- def RefreshNewData(self, *args, **kwargs):
- self.Inhibit(False)
- self.AccessLock.acquire()
- if self.HasAcquiredLock:
- DEBUG_REFRESH_LOCK.release()
- self.HasAcquiredLock = False
- if gettime() - self.LastRefreshTime > REFRESH_PERIOD:
- self.LastRefreshTime = gettime()
- self.AccessLock.release()
-
-#-------------------------------------------------------------------------------
# Helpers for highlighting text
#-------------------------------------------------------------------------------
@@ -1377,13 +1133,7 @@
def GetComputedValue(self):
if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
- wire_type = self.GetType()
- if wire_type == "STRING":
- return "'%s'"%self.Value
- elif wire_type == "WSTRING":
- return "\"%s\""%self.Value
- else:
- return str(self.Value)
+ return self.Value
return None
def GetToolTipValue(self):
@@ -1960,13 +1710,7 @@
def GetComputedValue(self):
if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
- wire_type = self.GetEndConnectedType()
- if wire_type == "STRING":
- return "'%s'"%self.Value
- elif wire_type == "WSTRING":
- return "\"%s\""%self.Value
- else:
- return str(self.Value)
+ return self.Value
return None
def GetToolTipValue(self):
--- a/graphics/LD_Objects.py Mon May 27 09:24:39 2013 +0200
+++ b/graphics/LD_Objects.py Mon May 27 13:09:54 2013 +0200
@@ -24,7 +24,8 @@
import wx
-from GraphicCommons import *
+from graphics.GraphicCommons import *
+from graphics.DebugDataConsumer import DebugDataConsumer
from plcopen.structures import *
#-------------------------------------------------------------------------------
--- a/graphics/SFC_Objects.py Mon May 27 09:24:39 2013 +0200
+++ b/graphics/SFC_Objects.py Mon May 27 13:09:54 2013 +0200
@@ -24,7 +24,8 @@
import wx
-from GraphicCommons import *
+from graphics.GraphicCommons import *
+from graphics.DebugDataConsumer import DebugDataConsumer
from plcopen.structures import *
def GetWireSize(block):
--- a/graphics/__init__.py Mon May 27 09:24:39 2013 +0200
+++ b/graphics/__init__.py Mon May 27 13:09:54 2013 +0200
@@ -28,4 +28,5 @@
from FBD_Objects import *
from LD_Objects import *
from SFC_Objects import *
-from RubberBand import RubberBand
\ No newline at end of file
+from RubberBand import RubberBand
+from DebugDataConsumer import DebugDataConsumer
\ No newline at end of file