184 return (-v_base[0], -v_base[1]) |
184 return (-v_base[0], -v_base[1]) |
185 elif dir_product == 0 and product(v_base, dir_target) != 0: |
185 elif dir_product == 0 and product(v_base, dir_target) != 0: |
186 return dir_target |
186 return dir_target |
187 return v_base |
187 return v_base |
188 |
188 |
189 SECOND = 1000000 |
|
190 MINUTE = 60 * SECOND |
|
191 HOUR = 60 * MINUTE |
|
192 DAY = 24 * HOUR |
|
193 |
|
194 def generate_time(value): |
|
195 microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds) |
|
196 negative = microseconds < 0 |
|
197 microseconds = abs(microseconds) |
|
198 data = "T#" |
|
199 not_null = False |
|
200 if negative: |
|
201 data += "-" |
|
202 for val, format in [(int(microseconds) / DAY, "%dd"), |
|
203 ((int(microseconds) % DAY) / HOUR, "%dh"), |
|
204 ((int(microseconds) % HOUR) / MINUTE, "%dm"), |
|
205 ((int(microseconds) % MINUTE) / SECOND, "%ds")]: |
|
206 if val > 0 or not_null: |
|
207 data += format % val |
|
208 not_null = True |
|
209 data += "%gms" % (microseconds % SECOND / 1000.) |
|
210 return data |
|
211 |
|
212 def generate_date(value): |
|
213 base_date = datetime.datetime(1970, 1, 1) |
|
214 date = base_date + value |
|
215 return date.strftime("DATE#%Y-%m-%d") |
|
216 |
|
217 def generate_datetime(value): |
|
218 base_date = datetime.datetime(1970, 1, 1) |
|
219 date_time = base_date + value |
|
220 return date_time.strftime("DT#%Y-%m-%d-%H:%M:%S.%f") |
|
221 |
|
222 def generate_timeofday(value): |
|
223 microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds) |
|
224 negative = microseconds < 0 |
|
225 microseconds = abs(microseconds) |
|
226 data = "TOD#" |
|
227 for val, format in [(int(microseconds) / HOUR, "%2.2d:"), |
|
228 ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), |
|
229 ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), |
|
230 (microseconds % SECOND, "%6.6d")]: |
|
231 data += format % val |
|
232 return data |
|
233 |
|
234 TYPE_TRANSLATOR = {"TIME": generate_time, |
|
235 "DATE": generate_date, |
|
236 "DT": generate_datetime, |
|
237 "TOD": generate_timeofday} |
|
238 |
|
239 def MiterPen(colour, width=1, style=wx.SOLID): |
189 def MiterPen(colour, width=1, style=wx.SOLID): |
240 pen = wx.Pen(colour, width, style) |
190 pen = wx.Pen(colour, width, style) |
241 pen.SetJoin(wx.JOIN_MITER) |
191 pen.SetJoin(wx.JOIN_MITER) |
242 pen.SetCap(wx.CAP_PROJECTING) |
192 pen.SetCap(wx.CAP_PROJECTING) |
243 return pen |
193 return pen |
244 |
|
245 #------------------------------------------------------------------------------- |
|
246 # Debug Data Consumer Class |
|
247 #------------------------------------------------------------------------------- |
|
248 |
|
249 class DebugDataConsumer: |
|
250 |
|
251 def __init__(self): |
|
252 self.LastValue = None |
|
253 self.Value = None |
|
254 self.DataType = None |
|
255 self.LastForced = False |
|
256 self.Forced = False |
|
257 self.Inhibited = False |
|
258 |
|
259 def Inhibit(self, inhibit): |
|
260 self.Inhibited = inhibit |
|
261 if not inhibit and self.LastValue is not None: |
|
262 self.SetForced(self.LastForced) |
|
263 self.SetValue(self.LastValue) |
|
264 self.LastValue = None |
|
265 |
|
266 def SetDataType(self, data_type): |
|
267 self.DataType = data_type |
|
268 |
|
269 def NewValue(self, tick, value, forced=False): |
|
270 value = TYPE_TRANSLATOR.get(self.DataType, lambda x:x)(value) |
|
271 if self.Inhibited: |
|
272 self.LastValue = value |
|
273 self.LastForced = forced |
|
274 else: |
|
275 self.SetForced(forced) |
|
276 self.SetValue(value) |
|
277 |
|
278 def SetValue(self, value): |
|
279 self.Value = value |
|
280 |
|
281 def GetValue(self): |
|
282 return self.Value |
|
283 |
|
284 def SetForced(self, forced): |
|
285 self.Forced = forced |
|
286 |
|
287 def IsForced(self): |
|
288 return self.Forced |
|
289 |
|
290 #------------------------------------------------------------------------------- |
|
291 # Debug Viewer Class |
|
292 #------------------------------------------------------------------------------- |
|
293 |
|
294 REFRESH_PERIOD = 0.1 |
|
295 DEBUG_REFRESH_LOCK = Lock() |
|
296 |
|
297 class DebugViewer: |
|
298 |
|
299 def __init__(self, producer, debug, register_tick=True): |
|
300 self.DataProducer = None |
|
301 self.Debug = debug |
|
302 self.RegisterTick = register_tick |
|
303 self.Inhibited = False |
|
304 |
|
305 self.DataConsumers = {} |
|
306 |
|
307 self.LastRefreshTime = gettime() |
|
308 self.HasAcquiredLock = False |
|
309 self.AccessLock = Lock() |
|
310 self.TimerAccessLock = Lock() |
|
311 |
|
312 self.LastRefreshTimer = None |
|
313 |
|
314 self.SetDataProducer(producer) |
|
315 |
|
316 def __del__(self): |
|
317 self.DataProducer = None |
|
318 self.DeleteDataConsumers() |
|
319 if self.LastRefreshTimer is not None: |
|
320 self.LastRefreshTimer.Stop() |
|
321 if self.HasAcquiredLock: |
|
322 DEBUG_REFRESH_LOCK.release() |
|
323 |
|
324 def SetDataProducer(self, producer): |
|
325 if self.RegisterTick and self.Debug: |
|
326 if producer is not None: |
|
327 producer.SubscribeDebugIECVariable("__tick__", self) |
|
328 if self.DataProducer is not None: |
|
329 self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self) |
|
330 self.DataProducer = producer |
|
331 |
|
332 def IsDebugging(self): |
|
333 return self.Debug |
|
334 |
|
335 def Inhibit(self, inhibit): |
|
336 for consumer, iec_path in self.DataConsumers.iteritems(): |
|
337 consumer.Inhibit(inhibit) |
|
338 self.Inhibited = inhibit |
|
339 |
|
340 def AddDataConsumer(self, iec_path, consumer): |
|
341 if self.DataProducer is None: |
|
342 return None |
|
343 result = self.DataProducer.SubscribeDebugIECVariable(iec_path, consumer) |
|
344 if result is not None and consumer != self: |
|
345 self.DataConsumers[consumer] = iec_path |
|
346 consumer.SetDataType(self.GetDataType(iec_path)) |
|
347 return result |
|
348 |
|
349 def RemoveDataConsumer(self, consumer): |
|
350 iec_path = self.DataConsumers.pop(consumer, None) |
|
351 if iec_path is not None: |
|
352 self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer) |
|
353 |
|
354 def RegisterVariables(self): |
|
355 if self.RegisterTick and self.Debug and self.DataProducer is not None: |
|
356 self.DataProducer.SubscribeDebugIECVariable("__tick__", self) |
|
357 |
|
358 def GetDataType(self, iec_path): |
|
359 if self.DataProducer is not None: |
|
360 data_type = self.DataProducer.GetDebugIECVariableType(iec_path.upper()) |
|
361 if data_type is not None: |
|
362 return data_type |
|
363 |
|
364 infos = self.DataProducer.GetInstanceInfos(iec_path) |
|
365 if infos is not None: |
|
366 return infos["type"] |
|
367 return None |
|
368 |
|
369 def IsNumType(self, data_type): |
|
370 return self.DataProducer.IsNumType(data_type) |
|
371 |
|
372 def ForceDataValue(self, iec_path, value): |
|
373 if self.DataProducer is not None: |
|
374 self.DataProducer.ForceDebugIECVariable(iec_path, value) |
|
375 |
|
376 def ReleaseDataValue(self, iec_path): |
|
377 if self.DataProducer is not None: |
|
378 self.DataProducer.ReleaseDebugIECVariable(iec_path) |
|
379 |
|
380 def DeleteDataConsumers(self): |
|
381 if self.DataProducer is not None: |
|
382 for consumer, iec_path in self.DataConsumers.iteritems(): |
|
383 self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer) |
|
384 self.DataConsumers = {} |
|
385 |
|
386 def ShouldRefresh(self): |
|
387 if self: |
|
388 wx.CallAfter(self._ShouldRefresh) |
|
389 |
|
390 def _ShouldRefresh(self): |
|
391 if self: |
|
392 if DEBUG_REFRESH_LOCK.acquire(False): |
|
393 self.AccessLock.acquire() |
|
394 self.HasAcquiredLock = True |
|
395 self.AccessLock.release() |
|
396 self.RefreshNewData() |
|
397 else: |
|
398 self.TimerAccessLock.acquire() |
|
399 self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh) |
|
400 self.LastRefreshTimer.start() |
|
401 self.TimerAccessLock.release() |
|
402 |
|
403 def NewDataAvailable(self, tick, *args, **kwargs): |
|
404 self.TimerAccessLock.acquire() |
|
405 if self.LastRefreshTimer is not None: |
|
406 self.LastRefreshTimer.cancel() |
|
407 self.LastRefreshTimer=None |
|
408 self.TimerAccessLock.release() |
|
409 if self.IsShown() and not self.Inhibited: |
|
410 if gettime() - self.LastRefreshTime > REFRESH_PERIOD and DEBUG_REFRESH_LOCK.acquire(False): |
|
411 self.AccessLock.acquire() |
|
412 self.HasAcquiredLock = True |
|
413 self.AccessLock.release() |
|
414 self.LastRefreshTime = gettime() |
|
415 self.Inhibit(True) |
|
416 wx.CallAfter(self.RefreshViewOnNewData, *args, **kwargs) |
|
417 else: |
|
418 self.TimerAccessLock.acquire() |
|
419 self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh) |
|
420 self.LastRefreshTimer.start() |
|
421 self.TimerAccessLock.release() |
|
422 elif not self.IsShown() and self.HasAcquiredLock: |
|
423 DebugViewer.RefreshNewData(self) |
|
424 |
|
425 def RefreshViewOnNewData(self, *args, **kwargs): |
|
426 if self: |
|
427 self.RefreshNewData(*args, **kwargs) |
|
428 |
|
429 def RefreshNewData(self, *args, **kwargs): |
|
430 self.Inhibit(False) |
|
431 self.AccessLock.acquire() |
|
432 if self.HasAcquiredLock: |
|
433 DEBUG_REFRESH_LOCK.release() |
|
434 self.HasAcquiredLock = False |
|
435 if gettime() - self.LastRefreshTime > REFRESH_PERIOD: |
|
436 self.LastRefreshTime = gettime() |
|
437 self.AccessLock.release() |
|
438 |
194 |
439 #------------------------------------------------------------------------------- |
195 #------------------------------------------------------------------------------- |
440 # Helpers for highlighting text |
196 # Helpers for highlighting text |
441 #------------------------------------------------------------------------------- |
197 #------------------------------------------------------------------------------- |
442 |
198 |