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