Update Python rawBytes parsing (#1119)

*  data updates to capture multiple rawBytes packets associated with serde updates from late this past month

---------

Co-authored-by: Matt <matthew.morley.ca@gmail.com>
This commit is contained in:
Chris Gerth
2024-01-06 06:17:06 -06:00
committed by GitHub
parent 4068025572
commit 851f2e4e68
5 changed files with 138 additions and 269 deletions

View File

@@ -17,6 +17,10 @@ class PNPResult:
def createFromPacket(self, packet: Packet) -> Packet: def createFromPacket(self, packet: Packet) -> Packet:
self.isPresent = packet.decodeBoolean() self.isPresent = packet.decodeBoolean()
if not self.isPresent:
return packet
self.best = packet.decodeTransform() self.best = packet.decodeTransform()
self.alt = packet.decodeTransform() self.alt = packet.decodeTransform()
self.bestReprojError = packet.decodeDouble() self.bestReprojError = packet.decodeDouble()

View File

@@ -14,7 +14,7 @@ class VisionLEDMode(Enum):
kBlink = 2 kBlink = 2
lastVersionTimeCheck = 0.0 _lastVersionTimeCheck = 0.0
_VERSION_CHECK_ENABLED = True _VERSION_CHECK_ENABLED = True
@@ -26,41 +26,41 @@ def setVersionCheckEnabled(enabled: bool):
class PhotonCamera: class PhotonCamera:
def __init__(self, cameraName: str): def __init__(self, cameraName: str):
instance = ntcore.NetworkTableInstance.getDefault() instance = ntcore.NetworkTableInstance.getDefault()
self.name = cameraName self._name = cameraName
self._tableName = "photonvision" self._tableName = "photonvision"
photonvision_root_table = instance.getTable(self._tableName) photonvision_root_table = instance.getTable(self._tableName)
self.cameraTable = photonvision_root_table.getSubTable(cameraName) self._cameraTable = photonvision_root_table.getSubTable(cameraName)
self.path = self.cameraTable.getPath() self._path = self._cameraTable.getPath()
self.rawBytesEntry = self.cameraTable.getRawTopic("rawBytes").subscribe( self._rawBytesEntry = self._cameraTable.getRawTopic("rawBytes").subscribe(
"rawBytes", bytes([]), ntcore.PubSubOptions(periodic=0.01, sendAll=True) "rawBytes", bytes([]), ntcore.PubSubOptions(periodic=0.01, sendAll=True)
) )
self.driverModePublisher = self.cameraTable.getBooleanTopic( self._driverModePublisher = self._cameraTable.getBooleanTopic(
"driverModeRequest" "driverModeRequest"
).publish() ).publish()
self.driverModeSubscriber = self.cameraTable.getBooleanTopic( self._driverModeSubscriber = self._cameraTable.getBooleanTopic(
"driverMode" "driverMode"
).subscribe(False) ).subscribe(False)
self.inputSaveImgEntry = self.cameraTable.getIntegerTopic( self._inputSaveImgEntry = self._cameraTable.getIntegerTopic(
"inputSaveImgCmd" "inputSaveImgCmd"
).getEntry(0) ).getEntry(0)
self.outputSaveImgEntry = self.cameraTable.getIntegerTopic( self._outputSaveImgEntry = self._cameraTable.getIntegerTopic(
"outputSaveImgCmd" "outputSaveImgCmd"
).getEntry(0) ).getEntry(0)
self.pipelineIndexRequest = self.cameraTable.getIntegerTopic( self._pipelineIndexRequest = self._cameraTable.getIntegerTopic(
"pipelineIndexRequest" "pipelineIndexRequest"
).publish() ).publish()
self.pipelineIndexState = self.cameraTable.getIntegerTopic( self._pipelineIndexState = self._cameraTable.getIntegerTopic(
"pipelineIndexState" "pipelineIndexState"
).subscribe(0) ).subscribe(0)
self.heartbeatEntry = self.cameraTable.getIntegerTopic("heartbeat").subscribe( self._heartbeatEntry = self._cameraTable.getIntegerTopic("heartbeat").subscribe(
-1 -1
) )
self.ledModeRequest = photonvision_root_table.getIntegerTopic( self._ledModeRequest = photonvision_root_table.getIntegerTopic(
"ledModeRequest" "ledModeRequest"
).publish() ).publish()
self.ledModeState = photonvision_root_table.getIntegerTopic( self._ledModeState = photonvision_root_table.getIntegerTopic(
"ledModeState" "ledModeState"
).subscribe(-1) ).subscribe(-1)
self.versionEntry = photonvision_root_table.getStringTopic("version").subscribe( self.versionEntry = photonvision_root_table.getStringTopic("version").subscribe(
@@ -72,14 +72,14 @@ class PhotonCamera:
instance, ["/photonvision/"], ntcore.PubSubOptions(topicsOnly=True) instance, ["/photonvision/"], ntcore.PubSubOptions(topicsOnly=True)
) )
self.prevHeartbeat = 0 self._prevHeartbeat = 0
self.prevHeartbeatChangeTime = Timer.getFPGATimestamp() self._prevHeartbeatChangeTime = Timer.getFPGATimestamp()
def getLatestResult(self) -> PhotonPipelineResult: def getLatestResult(self) -> PhotonPipelineResult:
self._versionCheck() self._versionCheck()
retVal = PhotonPipelineResult() retVal = PhotonPipelineResult()
packetWithTimestamp = self.rawBytesEntry.getAtomic() packetWithTimestamp = self._rawBytesEntry.getAtomic()
byteList = packetWithTimestamp.value byteList = packetWithTimestamp.value
timestamp = packetWithTimestamp.time timestamp = packetWithTimestamp.time
@@ -94,57 +94,57 @@ class PhotonCamera:
return retVal return retVal
def getDriverMode(self) -> bool: def getDriverMode(self) -> bool:
return self.driverModeSubscriber.get() return self._driverModeSubscriber.get()
def setDriverMode(self, driverMode: bool) -> None: def setDriverMode(self, driverMode: bool) -> None:
self.driverModePublisher.set(driverMode) self._driverModePublisher.set(driverMode)
def takeInputSnapshot(self) -> None: def takeInputSnapshot(self) -> None:
self.inputSaveImgEntry.set(self.inputSaveImgEntry.get() + 1) self._inputSaveImgEntry.set(self._inputSaveImgEntry.get() + 1)
def takeOutputSnapshot(self) -> None: def takeOutputSnapshot(self) -> None:
self.outputSaveImgEntry.set(self.outputSaveImgEntry.get() + 1) self._outputSaveImgEntry.set(self._outputSaveImgEntry.get() + 1)
def getPipelineIndex(self) -> int: def getPipelineIndex(self) -> int:
return self.pipelineIndexState.get(0) return self._pipelineIndexState.get(0)
def setPipelineIndex(self, index: int) -> None: def setPipelineIndex(self, index: int) -> None:
self.pipelineIndexRequest.set(index) self._pipelineIndexRequest.set(index)
def getLEDMode(self) -> VisionLEDMode: def getLEDMode(self) -> VisionLEDMode:
mode = self.ledModeState.get() mode = self._ledModeState.get()
return VisionLEDMode(mode) return VisionLEDMode(mode)
def setLEDMode(self, led: VisionLEDMode) -> None: def setLEDMode(self, led: VisionLEDMode) -> None:
self.ledModeRequest.set(led.value) self._ledModeRequest.set(led.value)
def getName(self) -> str: def getName(self) -> str:
return self.name return self._name
def isConnected(self) -> bool: def isConnected(self) -> bool:
curHeartbeat = self.heartbeatEntry.get() curHeartbeat = self._heartbeatEntry.get()
now = Timer.getFPGATimestamp() now = Timer.getFPGATimestamp()
if curHeartbeat != self.prevHeartbeat: if curHeartbeat != self._prevHeartbeat:
self.prevHeartbeat = curHeartbeat self._prevHeartbeat = curHeartbeat
self.prevHeartbeatChangeTime = now self._prevHeartbeatChangeTime = now
return (now - self.prevHeartbeatChangeTime) < 0.5 return (now - self._prevHeartbeatChangeTime) < 0.5
def _versionCheck(self) -> None: def _versionCheck(self) -> None:
global lastVersionTimeCheck global _lastVersionTimeCheck
if not _VERSION_CHECK_ENABLED: if not _VERSION_CHECK_ENABLED:
return return
if (Timer.getFPGATimestamp() - lastVersionTimeCheck) < 5.0: if (Timer.getFPGATimestamp() - _lastVersionTimeCheck) < 5.0:
return return
lastVersionTimeCheck = Timer.getFPGATimestamp() _lastVersionTimeCheck = Timer.getFPGATimestamp()
if not self.heartbeatEntry.exists(): if not self._heartbeatEntry.exists():
cameraNames = ( cameraNames = (
self.cameraTable.getInstance().getTable(self._tableName).getSubTables() self._cameraTable.getInstance().getTable(self._tableName).getSubTables()
) )
if len(cameraNames) == 0: if len(cameraNames) == 0:
wpilib.reportError( wpilib.reportError(
@@ -153,13 +153,13 @@ class PhotonCamera:
) )
else: else:
wpilib.reportError( wpilib.reportError(
f"PhotonVision coprocessor at path {self.path} not found in Network Tables. Double check that your camera names match! Only the following camera names were found: { ''.join(cameraNames)}", f"PhotonVision coprocessor at path {self._path} not found in Network Tables. Double check that your camera names match! Only the following camera names were found: { ''.join(cameraNames)}",
True, True,
) )
elif not self.isConnected(): elif not self.isConnected():
wpilib.reportWarning( wpilib.reportWarning(
f"PhotonVision coprocessor at path {self.path} is not sending new data.", f"PhotonVision coprocessor at path {self._path} is not sending new data.",
True, True,
) )

View File

@@ -15,14 +15,17 @@ class PhotonPipelineResult:
def populateFromPacket(self, packet: Packet) -> Packet: def populateFromPacket(self, packet: Packet) -> Packet:
self.targets = [] self.targets = []
self.latencyMillis = packet.decodeDouble() self.latencyMillis = packet.decodeDouble()
self.multiTagResult = MultiTargetPNPResult()
self.multiTagResult.createFromPacket(packet)
targetCount = packet.decode8() targetCount = packet.decode8()
print(f"targetCount = {targetCount}")
for _ in range(targetCount): for _ in range(targetCount):
target = PhotonTrackedTarget() target = PhotonTrackedTarget()
target.createFromPacket(packet) target.createFromPacket(packet)
self.targets.append(target) self.targets.append(target)
self.multiTagResult = MultiTargetPNPResult()
self.multiTagResult.createFromPacket(packet)
return packet return packet
def setTimestampSeconds(self, timestampSec: float) -> None: def setTimestampSeconds(self, timestampSec: float) -> None:

File diff suppressed because one or more lines are too long

View File

@@ -6,6 +6,9 @@ from data import rawBytes3
from data import rawBytes4 from data import rawBytes4
from data import rawBytes5 from data import rawBytes5
from data import rawBytes6 from data import rawBytes6
from data import rawBytes7
from data import rawBytes8
from data import rawBytes9
def setupCommon(bytesIn): def setupCommon(bytesIn):
@@ -28,7 +31,7 @@ def test_byteParse2():
def test_byteParse3(): def test_byteParse3():
res = setupCommon(rawBytes3) res = setupCommon(rawBytes3)
assert len(res.getTargets()) == 0 assert len(res.getTargets()) >= 4
def test_byteParse4(): def test_byteParse4():
@@ -38,9 +41,24 @@ def test_byteParse4():
def test_byteParse5(): def test_byteParse5():
res = setupCommon(rawBytes5) res = setupCommon(rawBytes5)
assert len(res.getTargets()) == 1 assert len(res.getTargets()) == 2
def test_byteParse6(): def test_byteParse6():
res = setupCommon(rawBytes6) res = setupCommon(rawBytes6)
assert len(res.getTargets()) > 6 # assert len(res.getTargets()) >= 0
def test_byteParse7():
res = setupCommon(rawBytes7)
# assert len(res.getTargets()) >= 0
def test_byteParse8():
res = setupCommon(rawBytes8)
# assert len(res.getTargets()) >= 0
def test_byteParse9():
res = setupCommon(rawBytes9)
# assert len(res.getTargets()) >= 0