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