- Plotting added - Statistic calculation added - Save measurement data as XLSX feature added - Test controller functionality extracted from the GUI class
610 lines
24 KiB
Python
610 lines
24 KiB
Python
from collections.abc import Callable
|
|
import tkinter as tk
|
|
from tkinter import IntVar, StringVar, ttk
|
|
from typing import Callable, Literal
|
|
|
|
import serial.tools.list_ports
|
|
import netifaces
|
|
|
|
import gui.GuiCommon as gcm
|
|
|
|
class FlexPtpOptionsPanel(ttk.LabelFrame):
|
|
def __init_layout(self) -> None:
|
|
self.rowconfigure([0, 1, 2, 3 ], weight=1)
|
|
self.columnconfigure(0, weight=1)
|
|
self.columnconfigure(1, weight=3)
|
|
|
|
def __init_widgets(self) -> None:
|
|
# initialize labels
|
|
device_label = ttk.Label(self, text="Device:") # Serial device entry
|
|
device_label.grid(column=0, row=0, sticky=tk.E)
|
|
|
|
baudrate_label = ttk.Label(self, text="Baudrate:") # Baudrate entry
|
|
baudrate_label.grid(column=0, row=1, sticky=tk.E)
|
|
|
|
baudrate_label = ttk.Label(self, text="Parity:") # Parity selector line
|
|
baudrate_label.grid(column=0, row=2, sticky=tk.E)
|
|
|
|
baudrate_label = ttk.Label(self, text="Stopbits:") # Number of stopbits
|
|
baudrate_label.grid(column=0, row=3, sticky=tk.E)
|
|
|
|
# initialize value selectors
|
|
|
|
# Serial device
|
|
self.__sel_device = tk.StringVar() # device var
|
|
device_combobox = ttk.Combobox(self, textvariable=self.__sel_device)
|
|
device_combobox["values"] = list(map(lambda p: p.device, filter(lambda p: p.subsystem == "usb", serial.tools.list_ports.comports()))) # list of possible devices
|
|
device_combobox.current(0)
|
|
device_combobox.grid(column=1, row=0, sticky=tk.EW, padx=4)
|
|
|
|
# Baudrate (default: 115200)
|
|
self.__sel_baudrate = tk.IntVar() # baudrate var
|
|
baudrate_combobox = ttk.Combobox(self, textvariable=self.__sel_baudrate)
|
|
baudrate_combobox["values"] = (115200, 57600, 38400, 19200, 9600, 4800) # possible baudrates
|
|
baudrate_combobox.current(0)
|
|
baudrate_combobox.grid(column=1, row=1, sticky=tk.EW, padx=4)
|
|
|
|
# Parity (default: NONE)
|
|
self.__sel_parity = tk.StringVar() # parity var
|
|
self.__sel_parity.set(serial.PARITY_NONE)
|
|
parity_frame = tk.Frame(self)
|
|
parity_frame.grid(column=1, row=2, sticky=tk.W)
|
|
|
|
parity_none = ttk.Radiobutton(parity_frame, text="None", variable=self.__sel_parity, value=serial.PARITY_NONE) # no parity
|
|
parity_none.pack(anchor=tk.W, side=tk.LEFT)
|
|
|
|
parity_even = ttk.Radiobutton(parity_frame, text="Even", variable=self.__sel_parity, value=serial.PARITY_EVEN) # even parity
|
|
parity_even.pack(anchor=tk.W, side=tk.LEFT)
|
|
|
|
parity_odd = ttk.Radiobutton(parity_frame, text="Odd", variable=self.__sel_parity, value=serial.PARITY_ODD) # odd parity
|
|
parity_odd.pack(anchor=tk.W, side=tk.LEFT)
|
|
|
|
# Stopbits (default: 1)
|
|
self.__sel_stopbits = tk.IntVar()
|
|
self.__sel_stopbits.set(1)
|
|
stopbits_frame = tk.Frame(self)
|
|
stopbits_frame.grid(column=1, row=3, sticky=tk.W)
|
|
|
|
stopbits_one = ttk.Radiobutton(stopbits_frame, text="1", variable=self.__sel_stopbits, value=1)
|
|
stopbits_one.pack(anchor=tk.W, side=tk.LEFT)
|
|
|
|
stopbits_two = ttk.Radiobutton(stopbits_frame, text="2", variable=self.__sel_stopbits, value=2)
|
|
stopbits_two.pack(anchor=tk.W, side=tk.LEFT)
|
|
|
|
|
|
def __init__(self, master: tk.Misc | None = None) -> None:
|
|
super().__init__(master)
|
|
self.configure(labelwidget=ttk.Label(self, text="flexPTP options", font=gcm.TITLE_FONT))
|
|
|
|
self.__init_layout() # initialize panel's inner layout
|
|
self.__init_widgets() # initialize widgets
|
|
|
|
def get_settings(self) -> dict:
|
|
return {
|
|
"device": self.__sel_device.get(),
|
|
"baudrate": self.__sel_baudrate.get(),
|
|
"parity": self.__sel_parity.get(),
|
|
"stopbits": self.__sel_stopbits.get()
|
|
}
|
|
|
|
|
|
class LinuxPtpOptionsPanel(ttk.LabelFrame):
|
|
def __init_layout(self) -> None:
|
|
self.rowconfigure([0, 1, 2], weight=1)
|
|
# self.rowconfigure(2, weight=100)
|
|
self.columnconfigure(0, weight=1)
|
|
self.columnconfigure(1, weight=3)
|
|
|
|
def __init_widgets(self) -> None:
|
|
# initialize labels
|
|
path_label = ttk.Label(self, text="Path:") # linuxptp binary path
|
|
path_label.grid(column=0, row=0, sticky=tk.E)
|
|
|
|
if_label = ttk.Label(self, text="Interface:") # network interface
|
|
if_label.grid(column=0, row=1, sticky=tk.E)
|
|
|
|
arg_label = ttk.Label(self, text="Arguments:") # additional arguments passed to the linuxptp
|
|
arg_label.grid(column=0, row=2, sticky=tk.E)
|
|
|
|
# initialize entry widgets
|
|
|
|
# linuxptp binary path entry (default: /usr/sbin/ptp4l)
|
|
self.__linuxptp_path = tk.StringVar() # path var
|
|
self.__linuxptp_path.set("/usr/sbin/ptp4l")
|
|
path_entry = ttk.Entry(self, font=gcm.TERMINAL_FONT, textvariable=self.__linuxptp_path)
|
|
path_entry.grid(column=1, row=0, sticky=tk.EW, padx=4)
|
|
|
|
# linuxptp interface selector (default: first interface in list)
|
|
self.__linuxptp_interface = tk.StringVar() # interface var
|
|
baudrate_combobox = ttk.Combobox(self, textvariable=self.__linuxptp_interface)
|
|
baudrate_combobox["values"] = netifaces.interfaces() # list of available interfaces
|
|
baudrate_combobox.current(0)
|
|
baudrate_combobox.grid(column=1, row=1, sticky=tk.EW, padx=4)
|
|
|
|
# linuxptp additional argument entry (default: <empty>)
|
|
self.__linuxptp_args = tk.StringVar() # argument var
|
|
self.__linuxptp_args.set("")
|
|
args_entry = ttk.Entry(self, font=gcm.TERMINAL_FONT, textvariable=self.__linuxptp_args)
|
|
args_entry.grid(column=1, row=2, sticky=tk.EW, padx=4)
|
|
|
|
def __init__(self, master: tk.Misc | None = None) -> None:
|
|
super().__init__(master)
|
|
self.configure(labelwidget=ttk.Label(self, text="linuxptp options", font=gcm.TITLE_FONT))
|
|
|
|
self.__init_layout() # initialize layout
|
|
self.__init_widgets() # initialize widgets
|
|
|
|
def get_settings(self) -> dict:
|
|
return {
|
|
"path": self.__linuxptp_path.get(),
|
|
"interface": self.__linuxptp_interface.get(),
|
|
"arguments": self.__linuxptp_args.get()
|
|
}
|
|
|
|
|
|
class BasicTestCasesPanel(ttk.LabelFrame):
|
|
# test modes and grid positions
|
|
MODES = {
|
|
"e2e_l4": {
|
|
"row": 1,
|
|
"col": 1,
|
|
},
|
|
"e2e_l2": {
|
|
"row": 2,
|
|
"col": 1,
|
|
},
|
|
"p2p_l4": {
|
|
"row": 1,
|
|
"col": 2,
|
|
},
|
|
"p2p_l2": {
|
|
"row": 2,
|
|
"col": 2,
|
|
},
|
|
}
|
|
|
|
def __init_styles(self) -> None:
|
|
style = ttk.Style()
|
|
style.configure("BasicTestChkBox.TCheckbutton", font=gcm.TERMINAL_FONT)
|
|
style.configure("BasicTestSquare.TFrame", bordercolor="#003039", borderwidth=1, relief="solid")
|
|
style.configure("BasicTestLabel.TLabel", font=gcm.MODE_FONT)
|
|
|
|
def __init_layout(self) -> None:
|
|
self.rowconfigure([0], weight=2)
|
|
self.rowconfigure([1, 2], weight=8)
|
|
self.columnconfigure([0], weight=2)
|
|
self.columnconfigure([1, 2], weight=8)
|
|
|
|
def __init_testcase_grid(self) -> None:
|
|
# initialize labels
|
|
e2e_label = ttk.Label(self, text="E2E", style="BasicTestLabel.TLabel", padding=6)
|
|
e2e_label.grid(row=0, column=1)
|
|
|
|
p2p_label = ttk.Label(self, text="P2P", style="BasicTestLabel.TLabel")
|
|
p2p_label.grid(row=0, column=2)
|
|
|
|
l4_label = ttk.Label(self, text="L4", style="BasicTestLabel.TLabel", padding=10)
|
|
l4_label.grid(row=1, column=0)
|
|
|
|
l2_label = ttk.Label(self, text="L2", style="BasicTestLabel.TLabel")
|
|
l2_label.grid(row=2, column=0)
|
|
|
|
# initialize mode selectors for all test cases
|
|
self.__test_cases = {}
|
|
for name, pos in self.MODES.items():
|
|
# cell frame containing all checkboxes
|
|
oframe = ttk.Frame(self, width=100, height=100, style="BasicTestSquare.TFrame")
|
|
oframe.grid(row=pos["row"], column=pos["col"], sticky=tk.NSEW) # place to their designated grid position
|
|
oframe.pack_propagate(False)
|
|
|
|
# internal frame
|
|
iframe = tk.Frame(oframe)
|
|
iframe.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
|
|
|
|
# "Master" checkbox
|
|
m = tk.BooleanVar()
|
|
m_chk = ttk.Checkbutton(iframe, text="Master", variable=m, style="BasicTestChkBox.TCheckbutton")
|
|
m_chk.pack(anchor=tk.CENTER, side=tk.TOP)
|
|
|
|
# "Slave" checkbox
|
|
s = tk.BooleanVar()
|
|
s_chk = ttk.Checkbutton(iframe, text="Slave ", variable=s, style="BasicTestChkBox.TCheckbutton")
|
|
s_chk.pack(anchor=tk.CENTER, side=tk.BOTTOM)
|
|
|
|
# store test case
|
|
self.__test_cases[name] = {
|
|
"type": "general",
|
|
"delmech": name[0:3].upper(),
|
|
"layer": name[4:6].upper(),
|
|
"logSyncInterval": 0,
|
|
"master": m,
|
|
"slave": s
|
|
}
|
|
|
|
|
|
def __init__(self, master: tk.Misc | None = None) -> None:
|
|
super().__init__(master, text="Basic modes")
|
|
|
|
self.__init_styles() # initialize widget styles
|
|
self.__init_layout() # initialize the layout
|
|
self.__init_testcase_grid() # initialize the grid widgets
|
|
|
|
def get_test_cases(self) -> dict:
|
|
return self.__test_cases
|
|
|
|
|
|
class DefinedTestCasesPanel(ttk.LabelFrame):
|
|
DEFINED_PROFILES = {
|
|
"gPTP": {
|
|
"logSyncInterval": -3
|
|
}
|
|
}
|
|
PROFILE_FONT = ("Ubuntu", 12, "italic")
|
|
|
|
def __init_styles(self) -> None:
|
|
style = ttk.Style()
|
|
style.configure("DefinedTestChkBox.TCheckbutton", font=gcm.TERMINAL_FONT)
|
|
|
|
def __init_widgets(self) -> None:
|
|
self.__test_cases = {}
|
|
ri = 0
|
|
for dprof_key in self.DEFINED_PROFILES.keys():
|
|
# fetch profile
|
|
dprof = self.DEFINED_PROFILES[dprof_key]
|
|
|
|
# test case label
|
|
label = ttk.Label(self, text=dprof_key + ":", font=self.PROFILE_FONT)
|
|
label.grid(row=ri, column=0, padx=6)
|
|
|
|
# "Master" checkbox
|
|
m = tk.BooleanVar()
|
|
m_chk = ttk.Checkbutton(self, text="Master", variable=m, style="DefinedTestChkBox.TCheckbutton")
|
|
m_chk.grid(row=ri, column=1)
|
|
|
|
# "Slave" checkbox
|
|
s = tk.BooleanVar()
|
|
s_chk = ttk.Checkbutton(self, text="Slave ", variable=s, style="DefinedTestChkBox.TCheckbutton")
|
|
s_chk.grid(row=ri, column=2)
|
|
|
|
# store test case
|
|
self.__test_cases[dprof_key] = {
|
|
"type": "defined",
|
|
"logSyncInterval": dprof["logSyncInterval"],
|
|
"master": m,
|
|
"slave": s
|
|
}
|
|
|
|
def __init__(self, master: tk.Misc | None = None) -> None:
|
|
super().__init__(master, text="Defined profiles")
|
|
|
|
self.__init_styles()
|
|
self.__init_widgets()
|
|
|
|
def get_test_cases(self) -> dict:
|
|
return self.__test_cases
|
|
|
|
|
|
class TestCasePanel(ttk.LabelFrame):
|
|
def __init_layout(self) -> None:
|
|
pass
|
|
|
|
def __init_widgets(self) -> None:
|
|
self.__basic_test_panel = BasicTestCasesPanel(self)
|
|
self.__basic_test_panel.pack(expand=False, fill=tk.BOTH, anchor=tk.W, side=tk.LEFT, padx=4, pady=4)
|
|
|
|
self.__defined_test_panel = DefinedTestCasesPanel(self)
|
|
self.__defined_test_panel.pack(expand=True, fill="both", anchor=tk.W, side=tk.LEFT, padx=4, pady=4)
|
|
|
|
|
|
def __init__(self, master: tk.Misc | None = None) -> None:
|
|
super().__init__(master)
|
|
self.configure(labelwidget= ttk.Label(self, text="Test cases", font=gcm.TITLE_FONT))
|
|
|
|
self.__init_layout()
|
|
self.__init_widgets()
|
|
|
|
def get_test_cases(self) -> dict:
|
|
return dict(self.__basic_test_panel.get_test_cases()) | dict(self.__defined_test_panel.get_test_cases())
|
|
|
|
|
|
class TestParametersPanel(ttk.LabelFrame):
|
|
def __init_styles(self) -> None:
|
|
style = ttk.Style()
|
|
style.configure("Parameters.TLabel", font=gcm.TERMINAL_FONT)
|
|
|
|
def __init_layout(self) -> None:
|
|
self.columnconfigure([0, 1], weight=1)
|
|
|
|
def __init_widgets(self) -> None:
|
|
# linuxptp initialization timeout
|
|
linuxptp_init_timeout_label = ttk.Label(self, text="linuxptp init timeout")
|
|
linuxptp_init_timeout_label.grid(row=0, column=0)
|
|
|
|
# BMCA settle timeout
|
|
bmca_timeout_label = ttk.Label(self, text="BMCA timeout")
|
|
bmca_timeout_label.grid(row=1, column=0)
|
|
|
|
# sync timeout
|
|
sync_timeout_label = ttk.Label(self, text="Sync timeout")
|
|
sync_timeout_label.grid(row=2, column=0)
|
|
|
|
# minimum number of sync cycles
|
|
min_sync_cycles_label = ttk.Label(self, text="Min. sync cycles")
|
|
min_sync_cycles_label.grid(row=3, column=0)
|
|
|
|
# max number of sync cycles
|
|
max_sync_cycles_label = ttk.Label(self, text="Max. sync cycles")
|
|
max_sync_cycles_label.grid(row=4, column=0)
|
|
|
|
# target variance
|
|
target_variance_label = ttk.Label(self, text="Target variance")
|
|
target_variance_label.grid(row=5, column=0)
|
|
|
|
# averaging window size
|
|
averaging_window_size_label = ttk.Label(self, text="Averaging window size")
|
|
averaging_window_size_label.grid(row=6, column=0)
|
|
|
|
# ----------
|
|
|
|
# linuxptp init timeout
|
|
self.__linuxptp_init_timeout_str = StringVar()
|
|
linuxptp_init_timeout_spinner = ttk.Spinbox(self, format="%.0f s", textvariable=self.__linuxptp_init_timeout_str)
|
|
linuxptp_init_timeout_spinner["from"] = 2
|
|
linuxptp_init_timeout_spinner["to"] = 60
|
|
linuxptp_init_timeout_spinner.set("5 s")
|
|
linuxptp_init_timeout_spinner.grid(row=0, column=1)
|
|
|
|
# BMCA timeout
|
|
self.__bmca_timeout_str = StringVar()
|
|
bmca_timeout_spinner = ttk.Spinbox(self, format="%.0f s", textvariable=self.__bmca_timeout_str)
|
|
bmca_timeout_spinner["from"] = 2
|
|
bmca_timeout_spinner["to"] = 300
|
|
bmca_timeout_spinner.set("60 s")
|
|
bmca_timeout_spinner.grid(row=1, column=1)
|
|
|
|
# Sync timeout
|
|
self.__sync_timeout_str = StringVar()
|
|
sync_timeout_spinner = ttk.Spinbox(self, format="%.0f cycles", textvariable=self.__sync_timeout_str)
|
|
sync_timeout_spinner["from"] = 2
|
|
sync_timeout_spinner["to"] = 300
|
|
sync_timeout_spinner.set("4 cycles")
|
|
sync_timeout_spinner.grid(row=2, column=1)
|
|
|
|
def crosscheck_min_max_cycles(correct_this: Literal["min", "max"]) -> None:
|
|
min_cycles = int(self.__min_sync_cycles_str.get()[:-6])
|
|
max_cycles = int(self.__max_sync_cycles_str.get()[:-6])
|
|
|
|
match correct_this:
|
|
case "min":
|
|
if max_cycles <= min_cycles:
|
|
min_cycles = max_cycles - 1
|
|
case "max":
|
|
if min_cycles >= max_cycles:
|
|
max_cycles = min_cycles + 1
|
|
|
|
self.__min_sync_cycles_str.set("{:d} cycles".format(min_cycles))
|
|
self.__max_sync_cycles_str.set("{:d} cycles".format(max_cycles))
|
|
|
|
def crosscheck_based_on_min() -> None:
|
|
crosscheck_min_max_cycles("max")
|
|
|
|
def crosscheck_based_on_max() -> None:
|
|
crosscheck_min_max_cycles("min")
|
|
|
|
# Min Sync cycles
|
|
self.__min_sync_cycles_str = StringVar()
|
|
max_sync_cycles_spinner = ttk.Spinbox(self, format="%.0f cycles", textvariable=self.__min_sync_cycles_str, command=crosscheck_based_on_min)
|
|
max_sync_cycles_spinner["from"] = 0
|
|
max_sync_cycles_spinner["to"] = 10000000 - 1
|
|
max_sync_cycles_spinner.set("0 cycles")
|
|
max_sync_cycles_spinner.grid(row=3, column=1)
|
|
|
|
# Max Sync cycles
|
|
self.__max_sync_cycles_str = StringVar()
|
|
max_sync_cycles_spinner = ttk.Spinbox(self, format="%.0f cycles", textvariable=self.__max_sync_cycles_str, command=crosscheck_based_on_max)
|
|
max_sync_cycles_spinner["from"] = 2
|
|
max_sync_cycles_spinner["to"] = 10000000
|
|
max_sync_cycles_spinner.set("100 cycles")
|
|
max_sync_cycles_spinner.grid(row=4, column=1)
|
|
|
|
# Target variance
|
|
self.__target_variance_str = StringVar()
|
|
target_variance_spinner = ttk.Spinbox(self, format="%.0f ns^2", textvariable=self.__target_variance_str)
|
|
target_variance_spinner["from"] = 10
|
|
target_variance_spinner["to"] = 10**18
|
|
target_variance_spinner["increment"] = 10
|
|
target_variance_spinner.set("2500 ns^2")
|
|
target_variance_spinner.grid(row=5, column=1)
|
|
|
|
# Averaging window size
|
|
self.__averaging_window_size_str = StringVar()
|
|
averaging_window_size_spinner = ttk.Spinbox(self, format="%.0f samples", textvariable=self.__averaging_window_size_str)
|
|
averaging_window_size_spinner["from"] = 1
|
|
averaging_window_size_spinner["to"] = 10000
|
|
averaging_window_size_spinner["increment"] = 1
|
|
averaging_window_size_spinner.set("10 samples")
|
|
averaging_window_size_spinner.grid(row=6, column=1)
|
|
|
|
# ----------
|
|
|
|
# apply styling
|
|
for widget in self.winfo_children():
|
|
match widget:
|
|
case ttk.Label():
|
|
widget.configure(style="Parameters.TLabel")
|
|
widget.grid(sticky=tk.E)
|
|
|
|
def __init__(self, master: tk.Misc | None = None) -> None:
|
|
super().__init__(master)
|
|
|
|
self.__init_styles()
|
|
self.__init_layout()
|
|
self.__init_widgets()
|
|
|
|
self.configure(labelwidget=ttk.Label(self, text="Parameters", font=gcm.TITLE_FONT))
|
|
|
|
def get_parameters(self) -> dict:
|
|
return {
|
|
"linuxptp_init_timeout": int(self.__linuxptp_init_timeout_str.get()[:-1]),
|
|
"bmca_timeout": int(self.__bmca_timeout_str.get()[:-1]),
|
|
"sync_timeout": int(self.__sync_timeout_str.get()[:-6]),
|
|
"sync_min_cycles": int(self.__min_sync_cycles_str.get()[:-6]),
|
|
"sync_max_cycles": int(self.__max_sync_cycles_str.get()[:-6]),
|
|
"target_variance": int(self.__target_variance_str.get()[:-4]),
|
|
"averaging_window_size": int(self.__averaging_window_size_str.get()[:-7]),
|
|
}
|
|
|
|
class TestManagerMonitorPanel(ttk.LabelFrame):
|
|
def __init_layout(self) -> None:
|
|
pass
|
|
|
|
def __init_widgets(self) -> None:
|
|
self.__start_stop_btn = ttk.Button(self, text="START", command=self.__start_stop_tests)
|
|
self.__start_stop_btn.pack()
|
|
|
|
tw = ttk.Treeview(self, columns=("name", "result", "start", "end", "duration", "comments"), selectmode="none")
|
|
tw.heading("#0", text="#")
|
|
tw.heading("name", text="Name")
|
|
tw.heading("result", text="Result")
|
|
tw.heading("start", text="Start")
|
|
tw.heading("end", text="End")
|
|
tw.heading("duration", text="Duration")
|
|
tw.heading("comments", text="Comments")
|
|
|
|
tw.tag_configure("passed", background="LawnGreen")
|
|
tw.tag_configure("failed", background="Salmon")
|
|
tw.tag_configure("errored", background="firebrick4", foreground="white")
|
|
tw.tag_configure("in_progress", background="Gold")
|
|
tw.tag_configure("pending", font=("TkDefaultFont", 9, "italic"), foreground="Gray")
|
|
|
|
tw.pack(expand=True, fill="x")
|
|
|
|
self.__test_tw = tw
|
|
|
|
def __init__(self, master: tk.Misc) -> None:
|
|
super().__init__(master)
|
|
self.configure(labelwidget=ttk.Label(self, text="Test controls", font=gcm.TITLE_FONT))
|
|
|
|
self.__start_stop_tests_cb: Callable[..., Literal["stopped", "running"]] | None = None
|
|
|
|
self.__init_layout()
|
|
self.__init_widgets()
|
|
|
|
def __start_stop_tests(self) -> None:
|
|
# momentarily disable the start/stop button
|
|
self.__start_stop_btn.configure(state="disabled")
|
|
|
|
# invoke start/stop callback
|
|
test_state: Literal["stopped", "running"] = "stopped"
|
|
if self.__start_stop_tests_cb is not None:
|
|
test_state = self.__start_stop_tests_cb()
|
|
|
|
# set button state according to the test state (running/stopped)
|
|
self.set_test_state(test_state)
|
|
|
|
# re-enable start/stop button
|
|
self.__start_stop_btn.configure(state="enabled")
|
|
|
|
def register_start_stop_tests_cb(self, start_stop_tests_cb: Callable) -> None:
|
|
self.__start_stop_tests_cb = start_stop_tests_cb
|
|
|
|
def get_test_treeview(self) -> ttk.Treeview:
|
|
return self.__test_tw
|
|
|
|
def set_test_state(self, state: Literal["stopped", "running"]) -> None:
|
|
# set button state according to the test state (running/stopped)
|
|
if state == "running":
|
|
self.__start_stop_btn.configure(text="STOP")
|
|
else:
|
|
self.__start_stop_btn.configure(text="START")
|
|
|
|
|
|
class SetupTab(ttk.Frame):
|
|
def __init_layout(self) -> None:
|
|
self.columnconfigure(0, weight=1)
|
|
self.columnconfigure(1, weight=4)
|
|
self.rowconfigure([0, 1], weight=1)
|
|
self.rowconfigure(2, weight=100)
|
|
|
|
def __init_widgets(self) -> None:
|
|
self.__flexPtp_options_panel = FlexPtpOptionsPanel(self)
|
|
self.__flexPtp_options_panel.grid(row=0, column=0, sticky=tk.NSEW, ipady=4, padx=4)
|
|
|
|
self.__linuxptp_options_panel = LinuxPtpOptionsPanel(self)
|
|
self.__linuxptp_options_panel.grid(row=1, rowspan=1, column=0, sticky=tk.NSEW, ipady=4, padx=4)
|
|
|
|
self.__test_case_panel = TestCasePanel(self)
|
|
self.__test_case_panel.grid(row=0, rowspan=2, column=1, sticky=tk.NSEW, ipady=4, padx=4)
|
|
|
|
self.__test_params_panel = TestParametersPanel(self)
|
|
self.__test_params_panel.grid(row=0, rowspan=3, column=2, sticky=tk.NSEW, ipadx=4, ipady=4)
|
|
|
|
self.__test_mgr_monitor_panel = TestManagerMonitorPanel(self)
|
|
self.__test_mgr_monitor_panel.grid(row=2, column=0, columnspan=2, sticky=tk.NSEW, ipady=4, padx=4)
|
|
|
|
def __init__(self, master: tk.Misc | None = None) -> None:
|
|
super().__init__(master)
|
|
|
|
self.__init_layout()
|
|
self.__init_widgets()
|
|
|
|
self.__start_stop_tests_cb : Callable[..., Literal["stopped", "running"]] | None = None
|
|
self.__test_mgr_monitor_panel.register_start_stop_tests_cb(self.__start_stop_tests)
|
|
|
|
def __start_stop_tests(self) -> str:
|
|
test_state: Literal["stopped", "running"] = "stopped"
|
|
if self.__start_stop_tests_cb is not None: # invoke callback
|
|
test_state = self.__start_stop_tests_cb()
|
|
|
|
# set widget state by test state
|
|
self.set_test_state(test_state)
|
|
|
|
# return with current test state
|
|
return test_state
|
|
|
|
def register_start_stop_tests_cb(self, start_stop_tests_cb) -> None:
|
|
self.__start_stop_tests_cb = start_stop_tests_cb
|
|
|
|
|
|
def get_settings(self) -> dict:
|
|
settings = {
|
|
"flexPTP": self.__flexPtp_options_panel.get_settings(),
|
|
"linuxptp": self.__linuxptp_options_panel.get_settings()
|
|
}
|
|
return settings
|
|
|
|
def get_parameters(self) -> dict:
|
|
return self.__test_params_panel.get_parameters()
|
|
|
|
def set_option_widgets_state(self, state: Literal["normal", "disabled"]) -> None:
|
|
# collect frames to deal with
|
|
frames = [ self.__flexPtp_options_panel, self.__linuxptp_options_panel, self.__test_case_panel, self.__test_params_panel ]
|
|
|
|
# create a recursive state configuration function that traverses widgets
|
|
def set_widget_state(parent: tk.Widget | tk.Toplevel) -> None:
|
|
for widget in parent.winfo_children():
|
|
if len(widget.winfo_children()) == 0 and widget.widgetName != "frame":
|
|
widget.configure(state=state) # type: ignore
|
|
else:
|
|
set_widget_state(widget)
|
|
|
|
# apply required state to all specified frames
|
|
for frame in frames:
|
|
set_widget_state(frame)
|
|
|
|
def get_test_cases(self) -> dict:
|
|
return self.__test_case_panel.get_test_cases()
|
|
|
|
def get_test_treeview(self) -> ttk.Treeview:
|
|
return self.__test_mgr_monitor_panel.get_test_treeview()
|
|
|
|
def set_test_state(self, state: Literal["stopped", "running"]) -> None:
|
|
self.__test_mgr_monitor_panel.set_test_state(state)
|
|
|
|
if state == "running":
|
|
self.set_option_widgets_state("disabled")
|
|
else:
|
|
self.set_option_widgets_state("normal")
|
|
|
|
|