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