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