GUI...
This commit is contained in:
parent
df1d6a9f1c
commit
cdbec108cb
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
__pycache__/
|
||||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -12,8 +12,10 @@
|
|||||||
"program": "${workspaceFolder}/test_main.py",
|
"program": "${workspaceFolder}/test_main.py",
|
||||||
"python": ".venv/bin/python",
|
"python": ".venv/bin/python",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
|
"justMyCode": false,
|
||||||
"args": [
|
"args": [
|
||||||
"/dev/ttyACM0",
|
"/dev/ttyACM0",
|
||||||
|
"enp52s0f0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,40 @@
|
|||||||
|
import sys, os
|
||||||
|
import select
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
import re
|
import re
|
||||||
|
from threading import Thread, Lock
|
||||||
|
|
||||||
class DeviceInterface:
|
class DeviceInterface:
|
||||||
"""
|
"""
|
||||||
Helper class for interfacing the device running flexPTP.
|
Helper class for interfacing the device running flexPTP.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def reader_routine(self) -> None:
|
||||||
|
"""
|
||||||
|
Thread routine for receiving all incoming communication from
|
||||||
|
the device.
|
||||||
|
"""
|
||||||
|
|
||||||
|
while not self.device.closed:
|
||||||
|
try:
|
||||||
|
data = self.device.readline() # read serial port line-by-line
|
||||||
|
|
||||||
|
if type(data) is bytes:
|
||||||
|
# remove colorization escape sequences
|
||||||
|
data = re.sub(r"\x1b\[[0-9;]*m", "", data.decode())
|
||||||
|
|
||||||
|
if len(data) > 0: # write (remaining) data to the pipe
|
||||||
|
os.write(self.__outpipe_rw_fd[1], data.encode())
|
||||||
|
|
||||||
|
if self.__out_cb is not None: # invoke callback if provided
|
||||||
|
self.__out_cb(data)
|
||||||
|
|
||||||
|
except:
|
||||||
|
break # handle closing the device
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, url: str, options: dict = {}) -> None:
|
def __init__(self, url: str, options: dict = {}) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize the interface.
|
Initialize the interface.
|
||||||
@ -15,7 +44,7 @@ class DeviceInterface:
|
|||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.device = serial.serial_for_url(url, timeout=0.05) # open remote device
|
self.device = serial.serial_for_url(url, timeout=None) # open remote device
|
||||||
|
|
||||||
# set serial port options if applicable
|
# set serial port options if applicable
|
||||||
if "baudrate" in options:
|
if "baudrate" in options:
|
||||||
@ -27,16 +56,38 @@ class DeviceInterface:
|
|||||||
if "stopbits" in options:
|
if "stopbits" in options:
|
||||||
self.device.stopbits = options["stopbits"]
|
self.device.stopbits = options["stopbits"]
|
||||||
|
|
||||||
def read_until(self, expected: bytes = serial.LF) -> bytes:
|
# ----
|
||||||
"""
|
|
||||||
Read from the device until a specific sequence is found.
|
self.__out_cb: Callable | None = None
|
||||||
|
|
||||||
|
r, w = os.pipe()
|
||||||
|
self.__outpipe_rw_fd = [ r, w ]
|
||||||
|
self.__outpipe_rw = [ os.fdopen(r, "rb"), os.fdopen(w, "wb") ]
|
||||||
|
|
||||||
|
self.__reader_thread = Thread(target=self.reader_routine)
|
||||||
|
self.__reader_thread.start()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
"""
|
||||||
|
Close the device interface.
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.device.close()
|
||||||
|
self.__reader_thread.join()
|
||||||
|
self.__outpipe_rw[0].close()
|
||||||
|
self.__outpipe_rw[1].close()
|
||||||
|
|
||||||
|
def readline(self) -> bytes:
|
||||||
|
"""
|
||||||
|
Read a line from the device.
|
||||||
|
|
||||||
:param bytes expected: delimiter sequence
|
|
||||||
:return: bytes read
|
:return: bytes read
|
||||||
:rtype: bytes
|
:rtype: bytes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.device.read_until(expected=expected)
|
return self.__outpipe_rw[0].readline().encode()
|
||||||
|
|
||||||
|
|
||||||
def write(self, data: bytes) -> None:
|
def write(self, data: bytes) -> None:
|
||||||
@ -60,15 +111,32 @@ class DeviceInterface:
|
|||||||
:rtype: str | None
|
:rtype: str | None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.device.write((cmd.strip("\r\n") + "\r\n").encode()) # sanitize commands
|
pure_final_cmd = cmd.strip("\r\n")
|
||||||
self.device.read_until(cmd.encode()) # flush echo
|
final_cmd = (pure_final_cmd + "\r\n").encode() # sanitize commands
|
||||||
|
#self.skip(len(final_cmd)) # flush echo
|
||||||
|
self.write(final_cmd) # send command
|
||||||
|
|
||||||
|
# wait for the echo to arrive
|
||||||
|
line = ""
|
||||||
|
while line != pure_final_cmd:
|
||||||
|
line = self.__outpipe_rw[0].readline().decode().strip()
|
||||||
|
|
||||||
if expect_results: # store results if required
|
if expect_results: # store results if required
|
||||||
timeout = False
|
timeout = False
|
||||||
results = ""
|
results = ""
|
||||||
|
|
||||||
|
poller = select.poll()
|
||||||
|
poller.register(self.__outpipe_rw_fd[0], select.POLLIN)
|
||||||
|
|
||||||
while not timeout: # store continuous chunks
|
while not timeout: # store continuous chunks
|
||||||
data = self.device.read(32)
|
res = poller.poll(100)
|
||||||
if len(data) > 0:
|
|
||||||
results += data.decode()
|
if len(res) > 0 and res[0][1] == select.POLLIN:
|
||||||
|
data = self.__outpipe_rw[0].read(1)
|
||||||
|
if len(data) > 0:
|
||||||
|
results += data.decode()
|
||||||
|
else:
|
||||||
|
timeout = True
|
||||||
else:
|
else:
|
||||||
timeout = True
|
timeout = True
|
||||||
|
|
||||||
@ -82,3 +150,14 @@ class DeviceInterface:
|
|||||||
return results
|
return results
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def register_out_callback(self, cb: Callable) -> None:
|
||||||
|
"""
|
||||||
|
Register callback for receiving all printed messages from the
|
||||||
|
device.
|
||||||
|
|
||||||
|
:param Callable cb: callback invoked at each reception
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.__out_cb = cb
|
||||||
55
FlexPtpController.py
Normal file
55
FlexPtpController.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
from DeviceInterface import DeviceInterface
|
||||||
|
|
||||||
|
class FlexPtpController:
|
||||||
|
def __init__(self, di: DeviceInterface) -> None:
|
||||||
|
self.__di = di
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset_flexptp(self) -> None:
|
||||||
|
self.__di.execute_command("ptp reset")
|
||||||
|
|
||||||
|
def start_e2e_udp(self) -> None:
|
||||||
|
self.__di.execute_command("ptp profile preset default")
|
||||||
|
|
||||||
|
def start_p2p_udp(self) -> None:
|
||||||
|
self.__di.execute_command("ptp profile preset defp2p")
|
||||||
|
|
||||||
|
def start_e2e_l2(self) -> None:
|
||||||
|
self.__di.execute_command("ptp profile preset default")
|
||||||
|
self.__di.execute_command("ptp transport 802.3")
|
||||||
|
self.reset_flexptp()
|
||||||
|
|
||||||
|
def start_p2p_l2(self) -> None:
|
||||||
|
self.__di.execute_command("ptp profile preset defp2p")
|
||||||
|
self.__di.execute_command("ptp transport 802.3")
|
||||||
|
self.reset_flexptp()
|
||||||
|
|
||||||
|
def start_gPTP(self) -> None:
|
||||||
|
self.__di.execute_command("ptp profile preset gPTP")
|
||||||
|
|
||||||
|
def start_by_id(self, id: str) -> None:
|
||||||
|
id = id.replace("_", "").upper()
|
||||||
|
if id == "E2EL4":
|
||||||
|
self.start_e2e_udp()
|
||||||
|
elif id == "E2EL2":
|
||||||
|
self.start_e2e_l2()
|
||||||
|
elif id == "P2PL4":
|
||||||
|
self.start_p2p_udp()
|
||||||
|
elif id == "P2PL2":
|
||||||
|
self.start_p2p_l2()
|
||||||
|
elif id == "GPTP":
|
||||||
|
self.start_gPTP()
|
||||||
|
|
||||||
|
def set_priority(self, priority1: int, priority2: int) -> None:
|
||||||
|
self.__di.execute_command("ptp priority {:d} {:d}".format(priority1, priority2))
|
||||||
|
|
||||||
|
def set_domain(self, domain: int) -> None:
|
||||||
|
self.__di.execute_command("ptp domain {:d}".format(domain))
|
||||||
|
|
||||||
|
def disable_all_logging(self) -> None:
|
||||||
|
logging_types = [ "def", "corr", "ts", "info", "locked", "bmca" ]
|
||||||
|
for lt in logging_types:
|
||||||
|
self.__di.execute_command("ptp log " + lt + " off", expect_results=False)
|
||||||
|
|
||||||
|
def set_servo_offset(self, offset: int) -> None:
|
||||||
|
self.__di.execute_command("ptp servo offset {:d}".format(offset))
|
||||||
483
GUI.py
Normal file
483
GUI.py
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
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()
|
||||||
@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
class LinuxPtpObserver:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
pass
|
|
||||||
51
LinuxPtpObserver.py
Normal file
51
LinuxPtpObserver.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import select
|
||||||
|
from threading import Thread
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
class LinuxPtpObserver:
|
||||||
|
def __init__(self, ni: str) -> None:
|
||||||
|
self.__ni = ni
|
||||||
|
self.__process: subprocess.Popen
|
||||||
|
self.__observer_thread: Thread
|
||||||
|
self.__observer_cb: Callable | None = None
|
||||||
|
pass
|
||||||
|
|
||||||
|
def observer_routine(self) -> None:
|
||||||
|
if self.__process.stdout is not None:
|
||||||
|
pfd = select.poll()
|
||||||
|
pfd.register(self.__process.stdout.fileno(), select.POLLIN)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
while self.__process.poll() is None:
|
||||||
|
res = pfd.poll()
|
||||||
|
|
||||||
|
if len(res) > 0 and res[0][1] & select.POLLIN:
|
||||||
|
data = self.__process.stdout.readline()
|
||||||
|
if self.__observer_cb is not None:
|
||||||
|
self.__observer_cb(data)
|
||||||
|
|
||||||
|
|
||||||
|
def start_linuxptp(self, profile: str) -> None:
|
||||||
|
cmd = ["sudo", "ptp4l", "--priority1=127", "--priority2=255", "--gmCapable=1", "--neighborPropDelayThresh=100000",
|
||||||
|
"--min_neighbor_prop_delay=-20000000", "--assume_two_step=1", "--ptp_minor_version=0",
|
||||||
|
"-i", self.__ni, "-f", "linuxptp_configs/{:s}.cfg".format(profile), "-m", "-l", "6"]
|
||||||
|
|
||||||
|
self.__process = subprocess.Popen(
|
||||||
|
cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__observer_thread = Thread(target=self.observer_routine)
|
||||||
|
self.__observer_thread.start()
|
||||||
|
|
||||||
|
def stop_linuxptp(self) -> None:
|
||||||
|
self.__process.send_signal(signal.SIGKILL)
|
||||||
|
self.__observer_thread.join()
|
||||||
|
|
||||||
|
def register_observer_callback(self, cb : Callable) -> None:
|
||||||
|
self.__observer_cb = cb
|
||||||
@ -1,45 +1,40 @@
|
|||||||
from DeviceInterface import DeviceInterface
|
from DeviceInterface import DeviceInterface
|
||||||
from LinuxPtpController import LinuxPtpObserver
|
from LinuxPtpObserver import LinuxPtpObserver
|
||||||
|
from FlexPtpController import FlexPtpController
|
||||||
|
|
||||||
class TestController:
|
class TestController:
|
||||||
def __reset_flexptp(self) -> None:
|
def __prepare_device(self) -> None:
|
||||||
self.__di.execute_command("ptp reset")
|
self.__fptp_controller.disable_all_logging()
|
||||||
|
self.__fptp_controller.set_priority(128, 255)
|
||||||
def __start_e2e_l4(self) -> None:
|
self.__fptp_controller.set_domain(0)
|
||||||
self.__di.execute_command("ptp profile preset default")
|
self.__fptp_controller.reset_flexptp()
|
||||||
|
|
||||||
def __start_p2p_l4(self) -> None:
|
|
||||||
self.__di.execute_command("ptp profile preset defp2p")
|
|
||||||
|
|
||||||
def __start_e2e_l2(self) -> None:
|
|
||||||
self.__di.execute_command("ptp profile preset default")
|
|
||||||
self.__di.execute_command("ptp transport 802.3")
|
|
||||||
self.__reset_flexptp()
|
|
||||||
|
|
||||||
def __start_p2p_l2(self) -> None:
|
|
||||||
self.__di.execute_command("ptp profile preset defp2p")
|
|
||||||
self.__di.execute_command("ptp transport 802.3")
|
|
||||||
self.__reset_flexptp()
|
|
||||||
|
|
||||||
def __start_gPTP(self) -> None:
|
|
||||||
self.__di.execute_command("ptp profile preset gPTP")
|
|
||||||
|
|
||||||
def __set_priority(self, priority1: int, priority2: int) -> None:
|
|
||||||
self.__di.execute_command("ptp priority {:d} {:d}".format(priority1, priority2))
|
|
||||||
|
|
||||||
def __set_domain(self, domain: int) -> None:
|
|
||||||
self.__di.execute_command("ptp domain {:d}".format(domain))
|
|
||||||
|
|
||||||
def __disable_all_logging(self) -> None:
|
|
||||||
logging_types = [ "def", "corr", "ts", "info", "locked", "bmca" ]
|
|
||||||
for lt in logging_types:
|
|
||||||
self.__di.execute_command("ptp log " + lt + " off", expect_results=False)
|
|
||||||
|
|
||||||
def __set_servo_offset(self, offset: int) -> None:
|
|
||||||
self.__di.execute_command("ptp servo offset {:d}".format(offset))
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, di: DeviceInterface, lptp_observer: LinuxPtpObserver) -> None:
|
def __init__(self, di: DeviceInterface, lptp_observer: LinuxPtpObserver) -> None:
|
||||||
self.__di = di
|
self.__di = di
|
||||||
|
self.__fptp_controller = FlexPtpController(di)
|
||||||
self.__lptp_observer = lptp_observer
|
self.__lptp_observer = lptp_observer
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def start_test_e2e_udp(self) -> None:
|
||||||
|
self.__fptp_controller.start_e2e_udp()
|
||||||
|
self.__lptp_observer.start_linuxptp("E2E_UDP")
|
||||||
|
|
||||||
|
def start_test_e2e_l2(self) -> None:
|
||||||
|
self.__fptp_controller.start_e2e_l2()
|
||||||
|
self.__lptp_observer.start_linuxptp("E2E_L2")
|
||||||
|
|
||||||
|
def start_test_p2p_udp(self) -> None:
|
||||||
|
self.__fptp_controller.start_p2p_udp()
|
||||||
|
self.__lptp_observer.start_linuxptp("P2P_UDP")
|
||||||
|
|
||||||
|
def start_test_p2p_l2(self) -> None:
|
||||||
|
self.__fptp_controller.start_p2p_l2()
|
||||||
|
self.__lptp_observer.start_linuxptp("P2P_L2")
|
||||||
|
|
||||||
|
def start_test_gPTP(self) -> None:
|
||||||
|
self.__fptp_controller.start_gPTP()
|
||||||
|
self.__lptp_observer.start_linuxptp("gPTP")
|
||||||
|
|
||||||
|
def stop_test(self) -> None:
|
||||||
|
self.__lptp_observer.stop_linuxptp()
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
BIN
media/flexPTP_test.png
Normal file
BIN
media/flexPTP_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
122
media/flexPTP_test.svg
Normal file
122
media/flexPTP_test.svg
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="30.975mm"
|
||||||
|
height="30.975mm"
|
||||||
|
viewBox="0 0 30.974999 30.975"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
|
||||||
|
sodipodi:docname="flexPTP_test.svg"
|
||||||
|
inkscape:export-filename="flexPTP_test.png"
|
||||||
|
inkscape:export-xdpi="104.96207"
|
||||||
|
inkscape:export-ydpi="104.96207"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.8284271"
|
||||||
|
inkscape:cx="66.468038"
|
||||||
|
inkscape:cy="70.357125"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1172"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g6">
|
||||||
|
<inkscape:grid
|
||||||
|
id="grid6"
|
||||||
|
units="mm"
|
||||||
|
originx="-68.612541"
|
||||||
|
originy="-78.809494"
|
||||||
|
spacingx="0.99999998"
|
||||||
|
spacingy="1"
|
||||||
|
empcolor="#0099e5"
|
||||||
|
empopacity="0.30196078"
|
||||||
|
color="#0099e5"
|
||||||
|
opacity="0.14901961"
|
||||||
|
empspacing="5"
|
||||||
|
dotted="false"
|
||||||
|
gridanglex="30"
|
||||||
|
gridanglez="30"
|
||||||
|
visible="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Réteg 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-68.612536,-78.809498)">
|
||||||
|
<g
|
||||||
|
id="g6"
|
||||||
|
style="fill:#004455"
|
||||||
|
transform="matrix(9.6062465,0,0,9.6062465,-590.49611,-678.25486)"
|
||||||
|
inkscape:export-filename="flexPTP_logo.png"
|
||||||
|
inkscape:export-xdpi="168.45497"
|
||||||
|
inkscape:export-ydpi="168.45497">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:2px;line-height:0.75;font-family:'Roboto Condensed';-inkscape-font-specification:'Roboto Condensed, ';letter-spacing:0px;fill:#004455;stroke-width:0.499999;stroke-linecap:square;stroke-miterlimit:4.1;paint-order:stroke fill markers"
|
||||||
|
x="68.626129"
|
||||||
|
y="81.948547"
|
||||||
|
id="text1"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';fill:#004455;stroke-width:0.5"
|
||||||
|
x="68.626129"
|
||||||
|
y="81.948547"
|
||||||
|
id="tspan2">PTP</tspan></text>
|
||||||
|
<path
|
||||||
|
d="m 70.907758,79.254163 c -0.03,0 -0.08198,2.6e-5 -0.08198,0.06203 0,0.06 0.05398,0.05998 0.08198,0.05998 h 0.07999 l 0.235997,0.295977 -0.247993,0.322015 h -0.07999 c -0.03,0 -0.08198,2.5e-5 -0.08198,0.06202 0,0.06 0.05398,0.05998 0.08198,0.05998 h 0.234006 c 0.03,0 0.07999,1.9e-5 0.07999,-0.05998 0,-0.062 -0.04198,-0.06202 -0.103985,-0.06202 l 0.171981,-0.246003 0.178007,0.246003 c -0.056,0 -0.100005,2.5e-5 -0.100005,0.06202 0,0.06 0.04999,0.05998 0.07999,0.05998 h 0.234007 c 0.028,0 0.08198,1.9e-5 0.08198,-0.05998 0,-0.062 -0.05198,-0.06202 -0.08198,-0.06202 h -0.07999 l -0.256009,-0.322015 0.227982,-0.295977 h 0.07999 c 0.028,0 0.08204,1.9e-5 0.08204,-0.05998 0,-0.062 -0.05204,-0.06203 -0.08204,-0.06203 h -0.233953 c -0.03,0 -0.08005,-1.9e-5 -0.08005,0.05998 0,0.062 0.04401,0.06203 0.09801,0.06203 l -0.147989,0.201998 -0.152023,-0.201998 c 0.052,0 0.09602,-2.6e-5 0.09602,-0.06203 0,-0.06 -0.04999,-0.05998 -0.07999,-0.05998 z"
|
||||||
|
style="-inkscape-font-specification:'Latin Modern Mono';fill:#009f66;fill-opacity:1;stroke-width:0.5;stroke-linecap:square;stroke-miterlimit:4.1;paint-order:stroke fill markers"
|
||||||
|
id="path9" />
|
||||||
|
<path
|
||||||
|
d="m 70.268194,78.892179 c -0.238,0 -0.429981,0.200012 -0.429981,0.446012 0,0.244 0.201998,0.445957 0.459998,0.445957 0.264,0 0.358003,-0.179972 0.358003,-0.229972 0,-0.056 -0.05799,-0.056 -0.06999,-0.056 -0.036,0 -0.05201,0.006 -0.06601,0.044 -0.044,0.102 -0.152023,0.119962 -0.208023,0.119962 -0.15,0 -0.299967,-0.09999 -0.331967,-0.271986 h 0.595991 c 0.042,0 0.07999,3.3e-5 0.07999,-0.07397 0,-0.228 -0.12802,-0.42401 -0.38802,-0.42401 z m 0,0.122007 c 0.104,0 0.228002,0.04995 0.246002,0.255954 h -0.531975 c 0.028,-0.146 0.145973,-0.255954 0.285973,-0.255954 z"
|
||||||
|
style="-inkscape-font-specification:'Latin Modern Mono';stroke-width:0.5;stroke-linecap:square;stroke-miterlimit:4.1;paint-order:stroke fill markers"
|
||||||
|
id="path7"
|
||||||
|
sodipodi:nodetypes="csssscscssccsccs" />
|
||||||
|
<path
|
||||||
|
d="m 69.278534,79.256154 c 0.032,0 0.07999,2.9e-5 0.07999,0.06203 0,0.06 -0.05005,0.05998 -0.08005,0.05998 h 0.257999 V 80.35615 H 69.27848 c -0.032,0 -0.08198,2.5e-5 -0.08198,0.06203 0,0.06 0.05199,0.05998 0.07999,0.05998 h 0.658016 c 0.03,0 0.07999,1.9e-5 0.07999,-0.05998 0,-0.062 -0.04799,-0.06203 -0.07999,-0.06203 h -0.25999 v -1.017955 c 0,-0.062 -0.01199,-0.08204 -0.07999,-0.08204 z"
|
||||||
|
style="-inkscape-font-specification:'Latin Modern Mono';stroke-width:0.5;stroke-linecap:square;stroke-miterlimit:4.1;paint-order:stroke fill markers"
|
||||||
|
id="path6" />
|
||||||
|
<path
|
||||||
|
d="m 69.27848,79.256154 c -0.032,0 -0.08198,2.5e-5 -0.08198,0.06203 0,0.06 0.05199,0.05998 0.07999,0.05998 h 0.002 c 0.03,0 0.08005,1.9e-5 0.08005,-0.05998 0,-0.062 -0.04805,-0.06203 -0.08005,-0.06203 z"
|
||||||
|
style="-inkscape-font-specification:'Latin Modern Mono';stroke-width:0.5;stroke-linecap:square;stroke-miterlimit:4.1;paint-order:stroke fill markers"
|
||||||
|
id="path5" />
|
||||||
|
<path
|
||||||
|
d="m 69.210484,78.884164 c -0.16,0 -0.313999,0.09201 -0.313999,0.266014 v 0.105976 h -0.200009 c -0.032,0 -0.08198,2.5e-5 -0.08198,0.06203 0,0.06 0.04999,0.05998 0.07999,0.05998 h 0.201999 v 0.617992 h -0.201999 c -0.03,0 -0.08198,3.5e-5 -0.08198,0.06003 0,0.062 0.05198,0.06197 0.08198,0.06197 h 0.542035 c 0.03,0 0.08198,1.9e-5 0.08198,-0.05998 0,-0.062 -0.05198,-0.06203 -0.08198,-0.06203 h -0.201999 v -0.617992 h 0.241968 c -0.028,0 -0.07999,1.5e-5 -0.07999,-0.05998 0,-0.062 0.04998,-0.06203 0.08198,-0.06203 h -0.243958 v -0.09398 c 0,-0.156 0.133983,-0.156005 0.193983,-0.156005 0,0.008 0.01801,0.08602 0.08801,0.08602 0.04,0 0.08596,-0.03201 0.08596,-0.08801 0,-0.12 -0.159993,-0.120016 -0.191993,-0.120016 z"
|
||||||
|
style="-inkscape-font-specification:'Latin Modern Mono';stroke-width:0.5;stroke-linecap:square;stroke-miterlimit:4.1;paint-order:stroke fill markers"
|
||||||
|
id="path4" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:0.367237px;line-height:1.05;font-family:'Roboto Condensed';-inkscape-font-specification:'Roboto Condensed, ';text-align:justify;letter-spacing:0.00413143px;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:none;fill-opacity:1;stroke:#b8913f;stroke-width:0.0104099;stroke-linecap:square;stroke-miterlimit:4.1;paint-order:markers fill stroke"
|
||||||
|
x="71.461769"
|
||||||
|
y="79.758598"
|
||||||
|
id="text9"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan9"
|
||||||
|
style="stroke-width:0.0104099"
|
||||||
|
x="71.461769"
|
||||||
|
y="79.758598" /></text>
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ff461a;stroke-width:0.0520495;stroke-linecap:square;stroke-miterlimit:4.1;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||||
|
id="rect9"
|
||||||
|
width="1.0707279"
|
||||||
|
height="1.019085"
|
||||||
|
x="70.740189"
|
||||||
|
y="79.169914" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.5 KiB |
9
requirements.txt
Normal file
9
requirements.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
future==1.0.0
|
||||||
|
iso8601==2.1.0
|
||||||
|
netifaces==0.11.0
|
||||||
|
netinterfaces==0.1.0
|
||||||
|
pillow==12.2.0
|
||||||
|
pyserial==3.5
|
||||||
|
PyYAML==6.0.3
|
||||||
|
ttk-text==0.3.3
|
||||||
|
ttkthemes==3.3.0
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
COMMON_FLAGS=--priority1=127 --priority2=255 --gmCapable=1 --neighborPropDelayThresh=100000 --min_neighbor_prop_delay=-20000000 --assume_two_step=1 --ptp_minor_version=0
|
COMMON_FLAGS="--priority1=127 --priority2=255 --gmCapable=1 --neighborPropDelayThresh=100000 --min_neighbor_prop_delay=-20000000 --assume_two_step=1 --ptp_minor_version=0"
|
||||||
|
|
||||||
ptp4l -i "$1" -f "linuxptp_configs/$2.cfg" -m -l 6 $COMMON_FLAGS
|
sudo ptp4l -i "$1" -f "linuxptp_configs/$2.cfg" -m -l 6 $COMMON_FLAGS
|
||||||
38
test_main.py
38
test_main.py
@ -1,12 +1,23 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import optparse
|
import optparse
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
import DeviceInterface
|
import DeviceInterface
|
||||||
|
from LinuxPtpObserver import LinuxPtpObserver
|
||||||
|
from TestController import TestController
|
||||||
|
from GUI import GUI
|
||||||
|
|
||||||
# ----------------------------------
|
# ----------------------------------
|
||||||
|
|
||||||
usage = "<device/url> [-s <baudrate>]"
|
gui = GUI()
|
||||||
|
|
||||||
|
gui.mainloop()
|
||||||
|
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
usage = "<device/url> <interface> [-s <baudrate>]"
|
||||||
|
|
||||||
parser = optparse.OptionParser(usage=usage)
|
parser = optparse.OptionParser(usage=usage)
|
||||||
parser.add_option("-s", dest="baudrate", default=115200)
|
parser.add_option("-s", dest="baudrate", default=115200)
|
||||||
@ -23,10 +34,33 @@ di = DeviceInterface.DeviceInterface(url=args[0], options={"baudrate": opts.baud
|
|||||||
#results = di.execute_command("osinfo")
|
#results = di.execute_command("osinfo")
|
||||||
#print(results)
|
#print(results)
|
||||||
|
|
||||||
|
def echo(str: str) -> None:
|
||||||
|
print(str, end="")
|
||||||
|
|
||||||
|
di.register_out_callback(echo)
|
||||||
|
|
||||||
# get device clock identity
|
# get device clock identity
|
||||||
OWN_CLOCK_ID_KEY = "Own clock ID"
|
OWN_CLOCK_ID_KEY = "Own clock ID"
|
||||||
clock_id = di.execute_command("ptp info", separate_results=True)
|
clock_id = di.execute_command("ptp info", separate_results=True)
|
||||||
if type(clock_id) == dict[str, str] and OWN_CLOCK_ID_KEY in clock_id:
|
if type(clock_id) == dict and OWN_CLOCK_ID_KEY in clock_id:
|
||||||
print("Own clock ID:", clock_id[OWN_CLOCK_ID_KEY])
|
print("Own clock ID:", clock_id[OWN_CLOCK_ID_KEY])
|
||||||
else:
|
else:
|
||||||
print("Could not retrieve device clock ID!")
|
print("Could not retrieve device clock ID!")
|
||||||
|
|
||||||
|
lptp_observer = LinuxPtpObserver(args[1])
|
||||||
|
lptp_observer.register_observer_callback(echo)
|
||||||
|
|
||||||
|
tc = TestController(di, lptp_observer)
|
||||||
|
|
||||||
|
tc.start_test_gPTP()
|
||||||
|
|
||||||
|
time.sleep(15)
|
||||||
|
|
||||||
|
tc.stop_test()
|
||||||
|
tc.start_test_e2e_udp()
|
||||||
|
|
||||||
|
time.sleep(15)
|
||||||
|
|
||||||
|
tc.stop_test()
|
||||||
|
|
||||||
|
di.close()
|
||||||
Loading…
x
Reference in New Issue
Block a user