From df1d6a9f1c96fc2dae8e56e82c3402f05937e27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Wiesner?= Date: Thu, 23 Apr 2026 10:34:28 +0200 Subject: [PATCH] - initial --- .vscode/launch.json | 20 +++++ DeviceInterface.py | 84 ++++++++++++++++++++ LinuxPtpController.py | 4 + TestController.py | 45 +++++++++++ __pycache__/DeviceInterface.cpython-313.pyc | Bin 0 -> 3794 bytes linuxptp_configs/E2E_L2.cfg | 6 ++ linuxptp_configs/E2E_UDP.cfg | 6 ++ linuxptp_configs/P2P_L2.cfg | 6 ++ linuxptp_configs/P2P_UDP.cfg | 6 ++ linuxptp_configs/gPTP.cfg | 10 +++ start_linuxptp.sh | 5 ++ test_main.py | 32 ++++++++ 12 files changed, 224 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 DeviceInterface.py create mode 100644 LinuxPtpController.py create mode 100644 TestController.py create mode 100644 __pycache__/DeviceInterface.cpython-313.pyc create mode 100644 linuxptp_configs/E2E_L2.cfg create mode 100644 linuxptp_configs/E2E_UDP.cfg create mode 100644 linuxptp_configs/P2P_L2.cfg create mode 100644 linuxptp_configs/P2P_UDP.cfg create mode 100644 linuxptp_configs/gPTP.cfg create mode 100755 start_linuxptp.sh create mode 100644 test_main.py diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..35806d7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Debug", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/test_main.py", + "python": ".venv/bin/python", + "console": "integratedTerminal", + "args": [ + "/dev/ttyACM0", + ] + } + ] +} \ No newline at end of file diff --git a/DeviceInterface.py b/DeviceInterface.py new file mode 100644 index 0000000..bee8aca --- /dev/null +++ b/DeviceInterface.py @@ -0,0 +1,84 @@ +import serial +import re + +class DeviceInterface: + """ + Helper class for interfacing the device running flexPTP. + """ + + def __init__(self, url: str, options: dict = {}) -> None: + """ + Initialize the interface. + + :param str url: url of the device (tty, socket etc.) + :param dict options: collection of several options (baudrate, bytesize, parity, stopbits etc.) + :rtype: None + """ + + self.device = serial.serial_for_url(url, timeout=0.05) # open remote device + + # set serial port options if applicable + if "baudrate" in options: + self.device.baudrate = options["baudrate"] + if "bytesize" in options: + self.device.bytesize = options["bytesize"] + if "parity" in options: + self.device.parity = options["parity"] + if "stopbits" in options: + self.device.stopbits = options["stopbits"] + + def read_until(self, expected: bytes = serial.LF) -> bytes: + """ + Read from the device until a specific sequence is found. + + :param bytes expected: delimiter sequence + :return: bytes read + :rtype: bytes + """ + + return self.device.read_until(expected=expected) + + + def write(self, data: bytes) -> None: + """ + Write data onto the device + + :param bytes data: data to transfer + """ + + self.device.write(data) + + + def execute_command(self, cmd: str, expect_results: bool = True, separate_results: bool = False) -> str | dict[str, str] | None: + """ + Execute command on the device + + :param str cmd: command to launch + :param bool expect_results: is there any return values expected and awaited? + :param bool separate_results: if True a key-value separation attempted on the results + :return: results (if requested) + :rtype: str | None + """ + + self.device.write((cmd.strip("\r\n") + "\r\n").encode()) # sanitize commands + self.device.read_until(cmd.encode()) # flush echo + if expect_results: # store results if required + timeout = False + results = "" + while not timeout: # store continuous chunks + data = self.device.read(32) + if len(data) > 0: + results += data.decode() + else: + timeout = True + + if separate_results: # separation requested + records = re.findall("^[ ]*([^:]+)[ ]*:[ ]*(.+)[ ]*$", results.strip(), flags=re.MULTILINE) + results = dict[str, str]() + for rec in records: + results[rec[0]] = rec[1].strip() + return results + else: + return results + else: + return None \ No newline at end of file diff --git a/LinuxPtpController.py b/LinuxPtpController.py new file mode 100644 index 0000000..b87f5af --- /dev/null +++ b/LinuxPtpController.py @@ -0,0 +1,4 @@ + +class LinuxPtpObserver: + def __init__(self) -> None: + pass \ No newline at end of file diff --git a/TestController.py b/TestController.py new file mode 100644 index 0000000..2fc35e2 --- /dev/null +++ b/TestController.py @@ -0,0 +1,45 @@ +from DeviceInterface import DeviceInterface +from LinuxPtpController import LinuxPtpObserver + +class TestController: + def __reset_flexptp(self) -> None: + self.__di.execute_command("ptp reset") + + def __start_e2e_l4(self) -> None: + self.__di.execute_command("ptp profile preset default") + + 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: + self.__di = di + self.__lptp_observer = lptp_observer + pass \ No newline at end of file diff --git a/__pycache__/DeviceInterface.cpython-313.pyc b/__pycache__/DeviceInterface.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4354e3ffe7a83305ddffacb86912ce2144ba15e GIT binary patch literal 3794 zcmb7GU2GJ|5$>6t*&WaN@366r4W{`EUSsSNXOix$&zogYvc$1onG?16HB+_`T_Lf!)dh5pOw0ii< zWAa!W{&K%4gQC3&LbN#c{fgF5(NeE9@`mwvYJ=Y}#I9N>Bybg$~iirn@Sg$d6pIIbV(%qwzd z*o>_?AlilBFH`|tHM+VqQgQMs0Vdl?M$iOJ91t_aUR~E>8S(qDYBUp)5w#eq%#e}j zI@c($S9jK=bu!W$WLEWP@;Vv$4KlbCG%a3{*%;Use}hask{M5oCsNAvAJpTHqEtr>?N112#rXr!NWqP!7DQRjJ zY@3;QL#V)IR~a`zS9oW%>w;0ta>HXi^um zJPE$ym}R^UyL#wSF<)|-o#W_74lzz#ylqOU-`{t!kY{~t!Wie4+jn+Q8#;WpuM(zv z;Z$BAWXqvsYnCx8M)-~T&+O{u}=zP7z z=e4vPBemOJfEmPB*yIGcR6;ULNSVR9Ff1IFq$vYH&PRxsTLgqveY-{B#6C&tJkvS%#7J^05$-au#;Ld4eVH$A5w(=IB&8m=^ z4lyDfMjPd@CD6jS3dGfvz+1t$*53B$ZLT9k-;_)fvO-R^;jP2Ez_)4%7 z*4@p}eMHI|NMrNunOieU4ZBJWyXGes8g?x-yt|;h%Ue*3lr=nKoo~@=fp60=`F6hD z50HnA5~w5M^bge!Ag=&UVGCMtyuunkuV#GcLu3K}AtSPJ!*O$rtHY63MTeLmb zZ*(ES5Go>yiD4Ht%*wz`h}+-JO(Uyl8YzP^CPdEDguOr>!Gy1B`zzlOC;g_t$26{L z8R7rbDB@!QoAEcWN^~SG=ExZ-qGpc;wJ2m+*N-5ERW(hDY<1$Jh-^X2#3QOEr+*GR zK~SGNJJJxXzZ#zA|t=<2BN*z z$V2_DZkjR>a}uK%P(d4Q``=mkIN44jjo`mT6FCM^>nIsTtHJAklLhC_X+bUC4Sc5{ zw3Cof8F^>$A6HXJqYd&*Jv_5_ zLcy+JSQO}K#5#BgaYhYi3P1~=IrOSw7a7#X3LwK$mKxU#$jj`(b;_#q2Gr%~8D0c! z^fH^;8z=&Z46+OBwBdOyKLL88LsU}m6?#?(v%VQat7Luue~lb?OnH zJg4A1j-YvFy7T-f{jj@h^nCimJt>@~gYVw(VrO3gi=9R0F$`|Qz|Nz&RmJO1x_(nz1RZ+_5yzk6ZV;Xhvc>y1C%_{)!< zod40%`Aa4EwO^i1JZta#rTd$~xyk#zi|yUBiKiXgX9t(RlbYZ5S?+iCXZEk1*`p7I zdq?iPzY-&xcYFzo>%*?8HNbW#uUJ=8;+Z3f~o8LZBWGfutQ|v2w1*&@`x`7MJ$MpFl;1DDvo~lH|o0U z8J=b85aryu04eM0`Xdq;7nXA@D;zugyT5|&r({JGWu>f;ef=w>N&ep4U>S$`ox$)} z`WnB=-K1yV?*|?YtdJUc;Nku<4v#l}jYGLDA=9~CWdg&)p|5aQX{K`9!*|LAhR0Gc zEPOB;ju$U4IDEmqx-dDj;Lens>!pj=%Y+;fhr|^^EMZ7|iDChBXgS)NiUmI4JunXr uzz^aK!7&U~