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