483 lines
18 KiB
Python
483 lines
18 KiB
Python
import queue
|
|
from tkinter import Tk, font, ttk
|
|
import serial
|
|
import serial.tools.list_ports
|
|
import ttk_text as ttkt
|
|
from ttkthemes import ThemedTk
|
|
import tkinter as tk
|
|
import netifaces
|
|
|
|
from DeviceInterface import DeviceInterface
|
|
from FlexPtpController import FlexPtpController
|
|
from LinuxPtpObserver import LinuxPtpObserver
|
|
|
|
class GUI:
|
|
def __init_flexPtp_options(self) -> None:
|
|
title = ttk.Label(self.__setup_tab, text="flexPTP options", font=self.__title_font)
|
|
frame = ttk.LabelFrame(self.__setup_tab, labelwidget=title)
|
|
frame.grid(row=0, column=0, sticky=tk.NSEW, ipady=4, padx=4)
|
|
#frame.pack(anchor="nw", fill="x", padx=12, ipady=4, side=tk.LEFT, expand=True)
|
|
|
|
frame.rowconfigure([0, 1, 2, 3 ], weight=1)
|
|
frame.columnconfigure(0, weight=1)
|
|
frame.columnconfigure(1, weight=3)
|
|
|
|
self.__flexPtp_options_frame = frame
|
|
|
|
device_label = ttk.Label(frame, text="Device:")
|
|
device_label.grid(column=0, row=0, sticky=tk.E)
|
|
|
|
baudrate_label = ttk.Label(frame, text="Baudrate:")
|
|
baudrate_label.grid(column=0, row=1, sticky=tk.E)
|
|
|
|
baudrate_label = ttk.Label(frame, text="Parity:")
|
|
baudrate_label.grid(column=0, row=2, sticky=tk.E)
|
|
|
|
baudrate_label = ttk.Label(frame, text="Stopbits:")
|
|
baudrate_label.grid(column=0, row=3, sticky=tk.E)
|
|
|
|
self.__sel_device = tk.StringVar()
|
|
device_combobox = ttk.Combobox(frame, textvariable=self.__sel_device)
|
|
device_combobox["values"] = list(map(lambda p: p.device, filter(lambda p: p.subsystem == "usb", serial.tools.list_ports.comports())))
|
|
device_combobox.current(0)
|
|
device_combobox.grid(column=1, row=0, sticky=tk.EW, padx=4)
|
|
|
|
#device_entry = ttk.Entry(frame, font=self.__console_font, textvariable=self.__sel_device)
|
|
#device_entry.grid(column=1, row=0, sticky=tk.EW, padx=4)
|
|
|
|
self.__sel_baudrate = tk.IntVar()
|
|
baudrate_combobox = ttk.Combobox(frame, textvariable=self.__sel_baudrate)
|
|
baudrate_combobox["values"] = (115200, 57600, 38400, 19200, 9600, 4800)
|
|
baudrate_combobox.current(0)
|
|
baudrate_combobox.grid(column=1, row=1, sticky=tk.EW, padx=4)
|
|
|
|
self.__sel_parity = tk.StringVar()
|
|
self.__sel_parity.set(serial.PARITY_NONE)
|
|
parity_frame = tk.Frame(frame)
|
|
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)
|
|
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)
|
|
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)
|
|
parity_odd.pack(anchor=tk.W, side=tk.LEFT)
|
|
|
|
self.__sel_stopbits = tk.IntVar()
|
|
self.__sel_stopbits.set(1)
|
|
stopbits_frame = tk.Frame(frame)
|
|
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_linuxptp_options(self) -> None:
|
|
title = ttk.Label(self.__setup_tab, text="linuxptp options", font=self.__title_font)
|
|
frame = ttk.LabelFrame(self.__setup_tab, labelwidget=title)
|
|
frame.grid(row=1, rowspan=1, column=0, sticky=tk.NSEW, ipady=4, padx=4)
|
|
# frame.pack(anchor="nw", fill="x", ipady=4, padx=12, side=tk.LEFT, expand=True)
|
|
|
|
frame.rowconfigure([0, 1, 2], weight=1)
|
|
# frame.rowconfigure(2, weight=100)
|
|
frame.columnconfigure(0, weight=1)
|
|
frame.columnconfigure(1, weight=3)
|
|
|
|
self.__linuxptp_options_frame = frame
|
|
|
|
path_label = ttk.Label(frame, text="Path:")
|
|
path_label.grid(column=0, row=0, sticky=tk.E)
|
|
|
|
if_label = ttk.Label(frame, text="Interface:")
|
|
if_label.grid(column=0, row=1, sticky=tk.E)
|
|
|
|
arg_label = ttk.Label(frame, text="Arguments:")
|
|
arg_label.grid(column=0, row=2, sticky=tk.E)
|
|
|
|
self.__linuxptp_path = tk.StringVar()
|
|
self.__linuxptp_path.set("/usr/sbin/ptp4l")
|
|
path_entry = ttk.Entry(frame, font=self.__console_font, textvariable=self.__linuxptp_path)
|
|
path_entry.grid(column=1, row=0, sticky=tk.EW, padx=4)
|
|
|
|
self.__linuxptp_interface = tk.StringVar()
|
|
baudrate_combobox = ttk.Combobox(frame, textvariable=self.__linuxptp_interface)
|
|
baudrate_combobox["values"] = netifaces.interfaces()
|
|
baudrate_combobox.current(0)
|
|
baudrate_combobox.grid(column=1, row=1, sticky=tk.EW, padx=4)
|
|
|
|
self.__linuxptp_args = tk.StringVar()
|
|
self.__linuxptp_args.set("")
|
|
args_entry = ttk.Entry(frame, font=self.__console_font, textvariable=self.__linuxptp_args)
|
|
args_entry.grid(column=1, row=2, sticky=tk.EW, padx=4)
|
|
|
|
|
|
def __init_test_cases(self) -> None:
|
|
title = ttk.Label(self.__setup_tab, text="Test cases", font=self.__title_font)
|
|
frame = ttk.LabelFrame(self.__setup_tab, labelwidget=title)
|
|
frame.grid(row=0, rowspan=2, column=1, sticky=tk.NSEW, ipady=4, padx=4)
|
|
# frame.pack(anchor="nw", fill="x", ipady=4, padx=12, side=tk.LEFT, expand=True)
|
|
|
|
self.__test_cases_frame = frame
|
|
|
|
basic_modes_frame = ttk.LabelFrame(frame, text="Basic modes")
|
|
basic_modes_frame.rowconfigure([0], weight=2)
|
|
basic_modes_frame.rowconfigure([1, 2], weight=8)
|
|
basic_modes_frame.columnconfigure([0], weight=2)
|
|
basic_modes_frame.columnconfigure([1, 2], weight=8)
|
|
basic_modes_frame.pack(expand=False, fill="both", anchor=tk.W, side=tk.LEFT, padx=4, pady=4)
|
|
|
|
e2e_label = ttk.Label(basic_modes_frame, text="E2E", style="ProfileLabel.TLabel", padding=6)
|
|
e2e_label.grid(row=0, column=1)
|
|
|
|
p2p_label = ttk.Label(basic_modes_frame, text="P2P", style="ProfileLabel.TLabel")
|
|
p2p_label.grid(row=0, column=2)
|
|
|
|
l4_label = ttk.Label(basic_modes_frame, text="L4", style="ProfileLabel.TLabel", padding=10)
|
|
l4_label.grid(row=1, column=0)
|
|
|
|
l2_label = ttk.Label(basic_modes_frame, text="L2", style="ProfileLabel.TLabel")
|
|
l2_label.grid(row=2, column=0)
|
|
|
|
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,
|
|
},
|
|
}
|
|
|
|
self.__test_cases = {}
|
|
|
|
for name, pos in modes.items():
|
|
oframe = ttk.Frame(basic_modes_frame, width=100, height=100, style="ProfileSquare.TFrame")
|
|
oframe.grid(row=pos["row"], column=pos["col"], sticky=tk.NSEW)
|
|
oframe.pack_propagate(False)
|
|
|
|
iframe = tk.Frame(oframe)
|
|
iframe.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
|
|
|
|
m = tk.BooleanVar()
|
|
m_chk = ttk.Checkbutton(iframe, text="Master", variable=m, style="ProfileChkBox.TCheckbutton")
|
|
m_chk.pack(anchor=tk.CENTER, side=tk.TOP)
|
|
|
|
s = tk.BooleanVar()
|
|
s_chk = ttk.Checkbutton(iframe, text="Slave ", variable=s, style="ProfileChkBox.TCheckbutton")
|
|
s_chk.pack(anchor=tk.CENTER, side=tk.BOTTOM)
|
|
|
|
self.__test_cases[name] = {
|
|
"type": "general",
|
|
"delmech": name[0:3].upper(),
|
|
"layer": name[4:6].upper(),
|
|
"master": m,
|
|
"slave": s
|
|
}
|
|
|
|
defined_profiles_frame = ttk.LabelFrame(frame, text="Defined profiles")
|
|
defined_profiles_frame.pack(expand=True, fill="both", anchor=tk.W, side=tk.LEFT, padx=4, pady=4)
|
|
|
|
defined_profiles = [ "gPTP" ]
|
|
|
|
ri = 0
|
|
for dprof in defined_profiles:
|
|
label = ttk.Label(defined_profiles_frame, text=dprof + ":", font=self.__profile_font)
|
|
label.grid(row=ri, column=0, padx=6)
|
|
|
|
m = tk.BooleanVar()
|
|
m_chk = ttk.Checkbutton(defined_profiles_frame, text="Master", variable=m, style="ProfileChkBox.TCheckbutton")
|
|
m_chk.grid(row=ri, column=1)
|
|
|
|
s = tk.BooleanVar()
|
|
s_chk = ttk.Checkbutton(defined_profiles_frame, text="Slave ", variable=s, style="ProfileChkBox.TCheckbutton")
|
|
s_chk.grid(row=ri, column=2)
|
|
|
|
self.__test_cases[dprof] = {
|
|
"type": "defined",
|
|
"master": m,
|
|
"slave": s
|
|
}
|
|
|
|
|
|
def __init_test_controller(self) -> None:
|
|
title = ttk.Label(self.__setup_tab, text="Test controls", font=self.__title_font)
|
|
frame = ttk.LabelFrame(self.__setup_tab, labelwidget=title)
|
|
frame.grid(row=2, column=0, columnspan=2, sticky=tk.NSEW, ipady=4, padx=4)
|
|
|
|
self.__tests_running = False
|
|
|
|
def start_stop_tests() -> None:
|
|
self.__start_stop_btn.configure(state="disabled")
|
|
|
|
if not self.__tests_running:
|
|
self.start_tests()
|
|
else:
|
|
self.stop_tests()
|
|
|
|
self.__tests_running = not self.__tests_running
|
|
|
|
if self.__tests_running:
|
|
self.__start_stop_btn.configure(text="STOP")
|
|
state = "disabled"
|
|
else:
|
|
self.__start_stop_btn.configure(text="START")
|
|
state = "normal"
|
|
|
|
frames = [ self.__flexPtp_options_frame, self.__linuxptp_options_frame, self.__test_cases_frame ]
|
|
|
|
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)
|
|
|
|
for frame in frames:
|
|
set_widget_state(frame)
|
|
|
|
self.__start_stop_btn.configure(state="enabled")
|
|
|
|
self.__start_stop_btn = ttk.Button(frame, text="START", command=start_stop_tests)
|
|
self.__start_stop_btn.pack()
|
|
|
|
tw = ttk.Treeview(frame, columns=("name", "result", "start", "end", "duration"), 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.tag_configure("passed", background="LawnGreen")
|
|
tw.tag_configure("failed", background="Salmon")
|
|
tw.tag_configure("in_progress", background="Gold")
|
|
tw.tag_configure("pending", font=("TkDefaultFont", 9, "italic"), foreground="Gray")
|
|
|
|
tw.pack()
|
|
|
|
self.__test_tw = tw
|
|
|
|
|
|
def __init_setup_tab(self) -> None:
|
|
self.__setup_tab = ttk.Frame(self.__tabs)
|
|
self.__setup_tab.columnconfigure(0, weight=1)
|
|
self.__setup_tab.columnconfigure(1, weight=4)
|
|
self.__setup_tab.rowconfigure([0, 1], weight=1)
|
|
self.__setup_tab.rowconfigure(2, weight=100)
|
|
self.__tabs.add(self.__setup_tab, text="Setup")
|
|
|
|
self.__init_flexPtp_options()
|
|
self.__init_linuxptp_options()
|
|
self.__init_test_cases()
|
|
self.__init_test_controller()
|
|
|
|
|
|
def __init_logtab(self) -> None:
|
|
self.__logtab = ttk.Frame(self.__tabs)
|
|
self.__tabs.add(self.__logtab, text="Logs")
|
|
|
|
self.__logtab.rowconfigure(0, weight=8)
|
|
self.__logtab.rowconfigure(1, weight=2)
|
|
|
|
self.__logtab.columnconfigure(0, weight=1)
|
|
self.__logtab.columnconfigure(1, weight=1)
|
|
|
|
terminal_settings = { "wrap": tk.WORD, "background": "#3D3D3D", "borderwidth": 0, "foreground": "white", "blockcursor": True, "insertbackground": "white"}
|
|
self.__flexPtp_log = tk.Text(self.__logtab, **terminal_settings)
|
|
self.__flexPtp_log.grid(column=0, row=0, sticky=tk.NSEW)
|
|
self.__flexPtp_log.configure(font=self.__console_font, state="disabled")
|
|
|
|
self.__linuxptp_log = tk.Text(self.__logtab, **terminal_settings)
|
|
self.__linuxptp_log.grid(column=1, row=0, sticky=tk.NSEW, pady=0)
|
|
self.__linuxptp_log.configure(font=self.__console_font, state="disabled")
|
|
|
|
self.__flexPtp_log_queue = queue.Queue()
|
|
self.__linuxptp_log_queue = queue.Queue()
|
|
|
|
|
|
def __init_test_results(self) -> None:
|
|
self.__results_tabs = ttk.Frame(self.__tabs)
|
|
self.__tabs.add(self.__results_tabs, text="Results")
|
|
|
|
|
|
def __init_tabs(self) -> None:
|
|
self.__init_setup_tab()
|
|
self.__init_logtab()
|
|
self.__init_test_results()
|
|
|
|
|
|
def __init_fonts(self) -> None:
|
|
self.__console_font = font.Font(family="Ubuntu Mono", size=9)
|
|
self.__title_font=font.Font(family="Ubuntu", size=14)
|
|
self.__mode_font=font.Font(family="Ubuntu", size=12, weight="bold")
|
|
self.__profile_font=font.Font(family="Ubuntu", size=12, slant="italic")
|
|
|
|
|
|
def __init_styles(self) -> None:
|
|
style = ttk.Style()
|
|
style.configure("ProfileChkBox.TCheckbutton", font=self.__console_font)
|
|
style.configure("ProfileSquare.TFrame", bordercolor="#003039", borderwidth=1, relief="solid")
|
|
style.configure("ProfileLabel.TLabel", font=self.__mode_font)
|
|
|
|
|
|
def __init_print_polls(self) -> None:
|
|
def print_logs() -> None:
|
|
while not self.__flexPtp_log_queue.empty():
|
|
self.__flexPtp_log.configure(state="normal")
|
|
self.__flexPtp_log.insert(tk.END, self.__flexPtp_log_queue.get())
|
|
self.__flexPtp_log.configure(state="disabled")
|
|
|
|
while not self.__linuxptp_log_queue.empty():
|
|
self.__linuxptp_log.configure(state="normal")
|
|
self.__linuxptp_log.insert(tk.END, self.__linuxptp_log_queue.get())
|
|
self.__linuxptp_log.configure(state="disabled")
|
|
|
|
self.__win.after(50, print_logs)
|
|
|
|
self.__win.after(50, print_logs)
|
|
|
|
|
|
def __init__(self) -> None:
|
|
self.__win = ThemedTk(theme="arc")
|
|
#self.__win.configure()
|
|
self.__win.title("flexPTP test suite")
|
|
self.__win.iconphoto(False, tk.PhotoImage(file="media/flexPTP_test.png"))
|
|
|
|
self.__init_fonts()
|
|
self.__init_styles()
|
|
|
|
self.__tabs = ttk.Notebook(self.__win)
|
|
self.__tabs.configure(width=1000, height=600)
|
|
|
|
self.__init_tabs()
|
|
|
|
self.__tabs.pack(expand=True, fill="both")
|
|
|
|
self.__init_print_polls()
|
|
|
|
|
|
def mainloop(self) -> None:
|
|
self.__win.mainloop()
|
|
|
|
|
|
def __gather_tests(self) -> None:
|
|
# gather test cases
|
|
self.__tests = []
|
|
for name, case in self.__test_cases.items():
|
|
ptype = case["type"]
|
|
|
|
template = {}
|
|
if ptype == "general":
|
|
template = { "type": ptype, "name": case["delmech"] + " " + case["layer"], "delmech": case["delmech"], "layer": case["layer"] }
|
|
elif ptype == "defined":
|
|
template = { "type": ptype, "name": name }
|
|
|
|
if case["master"].get():
|
|
master_mode = dict(template)
|
|
master_mode["mode"] = "master"
|
|
master_mode["name"] += " (master)"
|
|
self.__tests.append(master_mode)
|
|
|
|
if case["slave"].get():
|
|
slave_mode = dict(template)
|
|
slave_mode["mode"] = "slave"
|
|
slave_mode["name"] += " (slave)"
|
|
self.__tests.append(slave_mode)
|
|
|
|
|
|
def __populate_tests_treeview(self) -> None:
|
|
tw = self.__test_tw
|
|
items = tw.get_children()
|
|
if items != ():
|
|
tw.delete(*items)
|
|
|
|
seqnum = 1
|
|
n = len(self.__tests)
|
|
for test in self.__tests:
|
|
test["entry"] = tw.insert("", tk.END, text=str(seqnum) + "/" + str(n), values=(test["name"], "?", "Pending", "-", "-"), tags=("pending"))
|
|
seqnum += 1
|
|
|
|
|
|
def __open_flexPtp_interface(self) -> None:
|
|
url = self.__sel_device.get()
|
|
opts = {
|
|
"baudrate": self.__sel_baudrate.get(),
|
|
"parity": self.__sel_parity.get(),
|
|
"stopbits": self.__sel_stopbits.get()
|
|
}
|
|
|
|
def echo(data: str) -> None:
|
|
self.__flexPtp_log_queue.put(data.replace("\r", ""))
|
|
|
|
self.__device_interface = DeviceInterface(url, opts)
|
|
self.__device_interface.register_out_callback(echo)
|
|
self.__flexPtp_controller = FlexPtpController(self.__device_interface)
|
|
|
|
|
|
def __init_linuxptp_observer(self) -> None:
|
|
self.__linuxptp_observer = LinuxPtpObserver(self.__linuxptp_interface.get())
|
|
|
|
def echo(data) -> None:
|
|
self.__linuxptp_log_queue.put(str(data))
|
|
|
|
self.__linuxptp_observer.register_observer_callback(echo)
|
|
|
|
|
|
def __init_flexPtp(self) -> None:
|
|
ctrl = self.__flexPtp_controller
|
|
|
|
ctrl.disable_all_logging()
|
|
ctrl.set_domain(0)
|
|
ctrl.reset_flexptp()
|
|
|
|
|
|
def start_tests(self) -> None:
|
|
self.__gather_tests()
|
|
self.__populate_tests_treeview()
|
|
self.__open_flexPtp_interface()
|
|
self.__init_flexPtp()
|
|
self.__init_linuxptp_observer()
|
|
|
|
# ----
|
|
|
|
ctrl = self.__flexPtp_controller
|
|
observer = self.__linuxptp_observer
|
|
for test in self.__tests:
|
|
if test["type"] == "general":
|
|
id = test["delmech"] + "_" + test["layer"]
|
|
else:
|
|
id = test["name"]
|
|
|
|
ctrl.start_by_id(id)
|
|
|
|
priority1 = 128
|
|
if test["mode"] == "master":
|
|
priority1 = 100
|
|
|
|
ctrl.set_priority(priority1, 255)
|
|
|
|
observer.start_linuxptp(id)
|
|
|
|
|
|
|
|
for test in self.__tests:
|
|
pass
|
|
|
|
|
|
def stop_tests(self) -> None:
|
|
self.__linuxptp_observer.stop_linuxptp()
|
|
self.__device_interface.close() |