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}