Files
allwpilib/wpilibc/src/main/native/cpp/framework/TimedRobot.cpp

69 lines
1.9 KiB
C++
Raw Normal View History

// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
2017-07-08 10:50:56 -04:00
#include "wpi/framework/TimedRobot.hpp"
2017-07-08 10:50:56 -04:00
#include <stdint.h>
2017-07-08 10:50:56 -04:00
#include <cstdio>
#include <utility>
[cmd3] Enforce command lifetimes across all opmode and command scopes (#8705) Commands are no longer able to outlive their schedule-site's scope, regardless of how they were scheduled (set as a default command, bound to a trigger, or manually scheduled) As a consequence, default commands need better tracking so the default command setting can be released when their scope exits and the next-most appropriate default command can be rescheduled (eg, an opmode sets a default command, then the globally-scoped default is restored when the opmode exits). Some complexity is required here to make it work well for edge cases. Like `schedule()`, `setDefaultCommand()` will immediately start the new default command if called inside of another command to avoid 1-loop delays. However, this does not apply when called by the _current_ default command, as it would result in attempting to cancel the default command while it's mounted (which is impossible and would throw an exception) ```java class Robot extends OpModeRobot { final Drive drive = new Drive(); final CommandXboxController controller = new CommandXboxController(1); public Robot() { // global default command, active unless overridden in an opmode or command drive.setDefaultCommand(drive.stop()); // global trigger binding, always active controller.rightBumper().onTrue(drive.setX()); } } @Teleop class ExampleOpMode extends PeriodicOpMode { public ExampleOpMode(Robot robot) { // opmode-specific default command robot.drive.setDefaultCommand(robot.drive.operatorControl(robot.controller)); // opmode-specific binding robot.controller.leftBumper().whileTrue(robot.drive.stop()); // opmode-specific binding that takes precedence over the global binding // because it happens last; it "wins out" over the `setX()` binding robot.controller.rightBumper().onTrue(robot.drive.selfTest()); } @Override public void periodic() { Scheduler.getDefault().run(); } } ```
2026-04-09 20:05:42 -04:00
#include "wpi/driverstation/DriverStation.hpp"
#include "wpi/hal/DriverStation.hpp"
#include "wpi/hal/UsageReporting.hpp"
2025-11-07 19:56:21 -05:00
#include "wpi/system/Errors.hpp"
#include "wpi/system/RobotController.hpp"
2025-11-07 20:00:05 -05:00
using namespace wpi;
2017-07-08 10:50:56 -04:00
void TimedRobot::StartCompetition() {
if constexpr (IsSimulation()) {
SimulationInit();
}
// Tell the DS that the robot is ready to be enabled
std::puts("\n********** Robot program startup complete **********");
[cmd3] Enforce command lifetimes across all opmode and command scopes (#8705) Commands are no longer able to outlive their schedule-site's scope, regardless of how they were scheduled (set as a default command, bound to a trigger, or manually scheduled) As a consequence, default commands need better tracking so the default command setting can be released when their scope exits and the next-most appropriate default command can be rescheduled (eg, an opmode sets a default command, then the globally-scoped default is restored when the opmode exits). Some complexity is required here to make it work well for edge cases. Like `schedule()`, `setDefaultCommand()` will immediately start the new default command if called inside of another command to avoid 1-loop delays. However, this does not apply when called by the _current_ default command, as it would result in attempting to cancel the default command while it's mounted (which is impossible and would throw an exception) ```java class Robot extends OpModeRobot { final Drive drive = new Drive(); final CommandXboxController controller = new CommandXboxController(1); public Robot() { // global default command, active unless overridden in an opmode or command drive.setDefaultCommand(drive.stop()); // global trigger binding, always active controller.rightBumper().onTrue(drive.setX()); } } @Teleop class ExampleOpMode extends PeriodicOpMode { public ExampleOpMode(Robot robot) { // opmode-specific default command robot.drive.setDefaultCommand(robot.drive.operatorControl(robot.controller)); // opmode-specific binding robot.controller.leftBumper().whileTrue(robot.drive.stop()); // opmode-specific binding that takes precedence over the global binding // because it happens last; it "wins out" over the `setX()` binding robot.controller.rightBumper().onTrue(robot.drive.selfTest()); } @Override public void periodic() { Scheduler.getDefault().run(); } } ```
2026-04-09 20:05:42 -04:00
DriverStation::ObserveUserProgramStarting();
// Loop forever, calling the appropriate mode-dependent function
2017-07-08 10:50:56 -04:00
while (true) {
if (!m_callbacks.RunCallbacks(m_notifier)) {
break;
}
2017-07-08 10:50:56 -04:00
}
}
void TimedRobot::EndCompetition() {
[hal] Revamp notifiers (#8424) This changes the HAL notifier interface to: - Use wpiutil signal objects. This means waiting is done through the `WPI_WaitObject` API instead of a dedicated function and allows for higher level code to simultaneously wait on notifiers and other events. - Interval timers are supported at the HAL layer - Handlers are now required to acknowledge notifications. This is invisible to users unless they're directly using the HAL API. - For interval timers, an overrun count is maintained to detect if the handler didn't acknowledge The underlying implementation still uses condition variables for the actual waiting. In basic testing using this approach seemed to be lower jitter than timerfd. Currently, the simulation and systemcore implementations are nearly identical except for a few additional sim hook bits. This could be refactored, but keeping them separate may make sense to keep the systemcore implementation easy to read and reason about, or if we ever choose to use a different underlying timer implementation on systemcore. The simulation side API is unchanged in form but does change in function--waiting for notifiers now only waits for currently running (or newly signaled) notifiers to acknowledge. To avoid a race condition in sim stepTiming, users of the low level API must make any alarm updates (especially for one-shot alarms) prior to acknowledging the previous alarm. The only current use of the interval timer feature is the `Notifier` class. The `TimedRobot` implementation still uses a single notifier and its own interval timing logic to ensure consistent callback order. Using separate notifiers for each user-level interval would substantially increase complexity. `Watchdog` also doesn't use the interval timer, as it's looking for an amount of time since the last `set` call rather than a recurring interval time. To reduce flicker, the sim GUI uses a fade out when a timeout goes from set to unset. This fixes tsan for wpilib and commands, and also fixes some spurious test failures.
2025-11-29 11:00:18 -08:00
HAL_DestroyNotifier(m_notifier);
m_notifier = HAL_INVALID_HANDLE;
}
2025-11-07 20:01:58 -05:00
TimedRobot::TimedRobot(wpi::units::second_t period)
: IterativeRobotBase(period) {
m_startTime = std::chrono::microseconds{RobotController::GetMonotonicTime()};
AddPeriodic([=, this] { LoopFunc(); }, period);
int32_t status = 0;
[hal] Revamp notifiers (#8424) This changes the HAL notifier interface to: - Use wpiutil signal objects. This means waiting is done through the `WPI_WaitObject` API instead of a dedicated function and allows for higher level code to simultaneously wait on notifiers and other events. - Interval timers are supported at the HAL layer - Handlers are now required to acknowledge notifications. This is invisible to users unless they're directly using the HAL API. - For interval timers, an overrun count is maintained to detect if the handler didn't acknowledge The underlying implementation still uses condition variables for the actual waiting. In basic testing using this approach seemed to be lower jitter than timerfd. Currently, the simulation and systemcore implementations are nearly identical except for a few additional sim hook bits. This could be refactored, but keeping them separate may make sense to keep the systemcore implementation easy to read and reason about, or if we ever choose to use a different underlying timer implementation on systemcore. The simulation side API is unchanged in form but does change in function--waiting for notifiers now only waits for currently running (or newly signaled) notifiers to acknowledge. To avoid a race condition in sim stepTiming, users of the low level API must make any alarm updates (especially for one-shot alarms) prior to acknowledging the previous alarm. The only current use of the interval timer feature is the `Notifier` class. The `TimedRobot` implementation still uses a single notifier and its own interval timing logic to ensure consistent callback order. Using separate notifiers for each user-level interval would substantially increase complexity. `Watchdog` also doesn't use the interval timer, as it's looking for an amount of time since the last `set` call rather than a recurring interval time. To reduce flicker, the sim GUI uses a fade out when a timeout goes from set to unset. This fixes tsan for wpilib and commands, and also fixes some spurious test failures.
2025-11-29 11:00:18 -08:00
m_notifier = HAL_CreateNotifier(&status);
2025-11-07 20:00:43 -05:00
WPILIB_CheckErrorStatus(status, "InitializeNotifier");
2019-11-09 11:41:58 -08:00
HAL_SetNotifierName(m_notifier, "TimedRobot", &status);
2017-07-08 10:50:56 -04:00
HAL_ReportUsage("Framework", "TimedRobot");
2017-07-08 10:50:56 -04:00
}
2025-11-07 20:01:58 -05:00
TimedRobot::TimedRobot(wpi::units::hertz_t frequency)
: TimedRobot{1 / frequency} {}
TimedRobot::~TimedRobot() {
if (m_notifier != HAL_INVALID_HANDLE) {
[hal] Revamp notifiers (#8424) This changes the HAL notifier interface to: - Use wpiutil signal objects. This means waiting is done through the `WPI_WaitObject` API instead of a dedicated function and allows for higher level code to simultaneously wait on notifiers and other events. - Interval timers are supported at the HAL layer - Handlers are now required to acknowledge notifications. This is invisible to users unless they're directly using the HAL API. - For interval timers, an overrun count is maintained to detect if the handler didn't acknowledge The underlying implementation still uses condition variables for the actual waiting. In basic testing using this approach seemed to be lower jitter than timerfd. Currently, the simulation and systemcore implementations are nearly identical except for a few additional sim hook bits. This could be refactored, but keeping them separate may make sense to keep the systemcore implementation easy to read and reason about, or if we ever choose to use a different underlying timer implementation on systemcore. The simulation side API is unchanged in form but does change in function--waiting for notifiers now only waits for currently running (or newly signaled) notifiers to acknowledge. To avoid a race condition in sim stepTiming, users of the low level API must make any alarm updates (especially for one-shot alarms) prior to acknowledging the previous alarm. The only current use of the interval timer feature is the `Notifier` class. The `TimedRobot` implementation still uses a single notifier and its own interval timing logic to ensure consistent callback order. Using separate notifiers for each user-level interval would substantially increase complexity. `Watchdog` also doesn't use the interval timer, as it's looking for an amount of time since the last `set` call rather than a recurring interval time. To reduce flicker, the sim GUI uses a fade out when a timeout goes from set to unset. This fixes tsan for wpilib and commands, and also fixes some spurious test failures.
2025-11-29 11:00:18 -08:00
HAL_DestroyNotifier(m_notifier);
}
}
void TimedRobot::AddPeriodic(std::function<void()> callback,
2025-11-07 20:01:58 -05:00
wpi::units::second_t period,
wpi::units::second_t offset) {
m_callbacks.Add(std::move(callback), m_startTime, period, offset);
}