Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 14 additions & 17 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
},
],
"java.test.defaultConfig": "WPIlibUnitTests",
"svelte.enable-ts-plugin": true,
"files.exclude": {
"**/.git": true,
"**/.svn": true,
Expand All @@ -63,26 +62,29 @@
"**/.factorypath": true,
"**/*~": true
},
"wpilib.autoStartRioLog": false,
"cSpell.words": [
"ADIS",
"Accl",
"Accum",
"ADIS",
"Autoset",
"Backports",
"Bezier",
"Botpose",
"Brushless",
"CANcoder",
"CANdi",
"CANrange",
"CTRE",
"Canandcolor",
"Canandgyro",
"Canandmag",
"Canbus",
"CANcoder",
"CANdi",
"Checkstyle",
"Choreo",
"ChoreoLib",
"Cnfg",
"CTRE",
"DTheta",
"Deadband",
"Deadbands",
"Deadzone",
Expand All @@ -99,39 +101,39 @@
"Discretize",
"Discretizing",
"Doppel",
"DTheta",
"Dunkin",
"EEPROM",
"Expdelta",
"Falsi",
"Feedforward",
"Fullscreen",
"GSON",
"Gradlew",
"Grav",
"GRRDashboard",
"GSON",
"Holonomic",
"Intaking",
"Integ",
"Interpolatable",
"Itor",
"JoystickProfiles",
"Kalman",
"Keepalive",
"Lerp",
"Motorcontrol",
"Msgpack",
"Mult",
"Multiturn",
"NetworkTables",
"NTURI",
"NetworkTables",
"Odometry",
"Overcurrent",
"PAPF",
"PIDF",
"PhotonVision",
"Powerup",
"Protobuf",
"Pubuid",
"Quasistatic",
"REVPH",
"REVRobotics",
"Ratelimit",
"Ratelimited",
"Ratelimiter",
Expand All @@ -140,9 +142,8 @@
"ReduxLib",
"Reefscape",
"Regula",
"Repulsor",
"RevLib",
"REVPH",
"REVRobotics",
"RoboRIO",
"Sendables",
"Setpoint",
Expand All @@ -154,7 +155,6 @@
"Subuid",
"TalonFX",
"TalonSRX",
"Tauri",
"Teleop",
"Timesync",
"Topicsonly",
Expand All @@ -166,14 +166,11 @@
"Unsubscriber",
"Unsubscribers",
"Unused",
"URCL",
"Vbat",
"Vmax",
"WPIBlue",
"WPILib",
"WPILibJ",
"WPIMath",
"WPIRed",
"Writables"
]
}
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ Node.js is required to support linting via [Spotless](https://github.com/diffplu

```diff
diff --git a/build.gradle b/build.gradle
index 155f017..7670ad8 100644
index 834d562..a2837c8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,6 @@
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2025.3.2"
- id "com.diffplug.spotless" version "7.0.2"
- id "com.diffplug.spotless" version "7.0.3"
}

java {
@@ -83,30 +82,6 @@ dependencies {
implementation 'com.google.code.gson:gson:2.11.0'
@@ -91,30 +90,6 @@ dependencies {
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

-// Code formatting via spotless
Expand Down Expand Up @@ -81,7 +81,7 @@ index 155f017..7670ad8 100644
test {
useJUnitPlatform()
systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true'
@@ -134,5 +109,4 @@ wpi.java.configureTestTasks(test)
@@ -142,5 +117,4 @@ wpi.java.configureTestTasks(test)
// Configure string concat to always inline compile
tasks.withType(JavaCompile) {
options.compilerArgs.add '-XDstringConcat=inline'
Expand Down
12 changes: 11 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,17 @@ deploy {
// getTargetTypeClass is a shortcut to get the class type using a string

frcJava(getArtifactTypeClass('FRCJavaArtifact')) {
// Uncomment to enable VisualVM connections
jvmArgs.add("-XX:+UnlockExperimentalVMOptions")
jvmArgs.add("-XX:GCTimeRatio=5")
jvmArgs.add("-XX:+UseSerialGC")
jvmArgs.add("-XX:MaxGCPauseMillis=50")

// Only enable these arguments when using a RIO 2
jvmArgs.add("-Xmx100M")
jvmArgs.add("-Xms100M")
jvmArgs.add("-XX:+AlwaysPreTouch")

// Enable VisualVM connection
// jvmArgs.add("-Dcom.sun.management.jmxremote=true")
// jvmArgs.add("-Dcom.sun.management.jmxremote.port=1198")
// jvmArgs.add("-Dcom.sun.management.jmxremote.local.only=false")
Expand Down
88 changes: 88 additions & 0 deletions src/main/java/org/team340/lib/logging/EpilogueProxy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.team340.lib.logging;

import edu.wpi.first.epilogue.Epilogue;
import edu.wpi.first.epilogue.EpilogueConfiguration;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.EpilogueBackend;
import edu.wpi.first.wpilibj.DriverStation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.function.Consumer;
import org.team340.lib.util.Mutable;

//
// Do not fret if your IDE reports errors in this file!
//
// Epilogue is a generated class and may not have been seen yet by your
// editor, but your code will still build and function as normal.
//

/**
* Utility for interfacing with the generated {@code Epilogue}
* class. Keeps the occasional false negatives from the language
* server in one file.
*/
public final class EpilogueProxy {

private EpilogueProxy() {
throw new UnsupportedOperationException("This is a utility class!");
}

/**
* Configures Epilogue.
* @param configurator A consumer that mutates the provided {@link EpilogueConfiguration}.
*/
public static void configure(Consumer<EpilogueConfiguration> configurator) {
Epilogue.configure(configurator);
}

/**
* Gets the current epilogue configuration.
*/
public static EpilogueConfiguration getConfig() {
return Epilogue.getConfig();
}

/**
* Gets the configured root backend.
*/
public static EpilogueBackend getRootBackend() {
return getConfig().backend.getNested(getConfig().root);
}

/**
* Generates a logging function for the provided object via reflection. This
* method should not be invoked periodically due to performance implications.
* @param obj The object to generate the logger for.
*/
@SuppressWarnings("unchecked")
public static Consumer<EpilogueBackend> getLogger(Object obj) {
Field[] fields = Epilogue.class.getDeclaredFields();
Mutable<ClassSpecificLogger<Object>> objLogger = new Mutable<>(null);

for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
try {
Object value = field.get(obj);
if (value instanceof ClassSpecificLogger logger) {
Class<?> type = logger.getLoggedType();
if (type.equals(obj.getClass())) {
objLogger.value = logger;
break;
}
}
} catch (Exception e) {}
}
}

if (objLogger.value != null) {
return backend -> objLogger.value.tryUpdate(backend, obj, Epilogue.getConfig().errorHandler);
} else {
DriverStation.reportWarning(
"[EpilogueProxy] Unable to find logger for class \"" + obj.getClass().getSimpleName() + "\"",
true
);
return backend -> {};
}
}
}
124 changes: 124 additions & 0 deletions src/main/java/org/team340/lib/logging/LoggedRobot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package org.team340.lib.logging;

import com.ctre.phoenix6.SignalLogger;
import edu.wpi.first.epilogue.logging.EpilogueBackend;
import edu.wpi.first.hal.DriverStationJNI;
import edu.wpi.first.hal.NotifierJNI;
import edu.wpi.first.wpilibj.DataLogManager;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.IterativeRobotBase;
import edu.wpi.first.wpilibj.RobotController;
import java.util.function.Consumer;
import org.team340.lib.util.DisableWatchdog;
import org.team340.lib.util.Tunable;

/**
* LoggedRobot implements the IterativeRobotBase robot program framework,
* and is intended to be subclassed by a user creating a robot program.
*
* <p>The LoggedRobot class configures the program for logging data to NetworkTables
* via Epilogue, which is captured by the {@link DataLogManager} alongside Driver
* Station values and saved to file. Loop timings are also recorded using the
* {@link Profiler} class.
*
* <p>{@code periodic()} functions from the base class are called on an interval
* by a Notifier instance. Additionally, utility classes with periodic methods
* such as {@link Tunable} are updated on the same interval.
*/
public class LoggedRobot extends IterativeRobotBase {

public static final double DEFAULT_PERIOD = 0.02;

private final int notifier = NotifierJNI.initializeNotifier();
private final Consumer<EpilogueBackend> logger;

private long nextCycle = 0L;

/**
* Constructor for LoggedRobot.
*/
protected LoggedRobot() {
this(DEFAULT_PERIOD);
}

/**
* Constructor for LoggedRobot.
* @param period Period in seconds.
*/
protected LoggedRobot(double period) {
super(period);
NotifierJNI.setNotifierName(notifier, "LoggedRobot");

DriverStation.silenceJoystickConnectionWarning(true);
DisableWatchdog.in(this, "m_watchdog");

DataLogManager.start();
DriverStation.startDataLog(DataLogManager.getLog());
SignalLogger.enableAutoLogging(false);

EpilogueProxy.getConfig().root = "/Telemetry";
logger = EpilogueProxy.getLogger(this);
}

@Override
public void startCompetition() {
robotInit();
if (isSimulation()) {
simulationInit();
}

// Tell the DS that the robot is ready to be enabled
System.out.println("********** Robot program startup complete **********");
DriverStationJNI.observeUserProgramStarting();

// Loop forever, calling the appropriate mode-dependent function
while (true) {
long now = RobotController.getFPGATime();
if (nextCycle < now) {
nextCycle = now;
} else {
NotifierJNI.updateNotifierAlarm(notifier, nextCycle);
if (NotifierJNI.waitForNotifierAlarm(notifier) == 0L) {
break;
}
}

nextCycle += (long) (getPeriod() * 1000000.0);

Profiler.start("robot");
Profiler.run("loopFunction", () -> loopFunc());
Profiler.run("epilogue", () -> logger.accept(EpilogueProxy.getRootBackend()));
Profiler.run("tunables", Tunable::update);
Profiler.end();
}
}

@Override
public void endCompetition() {
NotifierJNI.stopNotifier(notifier);
}

@Override
public void close() {
NotifierJNI.stopNotifier(notifier);
NotifierJNI.cleanNotifier(notifier);
}

@Override
public void robotPeriodic() {}

@Override
public void simulationPeriodic() {}

@Override
public void disabledPeriodic() {}

@Override
public void autonomousPeriodic() {}

@Override
public void teleopPeriodic() {}

@Override
public void testPeriodic() {}
}
Loading