diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index dd261cf70c..22743a2283 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -1,23 +1,23 @@ name: Build and Distribute PhotonLibPy permissions: - id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing on: push: - branches: [ master ] + branches: [master] tags: - - 'v*' + - "v*" paths: - - '**' - - '!docs/**' - - '.github/**' + - "**" + - "!docs/**" + - ".github/**" pull_request: - branches: [ master ] + branches: [master] paths: - - '**' - - '!docs/**' - - '.github/**' + - "**" + - "!docs/**" + - ".github/**" merge_group: jobs: @@ -38,7 +38,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel pytest mypy + pip install setuptools wheel pytest mypy robotpy_build - name: Build wheel working-directory: ./photon-lib/py @@ -48,8 +48,8 @@ jobs: - name: Run Unit Tests working-directory: ./photon-lib/py run: | - pip install --no-cache-dir dist/*.whl - pytest + pip install --no-cache-dir dist/*.whl + pytest - name: Run mypy type checking uses: liskin/gh-problem-matcher-wrap@v3 @@ -58,7 +58,6 @@ jobs: run: | mypy --show-column-numbers --config-file photon-lib/py/pyproject.toml photon-lib - - name: Upload artifacts uses: actions/upload-artifact@master with: @@ -73,4 +72,4 @@ jobs: packages_dir: ./photon-lib/py/dist/ permissions: - id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing diff --git a/.github/workflows/robotpy.yml b/.github/workflows/robotpy.yml new file mode 100644 index 0000000000..9fd534f0a2 --- /dev/null +++ b/.github/workflows/robotpy.yml @@ -0,0 +1,27 @@ +--- +name: dist + +on: + pull_request: + push: + branches: + - main + tags: + - "*" + +defaults: + run: + working-directory: ./photon-lib/py + +jobs: + ci: + uses: robotpy/build-actions/.github/workflows/package-ci.yml@v2025 + with: + artifactory_repo_type: vendor + secrets: + META_REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + RTD_WEBHOOK: ${{ secrets.RTD_WEBHOOK }} + WPI_ARTIFACTORY_USERNAME: ${{ secrets.WPI_ARTIFACTORY_USERNAME }} + WPI_ARTIFACTORY_TOKEN: ${{ secrets.WPI_ARTIFACTORY_TOKEN }} + PYPI_API_TOKEN: ${{ secrets.PYPI_PASSWORD }} diff --git a/photon-lib/py/.gitignore b/photon-lib/py/.gitignore index f0c4ced6d6..e7e814b104 100644 --- a/photon-lib/py/.gitignore +++ b/photon-lib/py/.gitignore @@ -3,3 +3,10 @@ dist/ build/ .eggs/ photonlibpy/version.py +photonlibpy/timesync/include +photonlibpy/timesync/rpy-include +photonlibpy/timesync/__pycache__ +photonlibpy/timesync/pkgcfg.py +photonlibpy/timesync/_init*.py +photonlibpy/timesync/*.pyi +photonlibpy/timesync/*.so diff --git a/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py b/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py index cd46d787f5..5023bfe2a4 100644 --- a/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py +++ b/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py @@ -42,7 +42,9 @@ def pack(value: "PhotonPipelineResult") -> "Packet": ret = Packet() # metadata is of non-intrinsic type PhotonPipelineMetadata - ret.encodeBytes(PhotonPipelineMetadata.photonStruct.pack(value.metadata).getData()) + ret.encodeBytes( + PhotonPipelineMetadata.photonStruct.pack(value.metadata).getData() + ) # targets is a custom VLA! ret.encodeList(value.targets, PhotonTrackedTarget.photonStruct) diff --git a/photon-lib/py/photonlibpy/photonCamera.py b/photon-lib/py/photonlibpy/photonCamera.py index bcbc70d90b..0e04483d0b 100644 --- a/photon-lib/py/photonlibpy/photonCamera.py +++ b/photon-lib/py/photonlibpy/photonCamera.py @@ -27,7 +27,7 @@ from .packet import Packet from .targeting.photonPipelineResult import PhotonPipelineResult -from .version import PHOTONLIB_VERSION # type: ignore[import-untyped] +from .version import version as PHOTONLIB_VERSION # type: ignore[import-untyped] class VisionLEDMode(Enum): diff --git a/photon-lib/py/photonlibpy/timesync/__init__.py b/photon-lib/py/photonlibpy/timesync/__init__.py new file mode 100644 index 0000000000..a887543313 --- /dev/null +++ b/photon-lib/py/photonlibpy/timesync/__init__.py @@ -0,0 +1,3 @@ +from . import _init_photonlibpy_timesync + +from ._photonlibpy_timesync import * diff --git a/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncClient.yml b/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncClient.yml new file mode 100644 index 0000000000..c50abc632c --- /dev/null +++ b/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncClient.yml @@ -0,0 +1,20 @@ +--- +extra_includes: + - pybind11/chrono.h + +classes: + TimeSyncClient: + methods: + TimeSyncClient: + GetOffset: + GetMetadata: + Start: + Stop: + TimeSyncClient::Metadata: + attributes: + offset: + rtt2: + pingsSent: + pingsReceived: + pongsReceived: + lastPongTime: diff --git a/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncServer.yml b/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncServer.yml new file mode 100644 index 0000000000..574178cb77 --- /dev/null +++ b/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncServer.yml @@ -0,0 +1,10 @@ +--- +extra_includes: + - pybind11/chrono.h + +classes: + TimeSyncServer: + methods: + TimeSyncServer: + Start: + Stop: diff --git a/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncStructs.yml b/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncStructs.yml new file mode 100644 index 0000000000..f5c4467e8d --- /dev/null +++ b/photon-lib/py/photonlibpy/timesync/gen/photontimesync/TimeSyncStructs.yml @@ -0,0 +1,16 @@ +--- +classes: + TspPing: + methods: + TspPing: + attributes: + version: + message_id: + client_time: + force_no_trampoline: true + TspPong: + methods: + TspPong: + attributes: + server_time: + force_no_trampoline: true diff --git a/photon-lib/py/photonlibpy/timesync/lib/libphotontargeting.so b/photon-lib/py/photonlibpy/timesync/lib/libphotontargeting.so new file mode 100644 index 0000000000..90b0818f38 Binary files /dev/null and b/photon-lib/py/photonlibpy/timesync/lib/libphotontargeting.so differ diff --git a/photon-lib/py/photonlibpy/timesync/patches/no-wpi-print-incl.patch b/photon-lib/py/photonlibpy/timesync/patches/no-wpi-print-incl.patch new file mode 100644 index 0000000000..bb1d2b00f2 --- /dev/null +++ b/photon-lib/py/photonlibpy/timesync/patches/no-wpi-print-incl.patch @@ -0,0 +1,36 @@ +diff --git a/photon-targeting/src/main/native/include/net/TimeSyncClient.h b/photon-targeting/src/main/native/include/net/TimeSyncClient.h +index 82e5b0eb..9fed4186 100644 +--- a/photon-targeting/src/main/native/include/net/TimeSyncClient.h ++++ b/photon-targeting/src/main/native/include/net/TimeSyncClient.h +@@ -37,7 +37,6 @@ + + #include + #include +-#include + #include + #include + +diff --git a/photon-targeting/src/main/native/include/net/TimeSyncServer.h b/photon-targeting/src/main/native/include/net/TimeSyncServer.h +index 3a85d390..e271f93f 100644 +--- a/photon-targeting/src/main/native/include/net/TimeSyncServer.h ++++ b/photon-targeting/src/main/native/include/net/TimeSyncServer.h +@@ -36,7 +36,6 @@ + #include + + #include +-#include + #include + + #include "TimeSyncStructs.h" +diff --git a/photon-targeting/src/main/native/include/net/TimeSyncStructs.h b/photon-targeting/src/main/native/include/net/TimeSyncStructs.h +index 88702597..a4cd7184 100644 +--- a/photon-targeting/src/main/native/include/net/TimeSyncStructs.h ++++ b/photon-targeting/src/main/native/include/net/TimeSyncStructs.h +@@ -87,5 +87,5 @@ struct wpi::Struct { + } + }; + +-static_assert(wpi::StructSerializable); +-static_assert(wpi::StructSerializable); ++// static_assert(wpi::StructSerializable); ++// static_assert(wpi::StructSerializable); diff --git a/photon-lib/py/photonlibpy/timesync/src/module.cpp b/photon-lib/py/photonlibpy/timesync/src/module.cpp new file mode 100644 index 0000000000..33ae3811a2 --- /dev/null +++ b/photon-lib/py/photonlibpy/timesync/src/module.cpp @@ -0,0 +1,3 @@ +#include + +RPYBUILD_PYBIND11_MODULE(m) { initWrapper(m); } diff --git a/photon-lib/py/pyproject.toml b/photon-lib/py/pyproject.toml index 4dd0d50787..04092db818 100644 --- a/photon-lib/py/pyproject.toml +++ b/photon-lib/py/pyproject.toml @@ -1,2 +1,58 @@ -[tool.mypy] -exclude = ["build","setup.py"] +[tool.robotpy-build.metadata] +name = "photonlibpy" +description = "A Pure-python implementation of PhotonLib" +author = "Photonvision Development Team" +url = "https://photonvision.org" +license = "GPLv3" +author_email = "robotpy@googlegroups.com" # required - replace with something more suitable +install_requires = [ +] + +[build-system] +requires = [ + "robotpy-build<2026.0.0,>=2025.0.0a1", + "pyntcore<2026.0.0,>=2025.0.0b1", + "robotpy-apriltag", + "robotpy-wpimath<2026.0.0,>=2025.0.0b1", + "wpilib<2026.0.0,>=2025.0.0b1", +] + +[tool.robotpy-build] +base_package = "photonlibpy" +update_init = ["photonlibpy.timesync photonlibpy.timesync._photonlibpy_timesync"] + +# ======================== Begin photon-timesync stuff ======================== + +[tool.robotpy-build.wrappers."photonlibpy.timesync"] +name = "photonlibpy_timesync" + +depends = [ + "ntcore", + "wpimath", + "wpinet", + "wpiutil", +] + +sources = [ + "photonlibpy/timesync/src/module.cpp", +] + +generation_data = "photonlibpy/timesync/gen/photontimesync" + +[tool.robotpy-build.wrappers."photonlibpy.timesync".maven_lib_download] +artifact_id = "photontargeting-cpp" +group_id = "org.photonvision" +repo_url = "file:///~/.m2/repository" +version = "dev-v2025.0.0-beta-4-13-gc6362cf8" +libs = ["photontargeting"] +[[tool.robotpy-build.wrappers."photonlibpy.timesync".maven_lib_download.header_patches]] +# TODO This patch takes out the default argument to the constructor. Put it back!! +patch = "photonlibpy/timesync/patches/no-wpi-print-incl.patch" +strip = 6 + + +[tool.robotpy-build.wrappers."photonlibpy.timesync".autogen_headers] +# photontargeting +TimeSyncStructs = "net/TimeSyncStructs.h" +TimeSyncClient = "net/TimeSyncClient.h" +TimeSyncServer = "net/TimeSyncServer.h" diff --git a/photon-lib/py/setup.py b/photon-lib/py/setup.py index b72d1932c3..d2e44b4647 100644 --- a/photon-lib/py/setup.py +++ b/photon-lib/py/setup.py @@ -1,74 +1,3 @@ -import re -import subprocess +from robotpy_build.setup import setup -from setuptools import find_packages, setup - -gitDescribeResult = ( - subprocess.check_output(["git", "describe", "--tags", "--match=v*", "--always"]) - .decode("utf-8") - .strip() -) - -m = re.search( - r"(v[0-9]{4}\.[0-9]{1}\.[0-9]{1})-?((?:beta)?(?:alpha)?)-?([0-9\.]*)", - gitDescribeResult, -) - -# Extract the first portion of the git describe result -# which should be PEP440 compliant -if m: - versionString = m.group(0) - # Hack -- for strings like v2024.1.1, do NOT add matruity/suffix - if len(m.group(2)) > 0: - print("using beta group matcher") - prefix = m.group(1) - maturity = m.group(2) - suffix = m.group(3).replace(".", "") - versionString = f"{prefix}.{maturity}.{suffix}" - else: - split = gitDescribeResult.split("-") - if len(split) == 3: - year, commits, sha = split - # Chop off leading v from "v2024.1.2", and use "post" for commits to master since - versionString = f"{year[1:]}post{commits}" - print("using dev release " + versionString) - else: - year = gitDescribeResult - versionString = year[1:] - print("using full release " + versionString) - - -else: - print("Warning, no valid version found") - versionString = gitDescribeResult - -print(f"Building version {versionString}") - -# Put the version info into a python file for runtime access -with open("photonlibpy/version.py", "w", encoding="utf-8") as fp: - fp.write(f'PHOTONLIB_VERSION="{versionString}"\n') - fp.write(f'PHOTONVISION_VERSION="{gitDescribeResult}"\n') - - -descriptionStr = f"Pure-python implementation of PhotonLib for interfacing with PhotonVision on coprocessors. Implemented with PhotonVision version {gitDescribeResult} ." - -setup( - name="photonlibpy", - packages=find_packages(), - package_data={"photonlibpy": ["py.typed"]}, - version=versionString, - install_requires=[ - "numpy~=2.1", - "wpilib<2026,>=2025.0.0b1", - "robotpy-wpimath<2026,>=2025.0.0b1", - "robotpy-apriltag<2026,>=2025.0.0b1", - "robotpy-cscore<2026,>=2025.0.0b1", - "pyntcore<2026,>=2025.0.0b1", - "opencv-python;platform_machine!='roborio'", - ], - description=descriptionStr, - url="https://photonvision.org", - author="Photonvision Development Team", - long_description="A Pure-python implementation of PhotonLib", - long_description_content_type="text/markdown", -) +setup()