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 """ |