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.system; 006 007import static org.wpilib.units.Units.Seconds; 008 009import org.wpilib.driverstation.DriverStation; 010import org.wpilib.units.measure.Time; 011 012/** 013 * A timer class. 014 * 015 * <p>Note that if the user calls SimHooks.restartTiming(), they should also reset the timer so 016 * get() won't return a negative duration. 017 */ 018public class Timer { 019 /** 020 * Return the clock time in seconds. By default, the time is based on the FPGA hardware clock in 021 * seconds since the FPGA started. However, the return value of this method may be modified to use 022 * any time base, including non-monotonic time bases. 023 * 024 * @return Robot running time in seconds. 025 */ 026 public static double getTimestamp() { 027 return RobotController.getTime() / 1000000.0; 028 } 029 030 /** 031 * Return the system clock time in seconds. Return the time from the FPGA hardware clock in 032 * seconds since the FPGA started. 033 * 034 * @return Robot running time in seconds. 035 */ 036 public static double getFPGATimestamp() { 037 return RobotController.getFPGATime() / 1000000.0; 038 } 039 040 /** 041 * Return the approximate match time. The FMS does not send an official match time to the robots, 042 * but does send an approximate match time. The value will count down the time remaining in the 043 * current period (auto or teleop). Warning: This is not an official time (so it cannot be used to 044 * dispute ref calls or guarantee that a function will trigger before the match ends). 045 * 046 * <p>When connected to the real field, this number only changes in full integer increments, and 047 * always counts down. 048 * 049 * <p>When the DS is in practice mode, this number is a floating point number, and counts down. 050 * 051 * <p>When the DS is in teleop or autonomous mode, this number returns -1.0. 052 * 053 * <p>Simulation matches DS behavior without an FMS connected. 054 * 055 * @return Time remaining in current match period (auto or teleop) in seconds 056 */ 057 public static double getMatchTime() { 058 return DriverStation.getMatchTime(); 059 } 060 061 /** 062 * Pause the execution of the thread for a specified period of time. Motors will continue to run 063 * at their last assigned values, and sensors will continue to update. Only the task containing 064 * the wait will pause until the wait time is expired. 065 * 066 * @param period Length of time to pause 067 */ 068 public static void delay(final Time period) { 069 delay(period.in(Seconds)); 070 } 071 072 /** 073 * Pause the execution of the thread for a specified period of time given in seconds. Motors will 074 * continue to run at their last assigned values, and sensors will continue to update. Only the 075 * task containing the wait will pause until the wait time is expired. 076 * 077 * @param seconds Length of time to pause 078 */ 079 public static void delay(final double seconds) { 080 try { 081 Thread.sleep((long) (seconds * 1e3)); 082 } catch (final InterruptedException ex) { 083 Thread.currentThread().interrupt(); 084 } 085 } 086 087 private double m_startTime; 088 private double m_accumulatedTime; 089 private boolean m_running; 090 091 /** Timer constructor. */ 092 public Timer() { 093 reset(); 094 } 095 096 private double getMsClock() { 097 return RobotController.getTime() / 1000.0; 098 } 099 100 /** 101 * Get the current time from the timer. If the clock is running it is derived from the current 102 * system clock the start time stored in the timer class. If the clock is not running, then return 103 * the time when it was last stopped. 104 * 105 * @return Current time value for this timer in seconds 106 */ 107 public double get() { 108 if (m_running) { 109 return m_accumulatedTime + (getMsClock() - m_startTime) / 1000.0; 110 } else { 111 return m_accumulatedTime; 112 } 113 } 114 115 /** 116 * Reset the timer by setting the time to 0. 117 * 118 * <p>Make the timer startTime the current time so new requests will be relative now. 119 */ 120 public final void reset() { 121 m_accumulatedTime = 0; 122 m_startTime = getMsClock(); 123 } 124 125 /** 126 * Start the timer running. Just set the running flag to true indicating that all time requests 127 * should be relative to the system clock. Note that this method is a no-op if the timer is 128 * already running. 129 */ 130 public void start() { 131 if (!m_running) { 132 m_startTime = getMsClock(); 133 m_running = true; 134 } 135 } 136 137 /** 138 * Restart the timer by stopping the timer, if it is not already stopped, resetting the 139 * accumulated time, then starting the timer again. If you want an event to periodically reoccur 140 * at some time interval from the start time, consider using advanceIfElapsed() instead. 141 */ 142 public void restart() { 143 if (m_running) { 144 stop(); 145 } 146 reset(); 147 start(); 148 } 149 150 /** 151 * Stop the timer. This computes the time as of now and clears the running flag, causing all 152 * subsequent time requests to be read from the accumulated time rather than looking at the system 153 * clock. 154 */ 155 public void stop() { 156 m_accumulatedTime = get(); 157 m_running = false; 158 } 159 160 /** 161 * Check if the period specified has passed. 162 * 163 * @param period The period to check. 164 * @return Whether the period has passed. 165 */ 166 public boolean hasElapsed(Time period) { 167 return hasElapsed(period.in(Seconds)); 168 } 169 170 /** 171 * Check if the period specified has passed. 172 * 173 * @param seconds The period to check in seconds. 174 * @return Whether the period has passed. 175 */ 176 public boolean hasElapsed(double seconds) { 177 return get() >= seconds; 178 } 179 180 /** 181 * Check if the period specified has passed and if it has, advance the start time by that period. 182 * This is useful to decide if it's time to do periodic work without drifting later by the time it 183 * took to get around to checking. 184 * 185 * @param seconds The period to check. 186 * @return Whether the period has passed. 187 */ 188 public boolean advanceIfElapsed(double seconds) { 189 if (get() >= seconds) { 190 // Advance the start time by the period. 191 // Don't set it to the current time... we want to avoid drift. 192 m_startTime += seconds * 1000; 193 return true; 194 } else { 195 return false; 196 } 197 } 198 199 /** 200 * Whether the timer is currently running. 201 * 202 * @return true if running. 203 */ 204 public boolean isRunning() { 205 return m_running; 206 } 207}