QTools  8.0.4
Collection of Host-Based Tools
Loading...
Searching...
No Matches
qview.py
Go to the documentation of this file.
1#!/usr/bin/env python
2
3#=============================================================================
4# QView Monitoring for QP/Spy
5#
6# Q u a n t u m L e a P s
7# ------------------------
8# Modern Embedded Software
9#
10# Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
11#
12# SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
13#
14# This software is dual-licensed under the terms of the open source GNU
15# General Public License version 3 (or any later version), or alternatively,
16# under the terms of one of the closed source Quantum Leaps commercial
17# licenses.
18#
19# The terms of the open source GNU General Public License version 3
20# can be found at: <www.gnu.org/licenses/gpl-3.0>
21#
22# The terms of the closed source Quantum Leaps commercial licenses
23# can be found at: <www.state-machine.com/licensing>
24#
25# Redistributions in source code must retain this top-level comment block.
26# Plagiarizing this software to sidestep the license obligations is illegal.
27#
28# Contact information:
29# <www.state-machine.com>
30# <info@state-machine.com>
31#=============================================================================
32
33# pylint: disable=missing-module-docstring,
34# pylint: disable=missing-class-docstring,
35# pylint: disable=missing-function-docstring
36# pylint: disable=protected-access
37# pylint: disable=invalid-name
38# pylint: disable=broad-exception-caught
39
40from tkinter import *
41from tkinter.ttk import * # override the basic Tk widgets with Ttk widgets
42from tkinter.simpledialog import *
43from struct import pack
44
45import socket
46import time
47import sys
48import struct
49import traceback
50import webbrowser
51
52#=============================================================================
53# QView GUI
54# https://www.state-machine.com/qtools/qview.html
55#
56class QView:
57 ## current version of QView
58 VERSION = 804
59
60 # public static variables...
61 ## menu to be customized
62 custom_menu = None
63
64 ## canvas to be customized
65 canvas = None
66
67 ## frame to be customized
68 frame = None
69
70 # private class variables...
71 _text_lines = "end - 500 lines"
72 _attach_dialog = None
73 _have_info = False
74 _reset_request = False
75 _gui = None
76 _inst = None
77 _err = 0
78 _glb_filter = 0x00000000000000000000000000000000
79 _loc_filter = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
80
81 _dtypes = ("8-bit", "16-bit", "32-bit")
82 _dsizes = (1, 2, 4)
83
84 #-------------------------------------------------------------------------
85 # main entry point to QView
86 @staticmethod
87 def main(cust):
88 # set the only instance of QView, which might be customized
89 # sublcass of QView
90 QView._inst = cust
91
92 # process command-line arguments...
93 argv = sys.argv
94 argc = len(argv)
95 arg = 1 # skip the "qview" argument
96
97 if "-h" in argv or "--help" in argv or "?" in argv:
98 print("\nUsage: python qview.py "
99 "[qspy_host[:udp_port]] [local_port]\n\n"
100 "help at: https://www.state-machine.com/qtools/QView.html")
101 sys.exit(0)
102
103 if arg < argc:
104 host_port = argv[arg].split(":")
105 arg += 1
106 if len(host_port) > 0:
107 QSpy._host_addr[0] = host_port[0]
108 if len(host_port) > 1:
109 QSpy._host_addr[1] = int(host_port[1])
110
111 if arg < argc:
112 QSpy._local_port = int(argv[arg])
113
114 QSpy._host_addr = tuple(QSpy._host_addr) # convert to immutable tuple
115 #print("Connection: ", QSpy._host_addr, QSpy._local_port)
116
117 # create the QView GUI
118 QView._gui = Tk()
119 QView._gui.title(f"QView {QView.VERSION//100}."\
120 f"{(QView.VERSION//10) % 10}.{QView.VERSION % 10}")
121 QView._init_gui(QView._gui)
122
123 err = QSpy._init()
124 if err:
125 sys.exit(err) # simple return: event-loop is not running yet
126
127 QView._inst.on_init()
128
129 QSpy._attach()
130 QView._gui.mainloop()
131 QView._gui = None
132 QSpy._detach()
133
134 sys.exit(QView._err)
135
136 #---------------------------------------------------------------------------
137 # DSL for QView customizations
138
139 # kinds of objects for current_obj()...
140 OBJ_SM = 0
141 OBJ_AO = 1
142 OBJ_MP = 2
143 OBJ_EQ = 3
144 OBJ_TE = 4
145 OBJ_AP = 5
146
147 # global filter groups...
148 GRP_ALL= 0xF0
149 GRP_SM = 0xF1
150 GRP_AO = 0xF2
151 GRP_MP = 0xF3
152 GRP_EQ = 0xF4
153 GRP_TE = 0xF5
154 GRP_QF = 0xF6
155 GRP_SC = 0xF7
156 GRP_SEM= 0xF8
157 GRP_MTX= 0xF9
158 GRP_U0 = 0xFA
159 GRP_U1 = 0xFB
160 GRP_U2 = 0xFC
161 GRP_U3 = 0xFD
162 GRP_U4 = 0xFE
163 GRP_UA = 0xFF
164 GRP_ON = GRP_ALL
165 GRP_OFF= -GRP_ALL
166
167 # local filter groups...
168 IDS_ALL= 0xF0
169 IDS_AO = 0x80 + 0
170 IDS_EP = 0x80 + 64
171 IDS_EQ = 0x80 + 80
172 IDS_AP = 0x80 + 96
173
174 # on_init() callback
175 def on_init(self):
176 pass
177
178 # on_run() callback
179 def on_reset(self):
180 pass
181
182 # on_run() callback
183 def on_run(self):
184 pass
185
186 ## @brief Send the RESET packet to the Target
187 @staticmethod
189 if QView._have_info:
190 QSpy._sendTo(pack("<B", QSpy._TRGT_RESET))
191 else:
192 QView._reset_request = True
193
194 ## @brief executes a given command in the Target
195 # @sa qutest_dsl.command()
196 @staticmethod
197 def command(cmd_id, param1 = 0, param2 = 0, param3 = 0):
198 if isinstance(cmd_id, int):
199 QSpy._sendTo(pack("<BBIII", QSpy._TRGT_COMMAND,
200 cmd_id, param1, param2, param3))
201 else:
202 QSpy._sendTo(pack("<BBIII", QSpy._QSPY_SEND_COMMAND,
203 0, param1, param2, param3),
204 cmd_id) # add string command ID to end
205
206 ## @brief trigger system clock tick in the Target
207 # @sa qutest_dsl.tick()
208 @staticmethod
209 def tick(tick_rate = 0):
210 QSpy._sendTo(pack("<BB", QSpy._TRGT_TICK, tick_rate))
211
212 ## @brief peeks data in the Target
213 # @sa qutest_dsl.peek()
214 @staticmethod
215 def peek(offset, size, num):
216 QSpy._sendTo(pack("<BHBB", QSpy._TRGT_PEEK, offset, size, num))
217
218 ## @brief pokes data into the Target
219 # @sa qutest_dsl.poke()
220 @staticmethod
221 def poke(offset, size, data):
222 fmt = "<BHBB" + ("x","B","H","x","I")[size]
223 QSpy._sendTo(pack(fmt, QSpy._TRGT_POKE, offset, size, 1, data))
224
225 ## @brief Set/clear the Global-Filter in the Target.
226 # @sa qutest_dsl.glb_filter()
227 @staticmethod
228 def glb_filter(*args):
229 # internal helper function
230 def _apply(mask, is_neg):
231 if is_neg:
232 QView._glb_filter &= ~mask
233 else:
234 QView._glb_filter |= mask
235
236 QView._glb_filter = 0
237 for arg in args:
238 # NOTE: positive filter argument means 'add' (allow),
239 # negative filter argument meand 'remove' (disallow)
240 is_neg = False
241 if isinstance(arg, str):
242 is_neg = arg[0] == '-' # is request?
243 if is_neg:
244 arg = arg[1:]
245 try:
246 arg = QSpy._QS.index(arg)
247 except Exception:
248 QView._MessageDialog("Error in glb_filter()",
249 f'arg="{arg}"\n' +
250 traceback.format_exc(3))
251 sys.exit(-5) # return: event-loop might not be running yet
252 else:
253 is_neg = arg < 0
254 if is_neg:
255 arg = -arg
256
257 if arg < 0x7F:
258 _apply(1 << arg, is_neg)
259 elif arg == QView.GRP_ON:
260 _apply(QSpy._GLB_FLT_MASK_ALL, is_neg)
261 elif arg == QView.GRP_SM:
262 _apply(QSpy._GLB_FLT_MASK_SM, is_neg)
263 elif arg == QView.GRP_AO:
264 _apply(QSpy._GLB_FLT_MASK_AO, is_neg)
265 elif arg == QView.GRP_MP:
266 _apply(QSpy._GLB_FLT_MASK_MP, is_neg)
267 elif arg == QView.GRP_EQ:
268 _apply(QSpy._GLB_FLT_MASK_EQ, is_neg)
269 elif arg == QView.GRP_TE:
270 _apply(QSpy._GLB_FLT_MASK_TE, is_neg)
271 elif arg == QView.GRP_QF:
272 _apply(QSpy._GLB_FLT_MASK_QF, is_neg)
273 elif arg == QView.GRP_SC:
274 _apply(QSpy._GLB_FLT_MASK_SC, is_neg)
275 elif arg == QView.GRP_SEM:
276 _apply(QSpy._GLB_FLT_MASK_SEM, is_neg)
277 elif arg == QView.GRP_MTX:
278 _apply(QSpy._GLB_FLT_MASK_MTX, is_neg)
279 elif arg == QView.GRP_U0:
280 _apply(QSpy._GLB_FLT_MASK_U0, is_neg)
281 elif arg == QView.GRP_U1:
282 _apply(QSpy._GLB_FLT_MASK_U1, is_neg)
283 elif arg == QView.GRP_U2:
284 _apply(QSpy._GLB_FLT_MASK_U2, is_neg)
285 elif arg == QView.GRP_U3:
286 _apply(QSpy._GLB_FLT_MASK_U3, is_neg)
287 elif arg == QView.GRP_U4:
288 _apply(QSpy._GLB_FLT_MASK_U4, is_neg)
289 elif arg == QView.GRP_UA:
290 _apply(QSpy._GLB_FLT_MASK_UA, is_neg)
291 else:
292 assert 0, f"invalid global filter arg=0x{arg:02x}"
293
294 QSpy._sendTo(pack("<BBQQ", QSpy._TRGT_GLB_FILTER, 16,
295 QView._glb_filter & 0xFFFFFFFFFFFFFFFF,
296 QView._glb_filter >> 64))
297 QView._updateMenus()
298
299 ## @brief Set/clear the Local-Filter in the Target.
300 # @sa qutest_dsl.loc_filter()
301 @staticmethod
302 def loc_filter(*args):
303 # internal helper function
304 def _apply(mask, is_neg):
305 if is_neg:
306 QView._loc_filter &= ~mask
307 else:
308 QView._loc_filter |= mask
309
310 for arg in args:
311 # NOTE: positive filter argument means 'add' (allow),
312 # negative filter argument means 'remove' (disallow)
313 is_neg = (arg < 0)
314 if is_neg:
315 arg = -arg
316
317 if arg < 0x7F:
318 _apply(1 << arg, is_neg)
319 elif arg == QView.IDS_ALL:
320 _apply(QSpy._LOC_FLT_MASK_ALL, is_neg)
321 elif arg == QView.IDS_AO:
322 _apply(QSpy._LOC_FLT_MASK_AO, is_neg)
323 elif arg == QView.IDS_EP:
324 _apply(QSpy._LOC_FLT_MASK_EP, is_neg)
325 elif arg == QView.IDS_EQ:
326 _apply(QSpy._LOC_FLT_MASK_EQ, is_neg)
327 elif arg == QView.IDS_AP:
328 _apply(QSpy._LOC_FLT_MASK_AP, is_neg)
329 else:
330 assert 0, f"invalid local filter arg=0x{arg:02x}"
331
332 QSpy._sendTo(pack("<BBQQ", QSpy._TRGT_LOC_FILTER, 16,
333 QView._loc_filter & 0xFFFFFFFFFFFFFFFF,
334 QView._loc_filter >> 64))
335
336 ## @brief Set/clear the Active-Object Local-Filter in the Target.
337 # @sa qutest_dsl.ao_filter()
338 @staticmethod
339 def ao_filter(obj_id):
340 # NOTE: positive obj_id argument means 'add' (allow),
341 # negative obj_id argument means 'remove' (disallow)
342 remove = 0
343 QView._locAO_OBJ.set(obj_id)
344 QView._menu_loc_filter.entryconfig("AO-OBJ...",
345 accelerator=QView._locAO_OBJ.get())
346 if isinstance(obj_id, str):
347 if obj_id[0:1] == '-': # is it remvoe request?
348 obj_id = obj_id[1:]
349 remove = 1
350 QSpy._sendTo(pack("<BB" + QSpy._fmt[QSpy._size_objPtr],
351 QSpy._QSPY_SEND_AO_FILTER, remove, 0),
352 obj_id) # add string object-ID to end
353 else:
354 if obj_id < 0:
355 obj_id = -obj_id
356 remove = 1
357 QSpy._sendTo(pack("<BB" + QSpy._fmt[QSpy._size_objPtr],
358 QSpy._TRGT_AO_FILTER, remove, obj_id))
359
360 ## @brief Set the Current-Object in the Target.
361 # @sa qutest_dsl.current_obj()
362 @staticmethod
363 def current_obj(obj_kind, obj_id):
364 if obj_id == "":
365 return
366 if isinstance(obj_id, int):
367 QSpy._sendTo(pack("<BB" + QSpy._fmt[QSpy._size_objPtr],
368 QSpy._TRGT_CURR_OBJ, obj_kind, obj_id))
369 obj_id = f"0x{obj_id:08x}"
370 else:
371 QSpy._sendTo(pack("<BB" + QSpy._fmt[QSpy._size_objPtr],
372 QSpy._QSPY_SEND_CURR_OBJ, obj_kind, 0), obj_id)
373
374 QView._currObj[obj_kind].set(obj_id)
375 QView._menu_curr_obj.entryconfig(obj_kind, accelerator=obj_id)
376 QView._updateMenus()
377
378 ## @brief query the @ref current_obj() "current object" in the Target
379 # @sa qutest_dsl.query_curr()
380 @staticmethod
381 def query_curr(obj_kind):
382 QSpy._sendTo(pack("<BB", QSpy._TRGT_QUERY_CURR, obj_kind))
383
384 ## @brief publish a given event to subscribers in the Target
385 # @sa qutest_dsl.publish()
386 @staticmethod
387 def publish(signal, params = None):
388 QSpy._sendEvt(QSpy._EVT_PUBLISH, signal, params)
389
390 ## @brief post a given event to the current AO object in the Target
391 # @sa qutest_dsl.post()
392 @staticmethod
393 def post(signal, params = None):
394 QSpy._sendEvt(QSpy._EVT_POST, signal, params)
395
396 ## @brief take the top-most initial transition in the
397 # current SM object in the Target
398 # @sa qutest_dsl.init()
399 @staticmethod
400 def init(signal = 0, params = None):
401 QSpy._sendEvt(QSpy._EVT_INIT, signal, params)
402
403 ## @brief dispatch a given event in the current SM object in the Target
404 # @sa qutest_dsl.dispatch()
405 @staticmethod
406 def dispatch(signal, params = None):
407 QSpy._sendEvt(QSpy._EVT_DISPATCH, signal, params)
408
409 ## @brief Unpack a QS trace record
410 #
411 # @description
412 # The qunpack() facility is similar to Python `struct.unpack()`,
413 # specifically designed for unpacking binary QP/Spy packets.
414 # qunpack() handles all data formats supported by struct.unpack()`,
415 # plus data formats specific to QP/Spy. The main benefit of qunpack()
416 # is that it automatically applies the Target-supplied info about
417 # various the sizes of various elements, such as Target timestamp,
418 # Target object-pointer, Target event-signal, zero-terminated string, etc.
419 ## @brief pokes data into the Target
420 # @sa qutest_dsl.poke()
421 #
422 # @param[in] fmt format string
423 # @param[in] bstr byte-string to unpack
424 #
425 # @returns
426 # The result is a tuple with elements corresponding to the format items.
427 #
428 # The additional format characters have the following meaning:
429 #
430 # - T : QP/Spy timestamp -> integer, 2..4-bytes (Target dependent)
431 # - O : QP/Spy object pointer -> integer, 2..8-bytes (Target dependent)
432 # - F : QP/Spy function pointer -> integer, 2..8-bytes (Target dependent)
433 # - S : QP/Spy event signal -> integer, 1..4-bytes (Target dependent)
434 # - Z : QP/Spy zero-terminated string -> string of n-bytes (variable length)
435 #
436 # @usage
437 # @include qunpack.py
438 #
439 @staticmethod
440 def qunpack(fmt, bstr):
441 n = 0
442 m = len(fmt)
443 bord = "<" # default little-endian byte order
444 if fmt[0:1] in ("@", "=", "<", ">", "!"):
445 bord = fmt[0:1]
446 n += 1
447 data = []
448 offset = 0
449 while n < m:
450 fmt1 = fmt[n:(n+1)]
451 u = ()
452 if fmt1 in ("B", "b", "c", "x", "?"):
453 u = struct.unpack_from(bord + fmt1, bstr, offset)
454 offset += 1
455 elif fmt1 in ("H", "h"):
456 u = struct.unpack_from(bord + fmt1, bstr, offset)
457 offset += 2
458 elif fmt1 in ("I", "L", "i", "l", "f"):
459 u = struct.unpack_from(bord + fmt1, bstr, offset)
460 offset += 4
461 elif fmt1 in ("Q", "q", "d"):
462 u = struct.unpack_from(bord + fmt1, bstr, offset)
463 offset += 8
464 elif fmt1 == "T":
465 u = struct.unpack_from(bord + QSpy._fmt[QSpy._size_tstamp],
466 bstr, offset)
467 offset += QSpy._size_tstamp
468 elif fmt1 == "O":
469 u = struct.unpack_from(bord + QSpy._fmt[QSpy._size_objPtr],
470 bstr, offset)
471 offset += QSpy._size_objPtr
472 elif fmt1 == "F":
473 u = struct.unpack_from(bord + QSpy._fmt[QSpy._size_funPtr],
474 bstr, offset)
475 offset += QSpy._size_funPtr
476 elif fmt1 == "S":
477 u = struct.unpack_from(bord + QSpy._fmt[QSpy._size_sig],
478 bstr, offset)
479 offset += QSpy._size_sig
480 elif fmt1 == "Z": # zero-terminated C-string
481 end = offset
482 while bstr[end]: # not zero-terminator?
483 end += 1
484 u = (bstr[offset:end].decode(),)
485 offset = end + 1 # inclue the terminating zero
486 else:
487 assert 0, "qunpack(): unknown format"
488 data.extend(u)
489 n += 1
490 return tuple(data)
491
492
493 @staticmethod
494 def _init_gui(root):
495 Tk.report_callback_exception = QView._trap_error
496
497 # menus...............................................................
498 main_menu = Menu(root, tearoff=0)
499 root.config(menu=main_menu)
500
501 # File menu...
502 m = Menu(main_menu, tearoff=0)
503 m.add_command(label="Save QSPY Dictionaries",
504 command=QView._onSaveDict)
505 m.add_command(label="Toggle QSPY Text Output",
506 command=QView._onSaveText)
507 m.add_command(label="Toggle QSPY Binary Output",
508 command=QView._onSaveBin)
509 m.add_command(label="Toggle Matlab Output",
510 command=QView._onSaveMatlab)
511 m.add_command(label="Toggle Sequence Output",
512 command=QView._onSaveSequence)
513 m.add_separator()
514 m.add_command(label="Exit", command=QView._quit)
515 main_menu.add_cascade(label="File", menu=m)
516
517 # View menu...
518 m = Menu(main_menu, tearoff=0)
519 QView._view_canvas = IntVar()
520 QView._view_frame = IntVar()
521 m.add_checkbutton(label="Canvas", variable=QView._view_canvas,
522 command=QView._onCanvasView)
523 m.add_checkbutton(label="Frame", variable=QView._view_frame,
524 command=QView._onFrameView)
525 main_menu.add_cascade(label="View", menu=m)
526
527 # Global-Filters menu...
528 m = Menu(main_menu, tearoff=0)
529 m.add_command(label="SM Group...", accelerator="[NONE]",
530 command=QView._onGlbFilter_SM)
531 m.add_command(label="AO Group...", accelerator="[NONE]",
532 command=QView._onGlbFilter_AO)
533 m.add_command(label="QF Group...", accelerator="[NONE]",
534 command=QView._onGlbFilter_QF)
535 m.add_command(label="TE Group...", accelerator="[NONE]",
536 command=QView._onGlbFilter_TE)
537 m.add_command(label="MP Group...", accelerator="[NONE]",
538 command=QView._onGlbFilter_MP)
539 m.add_command(label="EQ Group...", accelerator="[NONE]",
540 command=QView._onGlbFilter_EQ)
541 m.add_command(label="SC Group...", accelerator="[NONE]",
542 command=QView._onGlbFilter_SC)
543 m.add_command(label="SEM Group...", accelerator="[NONE]",
544 command=QView._onGlbFilter_SEM)
545 m.add_command(label="MTX Group...", accelerator="[NONE]",
546 command=QView._onGlbFilter_MTX)
547 m.add_separator()
548 m.add_command(label="U0 Group...", accelerator="[NONE]",
549 command=QView._onGlbFilter_U0)
550 m.add_command(label="U1 Group...", accelerator="[NONE]",
551 command=QView._onGlbFilter_U1)
552 m.add_command(label="U2 Group...", accelerator="[NONE]",
553 command=QView._onGlbFilter_U2)
554 m.add_command(label="U3 Group...", accelerator="[NONE]",
555 command=QView._onGlbFilter_U3)
556 m.add_command(label="U4 Group...", accelerator="[NONE]",
557 command=QView._onGlbFilter_U4)
558 main_menu.add_cascade(label="Global-Filters", menu=m)
559 QView._menu_glb_filter = m
560
561 # Local-Filters menu...
562 m = Menu(main_menu, tearoff=0)
563 m.add_command(label="AO IDs...", accelerator="[NONE]",
564 command=QView._onLocFilter_AO)
565 m.add_command(label="EP IDs...", accelerator="[NONE]",
566 command=QView._onLocFilter_EP)
567 m.add_command(label="EQ IDs...", accelerator="[NONE]",
568 command=QView._onLocFilter_EQ)
569 m.add_command(label="AP IDs...", accelerator="[NONE]",
570 command=QView._onLocFilter_AP)
571 m.add_separator()
572 m.add_command(label="AO-OBJ...", command=QView._onLocFilter_AO_OBJ)
573 main_menu.add_cascade(label="Local-Filters", menu=m)
574 QView._menu_loc_filter = m
575
576 # Current-Obj menu...
577 m = Menu(main_menu, tearoff=0)
578 m.add_command(label="SM_OBJ", command=QView._onCurrObj_SM)
579 m.add_command(label="AO_OBJ", command=QView._onCurrObj_AO)
580 m.add_command(label="MP_OBJ", command=QView._onCurrObj_MP)
581 m.add_command(label="EQ_OBJ", command=QView._onCurrObj_EQ)
582 m.add_command(label="TE_OBJ", command=QView._onCurrObj_TE)
583 m.add_command(label="AP_OBJ", command=QView._onCurrObj_AP)
584 m.add_separator()
585 m1 = Menu(m, tearoff=0)
586 m1.add_command(label="SM_OBJ", command=QView._onQueryCurr_SM)
587 m1.add_command(label="AO_OBJ", command=QView._onQueryCurr_AO)
588 m1.add_command(label="MP_OBJ", command=QView._onQueryCurr_MP)
589 m1.add_command(label="EQ_OBJ", command=QView._onQueryCurr_EQ)
590 m1.add_command(label="TE_OBJ", command=QView._onQueryCurr_TE)
591 m1.add_command(label="AP_OBJ", command=QView._onQueryCurr_AP)
592 m.add_cascade(label="Query Current", menu=m1)
593 main_menu.add_cascade(label="Current-Obj", menu=m)
594 QView._menu_curr_obj = m
595
596 # Commands menu...
597 m = Menu(main_menu, tearoff=0)
598 m.add_command(label="Reset Target", command=QView.reset_target)
599 m.add_command(label="Query Target Info", command=QView._onTargetInfo)
600 m.add_command(label="Tick[0]", command=QView._onTick0)
601 m.add_command(label="Tick[1]", command=QView._onTick1)
602 m.add_command(label="Command...", command=QView._CommandDialog)
603 m.add_command(label="Show Note...", command=QView._NoteDialog)
604 m.add_command(label="Clear QSPY Screen", command=QView._onClearQspy)
605 m.add_separator()
606 m.add_command(label="Peek...", command=QView._PeekDialog)
607 m.add_command(label="Poke...", command=QView._PokeDialog)
608 main_menu.add_cascade(label="Commands", menu=m)
609 QView._menu_commands = m
610
611 # Events menu...
612 m = Menu(main_menu, tearoff=0)
613 m.add_command(label="Publish...", command=QView._onEvt_PUBLISH)
614 m.add_command(label="Post...", command=QView._onEvt_POST)
615 m.add_command(label="Init SM", command=QView._onEvt_INIT)
616 m.add_command(label="Dispatch...", command=QView._onEvt_DISPATCH)
617 main_menu.add_cascade(label="Events", menu=m)
618 QView._menu_events = m
619
620 # Custom menu...
621 m = Menu(main_menu, tearoff=0)
622 m.add_separator()
623 main_menu.add_cascade(label="Custom", menu=m)
624 QView.custom_menu = m
625
626 # Help menu...
627 m = Menu(main_menu, tearoff=0)
628 m.add_command(label="Online Help", command=QView._onHelp)
629 m.add_separator()
630 m.add_command(label="About...", command=QView._onAbout)
631 main_menu.add_cascade(label="Help", menu=m)
632
633 # statusbar (pack before text-area) ..................................
634 QView._scroll_text = IntVar()
635 QView._scroll_text.set(1) # text scrolling enabled
636 QView._echo_text = IntVar()
637 QView._echo_text.set(0) # text echo disabled
638 frame = Frame(root, borderwidth=1, relief="raised")
639 QView._target = Label(frame, height=2,
640 text="Target: " + QSpy._fmt_target)
641 QView._target.pack(side="left")
642 c = Checkbutton(frame, text="Scroll", variable=QView._scroll_text)
643 c.pack(side="right")
644 c = Checkbutton(frame, text="Echo", variable=QView._echo_text,
645 command=QSpy._reattach)
646 c.pack(side="right")
647 QView._tx = Label(frame, width=6, anchor=E,
648 borderwidth=1, relief="sunken")
649 QView._tx.pack(side="right")
650 Label(frame, text="Tx ").pack(side="right")
651 QView._rx = Label(frame, width=8, anchor=E,
652 borderwidth=1, relief="sunken")
653 QView._rx.pack(side="right")
654 Label(frame, text="Rx ").pack(side="right")
655 frame.pack(side="bottom", fill="x", pady=0)
656
657 # text-area with scrollbar............................................
658 frame = Frame(root, borderwidth=1, relief="sunken")
659 scrollbar = Scrollbar(frame)
660 QView._text = Text(frame, width=100, height=30,
661 wrap="word", yscrollcommand=scrollbar.set)
662 QView._text.bind("<Key>", lambda e: "break") # read-only text
663 scrollbar.config(command=QView._text.yview)
664 scrollbar.pack(side="right", fill="y")
665 QView._text.pack(side="left", fill="both", expand=True)
666 frame.pack(side="left", fill="both", expand=True)
667
668 # canvas..............................................................
669 QView._canvas_toplevel = Toplevel()
670 QView._canvas_toplevel.withdraw() # start not showing
671 QView._canvas_toplevel.protocol("WM_DELETE_WINDOW",
672 QView._onCanvasClose)
673 QView._canvas_toplevel.title("QView -- Canvas")
674 QView.canvas = Canvas(QView._canvas_toplevel)
675 QView.canvas.pack()
676
677 # frame..............................................................
678 QView._frame_toplevel = Toplevel()
679 QView._frame_toplevel.withdraw() # start not showing
680 QView._frame_toplevel.protocol("WM_DELETE_WINDOW",
681 QView._onFrameClose)
682 QView._frame_toplevel.title("QView -- Frame")
683 QView.frame = Frame(QView._frame_toplevel)
684 QView.frame.pack()
685
686 # tkinter variables for dialog boxes .................................
687 QView._locAO_OBJ = StringVar()
688 QView._currObj = (StringVar(), StringVar(), StringVar(),
689 StringVar(), StringVar(), StringVar())
690 QView._command = StringVar()
691 QView._command_p1 = StringVar()
692 QView._command_p2 = StringVar()
693 QView._command_p3 = StringVar()
694 QView._note = StringVar()
695 QView._note_kind = StringVar(value=0)
696 QView._peek_offs = StringVar()
697 QView._peek_dtype = StringVar(value=QView._dtypes[2])
698 QView._peek_len = StringVar()
699 QView._poke_offs = StringVar()
700 QView._poke_dtype = StringVar(value=QView._dtypes[2])
701 QView._poke_data = StringVar()
702 QView._evt_act = StringVar()
703 QView._evt_sig = StringVar()
704 QView._evt_par = (StringVar(), StringVar(), StringVar(),
705 StringVar(), StringVar(), StringVar(),
706 StringVar(), StringVar(), StringVar())
707 QView._evt_dtype = (StringVar(), StringVar(), StringVar(),
708 StringVar(), StringVar(), StringVar(),
709 StringVar(), StringVar(), StringVar())
710 for i in range(len(QView._evt_par)):
711 QView._evt_dtype[i].set(QView._dtypes[2])
712
713 QView._updateMenus()
714
715
716 # public static functions...
717
718 ## Set QView customization.
719 # @param cust the customization class instance
720 @staticmethod
722 print("This QView version no longer supports QView.customize()\n",
723 " use QView.main(<cust()>) instead")
724 sys.exit(-1)
725
726 ## Print a string to the Text area
727 @staticmethod
728 def print_text(string):
729 QView._text.delete(1.0, QView._text_lines)
730 QView._text.insert(END, "\n")
731 QView._text.insert(END, string)
732 if QView._scroll_text.get():
733 QView._text.yview_moveto(1) # scroll to the bottom
734
735 ## Make the canvas visible
736 # (to be used in the constructor of the customization class)
737 @staticmethod
738 def show_canvas(view=1):
739 QView._view_canvas.set(view)
740
741 ## Make the frame visible
742 # (to be used in the constructor of the customization class)
743 @staticmethod
744 def show_frame(view=1):
745 QView._view_frame.set(view)
746
747 # private static functions...
748 @staticmethod
749 def _quit(err=0):
750 QView._err = err
751 QView._gui.quit()
752
753 @staticmethod
754 def _onExit():
755 QView._quit()
756
757 @staticmethod
758 def _onReset():
759 QView._glb_filter = 0
760 QView._loc_filter = QSpy._LOC_FLT_MASK_ALL
761 QView._locAO_OBJ.set("")
762 for i, e in enumerate(QView._currObj):
763 QView._currObj[i].set("")
764 QView._updateMenus()
765
766 @staticmethod
768
769 # internal helper function
770 def _update_glb_filter_menu(label, mask):
771 x = QView._glb_filter & mask
772 if x == 0:
773 status = "[ - ]"
774 elif x == mask:
775 status = "[ + ]"
776 else:
777 status = "[+-]"
778 QView._menu_glb_filter.entryconfig(label,
779 accelerator=status)
780
781 # internal helper function
782 def _update_loc_filter_menu(label, mask):
783 x = QView._loc_filter & mask
784 if x == 0:
785 status = "[ - ]"
786 elif x == mask:
787 status = "[ + ]"
788 else:
789 status = "[+-]"
790 QView._menu_loc_filter.entryconfig(label,
791 accelerator=status)
792
793 for i, e in enumerate(QView._currObj):
794 QView._menu_curr_obj.entryconfig(i,
795 accelerator=QView._currObj[i].get())
796 QView._menu_events.entryconfig(0,
797 accelerator=QView._currObj[QView.OBJ_AO].get())
798 QView._menu_events.entryconfig(1,
799 accelerator=QView._currObj[QView.OBJ_AO].get())
800 QView._menu_events.entryconfig(2,
801 accelerator=QView._currObj[QView.OBJ_SM].get())
802 QView._menu_events.entryconfig(3,
803 accelerator=QView._currObj[QView.OBJ_SM].get())
804 QView._menu_commands.entryconfig(8,
805 accelerator=QView._currObj[QView.OBJ_AP].get())
806 QView._menu_commands.entryconfig(9,
807 accelerator=QView._currObj[QView.OBJ_AP].get())
808 state_SM = "normal"
809 state_AO = "normal"
810 state_AP = "normal"
811 if QView._currObj[QView.OBJ_SM].get() == "":
812 state_SM = "disabled"
813 if QView._currObj[QView.OBJ_AO].get() == "":
814 state_AO = "disabled"
815 if QView._currObj[QView.OBJ_AP].get() == "":
816 state_AP ="disabled"
817 QView._menu_events.entryconfig(0, state=state_AO)
818 QView._menu_events.entryconfig(1, state=state_AO)
819 QView._menu_events.entryconfig(2, state=state_SM)
820 QView._menu_events.entryconfig(3, state=state_SM)
821 QView._menu_commands.entryconfig(8, state=state_AP)
822 QView._menu_commands.entryconfig(9, state=state_AP)
823
824 _update_glb_filter_menu("SM Group...", QSpy._GLB_FLT_MASK_SM)
825 _update_glb_filter_menu("AO Group...", QSpy._GLB_FLT_MASK_AO)
826 _update_glb_filter_menu("QF Group...", QSpy._GLB_FLT_MASK_QF)
827 _update_glb_filter_menu("TE Group...", QSpy._GLB_FLT_MASK_TE)
828 _update_glb_filter_menu("MP Group...", QSpy._GLB_FLT_MASK_MP)
829 _update_glb_filter_menu("EQ Group...", QSpy._GLB_FLT_MASK_EQ)
830 _update_glb_filter_menu("SC Group...", QSpy._GLB_FLT_MASK_SC)
831 _update_glb_filter_menu("SEM Group...", QSpy._GLB_FLT_MASK_SEM)
832 _update_glb_filter_menu("MTX Group...", QSpy._GLB_FLT_MASK_MTX)
833 _update_glb_filter_menu("U0 Group...", QSpy._GLB_FLT_MASK_U0)
834 _update_glb_filter_menu("U1 Group...", QSpy._GLB_FLT_MASK_U1)
835 _update_glb_filter_menu("U2 Group...", QSpy._GLB_FLT_MASK_U2)
836 _update_glb_filter_menu("U3 Group...", QSpy._GLB_FLT_MASK_U3)
837 _update_glb_filter_menu("U4 Group...", QSpy._GLB_FLT_MASK_U4)
838
839 _update_loc_filter_menu("AO IDs...", QSpy._LOC_FLT_MASK_AO)
840 _update_loc_filter_menu("EP IDs...", QSpy._LOC_FLT_MASK_EP)
841 _update_loc_filter_menu("EQ IDs...", QSpy._LOC_FLT_MASK_EQ)
842 _update_loc_filter_menu("AP IDs...", QSpy._LOC_FLT_MASK_AP)
843 QView._menu_loc_filter.entryconfig("AO-OBJ...",
844 accelerator=QView._locAO_OBJ.get())
845
846 @staticmethod
848 QView._showerror("Runtime Error",
849 traceback.format_exc(3))
850 QView._quit(-3)
851
852 @staticmethod
853 def _assert(cond, message):
854 if not cond:
855 QView._showerror("Assertion",
856 message)
857 QView._quit(-3)
858
859 @staticmethod
861 QSpy._sendTo(pack("<B", QSpy._QSPY_SAVE_DICT))
862
863 @staticmethod
865 QSpy._sendTo(pack("<B", QSpy._QSPY_TEXT_OUT))
866
867 @staticmethod
869 QSpy._sendTo(pack("<B", QSpy._QSPY_BIN_OUT))
870
871 @staticmethod
873 QSpy._sendTo(pack("<B", QSpy._QSPY_MATLAB_OUT))
874
875 @staticmethod
877 QSpy._sendTo(pack("<B", QSpy._QSPY_SEQUENCE_OUT))
878
879 @staticmethod
881 if QView._view_canvas.get():
882 QView._canvas_toplevel.state("normal")
883 # make the canvas jump to the front
884 QView._canvas_toplevel.attributes("-topmost", 1)
885 QView._canvas_toplevel.attributes("-topmost", 0)
886 else:
887 QView._canvas_toplevel.withdraw()
888
889 @staticmethod
891 QView._view_canvas.set(0)
892 QView._canvas_toplevel.withdraw()
893
894 @staticmethod
896 if QView._view_frame.get():
897 QView._frame_toplevel.state("normal")
898 # make the frame jump to the front
899 QView._frame_toplevel.attributes("-topmost", 1)
900 QView._frame_toplevel.attributes("-topmost", 0)
901 else:
902 QView._frame_toplevel.withdraw()
903
904 @staticmethod
906 QView._view_frame.set(0)
907 QView._frame_toplevel.withdraw()
908
909 @staticmethod
911 QView._GlbFilterDialog("SM Group", QSpy._GLB_FLT_MASK_SM)
912
913 @staticmethod
915 QView._GlbFilterDialog("AO Group", QSpy._GLB_FLT_MASK_AO)
916
917 @staticmethod
919 QView._GlbFilterDialog("QF Group", QSpy._GLB_FLT_MASK_QF)
920
921 @staticmethod
923 QView._GlbFilterDialog("TE Group", QSpy._GLB_FLT_MASK_TE)
924
925 @staticmethod
927 QView._GlbFilterDialog("EQ Group", QSpy._GLB_FLT_MASK_EQ)
928
929 @staticmethod
931 QView._GlbFilterDialog("MP Group", QSpy._GLB_FLT_MASK_MP)
932
933 @staticmethod
935 QView._GlbFilterDialog("SC Group", QSpy._GLB_FLT_MASK_SC)
936
937 @staticmethod
939 QView._GlbFilterDialog("SEM Group", QSpy._GLB_FLT_MASK_SEM)
940
941 @staticmethod
943 QView._GlbFilterDialog("MTX Group", QSpy._GLB_FLT_MASK_MTX)
944
945 @staticmethod
947 QView._GlbFilterDialog("U0 Group", QSpy._GLB_FLT_MASK_U0)
948
949 @staticmethod
951 QView._GlbFilterDialog("U1 Group", QSpy._GLB_FLT_MASK_U1)
952
953 @staticmethod
955 QView._GlbFilterDialog("U2 Group", QSpy._GLB_FLT_MASK_U2)
956
957 @staticmethod
959 QView._GlbFilterDialog("U3 Group", QSpy._GLB_FLT_MASK_U3)
960
961 @staticmethod
963 QView._GlbFilterDialog("U4 Group", QSpy._GLB_FLT_MASK_U4)
964
965 @staticmethod
967 QView._LocFilterDialog("AO IDs", QSpy._LOC_FLT_MASK_AO)
968
969 @staticmethod
971 QView._LocFilterDialog("EP IDs", QSpy._LOC_FLT_MASK_EP)
972
973 @staticmethod
975 QView._LocFilterDialog("EQ IDs", QSpy._LOC_FLT_MASK_EQ)
976
977 @staticmethod
979 QView._LocFilterDialog("AP IDs", QSpy._LOC_FLT_MASK_AP)
980
981 @staticmethod
985 @staticmethod
987 QView._CurrObjDialog(QView.OBJ_SM, "SM_OBJ")
988 QView._updateMenus()
989
990 @staticmethod
992 QView._CurrObjDialog(QView.OBJ_AO, "AO_OBJ")
993 QView._updateMenus()
994
995 @staticmethod
997 QView._CurrObjDialog(QView.OBJ_MP, "MP_OBJ")
998
999 @staticmethod
1001 QView._CurrObjDialog(QView.OBJ_EQ, "EQ_OBJ")
1002
1003 @staticmethod
1005 QView._CurrObjDialog(QView.OBJ_TE, "TE_OBJ")
1006
1007 @staticmethod
1009 QView._CurrObjDialog(QView.OBJ_AP, "AP_OBJ")
1010 QView._updateMenus()
1011
1012 @staticmethod
1014 QView.query_curr(QView.OBJ_SM)
1015
1016 @staticmethod
1018 QView.query_curr(QView.OBJ_AO)
1019
1020 @staticmethod
1022 QView.query_curr(QView.OBJ_MP)
1023
1024 @staticmethod
1026 QView.query_curr(QView.OBJ_EQ)
1027
1028 @staticmethod
1030 QView.query_curr(QView.OBJ_TE)
1031
1032 @staticmethod
1034 QView.query_curr(QView.OBJ_AP)
1035
1036 @staticmethod
1038 QSpy._sendTo(pack("<B", QSpy._TRGT_INFO))
1039
1040 @staticmethod
1042 QSpy._sendTo(pack("<B", QSpy._QSPY_CLEAR_SCREEN))
1043
1044 @staticmethod
1046 QView.tick(0)
1047
1048 @staticmethod
1050 QView.tick(1)
1051
1052 @staticmethod
1054 QView._EvtDialog("Publish Event", QView.publish)
1055
1056 @staticmethod
1058 QView._EvtDialog("Post Event", QView.post)
1059
1060 @staticmethod
1062 QView._EvtDialog("Init Event", QView.init)
1063
1064 @staticmethod
1066 QView._EvtDialog("Dispatch Event", QView.dispatch)
1067
1068 @staticmethod
1069 def _onHelp():
1070 webbrowser.open("https://www.state-machine.com/qtools/qview.html",
1071 new=2)
1072
1073 @staticmethod
1075 QView._MessageDialog("About QView",
1076 f"QView version {QView.VERSION//100}."\
1077 f"{(QView.VERSION//10) % 10}.{QView.VERSION % 10}"\
1078 "\n\nFor more information see:\n"\
1079 "https://www.state-machine.com/qtools/qview.html")
1080
1081 @staticmethod
1082 def _showerror(title, message):
1083 QView._gui.after_cancel(QSpy._after_id)
1084 QView._MessageDialog(title, message)
1085
1086 @staticmethod
1087 def _strVar_value(strVar, base=0):
1088 val = strVar.get().replace(" ", "") # cleanup spaces
1089 strVar.set(val)
1090 try:
1091 value = int(val, base=base)
1092 return value # integer
1093 except Exception:
1094 return val # string
1095
1096
1097 #-------------------------------------------------------------------------
1098 # private dialog boxes...
1099 #
1100 class _AttachDialog(Dialog):
1101 def __init__(self):
1102 QView._attach_dialog = self
1103 super().__init__(QView._gui, "Attach to QSpy")
1104
1105 def body(self, master):
1106 self.resizable(height=False, width=False)
1107 Label(master,
1108 text="Make sure that QSPY back-end is running and\n"
1109 "not already used by other front-end.\n\n"
1110 "Press Attach to re-try to attach or\n"
1111 "Close to quit.").pack()
1112
1113 def buttonbox(self):
1114 box = Frame(self)
1115 w = Button(box, text="Attach", width=10, command=self.ok,
1116 default=ACTIVE)
1117 w.pack(side=LEFT, padx=5, pady=5)
1118 w = Button(box, text="Close", width=10, command=self.cancel)
1119 w.pack(side=LEFT, padx=5, pady=5)
1120 self.bind("<Return>", self.ok)
1121 self.bind("<Escape>", self.cancel)
1122 box.pack()
1123
1124 def close(self):
1125 super().cancel()
1126 QView._attach_dialog = None
1127
1128 def validate(self):
1129 QSpy._attach()
1130 return 0
1131
1132 def apply(self):
1133 QView._attach_dialog = None
1134
1135 def cancel(self, event=None):
1136 super().cancel()
1137 QView._quit()
1138
1139
1140 #.........................................................................
1141 # helper dialog box for message boxes, @sa QView._showerror()
1142 class _MessageDialog(Dialog):
1143 def __init__(self, title, message):
1144 self.message = message
1145 super().__init__(QView._gui, title)
1146
1147 def body(self, master):
1148 self.resizable(height=False, width=False)
1149 Label(master, text=self.message, justify=LEFT).pack()
1150
1151 def buttonbox(self):
1152 box = Frame(self)
1153 Button(box, text="OK", width=10, command=self.ok,
1154 default=ACTIVE).pack(side=LEFT, padx=5, pady=5)
1155 self.bind("<Return>", self.ok)
1156 box.pack()
1157
1158 #.........................................................................
1159 class _GlbFilterDialog(Dialog):
1160 def __init__(self, title, mask):
1161 self._title = title
1162 self._mask = mask
1163 super().__init__(QView._gui, title)
1164
1165 def body(self, master):
1166 N_ROW = 3
1167 Button(master, text="Select ALL", command=self._sel_all)\
1168 .grid(row=0,column=0, padx=2, pady=2, sticky=W+E)
1169 Button(master, text="Clear ALL", command=self._clr_all)\
1170 .grid(row=0,column=N_ROW-1, padx=2, pady=2, sticky=W+E)
1171 n = 0
1172 self._filter_var = []
1173 for i in range(QSpy._GLB_FLT_RANGE):
1174 if self._mask & (1 << i) != 0:
1175 self._filter_var.append(IntVar())
1176 if QView._glb_filter & (1 << i):
1177 self._filter_var[n].set(1)
1178 Checkbutton(master, text=QSpy._QS[i], anchor=W,
1179 variable=self._filter_var[n])\
1180 .grid(row=(n + N_ROW)//N_ROW,column=(n+N_ROW)%N_ROW,
1181 padx=2,pady=2,sticky=W)
1182 n += 1
1183
1184 def _sel_all(self):
1185 n = 0
1186 for i in range(QSpy._GLB_FLT_RANGE):
1187 if self._mask & (1 << i) != 0:
1188 self._filter_var[n].set(1)
1189 n += 1
1190
1191 def _clr_all(self):
1192 n = 0
1193 for i in range(QSpy._GLB_FLT_RANGE):
1194 if self._mask & (1 << i) != 0:
1195 self._filter_var[n].set(0)
1196 n += 1
1197
1198 def apply(self):
1199 n = 0
1200 for i in range(QSpy._GLB_FLT_RANGE):
1201 if self._mask & (1 << i) != 0:
1202 if self._filter_var[n].get():
1203 QView._glb_filter |= (1 << i)
1204 else:
1205 QView._glb_filter &= ~(1 << i)
1206 n += 1
1207 QSpy._sendTo(pack("<BBQQ", QSpy._TRGT_GLB_FILTER, 16,
1208 QView._glb_filter & 0xFFFFFFFFFFFFFFFF,
1209 QView._glb_filter >> 64))
1210 QView._updateMenus()
1211
1212 #.........................................................................
1213 class _LocFilterDialog(Dialog):
1214 def __init__(self, title, mask):
1215 self._title = title
1216 self._mask = mask
1217 super().__init__(QView._gui, title)
1218
1219 def body(self, master):
1220 N_ROW = 8
1221 Button(master, text="Select ALL", command=self._sel_all)\
1222 .grid(row=0,column=0, padx=2, pady=2, sticky=W+E)
1223 Button(master, text="Clear ALL", command=self._clr_all)\
1224 .grid(row=0,column=N_ROW-1, padx=2, pady=2, sticky=W+E)
1225 n = 0
1226 self._filter_var = []
1227 if self._mask == QSpy._LOC_FLT_MASK_AO:
1228 QS_id = "AO-prio=%d"
1229 else:
1230 QS_id = "QS-ID=%d"
1231 for i in range(QSpy._LOC_FLT_RANGE):
1232 if self._mask & (1 << i) != 0:
1233 self._filter_var.append(IntVar())
1234 if QView._loc_filter & (1 << i):
1235 self._filter_var[n].set(1)
1236 Checkbutton(master, text=QS_id%(i), anchor=W,
1237 variable=self._filter_var[n])\
1238 .grid(row=(n + N_ROW)//N_ROW,column=(n+N_ROW)%N_ROW,
1239 padx=2,pady=2,sticky=W)
1240 n += 1
1241
1242 def _sel_all(self):
1243 n = 0
1244 for i in range(QSpy._LOC_FLT_RANGE):
1245 if self._mask & (1 << i) != 0:
1246 self._filter_var[n].set(1)
1247 n += 1
1248
1249 def _clr_all(self):
1250 n = 0
1251 for i in range(QSpy._LOC_FLT_RANGE):
1252 if self._mask & (1 << i) != 0:
1253 self._filter_var[n].set(0)
1254 n += 1
1255
1256 def apply(self):
1257 n = 0
1258 for i in range(QSpy._LOC_FLT_RANGE):
1259 if self._mask & (1 << i) != 0:
1260 if self._filter_var[n].get():
1261 QView._loc_filter |= (1 << i)
1262 else:
1263 QView._loc_filter &= ~(1 << i)
1264 n += 1
1265 QSpy._sendTo(pack("<BBQQ", QSpy._TRGT_LOC_FILTER, 16,
1266 QView._loc_filter & 0xFFFFFFFFFFFFFFFF,
1267 QView._loc_filter >> 64))
1268 QView._updateMenus()
1269
1270 #.........................................................................
1271 # deprecated
1273 def __init__(self):
1274 self._obj = None
1275 super().__init__(QView._gui, "Local AO-OBJ Filter")
1276
1277 def body(self, master):
1278 Label(master, text="AO-OBJ").grid(row=0,column=0,
1279 sticky=E,padx=2)
1280 Entry(master, relief=SUNKEN, width=25,
1281 textvariable=QView._locAO_OBJ).grid(row=0,column=1)
1282
1283 def validate(self):
1284 self._obj = QView._strVar_value(QView._locAO_OBJ)
1285 return 1
1286
1287 def apply(self):
1288 QView.ao_filter(self._obj)
1289
1290 #.........................................................................
1291 class _CurrObjDialog(Dialog):
1292 def __init__(self, obj_kind, label):
1293 self._obj_kind = obj_kind
1294 self._label = label
1295 self._obj = None
1296 super().__init__(QView._gui, "Current Object")
1297
1298 def body(self, master):
1299 Label(master, text=self._label).grid(row=0,column=0,
1300 sticky=E,padx=2)
1301 Entry(master, relief=SUNKEN, width=25,
1302 textvariable=QView._currObj[self._obj_kind])\
1303 .grid(row=0,column=1)
1304
1305 def validate(self):
1306 self._obj = QView._strVar_value(QView._currObj[self._obj_kind])
1307 if self._obj == "":
1308 self._obj = 0
1309 return 1
1310
1311 def apply(self):
1312 QView.current_obj(self._obj_kind, self._obj)
1313
1314 #.........................................................................
1315 class _CommandDialog(Dialog):
1316 def __init__(self):
1317 self._cmdId = None
1318 self._param1 = None
1319 self._param2 = None
1320 self._param3 = None
1321 super().__init__(QView._gui, "Command")
1322
1323 def body(self, master):
1324 Label(master, text="command").grid(row=0,column=0,sticky=E,padx=2)
1325 Entry(master, relief=SUNKEN, width=25,
1326 textvariable=QView._command).grid(row=0,column=1,pady=2)
1327 Label(master, text="param1").grid(row=1,column=0,sticky=E,padx=2)
1328 Entry(master, relief=SUNKEN, width=25,
1329 textvariable=QView._command_p1).grid(row=1,column=1,padx=2)
1330 Label(master, text="param2").grid(row=2,column=0,sticky=E,padx=2)
1331 Entry(master, relief=SUNKEN, width=25,
1332 textvariable=QView._command_p2).grid(row=2,column=1,padx=2)
1333 Label(master, text="param3").grid(row=3,column=0,sticky=E,padx=2)
1334 Entry(master, relief=SUNKEN, width=25,
1335 textvariable=QView._command_p3).grid(row=3,column=1,padx=2)
1336
1337 def validate(self):
1338 self._cmdId = QView._strVar_value(QView._command)
1339 if self._cmdId == "":
1340 QView._MessageDialog("Command Error", "empty command")
1341 return 0
1342 self._param1 = QView._strVar_value(QView._command_p1)
1343 if self._param1 == "":
1344 self._param1 = 0
1345 elif not isinstance(self._param1, int):
1346 QView._MessageDialog("Command Error", "param1 not integer")
1347 return 0
1348 self._param2 = QView._strVar_value(QView._command_p2)
1349 if self._param2 == "":
1350 self._param2 = 0
1351 elif not isinstance(self._param2, int):
1352 QView._MessageDialog("Command Error", "param2 not integer")
1353 return 0
1354 self._param3 = QView._strVar_value(QView._command_p3)
1355 if self._param3 == "":
1356 self._param3 = 0
1357 elif not isinstance(self._param3, int):
1358 QView._MessageDialog("Command Error", "param3 not integer")
1359 return 0
1360 return 1
1361
1362 def apply(self):
1363 QView.command(self._cmdId,
1364 self._param1, self._param2, self._param3)
1365
1366 #.........................................................................
1367 class _NoteDialog(Dialog):
1368 def __init__(self):
1369 self._note = None
1370 self._note_kind = None
1371 super().__init__(QView._gui, "Show Note")
1372
1373 def body(self, master):
1374 Label(master, text="message").grid(row=0,column=0,sticky=E,padx=2)
1375 Entry(master, relief=SUNKEN, width=65,
1376 textvariable=QView._note).grid(row=0,column=1,sticky=W,pady=2)
1377 Label(master, text="kind").grid(row=1,column=0,sticky=E,padx=2)
1378 Entry(master, relief=SUNKEN, width=5,
1379 textvariable=QView._note_kind).grid(row=1,column=1,sticky=W,padx=2)
1380
1381 def validate(self):
1382 self._note = QView._note.get()
1383 self._note_kind = QView._strVar_value(QView._note_kind)
1384 return 1
1385
1386 def apply(self):
1387 QSpy._sendTo(struct.pack("<BB", QSpy._QSPY_SHOW_NOTE, self._note_kind),
1388 self._note)
1389
1390 #.........................................................................
1391 class _PeekDialog(Dialog):
1392 def __init__(self):
1393 self._offs = None
1394 self._size = None
1395 self._len = None
1396 super().__init__(QView._gui, "Peek")
1397
1398 def body(self, master):
1399 Label(master, text="obj/addr").grid(row=0,column=0,
1400 sticky=E,padx=2)
1401 Label(master, text=QView._currObj[QView.OBJ_AP].get(),anchor=W,
1402 relief=SUNKEN).grid(row=0,column=1, columnspan=2,sticky=E+W)
1403 Label(master, text="offset").grid(row=1,column=0,sticky=E,padx=2)
1404 Entry(master, relief=SUNKEN, width=25,
1405 textvariable=QView._peek_offs).grid(row=1,column=1,
1406 columnspan=2)
1407 Label(master, text="n-units").grid(row=2,column=0,
1408 sticky=E,padx=2)
1409 Entry(master, relief=SUNKEN, width=12,
1410 textvariable=QView._peek_len).grid(row=2,column=1,
1411 sticky=E+W,padx=2)
1412 OptionMenu(master, QView._peek_dtype, *QView._dtypes).grid(row=2,
1413 column=2,sticky=E,padx=2)
1414
1415 def validate(self):
1416 if QView._currObj[QView.OBJ_AP].get() == "":
1417 QView._MessageDialog("Peek Error", "Current AP_OBJ not set")
1418 return 0
1419 self._offs = QView._strVar_value(QView._peek_offs)
1420 if not isinstance(self._offs, int):
1421 self._offs = 0
1422 i = QView._dtypes.index(QView._peek_dtype.get())
1423 self._size = QView._dsizes[i]
1424 self._len = QView._strVar_value(QView._peek_len)
1425 if not isinstance(self._len, int):
1426 self._len = 1
1427 return 1
1428
1429 def apply(self):
1430 QView.peek(self._offs, self._size, self._len)
1431
1432 #.........................................................................
1433 class _PokeDialog(Dialog):
1434 def __init__(self):
1435 self._offs = None
1436 self._size = None
1437 self._data = None
1438 super().__init__(QView._gui, "Poke")
1439
1440 def body(self, master):
1441 Label(master, text="obj/addr").grid(row=0,column=0,sticky=E,padx=2)
1442 Label(master, text=QView._currObj[QView.OBJ_AP].get(), anchor=W,
1443 relief=SUNKEN).grid(row=0,column=1,sticky=E+W)
1444 Label(master, text="offset").grid(row=1,column=0,sticky=E,padx=2)
1445 Entry(master, relief=SUNKEN, width=25,
1446 textvariable=QView._poke_offs).grid(row=1,column=1)
1447 OptionMenu(master, QView._poke_dtype,
1448 *QView._dtypes).grid(row=2,column=0,sticky=E,padx=2)
1449 Entry(master, relief=SUNKEN, width=25,
1450 textvariable=QView._poke_data).grid(row=2,column=1)
1451
1452 def validate(self):
1453 if QView._currObj[QView.OBJ_AP].get() == "":
1454 QView._MessageDialog("Poke Error", "Current AP_OBJ not set")
1455 return 0
1456 self._offs = QView._strVar_value(QView._poke_offs)
1457 if not isinstance(self._offs, int):
1458 self._offs = 0
1459
1460 self._data = QView._strVar_value(QView._poke_data)
1461 if not isinstance(self._data, int):
1462 QView._MessageDialog("Poke Error", "data not integer")
1463 return 0
1464 dtype = QView._poke_dtype.get()
1465 self._size = QView._dsizes[QView._dtypes.index(dtype)]
1466 if self._size == 1 and self._data > 0xFF:
1467 QView._MessageDialog("Poke Error", "8-bit data out of range")
1468 return 0
1469 if self._size == 2 and self._data > 0xFFFF:
1470 QView._MessageDialog("Poke Error", "16-bit data out of range")
1471 return 0
1472 if self._size == 4 and self._data > 0xFFFFFFFF:
1473 QView._MessageDialog("Poke Error", "32-bit data out of range")
1474 return 0
1475 return 1
1476
1477 def apply(self):
1478 QView.poke(self._offs, self._size, self._data)
1479
1480 #.........................................................................
1481 class _EvtDialog(Dialog):
1482 def __init__(self, title, action):
1483 self._action = action
1484 self._sig = None
1485 self._params = None
1486 if action == QView.dispatch:
1487 self._obj = QView._currObj[QView.OBJ_SM].get()
1488 else:
1489 self._obj = QView._currObj[QView.OBJ_AO].get()
1490 super().__init__(QView._gui, title)
1491
1492 def body(self, master):
1493 Label(master, text="obj/addr").grid(row=0,column=0,
1494 sticky=E,padx=2)
1495 Label(master, text=self._obj, anchor=W,
1496 relief=SUNKEN).grid(row=0,column=1,columnspan=2,sticky=E+W)
1497 Frame(master,height=2,borderwidth=1,relief=SUNKEN).grid(row=1,
1498 column=0,columnspan=3,sticky=E+W+N+S,pady=4)
1499 Label(master, text="sig").grid(row=2,column=0,sticky=E,
1500 padx=2,pady=4)
1501 Entry(master, relief=SUNKEN,
1502 textvariable=QView._evt_sig).grid(row=2,column=1,
1503 columnspan=2,sticky=E+W)
1504 for i, e in enumerate(QView._evt_par):
1505 Label(master, text=f"par{i+1}").grid(row=3+i,column=0,
1506 sticky=E,padx=2)
1507 OptionMenu(master, QView._evt_dtype[i], *QView._dtypes).grid(
1508 row=3+i,column=1,sticky=E,padx=2)
1509 Entry(master, relief=SUNKEN, width=18,
1510 textvariable=QView._evt_par[i]).grid(row=3+i,column=2,
1511 sticky=E+W)
1512
1513 def validate(self):
1514 self._sig = QView._strVar_value(QView._evt_sig)
1515 if self._sig == "":
1516 QView._MessageDialog("Event error", "empty event sig")
1517 return 0
1518 self._params = bytearray()
1519 for i, e in enumerate(QView._evt_par):
1520 par = QView._strVar_value(QView._evt_par[i])
1521 if par == "":
1522 break
1523 if not isinstance(par, int):
1524 QView._MessageDialog(f"Event Error: par{i}",
1525 "data not integer")
1526 return 0
1527 idx = QView._dtypes.index(QView._evt_dtype[i].get())
1528 size = QView._dsizes[idx]
1529 if size == 1 and par > 0xFF:
1530 QView._MessageDialog(f"Event Error: par{i}",
1531 "8-bit data out of range")
1532 return 0
1533 if size == 2 and par > 0xFFFF:
1534 QView._MessageDialog(f"Event Error: par{i}",
1535 "16-bit data out of range")
1536 return 0
1537 if size == 4 and par > 0xFFFFFFFF:
1538 QView._MessageDialog(f"Event Error: par{i}",
1539 "32-bit data out of range")
1540 return 0
1541
1542 fmt = QSpy._fmt_endian + ("B", "H", "I")[idx]
1543 self._params.extend(pack(fmt, par))
1544 return 1
1545
1546 def apply(self):
1547 self._action(self._sig, self._params)
1548
1549
1550#=============================================================================
1551## Helper class for UDP-communication with the QSpy front-end
1552# (non-blocking UDP-socket version for QView)
1553#
1554class QSpy:
1555 # private class variables...
1556 _sock = None
1557 _is_attached = False
1558 _tx_seq = 0
1559 _rx_seq = 0
1560 _host_addr = ["localhost", 7701] # list, to be converted to a tuple
1561 _local_port = 0 # let the OS decide the best local port
1562 _after_id = None
1563
1564 # formats of various packet elements from the Target
1565 _fmt_target = "UNKNOWN"
1566 _fmt_endian = "<"
1567 _size_objPtr = 4
1568 _size_funPtr = 4
1569 _size_tstamp = 4
1570 _size_sig = 2
1571 _size_evtSize = 2
1572 _size_evtSize = 2
1573 _size_queueCtr = 1
1574 _size_poolCtr = 2
1575 _size_poolBlk = 2
1576 _size_tevtCtr = 2
1577 _fmt = "xBHxLxxxQ"
1578
1579 # QSPY UDP socket poll interval [ms]
1580 # NOTE: the chosen value actually sleeps for one system clock tick,
1581 # which is typically 10ms
1582 _POLLI = 10
1583
1584 # tuple of QS records from the Target.
1585 # !!! NOTE: Must match qpc/include/qs.h !!!
1586 _QS = ("QS_EMPTY",
1587 # [1] SM records
1588 "QS_QEP_STATE_ENTRY", "QS_QEP_STATE_EXIT",
1589 "QS_QEP_STATE_INIT", "QS_QEP_INIT_TRAN",
1590 "QS_QEP_INTERN_TRAN", "QS_QEP_TRAN",
1591 "QS_QEP_IGNORED", "QS_QEP_DISPATCH",
1592 "QS_QEP_UNHANDLED",
1593
1594 # [10] Active Object (AO) records
1595 "QS_QF_ACTIVE_DEFER", "QS_QF_ACTIVE_RECALL",
1596 "QS_QF_ACTIVE_SUBSCRIBE", "QS_QF_ACTIVE_UNSUBSCRIBE",
1597 "QS_QF_ACTIVE_POST", "QS_QF_ACTIVE_POST_LIFO",
1598 "QS_QF_ACTIVE_GET", "QS_QF_ACTIVE_GET_LAST",
1599 "QS_QF_ACTIVE_RECALL_ATTEMPT",
1600
1601 # [19] Event Queue (EQ) records
1602 "QS_QF_EQUEUE_POST", "QS_QF_EQUEUE_POST_LIFO",
1603 "QS_QF_EQUEUE_GET", "QS_QF_EQUEUE_GET_LAST",
1604
1605 # [23] Framework (QF) records
1606 "QS_QF_NEW_ATTEMPT",
1607
1608 # [24] Memory Pool (MP) records
1609 "QS_QF_MPOOL_GET", "QS_QF_MPOOL_PUT",
1610
1611 # [26] Additional Framework (QF) records
1612 "QS_QF_PUBLISH", "QS_QF_NEW_REF",
1613 "QS_QF_NEW", "QS_QF_GC_ATTEMPT",
1614 "QS_QF_GC", "QS_QF_TICK",
1615
1616 # [32] Time Event (TE) records
1617 "QS_QF_TIMEEVT_ARM", "QS_QF_TIMEEVT_AUTO_DISARM",
1618 "QS_QF_TIMEEVT_DISARM_ATTEMPT", "QS_QF_TIMEEVT_DISARM",
1619 "QS_QF_TIMEEVT_REARM", "QS_QF_TIMEEVT_POST",
1620
1621 # [38] Additional (QF) records
1622 "QS_QF_DELETE_REF", "QS_QF_CRIT_ENTRY",
1623 "QS_QF_CRIT_EXIT", "QS_QF_ISR_ENTRY",
1624 "QS_QF_ISR_EXIT", "QS_QF_INT_DISABLE",
1625 "QS_QF_INT_ENABLE",
1626
1627 # [45] Additional Active Object (AO) records
1628 "QS_QF_ACTIVE_POST_ATTEMPT",
1629
1630 # [46] Additional Event Queue (EQ) records
1631 "QS_QF_EQUEUE_POST_ATTEMPT",
1632
1633 # [47] Additional Memory Pool (MP) records
1634 "QS_QF_MPOOL_GET_ATTEMPT",
1635
1636 # [48] Scheduler (SC) records
1637 "QS_SCHED_PREEMPT", "QS_SCHED_RESTORE",
1638 "QS_SCHED_LOCK", "QS_SCHED_UNLOCK",
1639 "QS_SCHED_NEXT", "QS_SCHED_IDLE",
1640
1641 # [54] Miscellaneous QS records (not maskable)
1642 "QS_ENUM_DICT",
1643
1644 # [55] Additional QEP records
1645 "QS_QEP_TRAN_HIST", "QS_QEP_TRAN_EP",
1646 "QS_QEP_TRAN_XP",
1647
1648 # [58] Miscellaneous QS records (not maskable)
1649 "QS_TEST_PAUSED", "QS_TEST_PROBE_GET",
1650 "QS_SIG_DICT", "QS_OBJ_DICT",
1651 "QS_FUN_DICT", "QS_USR_DICT",
1652 "QS_TARGET_INFO", "QS_TARGET_DONE",
1653 "QS_RX_STATUS", "QS_QUERY_DATA",
1654 "QS_PEEK_DATA", "QS_ASSERT_FAIL",
1655 "QS_QF_RUN",
1656
1657 # [71] Semaphore (SEM) records
1658 "QS_SEM_TAKE", "QS_SEM_BLOCK",
1659 "QS_SEM_SIGNAL", "QS_SEM_BLOCK_ATTEMPT",
1660
1661 # [75] Mutex (MTX) records
1662 "QS_MTX_LOCK", "QS_MTX_BLOCK",
1663 "QS_MTX_UNLOCK", "QS_MTX_LOCK_ATTEMPT",
1664 "QS_MTX_BLOCK_ATTEMPT", "QS_MTX_UNLOCK_ATTEMPT",
1665
1666 # [81] Reserved QS records
1667 "QS_QF_ACTIVE_DEFER_ATTEMPT",
1668 "QS_RESERVED_82", "QS_RESERVED_83",
1669 "QS_RESERVED_84", "QS_RESERVED_85",
1670 "QS_RESERVED_86", "QS_RESERVED_87",
1671 "QS_RESERVED_88", "QS_RESERVED_89",
1672 "QS_RESERVED_90", "QS_RESERVED_91",
1673 "QS_RESERVED_92", "QS_RESERVED_93",
1674 "QS_RESERVED_94", "QS_RESERVED_95",
1675 "QS_RESERVED_96", "QS_RESERVED_97",
1676 "QS_RESERVED_98", "QS_RESERVED_99",
1677
1678 # [100] Application-specific (User) QS records
1679 "QS_USER_00", "QS_USER_01",
1680 "QS_USER_02", "QS_USER_03",
1681 "QS_USER_04", "QS_USER_05",
1682 "QS_USER_06", "QS_USER_07",
1683 "QS_USER_08", "QS_USER_09",
1684 "QS_USER_10", "QS_USER_11",
1685 "QS_USER_12", "QS_USER_13",
1686 "QS_USER_14", "QS_USER_15",
1687 "QS_USER_16", "QS_USER_17",
1688 "QS_USER_18", "QS_USER_19",
1689 "QS_USER_20", "QS_USER_21",
1690 "QS_USER_22", "QS_USER_23",
1691 "QS_USER_24")
1692
1693 # global filter masks
1694 _GLB_FLT_MASK_ALL= 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
1695 _GLB_FLT_MASK_SM = 0x000000000000000003800000000003FE
1696 _GLB_FLT_MASK_AO = 0x0000000000020000000020000007FC00
1697 _GLB_FLT_MASK_QF = 0x000000000000000000001FC0FC800000
1698 _GLB_FLT_MASK_TE = 0x00000000000000000000003F00000000
1699 _GLB_FLT_MASK_EQ = 0x00000000000000000000400000780000
1700 _GLB_FLT_MASK_MP = 0x00000000000000000000800003000000
1701 _GLB_FLT_MASK_SC = 0x0000000000000000003F000000000000
1702 _GLB_FLT_MASK_SEM= 0x00000000000007800000000000000000
1703 _GLB_FLT_MASK_MTX= 0x000000000001F8000000000000000000
1704 _GLB_FLT_MASK_U0 = 0x000001F0000000000000000000000000
1705 _GLB_FLT_MASK_U1 = 0x00003E00000000000000000000000000
1706 _GLB_FLT_MASK_U2 = 0x0007C000000000000000000000000000
1707 _GLB_FLT_MASK_U3 = 0x00F80000000000000000000000000000
1708 _GLB_FLT_MASK_U4 = 0x1F000000000000000000000000000000
1709 _GLB_FLT_MASK_UA = 0x1FFFFFF0000000000000000000000000
1710 _GLB_FLT_RANGE = 125
1711
1712 # local filter masks
1713 _LOC_FLT_MASK_ALL= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
1714 _LOC_FLT_MASK_AO = 0x0000000000000001FFFFFFFFFFFFFFFE
1715 _LOC_FLT_MASK_EP = 0x000000000000FFFE0000000000000000
1716 _LOC_FLT_MASK_EQ = 0x00000000FFFF00000000000000000000
1717 _LOC_FLT_MASK_AP = 0xFFFFFFFF000000000000000000000000
1718 _LOC_FLT_RANGE = 128
1719
1720 # interesting packets from QSPY/Target...
1721 _PKT_TEXT_ECHO = 0
1722 _PKT_TARGET_INFO = 64
1723 _PKT_ASSERTION = 69
1724 _PKT_QF_RUN = 70
1725 _PKT_ATTACH_CONF = 128
1726 _PKT_DETACH = 129
1727
1728 # records to the Target...
1729 _TRGT_INFO = 0
1730 _TRGT_COMMAND = 1
1731 _TRGT_RESET = 2
1732 _TRGT_TICK = 3
1733 _TRGT_PEEK = 4
1734 _TRGT_POKE = 5
1735 _TRGT_FILL = 6
1736 _TRGT_TEST_SETUP = 7
1737 _TRGT_TEST_TEARDOWN = 8
1738 _TRGT_TEST_PROBE = 9
1739 _TRGT_GLB_FILTER = 10
1740 _TRGT_LOC_FILTER = 11
1741 _TRGT_AO_FILTER = 12
1742 _TRGT_CURR_OBJ = 13
1743 _TRGT_CONTINUE = 14
1744 _TRGT_QUERY_CURR = 15
1745 _TRGT_EVENT = 16
1746
1747 # packets to QSpy only...
1748 _QSPY_ATTACH = 128
1749 _QSPY_DETACH = 129
1750 _QSPY_SAVE_DICT = 130
1751 _QSPY_TEXT_OUT = 131
1752 _QSPY_BIN_OUT = 132
1753 _QSPY_MATLAB_OUT = 133
1754 _QSPY_SEQUENCE_OUT = 134
1755 _QSPY_CLEAR_SCREEN = 140
1756 _QSPY_SHOW_NOTE = 141
1757
1758 # packets to QSpy to be "massaged" and forwarded to the Target...
1759 _QSPY_SEND_EVENT = 135
1760 _QSPY_SEND_AO_FILTER = 136
1761 _QSPY_SEND_CURR_OBJ = 137
1762 _QSPY_SEND_COMMAND = 138
1763 _QSPY_SEND_TEST_PROBE = 139
1764
1765 # special event sub-commands for QSPY_SEND_EVENT
1766 _EVT_PUBLISH = 0
1767 _EVT_POST = 253
1768 _EVT_INIT = 254
1769 _EVT_DISPATCH = 255
1770
1771 @staticmethod
1772 def _init():
1773 # Create socket
1774 QSpy._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
1775 QSpy._sock.setblocking(0) # NON-BLOCKING socket
1776 try:
1777 QSpy._sock.bind(("0.0.0.0", QSpy._local_port))
1778 #print("bind: ", ("0.0.0.0", QSpy._local_port))
1779 except Exception:
1780 QView._showerror("UDP Socket Error",
1781 "Can't bind the UDP socket\n"
1782 "to the specified local_host.\n"
1783 "Check if other instances of qspyview\n"
1784 "or qutest are running...")
1785 QView._quit(-1)
1786 return -1
1787 return 0
1788
1789 @staticmethod
1790 def _attach():
1791 QSpy._is_attached = False
1792 QView._have_info = False
1793 if QView._echo_text.get():
1794 channels = 0x3
1795 else:
1796 channels = 0x1
1797 QSpy._sendTo(pack("<BB", QSpy._QSPY_ATTACH, channels))
1798 QSpy._attach_ctr = 50
1799 QSpy._after_id = QView._gui.after(1, QSpy._poll0) # start poll0
1800
1801 @staticmethod
1802 def _detach():
1803 if QSpy._sock is None:
1804 return
1805 QSpy._sendTo(pack("<B", QSpy._QSPY_DETACH))
1806 time.sleep(0.25) # let the socket finish sending the packet
1807 #QSpy._sock.shutdown(socket.SHUT_RDWR)
1808 QSpy._sock.close()
1809 QSpy._sock = None
1810
1811 @staticmethod
1813 # channels: 0x1-binary, 0x2-text, 0x3-both
1814 if QView._echo_text.get():
1815 channels = 0x3
1816 else:
1817 channels = 0x1
1818 QSpy._sendTo(pack("<BB", QSpy._QSPY_ATTACH, channels))
1819
1820 # poll the UDP socket until the QSpy confirms ATTACH
1821 @staticmethod
1822 def _poll0():
1823 #print("poll0 ", QSpy._attach_ctr)
1824 QSpy._attach_ctr -= 1
1825 if QSpy._attach_ctr == 0:
1826 if QView._attach_dialog is None:
1827 QView._AttachDialog() # launch the AttachDialog
1828 return
1829
1830 try:
1831 packet = QSpy._sock.recv(4096)
1832 if not packet:
1833 QView._showerror("UDP Socket Error",
1834 "Connection closed by QSpy")
1835 QView._quit(-1)
1836 return
1837 except OSError: # non-blocking socket...
1838 QSpy._after_id = QView._gui.after(QSpy._POLLI, QSpy._poll0)
1839 return # <======== most frequent return (no packet)
1840 except Exception:
1841 QView._showerror("UDP Socket Error",
1842 "Uknown UDP socket error")
1843 QView._quit(-1)
1844 return
1845
1846 # parse the packet...
1847 dlen = len(packet)
1848 if dlen < 2:
1849 QView._showerror("Communication Error",
1850 "UDP packet from QSpy too short")
1851 QView._quit(-2)
1852 return
1853
1854 recID = packet[1]
1855 if recID == QSpy._PKT_ATTACH_CONF:
1856 QSpy._is_attached = True
1857 if QView._attach_dialog is not None:
1858 QView._attach_dialog.close()
1859
1860 # send either reset or target-info request
1861 # (keep the poll0 loop running)
1862 if QView._reset_request:
1863 QView._reset_request = False
1864 QSpy._sendTo(pack("<B", QSpy._TRGT_RESET))
1865 else:
1866 QSpy._sendTo(pack("<B", QSpy._TRGT_INFO))
1867
1868 # switch to the regular polling...
1869 QSpy._after_id = QView._gui.after(QSpy._POLLI, QSpy._poll)
1870
1871 # only show the canvas, if visible
1872 QView._onCanvasView()
1873
1874 # only show the frame, if visible
1875 QView._onFrameView()
1876 elif recID == QSpy._PKT_DETACH:
1877 QView._quit()
1878
1879 # regullar poll of the UDP socket after it has attached.
1880 @staticmethod
1881 def _poll():
1882 while True:
1883 try:
1884 packet = QSpy._sock.recv(4096)
1885 if not packet:
1886 QView._showerror("UDP Socket Error",
1887 "Connection closed by QSpy")
1888 QView._quit(-1)
1889 return
1890 except OSError: # non-blocking socket...
1891 QSpy._after_id = QView._gui.after(QSpy._POLLI, QSpy._poll)
1892 return # <============= no packet at this time
1893 except Exception:
1894 QView._showerror("UDP Socket Error",
1895 "Uknown UDP socket error")
1896 QView._quit(-1)
1897 return
1898
1899 # parse the packet...
1900 dlen = len(packet)
1901 if dlen < 2:
1902 QView._showerror("UDP Socket Data Error",
1903 "UDP packet from QSpy too short")
1904 QView._quit(-2)
1905 return
1906
1907 recID = packet[1]
1908 if recID == QSpy._PKT_TEXT_ECHO:
1909 # no need to check QView._echo_text.get()
1910 # because the text channel is closed
1911 QView.print_text(packet[3:])
1912
1913 elif recID == QSpy._PKT_TARGET_INFO:
1914 if dlen == 18:
1915 QView._showerror("QP Version Error,",
1916 "QP 8.0.0 or newer required")
1917 QView._quit(-2)
1918 return
1919
1920 if dlen != 20:
1921 QView._showerror("UDP Socket Data Error",
1922 "Corrupted Target-info")
1923 QView._quit(-2)
1924 return
1925
1926 if packet[2] & 0x80 != 0: # big endian?
1927 QSpy._fmt_endian = ">"
1928
1929 tstamp = packet[7:20]
1930 QSpy._size_objPtr = tstamp[3] & 0x0F
1931 QSpy._size_funPtr = tstamp[3] >> 4
1932 QSpy._size_tstamp = tstamp[4] & 0x0F
1933 QSpy._size_sig = tstamp[0] & 0x0F
1934 QSpy._size_evtSize = tstamp[0] >> 4
1935 QSpy._size_queueCtr= tstamp[1] & 0x0F
1936 QSpy._size_poolCtr = tstamp[2] >> 4
1937 QSpy._size_poolBlk = tstamp[2] & 0x0F
1938 QSpy._size_tevtCtr = tstamp[1] >> 4
1939 QSpy._fmt_target = \
1940 f"{tstamp[12]:02d}{tstamp[11]:02d}{tstamp[10]:02d}"\
1941 f"{tstamp[9]:02d}{tstamp[8]:02d}{tstamp[7]:02d}"
1942 #print("******* Target:", QSpy._fmt_target)
1943 QView._target.configure(text=f"Target: {QSpy._fmt_target}")
1944 QView._have_info = True
1945
1946 # is this also target reset?
1947 if packet[2] != 0:
1948 QView._onReset()
1949 try:
1950 QView._inst.on_reset()
1951 except Exception:
1952 QView._showerror("Runtime Error",
1953 traceback.format_exc(3))
1954 QView._quit(-3)
1955 return
1956
1957 elif recID == QSpy._PKT_QF_RUN:
1958 try:
1959 QView._inst.on_run()
1960 except Exception:
1961 QView._showerror("Runtime Error",
1962 traceback.format_exc(3))
1963 QView._quit(-3)
1964 return
1965
1966 elif recID == QSpy._PKT_DETACH:
1967 QView._showerror("UDP Socket Data Error",
1968 "QSPY detached")
1969 QView._quit()
1970 return
1971
1972 elif recID <= 124: # other binary data
1973 # find the (global) handler for the packet
1974 handler = getattr(QView._inst,
1975 QSpy._QS[recID], None)
1976 if handler is not None:
1977 try:
1978 handler(packet) # call the packet handler
1979 except Exception:
1980 QView._showerror("Runtime Error",
1981 traceback.format_exc(3))
1982 QView._quit(-3)
1983 return
1984 QSpy._rx_seq += 1
1985 QView._rx.configure(text=f"{QSpy._rx_seq}")
1986
1987
1988 @staticmethod
1989 def _sendTo(packet, sig_name=None):
1990 tx_packet = bytearray([QSpy._tx_seq & 0xFF])
1991 tx_packet.extend(packet)
1992 if sig_name is not None:
1993 tx_packet.extend(bytes(sig_name, "utf-8"))
1994 tx_packet.extend(b"\0") # zero-terminate
1995 try:
1996 QSpy._sock.sendto(tx_packet, QSpy._host_addr)
1997 except Exception:
1998 QView._showerror("UDP Socket Error",
1999 traceback.format_exc(3))
2000 QView._quit(-1)
2001 QSpy._tx_seq += 1
2002 if not QView._gui is None:
2003 QView._tx.configure(text=f"{QSpy._tx_seq}")
2004
2005 @staticmethod
2006 def _sendEvt(ao_prio, signal, params = None):
2007 #print("evt:", signal, params)
2008 fmt = f"<BB{QSpy._fmt[QSpy._size_sig]}H"
2009 if params is not None:
2010 length = len(params)
2011 else:
2012 length = 0
2013
2014 if isinstance(signal, int):
2015 packet = bytearray(pack(
2016 fmt, QSpy._TRGT_EVENT, ao_prio, signal, length))
2017 if params is not None:
2018 packet.extend(params)
2019 QSpy._sendTo(packet)
2020 else:
2021 packet = bytearray(pack(
2022 fmt, QSpy._QSPY_SEND_EVENT, ao_prio, 0, length))
2023 if params is not None:
2024 packet.extend(params)
2025 QSpy._sendTo(packet, signal)
2026
2027#=============================================================================
2028# main entry point to QView
2029def main():
2030 QView.main(QView()) # standalone QView
2031
2032#=============================================================================
2033if __name__ == "__main__":
2034 main()
Helper class for UDP-communication with the QSpy front-end (non-blocking UDP-socket version for QView...
Definition qview.py:1554
_sendEvt(ao_prio, signal, params=None)
Definition qview.py:2006
_reattach()
Definition qview.py:1812
_sendTo(packet, sig_name=None)
Definition qview.py:1989
body(self, master)
Definition qview.py:1105
__init__(self, obj_kind, label)
Definition qview.py:1292
body(self, master)
Definition qview.py:1492
__init__(self, title, action)
Definition qview.py:1482
__init__(self, title, mask)
Definition qview.py:1160
__init__(self, title, mask)
Definition qview.py:1214
__init__(self, title, message)
Definition qview.py:1143
body(self, master)
Definition qview.py:1373
body(self, master)
Definition qview.py:1398
body(self, master)
Definition qview.py:1440
_onGlbFilter_MP()
Definition qview.py:930
on_init(self)
Definition qview.py:175
command(cmd_id, param1=0, param2=0, param3=0)
executes a given command in the Target
Definition qview.py:197
_onGlbFilter_EQ()
Definition qview.py:926
_onGlbFilter_SEM()
Definition qview.py:938
dispatch(signal, params=None)
dispatch a given event in the current SM object in the Target
Definition qview.py:406
_onFrameClose()
Definition qview.py:905
_onGlbFilter_MTX()
Definition qview.py:942
current_obj(obj_kind, obj_id)
Set the Current-Object in the Target.
Definition qview.py:363
show_canvas(view=1)
Make the canvas visible (to be used in the constructor of the customization class)
Definition qview.py:738
_onClearQspy()
Definition qview.py:1041
_onCurrObj_AO()
Definition qview.py:991
peek(offset, size, num)
peeks data in the Target
Definition qview.py:215
_onEvt_PUBLISH()
Definition qview.py:1053
_onSaveBin()
Definition qview.py:868
post(signal, params=None)
post a given event to the current AO object in the Target
Definition qview.py:393
qunpack(fmt, bstr)
Unpack a QS trace record.
Definition qview.py:440
_onQueryCurr_TE()
Definition qview.py:1029
_quit(err=0)
Definition qview.py:749
_onCanvasView()
Definition qview.py:880
loc_filter(*args)
Set/clear the Local-Filter in the Target.
Definition qview.py:302
_onEvt_DISPATCH()
Definition qview.py:1065
_strVar_value(strVar, base=0)
Definition qview.py:1087
_onCurrObj_AP()
Definition qview.py:1008
reset_target()
Send the RESET packet to the Target.
Definition qview.py:188
_onCurrObj_EQ()
Definition qview.py:1000
_onCanvasClose()
Definition qview.py:890
on_run(self)
Definition qview.py:183
_onCurrObj_TE()
Definition qview.py:1004
_onSaveDict()
Definition qview.py:860
_onQueryCurr_SM()
Definition qview.py:1013
query_curr(obj_kind)
query the current object in the Target
Definition qview.py:381
_onTargetInfo()
Definition qview.py:1037
_init_gui(root)
Definition qview.py:494
_onGlbFilter_U2()
Definition qview.py:954
print_text(string)
Print a string to the Text area.
Definition qview.py:728
_onGlbFilter_U1()
Definition qview.py:950
_onLocFilter_AO_OBJ()
Definition qview.py:982
init(signal=0, params=None)
take the top-most initial transition in the current SM object in the Target
Definition qview.py:400
poke(offset, size, data)
pokes data into the Target
Definition qview.py:221
_onQueryCurr_AP()
Definition qview.py:1033
glb_filter(*args)
Set/clear the Global-Filter in the Target.
Definition qview.py:228
_onLocFilter_EP()
Definition qview.py:970
_onGlbFilter_SM()
Definition qview.py:910
_onQueryCurr_MP()
Definition qview.py:1021
on_reset(self)
Definition qview.py:179
_onGlbFilter_SC()
Definition qview.py:934
_onGlbFilter_TE()
Definition qview.py:922
ao_filter(obj_id)
Set/clear the Active-Object Local-Filter in the Target.
Definition qview.py:339
_onLocFilter_AP()
Definition qview.py:978
_onEvt_POST()
Definition qview.py:1057
_onSaveSequence()
Definition qview.py:876
_onGlbFilter_U0()
Definition qview.py:946
_onCurrObj_MP()
Definition qview.py:996
_onSaveText()
Definition qview.py:864
_onSaveMatlab()
Definition qview.py:872
publish(signal, params=None)
publish a given event to subscribers in the Target
Definition qview.py:387
_onLocFilter_AO()
Definition qview.py:966
_onLocFilter_EQ()
Definition qview.py:974
_onFrameView()
Definition qview.py:895
_assert(cond, message)
Definition qview.py:853
tick(tick_rate=0)
trigger system clock tick in the Target
Definition qview.py:209
_onCurrObj_SM()
Definition qview.py:986
_onGlbFilter_AO()
Definition qview.py:914
_onQueryCurr_AO()
Definition qview.py:1017
_onGlbFilter_QF()
Definition qview.py:918
_onGlbFilter_U3()
Definition qview.py:958
_trap_error()
Definition qview.py:847
_onQueryCurr_EQ()
Definition qview.py:1025
_showerror(title, message)
Definition qview.py:1082
customize()
Set QView customization.
Definition qview.py:721
show_frame(view=1)
Make the frame visible (to be used in the constructor of the customization class)
Definition qview.py:744
_updateMenus()
Definition qview.py:767
_onEvt_INIT()
Definition qview.py:1061
_onGlbFilter_U4()
Definition qview.py:962
main()
Definition qview.py:2029