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}