Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
abaefc5
Squashed commit of the following:
Bobcat66 May 13, 2026
0fd012f
regenerated messages
Bobcat66 May 13, 2026
93c9855
wip
Bobcat66 May 15, 2026
db591c1
regenerated messages
Bobcat66 May 15, 2026
1dc7746
autogenerated test classes
Bobcat66 May 15, 2026
b93d8ab
linting
Bobcat66 May 15, 2026
44ac51e
added java tests
Bobcat66 May 16, 2026
1cd8a97
wpiformat
Bobcat66 May 16, 2026
f7cadf0
added print messages to serde tests
Bobcat66 May 16, 2026
d2b7e2c
started work on cpp tests
Bobcat66 May 19, 2026
89bbb8e
hopefully fixed tests. also wpiformat
Bobcat66 May 19, 2026
646713d
refactor + regenerate messages
Bobcat66 May 19, 2026
3f27395
added prints to java test classes
Bobcat66 May 19, 2026
b980762
fixed potential C++ bug
Bobcat66 May 19, 2026
1c98b20
quick bugfix in packet.java, also started protocol documentation
Bobcat66 May 19, 2026
d90ada4
forgot to run wpiformat award
Bobcat66 May 19, 2026
2f3ea3f
Merge branch 'main' into squash-photon-serde-enhancements
mcm001 May 19, 2026
da169d8
new java packet tests
Bobcat66 May 19, 2026
35945b0
protocol docs + example
Bobcat66 May 20, 2026
2ea25e3
updated gitignore
Bobcat66 May 20, 2026
e931bc3
updated gitignore and WIP on removing test classes from repository
Bobcat66 May 25, 2026
b1e47ec
Deleted old test classes, CI will break because the fixture is still …
Bobcat66 May 25, 2026
1584dcf
began work on autogenerating test fixtures
Bobcat66 May 25, 2026
f3b83ff
updated python test codegen
Bobcat66 May 25, 2026
76d0533
deleted extra code
Bobcat66 May 25, 2026
115a8c1
got cpp and java test fixtures to generate, still gotta fiddle around…
Bobcat66 May 26, 2026
681f943
removed outdated comments
Bobcat66 May 26, 2026
68c8fec
dhewiu
Bobcat66 May 26, 2026
0659ce8
removed old serde tests
Bobcat66 May 26, 2026
cb66fe9
updated linters. Also linting
Bobcat66 May 26, 2026
45afb77
Merge branch 'main' into squash-photon-serde-enhancements
Bobcat66 May 27, 2026
a1c61b6
moved serde tests out of photon-targeting and photonlibpy. python tes…
Bobcat66 May 27, 2026
cc38398
Merge branch 'main' into squash-photon-serde-enhancements
Bobcat66 May 29, 2026
1029c36
deleted testMessages.py
Bobcat66 May 29, 2026
72b9beb
added python tests and added them to workflow
Bobcat66 May 29, 2026
2d0b295
wpiformat my beloathed
Bobcat66 May 29, 2026
a2dad4d
fixedpython workflow file
Bobcat66 May 29, 2026
c361555
chat will this fix it
Bobcat66 May 29, 2026
3474281
i may be stupid
Bobcat66 May 29, 2026
534bc43
forgot to import the serdes in __init__.py
Bobcat66 May 29, 2026
009faa0
removed unused imports and fixed circular dependencies
Bobcat66 May 29, 2026
1d3cb7b
fixed python serde autotests
Bobcat66 May 29, 2026
d35ee07
removed unnecessary changes from packet.py
Bobcat66 May 29, 2026
8c71e05
i may be stupid
Bobcat66 May 29, 2026
a178050
added auto-generated comment to PyInit
Bobcat66 May 30, 2026
f6b8d4d
Merge branch 'PhotonVision:main' into squash-photon-serde-enhancements
Bobcat66 Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,43 @@ jobs:
- name: Run mypy type checking
run: mypy --show-column-numbers --config-file photon-lib/py/pyproject.toml photon-lib

test-photonserde-py:
needs: build-py
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.14

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest mypy
pip install -r photon-serde-tests/requirements.txt

- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: dist
path: ./photon-lib/py/dist/

