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