2023-12-16 12:32:49 -06:00
from enum import Enum
import ntcore
from wpilib import Timer
import wpilib
from photonlibpy . packet import Packet
from photonlibpy . photonPipelineResult import PhotonPipelineResult
from photonlibpy . version import PHOTONVISION_VERSION , PHOTONLIB_VERSION
class VisionLEDMode ( Enum ) :
kDefault = - 1
kOff = 0
kOn = 1
kBlink = 2
2024-01-06 06:17:06 -06:00
_lastVersionTimeCheck = 0.0
2023-12-16 12:32:49 -06:00
_VERSION_CHECK_ENABLED = True
def setVersionCheckEnabled ( enabled : bool ) :
2023-12-17 07:27:41 -06:00
global _VERSION_CHECK_ENABLED
2023-12-16 12:32:49 -06:00
_VERSION_CHECK_ENABLED = enabled
class PhotonCamera :
def __init__ ( self , cameraName : str ) :
instance = ntcore . NetworkTableInstance . getDefault ( )
2024-01-06 06:17:06 -06:00
self . _name = cameraName
2023-12-16 12:32:49 -06:00
self . _tableName = " photonvision "
photonvision_root_table = instance . getTable ( self . _tableName )
2024-01-06 06:17:06 -06:00
self . _cameraTable = photonvision_root_table . getSubTable ( cameraName )
self . _path = self . _cameraTable . getPath ( )
self . _rawBytesEntry = self . _cameraTable . getRawTopic ( " rawBytes " ) . subscribe (
2023-12-16 12:32:49 -06:00
" rawBytes " , bytes ( [ ] ) , ntcore . PubSubOptions ( periodic = 0.01 , sendAll = True )
)
2024-01-06 06:17:06 -06:00
self . _driverModePublisher = self . _cameraTable . getBooleanTopic (
2023-12-16 12:32:49 -06:00
" driverModeRequest "
) . publish ( )
2024-01-06 06:17:06 -06:00
self . _driverModeSubscriber = self . _cameraTable . getBooleanTopic (
2023-12-16 12:32:49 -06:00
" driverMode "
) . subscribe ( False )
2024-01-06 06:17:06 -06:00
self . _inputSaveImgEntry = self . _cameraTable . getIntegerTopic (
2023-12-16 12:32:49 -06:00
" inputSaveImgCmd "
) . getEntry ( 0 )
2024-01-06 06:17:06 -06:00
self . _outputSaveImgEntry = self . _cameraTable . getIntegerTopic (
2023-12-16 12:32:49 -06:00
" outputSaveImgCmd "
) . getEntry ( 0 )
2024-01-06 06:17:06 -06:00
self . _pipelineIndexRequest = self . _cameraTable . getIntegerTopic (
2023-12-16 12:32:49 -06:00
" pipelineIndexRequest "
) . publish ( )
2024-01-06 06:17:06 -06:00
self . _pipelineIndexState = self . _cameraTable . getIntegerTopic (
2023-12-16 12:32:49 -06:00
" pipelineIndexState "
) . subscribe ( 0 )
2024-01-06 06:17:06 -06:00
self . _heartbeatEntry = self . _cameraTable . getIntegerTopic ( " heartbeat " ) . subscribe (
2023-12-16 12:32:49 -06:00
- 1
)
2024-01-06 06:17:06 -06:00
self . _ledModeRequest = photonvision_root_table . getIntegerTopic (
2023-12-16 12:32:49 -06:00
" ledModeRequest "
) . publish ( )
2024-01-06 06:17:06 -06:00
self . _ledModeState = photonvision_root_table . getIntegerTopic (
2023-12-16 12:32:49 -06:00
" ledModeState "
) . subscribe ( - 1 )
self . versionEntry = photonvision_root_table . getStringTopic ( " version " ) . subscribe (
" "
)
# Existing is enough to make this multisubscriber do its thing
self . topicNameSubscriber = ntcore . MultiSubscriber (
instance , [ " /photonvision/ " ] , ntcore . PubSubOptions ( topicsOnly = True )
)
2024-01-06 06:17:06 -06:00
self . _prevHeartbeat = 0
self . _prevHeartbeatChangeTime = Timer . getFPGATimestamp ( )
2023-12-16 12:32:49 -06:00
def getLatestResult ( self ) - > PhotonPipelineResult :
self . _versionCheck ( )
retVal = PhotonPipelineResult ( )
2024-01-06 06:17:06 -06:00
packetWithTimestamp = self . _rawBytesEntry . getAtomic ( )
2023-12-16 12:32:49 -06:00
byteList = packetWithTimestamp . value
timestamp = packetWithTimestamp . time
if len ( byteList ) < 1 :
return retVal
else :
retVal . populateFromPacket ( Packet ( byteList ) )
# NT4 allows us to correct the timestamp based on when the message was sent
retVal . setTimestampSeconds (
timestamp / 1e-6 - retVal . getLatencyMillis ( ) / 1e-3
)
return retVal
def getDriverMode ( self ) - > bool :
2024-01-06 06:17:06 -06:00
return self . _driverModeSubscriber . get ( )
2023-12-16 12:32:49 -06:00
def setDriverMode ( self , driverMode : bool ) - > None :
2024-01-06 06:17:06 -06:00
self . _driverModePublisher . set ( driverMode )
2023-12-16 12:32:49 -06:00
def takeInputSnapshot ( self ) - > None :
2024-01-06 06:17:06 -06:00
self . _inputSaveImgEntry . set ( self . _inputSaveImgEntry . get ( ) + 1 )
2023-12-16 12:32:49 -06:00
def takeOutputSnapshot ( self ) - > None :
2024-01-06 06:17:06 -06:00
self . _outputSaveImgEntry . set ( self . _outputSaveImgEntry . get ( ) + 1 )
2023-12-16 12:32:49 -06:00
def getPipelineIndex ( self ) - > int :
2024-01-06 06:17:06 -06:00
return self . _pipelineIndexState . get ( 0 )
2023-12-16 12:32:49 -06:00
def setPipelineIndex ( self , index : int ) - > None :
2024-01-06 06:17:06 -06:00
self . _pipelineIndexRequest . set ( index )
2023-12-16 12:32:49 -06:00
def getLEDMode ( self ) - > VisionLEDMode :
2024-01-06 06:17:06 -06:00
mode = self . _ledModeState . get ( )
2023-12-16 12:32:49 -06:00
return VisionLEDMode ( mode )
def setLEDMode ( self , led : VisionLEDMode ) - > None :
2024-01-06 06:17:06 -06:00
self . _ledModeRequest . set ( led . value )
2023-12-16 12:32:49 -06:00
def getName ( self ) - > str :
2024-01-06 06:17:06 -06:00
return self . _name
2023-12-16 12:32:49 -06:00
def isConnected ( self ) - > bool :
2024-01-06 06:17:06 -06:00
curHeartbeat = self . _heartbeatEntry . get ( )
2023-12-16 12:32:49 -06:00
now = Timer . getFPGATimestamp ( )
2024-01-06 06:17:06 -06:00
if curHeartbeat != self . _prevHeartbeat :
self . _prevHeartbeat = curHeartbeat
self . _prevHeartbeatChangeTime = now
2023-12-16 12:32:49 -06:00
2024-01-06 06:17:06 -06:00
return ( now - self . _prevHeartbeatChangeTime ) < 0.5
2023-12-16 12:32:49 -06:00
def _versionCheck ( self ) - > None :
2024-01-06 06:17:06 -06:00
global _lastVersionTimeCheck
2023-12-17 07:27:41 -06:00
2023-12-16 12:32:49 -06:00
if not _VERSION_CHECK_ENABLED :
return
2024-01-06 06:17:06 -06:00
if ( Timer . getFPGATimestamp ( ) - _lastVersionTimeCheck ) < 5.0 :
2023-12-16 12:32:49 -06:00
return
2024-01-06 06:17:06 -06:00
_lastVersionTimeCheck = Timer . getFPGATimestamp ( )
2023-12-17 07:27:41 -06:00
2024-01-06 06:17:06 -06:00
if not self . _heartbeatEntry . exists ( ) :
2023-12-16 12:32:49 -06:00
cameraNames = (
2024-01-06 06:17:06 -06:00
self . _cameraTable . getInstance ( ) . getTable ( self . _tableName ) . getSubTables ( )
2023-12-16 12:32:49 -06:00
)
if len ( cameraNames ) == 0 :
wpilib . reportError (
" Could not find any PhotonVision coprocessors on NetworkTables. Double check that PhotonVision is running, and that your camera is connected! " ,
False ,
)
else :
wpilib . reportError (
2024-01-06 06:17:06 -06:00
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 ) } " ,
2023-12-16 12:32:49 -06:00
True ,
)
elif not self . isConnected ( ) :
wpilib . reportWarning (
2024-01-06 06:17:06 -06:00
f " PhotonVision coprocessor at path { self . _path } is not sending new data. " ,
2023-12-16 12:32:49 -06:00
True ,
)
versionString = self . versionEntry . get ( defaultValue = " " )
if len ( versionString ) > 0 and versionString != PHOTONVISION_VERSION :
2023-12-30 14:34:52 -06:00
# Verified version mismatch
bfw = """
\n \n \n
2024-01-01 16:30:57 -06:00
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >
>> > ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
2023-12-30 14:34:52 -06:00
>> >
>> > You are running an incompatible version
>> > of PhotonVision on your coprocessor !
>> >
>> > This is neither tested nor supported .
2024-01-01 16:30:57 -06:00
>> > You MUST update PhotonVision ,
>> > PhotonLib , or both .
2023-12-30 14:34:52 -06:00
>> >
2024-01-01 16:30:57 -06:00
>> > Your code will now crash .
>> > We hope your day gets better .
>> >
>> > ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >
2023-12-30 14:34:52 -06:00
\n \n
"""
wpilib . reportWarning ( bfw )
errText = f " Photon version { PHOTONLIB_VERSION } does not match coprocessor version { versionString } . Please install photonlibpy version { PHOTONLIB_VERSION } . "
wpilib . reportError ( errText , True )
raise Exception ( errText )