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}