[epilogue] Improve support for robot base classes in epilogue (#8886)

Generate an `update` method for any logged Robot class, not just
TimedRobot

Continue to generate a `bind` method for TimedRobot subclasses

Also removes unnecessary import statements for generated loggers, since
they're using fully-qualified names in the generated Epilogue class now.
This commit is contained in:
Sam Carlberg
2026-05-29 00:14:30 -04:00
committed by GitHub
parent 1e12408a7d
commit 4fa5dacfc4
3 changed files with 88 additions and 100 deletions

View File

@@ -382,9 +382,12 @@ public class AnnotationProcessor extends AbstractProcessor {
List<String> loggerClassNames = new ArrayList<>(); List<String> loggerClassNames = new ArrayList<>();
var mainRobotClasses = new ArrayList<TypeElement>(); var mainRobotClasses = new ArrayList<TypeElement>();
var timedRobotClasses = new ArrayList<TypeElement>();
// Used to check for a main robot class // Used to check for a main robot class
var robotBaseClass = var robotBaseClass =
processingEnv.getElementUtils().getTypeElement("org.wpilib.framework.RobotBase").asType();
var timedRobotClass =
processingEnv.getElementUtils().getTypeElement("org.wpilib.framework.TimedRobot").asType(); processingEnv.getElementUtils().getTypeElement("org.wpilib.framework.TimedRobot").asType();
boolean validFields = validateFields(annotatedElements); boolean validFields = validateFields(annotatedElements);
@@ -403,6 +406,9 @@ public class AnnotationProcessor extends AbstractProcessor {
if (processingEnv.getTypeUtils().isAssignable(clazz.getSuperclass(), robotBaseClass)) { if (processingEnv.getTypeUtils().isAssignable(clazz.getSuperclass(), robotBaseClass)) {
mainRobotClasses.add(clazz); mainRobotClasses.add(clazz);
} }
if (processingEnv.getTypeUtils().isAssignable(clazz.getSuperclass(), timedRobotClass)) {
timedRobotClasses.add(clazz);
}
loggerClassNames.add(StringUtils.loggerClassName(clazz)); loggerClassNames.add(StringUtils.loggerClassName(clazz));
} catch (IOException e) { } catch (IOException e) {
@@ -417,8 +423,10 @@ public class AnnotationProcessor extends AbstractProcessor {
} }
// Sort alphabetically // Sort alphabetically
mainRobotClasses.sort(Comparator.comparing(c -> c.getSimpleName().toString())); loggerClassNames.sort(Comparator.naturalOrder());
m_epilogueGenerator.writeEpilogueFile(loggerClassNames, mainRobotClasses); mainRobotClasses.sort(Comparator.comparing(TypeElement::toString));
timedRobotClasses.sort(Comparator.comparing(TypeElement::toString));
m_epilogueGenerator.writeEpilogueFile(loggerClassNames, mainRobotClasses, timedRobotClasses);
} }
private void warnOfNonLoggableElements(TypeElement clazz) { private void warnOfNonLoggableElements(TypeElement clazz) {

View File

@@ -38,12 +38,16 @@ public class EpilogueGenerator {
* *
* @param loggerClassNames the names of the generated logger classes. Each of these will be * @param loggerClassNames the names of the generated logger classes. Each of these will be
* instantiated in a public static field on the Epilogue class. * instantiated in a public static field on the Epilogue class.
* @param mainRobotClasses the main robot classes. May be empty. Used to generate a {@code bind()} * @param mainRobotClasses the main robot classes. May be empty. Used to generate a {@code
* method to add a callback hook to a TimedRobot to log itself. * update()} method for an easy entry point.
* @param timedRobotClasses the main robot classes that extend from TimedRobot. Used to generate a
* {@code bind()} method to add a callback hook to a TimedRobot to log itself.
*/ */
@SuppressWarnings("checkstyle:LineLength") // Source code templates exceed the line length limit @SuppressWarnings("checkstyle:LineLength") // Source code templates exceed the line length limit
public void writeEpilogueFile( public void writeEpilogueFile(
List<String> loggerClassNames, Collection<TypeElement> mainRobotClasses) { List<String> loggerClassNames,
Collection<TypeElement> mainRobotClasses,
Collection<TypeElement> timedRobotClasses) {
try { try {
var centralStore = var centralStore =
m_processingEnv.getFiler().createSourceFile("org.wpilib.epilogue.Epilogue"); m_processingEnv.getFiler().createSourceFile("org.wpilib.epilogue.Epilogue");
@@ -58,30 +62,6 @@ public class EpilogueGenerator {
out.println("import org.wpilib.hardware.hal.HAL;"); out.println("import org.wpilib.hardware.hal.HAL;");
out.println(); out.println();
loggerClassNames.stream()
.sorted()
.forEach(
name -> {
if (!name.contains(".")) {
// Logger is in the global namespace, don't need to import
return;
}
out.println("import " + name + ";");
});
m_customLoggers.values().stream()
.distinct()
.forEach(
loggerType -> {
var name = loggerType.asElement().toString();
if (!name.contains(".")) {
// Logger is in the global namespace, don't need to import
return;
}
out.println("import " + name + ";");
});
out.println();
out.println("public final class Epilogue {"); out.println("public final class Epilogue {");
// Usage reporting // Usage reporting
@@ -136,8 +116,6 @@ public class EpilogueGenerator {
""" """
.stripTrailing()); .stripTrailing());
// Only generate a binding if the robot class is a TimedRobot
if (!mainRobotClasses.isEmpty()) {
for (TypeElement mainRobotClass : mainRobotClasses) { for (TypeElement mainRobotClass : mainRobotClasses) {
String robotClassName = mainRobotClass.getQualifiedName().toString(); String robotClassName = mainRobotClass.getQualifiedName().toString();
@@ -159,6 +137,10 @@ public class EpilogueGenerator {
out.println( out.println(
" config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);"); " config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);");
out.println(" }"); out.println(" }");
}
for (TypeElement timedRobotClass : timedRobotClasses) {
String robotClassName = timedRobotClass.getQualifiedName().toString();
out.println(); out.println();
out.print( out.print(
@@ -186,7 +168,6 @@ public class EpilogueGenerator {
" }, config.loggingPeriod.in(Seconds), config.loggingPeriodOffset.in(Seconds));"); " }, config.loggingPeriod.in(Seconds), config.loggingPeriodOffset.in(Seconds));");
out.println(" }"); out.println(" }");
} }
}
out.println("}"); out.println("}");
} }

View File

@@ -35,8 +35,6 @@ class EpilogueGeneratorTest {
import org.wpilib.hardware.hal.HAL; import org.wpilib.hardware.hal.HAL;
import org.wpilib.epilogue.ExampleLogger;
public final class Epilogue { public final class Epilogue {
static { static {
HAL.reportUsage("Epilogue", ""); HAL.reportUsage("Epilogue", "");
@@ -90,8 +88,6 @@ class EpilogueGeneratorTest {
import org.wpilib.hardware.hal.HAL; import org.wpilib.hardware.hal.HAL;
import org.wpilib.epilogue.ExampleLogger;
public final class Epilogue { public final class Epilogue {
static { static {
HAL.reportUsage("Epilogue", ""); HAL.reportUsage("Epilogue", "");
@@ -115,6 +111,17 @@ class EpilogueGeneratorTest {
public static boolean shouldLog(Logged.Importance importance) { public static boolean shouldLog(Logged.Importance importance) {
return importance.compareTo(config.minimumImportance) >= 0; return importance.compareTo(config.minimumImportance) >= 0;
} }
/**
* Updates Epilogue. This must be called periodically in order for Epilogue to record
* new values. Alternatively, {@code bind()} can be used to update at an offset from
* the main robot loop.
*/
public static void update(org.wpilib.epilogue.Example robot) {
long start = System.nanoTime();
org_wpilib_epilogue_ExampleLogger.tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);
config.backend.log("Epilogue/Stats/Last Run", (System.nanoTime() - start) / 1e6);
}
} }
"""; """;
@@ -140,8 +147,6 @@ class EpilogueGeneratorTest {
import org.wpilib.hardware.hal.HAL; import org.wpilib.hardware.hal.HAL;
import org.wpilib.epilogue.ExampleLogger;
public final class Epilogue { public final class Epilogue {
static { static {
HAL.reportUsage("Epilogue", ""); HAL.reportUsage("Epilogue", "");
@@ -224,9 +229,6 @@ class EpilogueGeneratorTest {
import org.wpilib.hardware.hal.HAL; import org.wpilib.hardware.hal.HAL;
import org.wpilib.epilogue.AlphaBotLogger;
import org.wpilib.epilogue.BetaBotLogger;
public final class Epilogue { public final class Epilogue {
static { static {
HAL.reportUsage("Epilogue", ""); HAL.reportUsage("Epilogue", "");
@@ -263,6 +265,17 @@ class EpilogueGeneratorTest {
config.backend.log("Epilogue/Stats/Last Run", (System.nanoTime() - start) / 1e6); config.backend.log("Epilogue/Stats/Last Run", (System.nanoTime() - start) / 1e6);
} }
/**
* Updates Epilogue. This must be called periodically in order for Epilogue to record
* new values. Alternatively, {@code bind()} can be used to update at an offset from
* the main robot loop.
*/
public static void update(org.wpilib.epilogue.BetaBot robot) {
long start = System.nanoTime();
org_wpilib_epilogue_BetaBotLogger.tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);
config.backend.log("Epilogue/Stats/Last Run", (System.nanoTime() - start) / 1e6);
}
/** /**
* Binds Epilogue updates to a timed robot's update period. Log calls will be made at the * Binds Epilogue updates to a timed robot's update period. Log calls will be made at the
* same update rate as the robot's loop function, but will be offset by a full phase * same update rate as the robot's loop function, but will be offset by a full phase
@@ -284,17 +297,6 @@ class EpilogueGeneratorTest {
}, config.loggingPeriod.in(Seconds), config.loggingPeriodOffset.in(Seconds)); }, config.loggingPeriod.in(Seconds), config.loggingPeriodOffset.in(Seconds));
} }
/**
* Updates Epilogue. This must be called periodically in order for Epilogue to record
* new values. Alternatively, {@code bind()} can be used to update at an offset from
* the main robot loop.
*/
public static void update(org.wpilib.epilogue.BetaBot robot) {
long start = System.nanoTime();
org_wpilib_epilogue_BetaBotLogger.tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);
config.backend.log("Epilogue/Stats/Last Run", (System.nanoTime() - start) / 1e6);
}
/** /**
* Binds Epilogue updates to a timed robot's update period. Log calls will be made at the * Binds Epilogue updates to a timed robot's update period. Log calls will be made at the
* same update rate as the robot's loop function, but will be offset by a full phase * same update rate as the robot's loop function, but will be offset by a full phase
@@ -357,9 +359,6 @@ class EpilogueGeneratorTest {
import org.wpilib.hardware.hal.HAL; import org.wpilib.hardware.hal.HAL;
import org.wpilib.epilogue.ExampleLogger;
import org.wpilib.epilogue.CustomLogger;
public final class Epilogue { public final class Epilogue {
static { static {
HAL.reportUsage("Epilogue", ""); HAL.reportUsage("Epilogue", "");