Source code for pykit.loggedrobot
import hal
from wpilib import (
DSControlWord,
IterativeRobotBase,
RobotController,
Watchdog,
)
from pykit.logger import Logger
[docs]
class LoggedRobot(IterativeRobotBase):
"""
A robot base class that provides logging and replay functionality.
This class extends `IterativeRobotBase` and integrates with the `Logger`
to automatically handle the logging of robot data and periodic loops.
"""
default_period = 0.02 # seconds
[docs]
def printOverrunMessage(self):
"""Prints a message when the main loop overruns."""
print("Loop overrun detected!")
def __init__(self):
"""
Constructor for the LoggedRobot.
Initializes the robot, sets up the logger, and creates I/O objects.
"""
IterativeRobotBase.__init__(self, LoggedRobot.default_period)
self.useTiming = True
self._periodUs = int(self.getPeriod() * 1000000)
# Because in "robotpy test" this code starts at time 0
# and hal.waitForNotifierAlarm returns (current_time_or_stopped, status)
# with current_time_or_stopped assigned to 0 when hal.stopNotifier is called
# or when the the current time is 0, and hal.stopNotifier is signal to
# exit the infinite loop, the stop is prematurely detected at time 0.
# Force the program to wait until self._periodUs for the first periodic loop
# so that current_time_or_stopped will contain a non-zero current time and the
# infinite loop does not end prematurely.
self._nextCycleUs = 0 + self._periodUs
self.notifier = hal.initializeNotifier()[0]
self.watchdog = Watchdog(LoggedRobot.default_period, self.printOverrunMessage)
self.word = DSControlWord()
[docs]
def endCompetition(self) -> None:
"""Called at the end of the competition to clean up resources."""
hal.stopNotifier(self.notifier)
hal.cleanNotifier(self.notifier)
[docs]
def startCompetition(self) -> None:
"""
The main loop of the robot.
Handles timing, logging, and calling the periodic functions.
This method replaces the standard `IterativeRobotBase.startCompetition`
to inject logging and precise timing control.
"""
self.robotInit()
if self.isSimulation():
self._simulationInit()
self.initEnd = RobotController.getFPGATime()
Logger.periodicAfterUser(self.initEnd, 0)
print("Robot startup complete!")
hal.observeUserProgramStarting()
Logger.startReciever()
while True:
# Wait for next cycle using HAL notifier for precise timing
if self.useTiming:
currentTime = RobotController.getFPGATime()
if self._nextCycleUs < currentTime:
# Loop overrun detected - skip waiting and run immediately
self._nextCycleUs = currentTime
else:
hal.updateNotifierAlarm(self.notifier, int(self._nextCycleUs))
currentTimeOrStopped, status = hal.waitForNotifierAlarm(
self.notifier
)
if status != 0:
raise RuntimeError(
f"Error waiting for notifier alarm: status {status}"
)
if currentTimeOrStopped == 0:
break
self._nextCycleUs += self._periodUs
# Run logger pre-user code (load inputs from log or sensors)
periodicBeforeStart = RobotController.getFPGATime()
Logger.periodicBeforeUser()
# Execute user periodic code and measure timing
userCodeStart = RobotController.getFPGATime()
self._loopFunc()
userCodeEnd = RobotController.getFPGATime()
# Run logger post-user code (save outputs to log)
Logger.periodicAfterUser(
userCodeEnd - userCodeStart, userCodeStart - periodicBeforeStart
)