- name: Install PhotonLibPy package
working-directory: ./photon-lib/py
shell: bash
run: |
pip install --no-cache-dir dist/*.whl

- name: Run PhotonSerde tests
shell: bash
run: python -m pytest --import-mode=importlib photon-serde-tests/py/AutoSerdeTest.py

build-python-examples:
needs: build-py
strategy:
Expand Down
2 changes: 2 additions & 0 deletions .wpiformat
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ generatedFileExclude {
photon-lib/py/photonlibpy/generated/
photon-targeting/src/generated/
photon-targeting/src/main/native/cpp/photon/constrained_solvepnp/generate/
photon-serde-tests/src
photon-serde-tests/py
}

licenseUpdateExclude {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ spotless {
java {
target fileTree('.') {
include '**/*.java'
exclude '**/build/**', '**/build-*/**', '**/src/generated/**', "**/bin/generated-sources/**"
exclude '**/build/**', '**/build-*/**', '**/src/generated/**', '**/src/generated-test/**', "**/bin/generated-sources/**", "photon-serde-tests/src/**"
}
toggleOffOn()
googleJavaFormat()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,19 @@

from typing import TYPE_CHECKING


from ..packet import Packet
from ..targeting import * # noqa



if TYPE_CHECKING:
from ..targeting import MultiTargetPNPResult # noqa

from ..targeting import PnpResult # noqa



class MultiTargetPNPResultSerde:
# Message definition md5sum. See photon_packet.adoc for details
MESSAGE_VERSION = "541096947e9f3ca2d3f425ff7b04aa7b"
Expand All @@ -50,7 +55,7 @@ def pack(value: "MultiTargetPNPResult") -> "Packet":
ret.encodeBytes(PnpResult.photonStruct.pack(value.estimatedPose).getData())

# fiducialIDsUsed is a custom VLA!
ret.encodeShortList(value.fiducialIDsUsed)
ret.encodeListShimmed(value.fiducialIDsUsed, ret.encode16)
return ret

@staticmethod
Expand All @@ -60,8 +65,8 @@ def unpack(packet: "Packet") -> "MultiTargetPNPResult":
# estimatedPose is of non-intrinsic type PnpResult
ret.estimatedPose = PnpResult.photonStruct.unpack(packet)

# fiducialIDsUsed is a custom VLA!
ret.fiducialIDsUsed = packet.decodeShortList()
# fiducialIDsUsed is an intrinsic VLA!
ret.fiducialIDsUsed = packet.decodeListShimmed(packet.decode16)

return ret

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@

from typing import TYPE_CHECKING


from ..packet import Packet
from ..targeting import * # noqa



if TYPE_CHECKING:
from ..targeting import PhotonPipelineMetadata # noqa



class PhotonPipelineMetadataSerde:
# Message definition md5sum. See photon_packet.adoc for details
MESSAGE_VERSION = "ac0a45f686457856fb30af77699ea356"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,23 @@

from typing import TYPE_CHECKING


from ..packet import Packet
from ..targeting import * # noqa



if TYPE_CHECKING:
from ..targeting import MultiTargetPNPResult # noqa

from ..targeting import PhotonPipelineMetadata # noqa

from ..targeting import PhotonPipelineResult # noqa

from ..targeting import PhotonTrackedTarget # noqa



class PhotonPipelineResultSerde:
# Message definition md5sum. See photon_packet.adoc for details
MESSAGE_VERSION = "4b2ff16a964b5e2bf04be0c1454d91c4"
Expand All @@ -49,9 +56,7 @@ 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,19 @@

from typing import TYPE_CHECKING


from ..packet import Packet
from ..targeting import * # noqa



if TYPE_CHECKING:
from ..targeting import PhotonTrackedTarget # noqa

from ..targeting import TargetCorner # noqa



class PhotonTrackedTargetSerde:
# Message definition md5sum. See photon_packet.adoc for details
MESSAGE_VERSION = "cc6dbb5c5c1e0fa808108019b20863f1"
Expand Down Expand Up @@ -67,8 +72,10 @@ def pack(value: "PhotonTrackedTarget") -> "Packet":
# objDetectConf is of intrinsic type float32
ret.encodeFloat(value.objDetectConf)

# bestCameraToTarget is of shimmed type Transform3d
ret.encodeTransform(value.bestCameraToTarget)

# altCameraToTarget is of shimmed type Transform3d
ret.encodeTransform(value.altCameraToTarget)

# poseAmbiguity is of intrinsic type float64
Expand Down
6 changes: 6 additions & 0 deletions photon-lib/py/photonlibpy/generated/PnpResultSerde.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@

from typing import TYPE_CHECKING


from ..packet import Packet
from ..targeting import * # noqa



if TYPE_CHECKING:
from ..targeting import PnpResult # noqa



