generated from wpilibsuite/vendor-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP - try to port monitors but the packages can't see each other yet so it's all broken * Fix MonitoredSubsystem but its still broken * Try to add AdvantageKit, now none of my imports work instead of only some of them * Turns out excluding wpilibj made us unable to import wpilibj, now just need to fix other import issues * Add builder pattern and fix tests for Monitor * Fix WPI imports and MAYBE fix advantagekit, must test out of codespace to confirm * GREAT SCOTT GLORY HALLELUJAH IT BUILDS * Remove to-do comment that had already been done * Simplify boolean logic in Monitor and improve code comments * Update normal comments to be doc comments for fields of Monitor * Remove gradleRIO and manually add wpilib dependencies instead * Add ability to enable and disable logging for MonitoredSubsystem and for each individual Monitor * Make MonitoredSubsystem an abstract class to force users to override monitoredPeriodic * Add tests for monitor fault callbacks * Fix wpilib_interface's build.gradle so that imports work again --------- Co-authored-by: aidnem <>
- Loading branch information
Showing
7 changed files
with
606 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* This file was generated by the Gradle 'init' task. | ||
* | ||
* This generated file contains a sample Java library project to get you started. | ||
* For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.7/userguide/building_java_projects.html in the Gradle documentation. | ||
*/ | ||
|
||
plugins { | ||
// Apply the java-library plugin for API and implementation separation. | ||
id 'java-library' | ||
id "com.diffplug.spotless" version "6.24.0" | ||
|
||
} | ||
|
||
repositories { | ||
// Use Maven Central for resolving dependencies. | ||
mavenCentral() | ||
} | ||
|
||
spotless { | ||
// optional: limit format enforcement to just the files changed by this feature branch | ||
ratchetFrom 'origin/main' | ||
|
||
format 'misc', { | ||
// define the files to apply `misc` to | ||
target '*.gradle', '.gitattributes', '.gitignore' | ||
|
||
// define the steps to apply to those files | ||
trimTrailingWhitespace() | ||
indentWithTabs() // or spaces. Takes an integer argument if you don't like 4 | ||
endWithNewline() | ||
} | ||
java { | ||
// don't need to set target, it is inferred from java | ||
// Allow ignoring certain parts in formatting. | ||
toggleOffOn() | ||
// apply a specific flavor of google-java-format | ||
googleJavaFormat('1.19.2').aosp().reflowLongStrings() | ||
// fix formatting of type annotations | ||
formatAnnotations() | ||
} | ||
} | ||
|
||
compileJava.dependsOn 'spotlessApply' | ||
|
||
|
||
dependencies { | ||
// Use JUnit Jupiter for testing. | ||
testImplementation libs.junit.jupiter | ||
|
||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' | ||
|
||
// This dependency is exported to consumers, that is to say found on their compile classpath. | ||
api libs.commons.math3 | ||
|
||
// This dependency is used internally, and not exposed to consumers on their own compile classpath. | ||
implementation libs.guava | ||
} | ||
|
||
// Apply a specific Java toolchain to ease working on different environments. | ||
java { | ||
toolchain { | ||
languageVersion = JavaLanguageVersion.of(17) | ||
} | ||
} | ||
|
||
tasks.named('test') { | ||
// Use JUnit Platform for unit tests. | ||
useJUnitPlatform() | ||
} | ||
|
||
compileJava.dependsOn 'spotlessApply' |
290 changes: 290 additions & 0 deletions
290
monitors/src/main/java/coppercore/monitors/Monitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
package coppercore.monitors; | ||
|
||
import java.util.function.BooleanSupplier; | ||
|
||
public class Monitor { | ||
/** Name to log the status of the monitor under, used by MonitoredSubsystem */ | ||
String name; | ||
|
||
/** Should the monitor still report a fault after conditions return to normal? */ | ||
boolean sticky; | ||
|
||
/** How long the value can be unacceptable before a fault occurs */ | ||
double timeToFault; | ||
|
||
/** Supplier with which to check whether the value is acceptable */ | ||
BooleanSupplier isStateValid; | ||
|
||
/** Function to call when the fault happens */ | ||
Runnable faultCallback; | ||
|
||
/** | ||
* Should the monitor be logged by the MonitoredSubsystem? | ||
* | ||
* <p>Changing this value doesn't change the behavior of the monitor, it exists only to tell | ||
* MonitoredSubsystem whether or not to log the monitor. | ||
*/ | ||
boolean loggingEnabled; | ||
|
||
/** | ||
* Timestamp when the monitor was first triggered, or -1.0 if the monitor has been triggered for | ||
* less than 1 loop. | ||
*/ | ||
double triggeredTime = -1.0; | ||
|
||
// If triggeredTime's value is less than or equal to zero, the monitor has been triggered for | ||
// less than 1 | ||
// tick | ||
|
||
/** Whether the value is currently unnacceptable */ | ||
boolean triggered = false; // Is the value currently unnacceptable? | ||
|
||
/** | ||
* Whether the monitor has detected a fault | ||
* | ||
* <p>This means either the monitor has been triggered for the 'time to fault', or the monitor | ||
* is sticky and has faulted without being reset. | ||
*/ | ||
boolean faulted = false; // Has the monitor detected a fault? | ||
|
||
/** | ||
* Creates a fault Monitor. This constructor takes all parameters at once. There is also a | ||
* builder pattern supplied under MonitorBuilder. Using the builder is recommended because it | ||
* makes code much more readable, but is not required. | ||
* | ||
* @param name the name of the monitor, which will be used by MonitoredSubsystem for logging. | ||
* @param sticky whether the fault should remain faulted after conditions return to an | ||
* acceptable state. | ||
* @param isStateValid supplier for whether the state is CURRENTLY valid. This doesn't need to | ||
* handle persistence, the monitor class will handle this automatically. | ||
* @param timeToFault the time, in seconds, that isStateValid must return false before the a | ||
* fault is triggered. | ||
* @param faultCallback a function called on every periodic loop while the monitor is in a | ||
* faulted state. | ||
* @param loggingEnabled whether or not the monitor should be logged. This value is only used by | ||
* MonitoredSubsystem to enable or disable logging for each monitor. | ||
* @see MonitorBuilder | ||
*/ | ||
public Monitor( | ||
String name, | ||
boolean sticky, | ||
BooleanSupplier isStateValid, | ||
double timeToFault, | ||
Runnable faultCallback, | ||
boolean loggingEnabled) { | ||
this.name = name; | ||
this.sticky = sticky; | ||
this.timeToFault = timeToFault; | ||
this.isStateValid = isStateValid; | ||
|
||
this.faultCallback = faultCallback; | ||
|
||
this.loggingEnabled = loggingEnabled; | ||
} | ||
|
||
/** | ||
* This is the "main loop" of the Monitor. This should be called each in each periodic loop. | ||
* | ||
* @param currentTimeSeconds the current timestamp in seconds. This doesn't need to have a | ||
* specific frame of reference, it is used to detect when conditions have been unnacceptable | ||
* for enough time to fault. | ||
*/ | ||
public void periodic(double currentTimeSeconds) { | ||
// currentTimeSeconds doesn't need to be from a specific point in history | ||
// As long as the reference point is always the same, it could be from | ||
// the robot being turned on, initialized, etc. | ||
|
||
triggered = !isStateValid.getAsBoolean(); | ||
if (triggered) { | ||
// If triggered time is less than zero, this means it hasn't been set yet. | ||
// Therefore, this is the first loop that the monitor is triggered and we should | ||
// store the current timestamp to reference how long it's been triggered for later. | ||
if (triggeredTime <= 0.0) { | ||
triggeredTime = currentTimeSeconds; | ||
} | ||
|
||
// When triggered, the monitor will fault if either: | ||
// - It is already faulted (it can't transition from faulted to non-faulted while | ||
// triggered) | ||
// or | ||
// - It has been triggered for the timeToFault. | ||
faulted = faulted || ((currentTimeSeconds - triggeredTime) >= timeToFault); | ||
} else { | ||
if (!sticky) { | ||
faulted = false; | ||
} | ||
// If the monitor isn't triggered, it will only be faulted if it is sticky and already | ||
// faulted. | ||
faulted = faulted && sticky; | ||
|
||
// Use -1 as a sentinel value to indicate that the triggered time hasn't been stored | ||
// yet. | ||
triggeredTime = -1.0; | ||
} | ||
if (faulted) { | ||
faultCallback.run(); | ||
} | ||
} | ||
|
||
/** | ||
* Returns a boolean describing whether or not the monitor is faulted. A monitor is considered | ||
* faulted when it has been triggered for a time greater than or equal to its time to fault. A | ||
* monitor is also considered faulted if it is sticky and has ever been faulted. | ||
* | ||
* @return whether or not the monitor is currently faulted. | ||
*/ | ||
public boolean isFaulted() { | ||
return faulted; | ||
} | ||
|
||
/** | ||
* Returns a boolean describing whether or not the monitor is currently triggered. A monitor is | ||
* triggered when isStateValid returns false. | ||
* | ||
* @return whether or not the monitor is currently triggered | ||
*/ | ||
public boolean isTriggered() { | ||
return triggered; | ||
} | ||
|
||
/** | ||
* Reset a sticky fault. This means that, if a Monitor is sticky, and is currently faulted, | ||
* calling this function will return it to a non-faulted state. | ||
*/ | ||
public void resetStickyFault() { | ||
faulted = false; | ||
} | ||
|
||
/** | ||
* Get the name of the Monitor. | ||
* | ||
* @return a string, the name of the monitor that it uses for logging. | ||
*/ | ||
public String getName() { | ||
return name; | ||
} | ||
|
||
/** | ||
* Set whether or not the monitor should be logged. | ||
* | ||
* <p>Changing this value doesn't change the behavior of the monitor, it exists only to tell | ||
* MonitoredSubsystem whether or not to log the monitor. | ||
*/ | ||
public void setLoggingEnabled(boolean loggingEnabled) { | ||
this.loggingEnabled = loggingEnabled; | ||
} | ||
|
||
/** | ||
* Get whether or not the monitor should be logged. | ||
* | ||
* <p>Changing this value doesn't change the behavior of the monitor, it exists only to tell | ||
* MonitoredSubsystem whether or not to log the monitor. | ||
*/ | ||
public boolean getLoggingEnabled() { | ||
return loggingEnabled; | ||
} | ||
|
||
/** | ||
* This class is meant to build a fault monitor. Create a builder, then call withName, | ||
* withStickyness, withTimeToFault, and withIsStateValid, and withFaultCallback to configure its | ||
* fields. Once every field is configured, call build() to return a shiny new fault monitor. | ||
*/ | ||
public static class MonitorBuilder { | ||
String name; // Name to log the status of the monitor under | ||
boolean sticky; // Should the monitor still report a fault after conditions return to | ||
// normal? | ||
double timeToFault; // How long the value can be unacceptable before a fault occurs | ||
BooleanSupplier | ||
isStateValid; // Supplier with which to check whether the value is acceptable | ||
Runnable faultCallback; // Function to call when the fault happens | ||
boolean loggingEnabled = true; // Whether or not to log the monitor. Defaults to true. | ||
|
||
/** | ||
* Sets the name of the monitor. This name will be used when the monitor is logged by | ||
* MonitoredSubsystem. | ||
* | ||
* @param name the name of the monitor, which is used for logging by MonitoredSubsystem or | ||
* can be used for manual logging outside of a MonitoredSubsystem. | ||
* @return the monitor builder, so that successive builder calls can be chained | ||
*/ | ||
public MonitorBuilder withName(String name) { | ||
this.name = name; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets whether or not the monitor is sticky or not, and returns itself. | ||
* | ||
* @param sticky a boolean, whether or not the monitor should remain faulted after | ||
* conditions return to normal. | ||
* @return the monitor builder, so that successive builder calls can be chained. | ||
*/ | ||
public MonitorBuilder withStickyness(boolean sticky) { | ||
this.sticky = sticky; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets how long the monitor may be triggered before it faults, and returns itself. | ||
* | ||
* @param timeToFault a double, how long the monitor can be triggered (in an unnacceptable | ||
* state) before it becomes faulted in seconds. | ||
* @return the monitor builder, so that successive builder calls can be chained. | ||
*/ | ||
public MonitorBuilder withTimeToFault(double timeToFault) { | ||
this.timeToFault = timeToFault; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the supplier for whether or not the state is currently valid. | ||
* | ||
* @param isStateValid a boolean supplier, which should return true when the state is valid | ||
* and false when the state is invalid. This supplier doesn't need to account for | ||
* timeToFault, this is automatically handled by the monitor. | ||
* @return the monitor, so that successive builder calls can be chained. | ||
*/ | ||
public MonitorBuilder withIsStateValidSupplier(BooleanSupplier isStateValid) { | ||
this.isStateValid = isStateValid; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets how long the monitor may be triggered before it faults, and returns itself. | ||
* | ||
* @param faultCallback a runnable, which will be called periodic unnacceptable state) | ||
* before it becomes faulted in seconds. | ||
* @return the monitor builder, so that successive builder calls can be chained. | ||
*/ | ||
public MonitorBuilder withFaultCallback(Runnable faultCallback) { | ||
this.faultCallback = faultCallback; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets whether or not the monitor should be logged. | ||
* | ||
* <p>This value is only used to tell the MonitoredSubsystem whether or not to log each | ||
* monitor. This value defaults to true unless withLoggingEnabled(false) is called on the | ||
* builder! | ||
* | ||
* @param loggingEnabled whether or not the monitor should be logged | ||
* @return the monitor builder, so that successive builder calls can be chained. | ||
*/ | ||
public MonitorBuilder withLoggingEnabled(boolean loggingEnabled) { | ||
this.loggingEnabled = loggingEnabled; | ||
return this; | ||
} | ||
|
||
/** | ||
* Instantiates a monitor and returns it. This method should be called after all of the | ||
* fields of the monitor are configured using with[Field] methods. | ||
* | ||
* @return a monitor with the fields set by the builder. | ||
*/ | ||
public Monitor build() { | ||
return new Monitor( | ||
name, sticky, isStateValid, timeToFault, faultCallback, loggingEnabled); | ||
} | ||
} | ||
} |
Oops, something went wrong.