2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2025-11-07 19:55:43 -05:00
|
|
|
package org.wpilib.system;
|
2018-06-24 02:29:21 -05:00
|
|
|
|
2025-11-07 19:55:43 -05:00
|
|
|
import static org.wpilib.units.Units.Seconds;
|
2025-10-29 03:18:55 +00:00
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
import java.io.Closeable;
|
|
|
|
|
import java.util.PriorityQueue;
|
|
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
2025-11-07 19:57:21 -05:00
|
|
|
import org.wpilib.driverstation.DriverStation;
|
2025-11-29 11:00:18 -08:00
|
|
|
import org.wpilib.hardware.hal.HALUtil;
|
2025-11-07 19:57:21 -05:00
|
|
|
import org.wpilib.hardware.hal.NotifierJNI;
|
|
|
|
|
import org.wpilib.units.measure.Time;
|
2025-11-29 11:00:18 -08:00
|
|
|
import org.wpilib.util.WPIUtilJNI;
|
2018-06-24 02:29:21 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A class that's a wrapper around a watchdog timer.
|
|
|
|
|
*
|
|
|
|
|
* <p>When the timer expires, a message is printed to the console and an optional user-provided
|
|
|
|
|
* callback is invoked.
|
|
|
|
|
*
|
|
|
|
|
* <p>The watchdog is initialized disabled, so the user needs to call enable() before use.
|
|
|
|
|
*/
|
2018-12-01 00:05:33 -08:00
|
|
|
public class Watchdog implements Closeable, Comparable<Watchdog> {
|
2018-12-07 19:39:02 -08:00
|
|
|
// Used for timeout print rate-limiting
|
2025-02-10 07:23:04 -08:00
|
|
|
private static final long kMinPrintPeriod = (long) 1e6; // μs
|
2018-12-07 19:39:02 -08:00
|
|
|
|
2025-02-10 07:23:04 -08:00
|
|
|
private double m_startTime;
|
|
|
|
|
private double m_timeout;
|
|
|
|
|
private double m_expirationTime;
|
2018-12-01 00:05:33 -08:00
|
|
|
private final Runnable m_callback;
|
2025-02-10 07:23:04 -08:00
|
|
|
private double m_lastTimeoutPrint; // s
|
2018-06-24 02:29:21 -05:00
|
|
|
|
|
|
|
|
boolean m_isExpired;
|
|
|
|
|
|
2018-12-29 16:22:54 -08:00
|
|
|
boolean m_suppressTimeoutMessage;
|
|
|
|
|
|
2020-04-02 06:09:40 +03:00
|
|
|
private final Tracer m_tracer;
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
private static final PriorityQueue<Watchdog> m_watchdogs = new PriorityQueue<>();
|
|
|
|
|
private static ReentrantLock m_queueMutex = new ReentrantLock();
|
|
|
|
|
private static int m_notifier;
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
static {
|
2025-11-29 11:00:18 -08:00
|
|
|
m_notifier = NotifierJNI.createNotifier();
|
2020-07-21 22:58:16 -07:00
|
|
|
NotifierJNI.setNotifierName(m_notifier, "Watchdog");
|
2019-08-01 01:19:48 -04:00
|
|
|
startDaemonThread(Watchdog::schedulerFunc);
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Watchdog constructor.
|
|
|
|
|
*
|
2025-02-10 07:23:04 -08:00
|
|
|
* @param timeout The watchdog's timeout in seconds with microsecond resolution.
|
2018-06-24 02:29:21 -05:00
|
|
|
* @param callback This function is called when the timeout expires.
|
|
|
|
|
*/
|
2025-02-10 07:23:04 -08:00
|
|
|
public Watchdog(double timeout, Runnable callback) {
|
|
|
|
|
m_timeout = timeout;
|
2018-06-24 02:29:21 -05:00
|
|
|
m_callback = callback;
|
2020-04-02 06:09:40 +03:00
|
|
|
m_tracer = new Tracer();
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
|
2025-10-29 03:18:55 +00:00
|
|
|
/**
|
|
|
|
|
* Watchdog constructor.
|
|
|
|
|
*
|
|
|
|
|
* @param timeout The watchdog's timeout with microsecond resolution.
|
|
|
|
|
* @param callback This function is called when the timeout expires.
|
|
|
|
|
*/
|
|
|
|
|
public Watchdog(Time timeout, Runnable callback) {
|
|
|
|
|
this(timeout.in(Seconds), callback);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
@Override
|
|
|
|
|
public void close() {
|
|
|
|
|
disable();
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-27 13:25:56 -07:00
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object obj) {
|
2024-06-05 00:09:10 -04:00
|
|
|
return obj instanceof Watchdog watchdog
|
2025-02-10 07:23:04 -08:00
|
|
|
&& Double.compare(m_expirationTime, watchdog.m_expirationTime) == 0;
|
2020-09-27 13:25:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int hashCode() {
|
2025-02-10 07:23:04 -08:00
|
|
|
return Double.hashCode(m_expirationTime);
|
2020-09-27 13:25:56 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-24 16:04:02 -07:00
|
|
|
@Override
|
|
|
|
|
public int compareTo(Watchdog rhs) {
|
|
|
|
|
// Elements with sooner expiration times are sorted as lesser. The head of
|
|
|
|
|
// Java's PriorityQueue is the least element.
|
2025-02-10 07:23:04 -08:00
|
|
|
return Double.compare(m_expirationTime, rhs.m_expirationTime);
|
2021-09-24 16:04:02 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 20:46:47 -07:00
|
|
|
/**
|
|
|
|
|
* Returns the time in seconds since the watchdog was last fed.
|
|
|
|
|
*
|
|
|
|
|
* @return The time in seconds since the watchdog was last fed.
|
|
|
|
|
*/
|
2018-06-24 02:29:21 -05:00
|
|
|
public double getTime() {
|
2025-02-10 07:23:04 -08:00
|
|
|
return Timer.getFPGATimestamp() - m_startTime;
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the watchdog's timeout.
|
|
|
|
|
*
|
2025-02-10 07:23:04 -08:00
|
|
|
* @param timeout The watchdog's timeout in seconds with microsecond resolution.
|
2018-12-01 00:05:33 -08:00
|
|
|
*/
|
2025-02-10 07:23:04 -08:00
|
|
|
public void setTimeout(double timeout) {
|
|
|
|
|
m_startTime = Timer.getFPGATimestamp();
|
2020-04-02 06:09:40 +03:00
|
|
|
m_tracer.clearEpochs();
|
2018-12-01 00:05:33 -08:00
|
|
|
|
|
|
|
|
m_queueMutex.lock();
|
|
|
|
|
try {
|
2025-02-10 07:23:04 -08:00
|
|
|
m_timeout = timeout;
|
2018-12-01 00:05:33 -08:00
|
|
|
m_isExpired = false;
|
|
|
|
|
|
|
|
|
|
m_watchdogs.remove(this);
|
2025-02-10 07:23:04 -08:00
|
|
|
m_expirationTime = m_startTime + m_timeout;
|
2018-12-01 00:05:33 -08:00
|
|
|
m_watchdogs.add(this);
|
2020-07-21 22:58:16 -07:00
|
|
|
updateAlarm();
|
2018-12-01 00:05:33 -08:00
|
|
|
} finally {
|
|
|
|
|
m_queueMutex.unlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-10 20:46:47 -07:00
|
|
|
/**
|
|
|
|
|
* Returns the watchdog's timeout in seconds.
|
|
|
|
|
*
|
|
|
|
|
* @return The watchdog's timeout in seconds.
|
|
|
|
|
*/
|
2018-12-01 00:05:33 -08:00
|
|
|
public double getTimeout() {
|
|
|
|
|
m_queueMutex.lock();
|
|
|
|
|
try {
|
2025-02-10 07:23:04 -08:00
|
|
|
return m_timeout;
|
2018-12-01 00:05:33 -08:00
|
|
|
} finally {
|
|
|
|
|
m_queueMutex.unlock();
|
|
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 20:46:47 -07:00
|
|
|
/**
|
|
|
|
|
* Returns true if the watchdog timer has expired.
|
|
|
|
|
*
|
|
|
|
|
* @return True if the watchdog timer has expired.
|
|
|
|
|
*/
|
2018-06-24 02:29:21 -05:00
|
|
|
public boolean isExpired() {
|
2018-12-01 00:05:33 -08:00
|
|
|
m_queueMutex.lock();
|
|
|
|
|
try {
|
|
|
|
|
return m_isExpired;
|
|
|
|
|
} finally {
|
|
|
|
|
m_queueMutex.unlock();
|
|
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds time since last epoch to the list printed by printEpochs().
|
|
|
|
|
*
|
2020-04-02 06:09:40 +03:00
|
|
|
* @see Tracer#addEpoch(String)
|
2018-06-24 02:29:21 -05:00
|
|
|
* @param epochName The name to associate with the epoch.
|
|
|
|
|
*/
|
|
|
|
|
public void addEpoch(String epochName) {
|
2020-04-02 06:09:40 +03:00
|
|
|
m_tracer.addEpoch(epochName);
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Prints list of epochs added so far and their times.
|
2020-12-29 22:45:16 -08:00
|
|
|
*
|
2020-04-02 06:09:40 +03:00
|
|
|
* @see Tracer#printEpochs()
|
2018-06-24 02:29:21 -05:00
|
|
|
*/
|
|
|
|
|
public void printEpochs() {
|
2020-04-02 06:09:40 +03:00
|
|
|
m_tracer.printEpochs();
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resets the watchdog timer.
|
|
|
|
|
*
|
|
|
|
|
* <p>This also enables the timer if it was previously disabled.
|
|
|
|
|
*/
|
|
|
|
|
public void reset() {
|
|
|
|
|
enable();
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Enables the watchdog timer. */
|
2018-06-24 02:29:21 -05:00
|
|
|
public void enable() {
|
2025-02-10 07:23:04 -08:00
|
|
|
m_startTime = Timer.getFPGATimestamp();
|
2020-04-02 06:09:40 +03:00
|
|
|
m_tracer.clearEpochs();
|
2018-12-01 00:05:33 -08:00
|
|
|
|
|
|
|
|
m_queueMutex.lock();
|
|
|
|
|
try {
|
|
|
|
|
m_isExpired = false;
|
|
|
|
|
|
|
|
|
|
m_watchdogs.remove(this);
|
2025-02-10 07:23:04 -08:00
|
|
|
m_expirationTime = m_startTime + m_timeout;
|
2018-12-01 00:05:33 -08:00
|
|
|
m_watchdogs.add(this);
|
2020-07-21 22:58:16 -07:00
|
|
|
updateAlarm();
|
2018-12-01 00:05:33 -08:00
|
|
|
} finally {
|
|
|
|
|
m_queueMutex.unlock();
|
|
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
/** Disables the watchdog timer. */
|
2018-06-24 02:29:21 -05:00
|
|
|
public void disable() {
|
2018-12-01 00:05:33 -08:00
|
|
|
m_queueMutex.lock();
|
|
|
|
|
try {
|
|
|
|
|
m_watchdogs.remove(this);
|
2020-07-21 22:58:16 -07:00
|
|
|
updateAlarm();
|
2018-12-01 00:05:33 -08:00
|
|
|
} finally {
|
|
|
|
|
m_queueMutex.unlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-29 16:22:54 -08:00
|
|
|
/**
|
|
|
|
|
* Enable or disable suppression of the generic timeout message.
|
|
|
|
|
*
|
|
|
|
|
* <p>This may be desirable if the user-provided callback already prints a more specific message.
|
|
|
|
|
*
|
|
|
|
|
* @param suppress Whether to suppress generic timeout message.
|
|
|
|
|
*/
|
|
|
|
|
public void suppressTimeoutMessage(boolean suppress) {
|
|
|
|
|
m_suppressTimeoutMessage = suppress;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-12 08:46:21 -08:00
|
|
|
@SuppressWarnings("resource")
|
2020-07-21 22:58:16 -07:00
|
|
|
private static void updateAlarm() {
|
2023-12-03 16:21:32 -08:00
|
|
|
if (m_watchdogs.isEmpty()) {
|
2020-07-21 22:58:16 -07:00
|
|
|
NotifierJNI.cancelNotifierAlarm(m_notifier);
|
|
|
|
|
} else {
|
2025-11-29 11:00:18 -08:00
|
|
|
NotifierJNI.setNotifierAlarm(
|
|
|
|
|
m_notifier, (long) (m_watchdogs.peek().m_expirationTime * 1e6), 0, true);
|
2020-07-21 22:58:16 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
private static Thread startDaemonThread(Runnable target) {
|
|
|
|
|
Thread inst = new Thread(target);
|
|
|
|
|
inst.setDaemon(true);
|
|
|
|
|
inst.start();
|
|
|
|
|
return inst;
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:05:33 -08:00
|
|
|
private static void schedulerFunc() {
|
2020-07-21 22:58:16 -07:00
|
|
|
while (!Thread.currentThread().isInterrupted()) {
|
2025-11-29 11:00:18 -08:00
|
|
|
try {
|
|
|
|
|
WPIUtilJNI.waitForObject(m_notifier);
|
|
|
|
|
} catch (InterruptedException ex) {
|
|
|
|
|
Thread.currentThread().interrupt();
|
2020-07-21 22:58:16 -07:00
|
|
|
break;
|
|
|
|
|
}
|
2025-11-29 11:00:18 -08:00
|
|
|
long curTime = HALUtil.getFPGATime();
|
2018-12-01 00:05:33 -08:00
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
m_queueMutex.lock();
|
|
|
|
|
try {
|
2023-12-03 16:21:32 -08:00
|
|
|
if (m_watchdogs.isEmpty()) {
|
2020-07-21 22:58:16 -07:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the condition variable timed out, that means a Watchdog timeout
|
|
|
|
|
// has occurred, so call its timeout function.
|
|
|
|
|
Watchdog watchdog = m_watchdogs.poll();
|
|
|
|
|
|
|
|
|
|
double now = curTime * 1e-6;
|
2025-02-10 07:23:04 -08:00
|
|
|
if (now - watchdog.m_lastTimeoutPrint > kMinPrintPeriod) {
|
|
|
|
|
watchdog.m_lastTimeoutPrint = now;
|
2020-07-21 22:58:16 -07:00
|
|
|
if (!watchdog.m_suppressTimeoutMessage) {
|
|
|
|
|
DriverStation.reportWarning(
|
2025-02-10 07:23:04 -08:00
|
|
|
String.format("Watchdog not fed within %.6fs\n", watchdog.m_timeout), false);
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
// Set expiration flag before calling the callback so any
|
|
|
|
|
// manipulation of the flag in the callback (e.g., calling
|
|
|
|
|
// Disable()) isn't clobbered.
|
|
|
|
|
watchdog.m_isExpired = true;
|
2018-12-01 00:05:33 -08:00
|
|
|
|
2020-07-21 22:58:16 -07:00
|
|
|
m_queueMutex.unlock();
|
|
|
|
|
watchdog.m_callback.run();
|
|
|
|
|
m_queueMutex.lock();
|
|
|
|
|
|
|
|
|
|
updateAlarm();
|
2025-11-29 11:00:18 -08:00
|
|
|
|
|
|
|
|
NotifierJNI.acknowledgeNotifierAlarm(m_notifier);
|
2020-07-21 22:58:16 -07:00
|
|
|
} finally {
|
|
|
|
|
m_queueMutex.unlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-01 00:05:33 -08:00
|
|
|
}
|
2018-06-24 02:29:21 -05:00
|
|
|
}
|