editors/DebugViewer.py
changeset 1730 64d8f52bc8c8
parent 1571 486f94a8032c
child 1736 7e61baa047f0
equal deleted inserted replaced
1726:d51af006fa6b 1730:64d8f52bc8c8
    33 #-------------------------------------------------------------------------------
    33 #-------------------------------------------------------------------------------
    34 #                               Debug Viewer Class
    34 #                               Debug Viewer Class
    35 #-------------------------------------------------------------------------------
    35 #-------------------------------------------------------------------------------
    36 
    36 
    37 """
    37 """
    38 Class that implements common behavior of every viewers able to display debug 
    38 Class that implements common behavior of every viewers able to display debug
    39 values
    39 values
    40 """
    40 """
    41 
    41 
    42 class DebugViewer:
    42 class DebugViewer:
    43     
    43 
    44     def __init__(self, producer, debug, subscribe_tick=True):
    44     def __init__(self, producer, debug, subscribe_tick=True):
    45         """
    45         """
    46         Constructor
    46         Constructor
    47         @param producer: Object receiving debug value and dispatching them to
    47         @param producer: Object receiving debug value and dispatching them to
    48         consumers
    48         consumers
    50         @param subscribe_tick: Flag indicating that viewer need tick value to
    50         @param subscribe_tick: Flag indicating that viewer need tick value to
    51         synchronize
    51         synchronize
    52         """
    52         """
    53         self.Debug = debug
    53         self.Debug = debug
    54         self.SubscribeTick = subscribe_tick
    54         self.SubscribeTick = subscribe_tick
    55         
    55 
    56         # Flag indicating that consumer value update inhibited
    56         # Flag indicating that consumer value update inhibited
    57         # (DebugViewer is refreshing)
    57         # (DebugViewer is refreshing)
    58         self.Inhibited = False
    58         self.Inhibited = False
    59         
    59 
    60         # List of data consumers subscribed to DataProducer
    60         # List of data consumers subscribed to DataProducer
    61         self.DataConsumers = {}
    61         self.DataConsumers = {}
    62         
    62 
    63         # Time stamp indicating when last refresh have been initiated
    63         # Time stamp indicating when last refresh have been initiated
    64         self.LastRefreshTime = gettime()
    64         self.LastRefreshTime = gettime()
    65         # Flag indicating that DebugViewer has acquire common debug lock
    65         # Flag indicating that DebugViewer has acquire common debug lock
    66         self.HasAcquiredLock = False
    66         self.HasAcquiredLock = False
    67         # Lock for access to the two preceding variable
    67         # Lock for access to the two preceding variable
    68         self.AccessLock = Lock()
    68         self.AccessLock = Lock()
    69         
    69 
    70         # Timer to refresh Debug Viewer one last time in the case that a new
    70         # Timer to refresh Debug Viewer one last time in the case that a new
    71         # value have been received during refresh was inhibited and no one
    71         # value have been received during refresh was inhibited and no one
    72         # after refresh was activated
    72         # after refresh was activated
    73         self.LastRefreshTimer = None
    73         self.LastRefreshTimer = None
    74         # Lock for access to the timer
    74         # Lock for access to the timer
    75         self.TimerAccessLock = Lock()
    75         self.TimerAccessLock = Lock()
    76         
    76 
    77         # Set DataProducer and subscribe tick if needed
    77         # Set DataProducer and subscribe tick if needed
    78         self.SetDataProducer(producer)
    78         self.SetDataProducer(producer)
    79         
    79 
    80     def __del__(self):
    80     def __del__(self):
    81         """
    81         """
    82         Destructor
    82         Destructor
    83         """
    83         """
    84         # Unsubscribe all data consumers
    84         # Unsubscribe all data consumers
    85         self.UnsubscribeAllDataConsumers()
    85         self.UnsubscribeAllDataConsumers()
    86         
    86 
    87         # Delete reference to DataProducer
    87         # Delete reference to DataProducer
    88         self.DataProducer = None
    88         self.DataProducer = None
    89         
    89 
    90         # Stop last refresh timer
    90         # Stop last refresh timer
    91         if self.LastRefreshTimer is not None:
    91         if self.LastRefreshTimer is not None:
    92             self.LastRefreshTimer.cancel()
    92             self.LastRefreshTimer.cancel()
    93         
    93 
    94         # Release Common debug lock if DebugViewer has acquired it
    94         # Release Common debug lock if DebugViewer has acquired it
    95         if self.HasAcquiredLock:
    95         if self.HasAcquiredLock:
    96             DEBUG_REFRESH_LOCK.release()
    96             DEBUG_REFRESH_LOCK.release()
    97     
    97 
    98     def SetDataProducer(self, producer):
    98     def SetDataProducer(self, producer):
    99         """
    99         """
   100         Set Data Producer
   100         Set Data Producer
   101         @param producer: Data Producer
   101         @param producer: Data Producer
   102         """
   102         """
   103         # In the case that tick need to be subscribed and DebugViewer is
   103         # In the case that tick need to be subscribed and DebugViewer is
   104         # debugging
   104         # debugging
   105         if self.SubscribeTick and self.Debug:
   105         if self.SubscribeTick and self.Debug:
   106             
   106 
   107             # Subscribe tick to new data producer
   107             # Subscribe tick to new data producer
   108             if producer is not None:
   108             if producer is not None:
   109                 producer.SubscribeDebugIECVariable("__tick__", self, True)
   109                 producer.SubscribeDebugIECVariable("__tick__", self, True)
   110             
   110 
   111             # Unsubscribe tick from old data producer
   111             # Unsubscribe tick from old data producer
   112             if getattr(self, "DataProducer", None) is not None:
   112             if getattr(self, "DataProducer", None) is not None:
   113                 self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
   113                 self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
   114         
   114 
   115         # Save new data producer
   115         # Save new data producer
   116         self.DataProducer = producer
   116         self.DataProducer = producer
   117     
   117 
   118     def IsDebugging(self):
   118     def IsDebugging(self):
   119         """
   119         """
   120         Get flag indicating if Debug Viewer is debugging
   120         Get flag indicating if Debug Viewer is debugging
   121         @return: Debugging flag
   121         @return: Debugging flag
   122         """
   122         """
   123         return self.Debug
   123         return self.Debug
   124     
   124 
   125     def Inhibit(self, inhibit):
   125     def Inhibit(self, inhibit):
   126         """
   126         """
   127         Set consumer value update inhibit flag
   127         Set consumer value update inhibit flag
   128         @param inhibit: Inhibit flag
   128         @param inhibit: Inhibit flag
   129         """
   129         """
   130         # Inhibit every data consumers in list
   130         # Inhibit every data consumers in list
   131         for consumer, iec_path in self.DataConsumers.iteritems():
   131         for consumer, iec_path in self.DataConsumers.iteritems():
   132             consumer.Inhibit(inhibit)
   132             consumer.Inhibit(inhibit)
   133         
   133 
   134         # Save inhibit flag
   134         # Save inhibit flag
   135         self.Inhibited = inhibit
   135         self.Inhibited = inhibit
   136     
   136 
   137     def AddDataConsumer(self, iec_path, consumer, buffer_list=False):
   137     def AddDataConsumer(self, iec_path, consumer, buffer_list=False):
   138         """
   138         """
   139         Subscribe data consumer to DataProducer
   139         Subscribe data consumer to DataProducer
   140         @param iec_path: Path in PLC of variable needed by data consumer
   140         @param iec_path: Path in PLC of variable needed by data consumer
   141         @param consumer: Data consumer to subscribe
   141         @param consumer: Data consumer to subscribe
   143         subscription failed)
   143         subscription failed)
   144         """
   144         """
   145         # Return immediately if no DataProducer defined
   145         # Return immediately if no DataProducer defined
   146         if self.DataProducer is None:
   146         if self.DataProducer is None:
   147             return None
   147             return None
   148         
   148 
   149         # Subscribe data consumer to DataProducer
   149         # Subscribe data consumer to DataProducer
   150         result = self.DataProducer.SubscribeDebugIECVariable(
   150         result = self.DataProducer.SubscribeDebugIECVariable(
   151                         iec_path, consumer, buffer_list)
   151                         iec_path, consumer, buffer_list)
   152         if result is not None and consumer != self:
   152         if result is not None and consumer != self:
   153             
   153 
   154             # Store data consumer if successfully subscribed and inform
   154             # Store data consumer if successfully subscribed and inform
   155             # consumer of variable data type
   155             # consumer of variable data type
   156             self.DataConsumers[consumer] = iec_path
   156             self.DataConsumers[consumer] = iec_path
   157             consumer.SetDataType(self.GetDataType(iec_path))
   157             consumer.SetDataType(self.GetDataType(iec_path))
   158         
   158 
   159         return result
   159         return result
   160     
   160 
   161     def RemoveDataConsumer(self, consumer):
   161     def RemoveDataConsumer(self, consumer):
   162         """
   162         """
   163         Unsubscribe data consumer from DataProducer
   163         Unsubscribe data consumer from DataProducer
   164         @param consumer: Data consumer to unsubscribe
   164         @param consumer: Data consumer to unsubscribe
   165         """
   165         """
   166         # Remove consumer from data consumer list
   166         # Remove consumer from data consumer list
   167         iec_path = self.DataConsumers.pop(consumer, None)
   167         iec_path = self.DataConsumers.pop(consumer, None)
   168         
   168 
   169         # Unsubscribe consumer from DataProducer
   169         # Unsubscribe consumer from DataProducer
   170         if iec_path is not None:
   170         if iec_path is not None:
   171             self.DataProducer.UnsubscribeDebugIECVariable(
   171             self.DataProducer.UnsubscribeDebugIECVariable(
   172                         iec_path, consumer)
   172                         iec_path, consumer)
   173     
   173 
   174     def SubscribeAllDataConsumers(self):
   174     def SubscribeAllDataConsumers(self):
   175         """
   175         """
   176         Called to Subscribe all data consumers contained in DebugViewer.
   176         Called to Subscribe all data consumers contained in DebugViewer.
   177         May be overridden by inherited classes.
   177         May be overridden by inherited classes.
   178         """
   178         """
   179         # Subscribe tick if needed
   179         # Subscribe tick if needed
   180         if self.SubscribeTick and self.Debug and self.DataProducer is not None:
   180         if self.SubscribeTick and self.Debug and self.DataProducer is not None:
   181             self.DataProducer.SubscribeDebugIECVariable("__tick__", self, True)
   181             self.DataProducer.SubscribeDebugIECVariable("__tick__", self, True)
   182     
   182 
   183     def UnsubscribeAllDataConsumers(self, tick=True):
   183     def UnsubscribeAllDataConsumers(self, tick=True):
   184         """
   184         """
   185         Called to Unsubscribe all data consumers.
   185         Called to Unsubscribe all data consumers.
   186         """
   186         """
   187         if self.DataProducer is not None:
   187         if self.DataProducer is not None:
   188             
   188 
   189             # Unscribe tick if needed
   189             # Unscribe tick if needed
   190             if self.SubscribeTick and tick and self.Debug:
   190             if self.SubscribeTick and tick and self.Debug:
   191                 self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
   191                 self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
   192             
   192 
   193             # Unsubscribe all data consumers in list
   193             # Unsubscribe all data consumers in list
   194             for consumer, iec_path in self.DataConsumers.iteritems():
   194             for consumer, iec_path in self.DataConsumers.iteritems():
   195                 self.DataProducer.UnsubscribeDebugIECVariable(
   195                 self.DataProducer.UnsubscribeDebugIECVariable(
   196                             iec_path, consumer)
   196                             iec_path, consumer)
   197         
   197 
   198         self.DataConsumers = {}
   198         self.DataConsumers = {}
   199     
   199 
   200     def GetDataType(self, iec_path):
   200     def GetDataType(self, iec_path):
   201         """
   201         """
   202         Return variable data type.
   202         Return variable data type.
   203         @param iec_path: Path in PLC of variable
   203         @param iec_path: Path in PLC of variable
   204         @return: variable data type (None if not found)
   204         @return: variable data type (None if not found)
   205         """
   205         """
   206         if self.DataProducer is not None:
   206         if self.DataProducer is not None:
   207             
   207 
   208             # Search for variable informations in project compilation files
   208             # Search for variable informations in project compilation files
   209             data_type = self.DataProducer.GetDebugIECVariableType(
   209             data_type = self.DataProducer.GetDebugIECVariableType(
   210                             iec_path.upper())
   210                             iec_path.upper())
   211             if data_type is not None:
   211             if data_type is not None:
   212                 return data_type
   212                 return data_type
   213             
   213 
   214             # Search for variable informations in project data
   214             # Search for variable informations in project data
   215             infos = self.DataProducer.GetInstanceInfos(iec_path)
   215             infos = self.DataProducer.GetInstanceInfos(iec_path)
   216             if infos is not None:
   216             if infos is not None:
   217                 return infos.type
   217                 return infos.type
   218         
   218 
   219         return None
   219         return None
   220     
   220 
   221     def IsNumType(self, data_type):
   221     def IsNumType(self, data_type):
   222         """
   222         """
   223         Indicate if data type given is a numeric data type
   223         Indicate if data type given is a numeric data type
   224         @param data_type: Data type to test
   224         @param data_type: Data type to test
   225         @return: True if data type given is numeric
   225         @return: True if data type given is numeric
   226         """
   226         """
   227         if self.DataProducer is not None:
   227         if self.DataProducer is not None:
   228             return self.DataProducer.IsNumType(data_type)
   228             return self.DataProducer.IsNumType(data_type)
   229         
   229 
   230         return False
   230         return False
   231     
   231 
   232     def ForceDataValue(self, iec_path, value):
   232     def ForceDataValue(self, iec_path, value):
   233         """
   233         """
   234         Force PLC variable value
   234         Force PLC variable value
   235         @param iec_path: Path in PLC of variable to force
   235         @param iec_path: Path in PLC of variable to force
   236         @param value: Value forced
   236         @param value: Value forced
   237         """
   237         """
   238         if self.DataProducer is not None:
   238         if self.DataProducer is not None:
   239             self.DataProducer.ForceDebugIECVariable(iec_path, value)
   239             self.DataProducer.ForceDebugIECVariable(iec_path, value)
   240     
   240 
   241     def ReleaseDataValue(self, iec_path):
   241     def ReleaseDataValue(self, iec_path):
   242         """
   242         """
   243         Release PLC variable value
   243         Release PLC variable value
   244         @param iec_path: Path in PLC of variable to release
   244         @param iec_path: Path in PLC of variable to release
   245         """
   245         """
   246         if self.DataProducer is not None:
   246         if self.DataProducer is not None:
   247             self.DataProducer.ReleaseDebugIECVariable(iec_path)
   247             self.DataProducer.ReleaseDebugIECVariable(iec_path)
   248     
   248 
   249     def NewDataAvailable(self, ticks):
   249     def NewDataAvailable(self, ticks):
   250         """
   250         """
   251         Called by DataProducer for each tick captured
   251         Called by DataProducer for each tick captured
   252         @param tick: PLC tick captured
   252         @param tick: PLC tick captured
   253         All other parameters are passed to refresh function 
   253         All other parameters are passed to refresh function
   254         """
   254         """
   255         # Stop last refresh timer
   255         # Stop last refresh timer
   256         self.TimerAccessLock.acquire()
   256         self.TimerAccessLock.acquire()
   257         if self.LastRefreshTimer is not None:
   257         if self.LastRefreshTimer is not None:
   258             self.LastRefreshTimer.cancel()
   258             self.LastRefreshTimer.cancel()
   259             self.LastRefreshTimer=None
   259             self.LastRefreshTimer=None
   260         self.TimerAccessLock.release()
   260         self.TimerAccessLock.release()
   261         
   261 
   262         # Only try to refresh DebugViewer if it is visible on screen and not
   262         # Only try to refresh DebugViewer if it is visible on screen and not
   263         # already refreshing
   263         # already refreshing
   264         if self.IsShown() and not self.Inhibited:
   264         if self.IsShown() and not self.Inhibited:
   265             
   265 
   266             # Try to get acquire common refresh lock if minimum period between
   266             # Try to get acquire common refresh lock if minimum period between
   267             # two refresh has expired
   267             # two refresh has expired
   268             if gettime() - self.LastRefreshTime > REFRESH_PERIOD and \
   268             if gettime() - self.LastRefreshTime > REFRESH_PERIOD and \
   269                DEBUG_REFRESH_LOCK.acquire(False):
   269                DEBUG_REFRESH_LOCK.acquire(False):
   270                 self.StartRefreshing()
   270                 self.StartRefreshing()
   271             
   271 
   272             # If common lock wasn't acquired for any reason, restart last
   272             # If common lock wasn't acquired for any reason, restart last
   273             # refresh timer
   273             # refresh timer
   274             else:
   274             else:
   275                 self.StartLastRefreshTimer()
   275                 self.StartLastRefreshTimer()
   276         
   276 
   277         # In the case that DebugViewer isn't visible on screen and has already
   277         # In the case that DebugViewer isn't visible on screen and has already
   278         # acquired common refresh lock, reset DebugViewer
   278         # acquired common refresh lock, reset DebugViewer
   279         elif not self.IsShown() and self.HasAcquiredLock:
   279         elif not self.IsShown() and self.HasAcquiredLock:
   280             DebugViewer.RefreshNewData(self)
   280             DebugViewer.RefreshNewData(self)
   281     
   281 
   282     def ShouldRefresh(self):
   282     def ShouldRefresh(self):
   283         """
   283         """
   284         Callback function called when last refresh timer expired
   284         Callback function called when last refresh timer expired
   285         All parameters are passed to refresh function
   285         All parameters are passed to refresh function
   286         """
   286         """
   287         # Cancel if DebugViewer is not visible on screen
   287         # Cancel if DebugViewer is not visible on screen
   288         if self and self.IsShown():
   288         if self and self.IsShown():
   289             
   289 
   290             # Try to acquire common refresh lock
   290             # Try to acquire common refresh lock
   291             if DEBUG_REFRESH_LOCK.acquire(False):
   291             if DEBUG_REFRESH_LOCK.acquire(False):
   292                 self.StartRefreshing()
   292                 self.StartRefreshing()
   293             
   293 
   294             # Restart last refresh timer if common refresh lock acquired failed
   294             # Restart last refresh timer if common refresh lock acquired failed
   295             else:
   295             else:
   296                 self.StartLastRefreshTimer()
   296                 self.StartLastRefreshTimer()
   297     
   297 
   298     def StartRefreshing(self):
   298     def StartRefreshing(self):
   299         """
   299         """
   300         Called to initiate a refresh of DebugViewer
   300         Called to initiate a refresh of DebugViewer
   301         All parameters are passed to refresh function
   301         All parameters are passed to refresh function
   302         """
   302         """
   304         # lock acquired
   304         # lock acquired
   305         self.AccessLock.acquire()
   305         self.AccessLock.acquire()
   306         self.HasAcquiredLock = True
   306         self.HasAcquiredLock = True
   307         self.LastRefreshTime = gettime()
   307         self.LastRefreshTime = gettime()
   308         self.AccessLock.release()
   308         self.AccessLock.release()
   309         
   309 
   310         # Inhibit data consumer value update
   310         # Inhibit data consumer value update
   311         self.Inhibit(True)
   311         self.Inhibit(True)
   312         
   312 
   313         # Initiate DebugViewer refresh
   313         # Initiate DebugViewer refresh
   314         wx.CallAfter(self.RefreshNewData)
   314         wx.CallAfter(self.RefreshNewData)
   315     
   315 
   316     def StartLastRefreshTimer(self):
   316     def StartLastRefreshTimer(self):
   317         """
   317         """
   318         Called to start last refresh timer for the minimum time between 2
   318         Called to start last refresh timer for the minimum time between 2
   319         refresh
   319         refresh
   320         All parameters are passed to refresh function
   320         All parameters are passed to refresh function
   322         self.TimerAccessLock.acquire()
   322         self.TimerAccessLock.acquire()
   323         self.LastRefreshTimer = Timer(
   323         self.LastRefreshTimer = Timer(
   324             REFRESH_PERIOD, self.ShouldRefresh)
   324             REFRESH_PERIOD, self.ShouldRefresh)
   325         self.LastRefreshTimer.start()
   325         self.LastRefreshTimer.start()
   326         self.TimerAccessLock.release()
   326         self.TimerAccessLock.release()
   327     
   327 
   328     def RefreshNewData(self):
   328     def RefreshNewData(self):
   329         """
   329         """
   330         Called to refresh DebugViewer according to values received by data
   330         Called to refresh DebugViewer according to values received by data
   331         consumers
   331         consumers
   332         May be overridden by inherited classes
   332         May be overridden by inherited classes
   333         Can receive any parameters depending on what is needed by inherited
   333         Can receive any parameters depending on what is needed by inherited
   334         class 
   334         class
   335         """
   335         """
   336         if self:
   336         if self:
   337             # Activate data consumer value update
   337             # Activate data consumer value update
   338             self.Inhibit(False)
   338             self.Inhibit(False)
   339             
   339 
   340             # Release common refresh lock if acquired and update
   340             # Release common refresh lock if acquired and update
   341             # last refresh time
   341             # last refresh time
   342             self.AccessLock.acquire()
   342             self.AccessLock.acquire()
   343             if self.HasAcquiredLock:
   343             if self.HasAcquiredLock:
   344                 DEBUG_REFRESH_LOCK.release()
   344                 DEBUG_REFRESH_LOCK.release()