001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package org.wpilib.opmode;
006
007import java.util.Collections;
008import java.util.Set;
009import java.util.TreeSet;
010import org.wpilib.hardware.hal.HAL;
011import org.wpilib.internal.PeriodicPriorityQueue;
012import org.wpilib.system.RobotController;
013
014/**
015 * An opmode structure for periodic operation. This base class implements a loop that runs one or
016 * more functions periodically (on a set time interval aka loop period). The primary periodic
017 * callback function is the abstract periodic() function; the time interval for this callback is 20
018 * ms by default, but may be changed via passing a different time interval to OpModeRobot's
019 * constructor. Additional periodic callbacks with different intervals can be added using the
020 * addPeriodic() set of functions.
021 *
022 * <p>Lifecycle:
023 *
024 * <ul>
025 *   <li>constructed when opmode selected on driver station
026 *   <li>disabledPeriodic() called periodically as long as DS is disabled. Note this is not called
027 *       on a set time interval (it does not use the same time interval as periodic())
028 *   <li>when DS transitions from disabled to enabled, start() is called once
029 *   <li>while DS is enabled, periodic() is called periodically on the time interval set by
030 *       OpModeRobot's constructor, and additional periodic callbacks added via addPeriodic() are
031 *       called periodically on their set time intervals
032 *   <li>when DS transitions from enabled to disabled, or a different opmode is selected on the
033 *       driver station when the DS is enabled, end() is called, followed by close(); the object is
034 *       not reused
035 *   <li>if a different opmode is selected on the driver station when the DS is disabled, only
036 *       close() is called; the object is not reused
037 * </ul>
038 *
039 * <p>All lifecycle callbacks and periodic callbacks run synchronously on the same thread that
040 * invokes them. Interactions between opmodes and the robot framework do not require additional
041 * synchronization.
042 */
043public abstract class PeriodicOpMode implements OpMode {
044  private final Set<PeriodicPriorityQueue.Callback> m_callbacks;
045  private final long m_startTimeUs = RobotController.getMonotonicTime();
046
047  /** Constructor for PeriodicOpMode. */
048  @SuppressWarnings("this-escape")
049  protected PeriodicOpMode() {
050    m_callbacks = new TreeSet<>();
051    HAL.reportUsage("OpMode", "PeriodicOpMode");
052  }
053
054  @Override
055  public Set<PeriodicPriorityQueue.Callback> getCallbacks() {
056    return Collections.unmodifiableSet(m_callbacks);
057  }
058
059  /**
060   * Add a callback to run at a specific period.
061   *
062   * <p>This is scheduled on OpModeRobot's Notifier, so OpModeRobot and the callback run
063   * synchronously. Interactions between them are thread-safe.
064   *
065   * @param callback The callback to run.
066   * @param period The period at which to run the callback in seconds.
067   */
068  public final void addPeriodic(Runnable callback, double period) {
069    addPeriodic(callback, period, period);
070  }
071
072  /**
073   * Add a callback to run at a specific period with a starting time offset.
074   *
075   * <p>This is scheduled on TimedRobot's Notifier, so TimedRobot and the callback run
076   * synchronously. Interactions between them are thread-safe.
077   *
078   * @param callback The callback to run.
079   * @param period The period at which to run the callback in seconds.
080   * @param offset The offset from the common starting time in seconds. This is useful for
081   *     scheduling a callback in a different timeslot relative to TimedRobot.
082   */
083  public final void addPeriodic(Runnable callback, double period, double offset) {
084    m_callbacks.add(new PeriodicPriorityQueue.Callback(callback, m_startTimeUs, period, offset));
085  }
086}