class PnpResultSerde:
# Message definition md5sum. See photon_packet.adoc for details
MESSAGE_VERSION = "ae4d655c0a3104d88df4f5db144c1e86"
Expand All @@ -45,8 +49,10 @@ class PnpResultSerde:
def pack(value: "PnpResult") -> "Packet":
ret = Packet()

# best is of shimmed type Transform3d
ret.encodeTransform(value.best)

# alt is of shimmed type Transform3d
ret.encodeTransform(value.alt)

# bestReprojErr is of intrinsic type float64
Expand Down
4 changes: 4 additions & 0 deletions photon-lib/py/photonlibpy/generated/TargetCornerSerde.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@

from typing import TYPE_CHECKING


from ..packet import Packet
from ..targeting import * # noqa



if TYPE_CHECKING:
from ..targeting import TargetCorner # noqa



class TargetCornerSerde:
# Message definition md5sum. See photon_packet.adoc for details
MESSAGE_VERSION = "16f6ac0dedc8eaccb951f4895d9e18b6"
Expand Down
49 changes: 32 additions & 17 deletions photon-lib/py/photonlibpy/packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
###############################################################################

import struct
from typing import Generic, Optional, Protocol, TypeVar
from typing import Callable, Generic, Optional, Protocol, TypeVar

import wpilib
from wpimath import Quaternion, Rotation3d, Transform3d, Translation3d
Expand Down Expand Up @@ -200,12 +200,25 @@ def decodeList(self, serde: Serde[T]) -> list[T]:
retList.append(serde.unpack(self))
return retList

def decodeListShimmed(self, shim: Callable[[], T]) -> list[T]:
retList = []
arr_len = self.decode8()
for _ in range(arr_len):
retList.append(shim())
return retList

def decodeOptional(self, serde: Serde[T]) -> Optional[T]:
if self.decodeBoolean():
return serde.unpack(self)
else:
return None

def decodeOptionalShimmed(self, shim: Callable[[], T]) -> Optional[T]:
if self.decodeBoolean():
return shim()
else:
return None

def _encodeGeneric(self, packFormat, value):
"""
Append bytes to the packet data buffer.
Expand Down Expand Up @@ -255,22 +268,6 @@ def encodeBoolean(self, value: bool):
"""
self.encode8(1 if value else 0)

def encodeDoubleArray(self, values: list[float]):
"""
Encodes an array of doubles and appends it to the packet.
"""
self.encode8(len(values))
for value in values:
self.encodeDouble(value)

def encodeShortList(self, values: list[int]):
"""
Encodes a list of shorts, with length prefixed as a single byte.
"""
self.encode8(len(values))
for value in values:
self.encode16(value)

def encodeTransform(self, transform: Transform3d):
"""
Encodes a Transform3d (translation and rotation) and appends it to the packet.
Expand All @@ -297,6 +294,14 @@ def encodeList(self, values: list[T], serde: Serde[T]):
self.packetData = self.packetData + packed.getData()
self.size = len(self.packetData)

def encodeListShimmed(self, values: list[T], shim: Callable[[T], None]):
"""
Encodes a list of items using a specific serializer and appends it to the packet.
"""
self.encode8(len(values))
for item in values:
shim(item)

def encodeOptional(self, value: Optional[T], serde: Serde[T]):
"""
Encodes an optional value using a specific serializer.
Expand All @@ -309,6 +314,16 @@ def encodeOptional(self, value: Optional[T], serde: Serde[T]):
self.packetData = self.packetData + packed.getData()
self.size = len(self.packetData)

def encodeOptionalShimmed(self, value: Optional[T], shim: Callable[[T], None]):
"""
Encodes an optional value using a specific shimmed serializer.
"""
if value is None:
self.encodeBoolean(False)
else:
self.encodeBoolean(True)
shim(value)

def encodeBytes(self, value: bytes):
self.packetData = self.packetData + value
self.size = len(self.packetData)
5 changes: 5 additions & 0 deletions photon-serde-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
These are autogenerated tests for photon-serde. ALL CODE IN THIS DIRECTORY (except for build.gradle and requirements.txt) IS AUTOMATICALLY GENERATED

requirements.txt is only for version-controlling WPILib. Photonlibpy itself is installed separately, and SHOULD NOT be in the requirements.txt

These tests are separated from the main code as we don't use the autogenerated photon-serde test messages anywhere except in the tests, and we don't want to expose them to end-users in photon-lib
Loading
Loading