mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[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:
@@ -382,9 +382,12 @@ public class AnnotationProcessor extends AbstractProcessor {
|
||||
|
||||
List<String> loggerClassNames = new ArrayList<>();
|
||||
var mainRobotClasses = new ArrayList<TypeElement>();
|
||||
var timedRobotClasses = new ArrayList<TypeElement>();
|
||||
|
||||
// Used to check for a main robot class
|
||||
var robotBaseClass =
|
||||
processingEnv.getElementUtils().getTypeElement("org.wpilib.framework.RobotBase").asType();
|
||||
var timedRobotClass =
|
||||
processingEnv.getElementUtils().getTypeElement("org.wpilib.framework.TimedRobot").asType();
|
||||
|
||||
boolean validFields = validateFields(annotatedElements);
|
||||
@@ -403,6 +406,9 @@ public class AnnotationProcessor extends AbstractProcessor {
|
||||
if (processingEnv.getTypeUtils().isAssignable(clazz.getSuperclass(), robotBaseClass)) {
|
||||
mainRobotClasses.add(clazz);
|
||||
}
|
||||
if (processingEnv.getTypeUtils().isAssignable(clazz.getSuperclass(), timedRobotClass)) {
|
||||
timedRobotClasses.add(clazz);
|
||||
}
|
||||
|
||||
loggerClassNames.add(StringUtils.loggerClassName(clazz));
|
||||
} catch (IOException e) {
|
||||
@@ -417,8 +423,10 @@ public class AnnotationProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
// Sort alphabetically
|
||||
mainRobotClasses.sort(Comparator.comparing(c -> c.getSimpleName().toString()));
|
||||
m_epilogueGenerator.writeEpilogueFile(loggerClassNames, mainRobotClasses);
|
||||
loggerClassNames.sort(Comparator.naturalOrder());
|
||||
mainRobotClasses.sort(Comparator.comparing(TypeElement::toString));
|
||||
timedRobotClasses.sort(Comparator.comparing(TypeElement::toString));
|
||||
m_epilogueGenerator.writeEpilogueFile(loggerClassNames, mainRobotClasses, timedRobotClasses);
|
||||
}
|
||||
|
||||
private void warnOfNonLoggableElements(TypeElement clazz) {
|
||||
|
||||
@@ -38,12 +38,16 @@ public class EpilogueGenerator {
|
||||
*
|
||||
* @param loggerClassNames the names of the generated logger classes. Each of these will be
|
||||
* 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()}
|
||||
* method to add a callback hook to a TimedRobot to log itself.
|
||||
* @param mainRobotClasses the main robot classes. May be empty. Used to generate a {@code
|
||||
* 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
|
||||
public void writeEpilogueFile(
|
||||
List<String> loggerClassNames, Collection<TypeElement> mainRobotClasses) {
|
||||
List<String> loggerClassNames,
|
||||
Collection<TypeElement> mainRobotClasses,
|
||||
Collection<TypeElement> timedRobotClasses) {
|
||||
try {
|
||||
var centralStore =
|
||||
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();
|
||||
|
||||
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 {");
|
||||
|
||||
// Usage reporting
|
||||
@@ -136,56 +116,57 @@ public class EpilogueGenerator {
|
||||
"""
|
||||
.stripTrailing());
|
||||
|
||||
// Only generate a binding if the robot class is a TimedRobot
|
||||
if (!mainRobotClasses.isEmpty()) {
|
||||
for (TypeElement mainRobotClass : mainRobotClasses) {
|
||||
String robotClassName = mainRobotClass.getQualifiedName().toString();
|
||||
for (TypeElement mainRobotClass : mainRobotClasses) {
|
||||
String robotClassName = mainRobotClass.getQualifiedName().toString();
|
||||
|
||||
out.println();
|
||||
out.print(
|
||||
"""
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
""");
|
||||
out.println(" public static void update(" + robotClassName + " robot) {");
|
||||
out.println(" long start = System.nanoTime();");
|
||||
out.println(
|
||||
" "
|
||||
+ StringUtils.loggerFieldName(mainRobotClass)
|
||||
+ ".tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);");
|
||||
out.println(
|
||||
" config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);");
|
||||
out.println(" }");
|
||||
out.println();
|
||||
out.print(
|
||||
"""
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
""");
|
||||
out.println(" public static void update(" + robotClassName + " robot) {");
|
||||
out.println(" long start = System.nanoTime();");
|
||||
out.println(
|
||||
" "
|
||||
+ StringUtils.loggerFieldName(mainRobotClass)
|
||||
+ ".tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);");
|
||||
out.println(
|
||||
" config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);");
|
||||
out.println(" }");
|
||||
}
|
||||
|
||||
out.println();
|
||||
out.print(
|
||||
"""
|
||||
/**
|
||||
* 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
|
||||
* (for example, a 20ms update rate but 10ms offset from the main loop invocation) to
|
||||
* help avoid high CPU loads. However, this does mean that any logged data that reads
|
||||
* directly from sensors will be slightly different from data used in the main robot
|
||||
* loop.
|
||||
*/
|
||||
""");
|
||||
out.println(" public static void bind(" + robotClassName + " robot) {");
|
||||
out.println(" if (config.loggingPeriod == null) {");
|
||||
out.println(" config.loggingPeriod = Seconds.of(robot.getPeriod());");
|
||||
out.println(" }");
|
||||
out.println(" if (config.loggingPeriodOffset == null) {");
|
||||
out.println(" config.loggingPeriodOffset = config.loggingPeriod.div(2);");
|
||||
out.println(" }");
|
||||
out.println();
|
||||
out.println(" robot.addPeriodic(() -> {");
|
||||
out.println(" update(robot);");
|
||||
out.println(
|
||||
" }, config.loggingPeriod.in(Seconds), config.loggingPeriodOffset.in(Seconds));");
|
||||
out.println(" }");
|
||||
}
|
||||
for (TypeElement timedRobotClass : timedRobotClasses) {
|
||||
String robotClassName = timedRobotClass.getQualifiedName().toString();
|
||||
|
||||
out.println();
|
||||
out.print(
|
||||
"""
|
||||
/**
|
||||
* 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
|
||||
* (for example, a 20ms update rate but 10ms offset from the main loop invocation) to
|
||||
* help avoid high CPU loads. However, this does mean that any logged data that reads
|
||||
* directly from sensors will be slightly different from data used in the main robot
|
||||
* loop.
|
||||
*/
|
||||
""");
|
||||
out.println(" public static void bind(" + robotClassName + " robot) {");
|
||||
out.println(" if (config.loggingPeriod == null) {");
|
||||
out.println(" config.loggingPeriod = Seconds.of(robot.getPeriod());");
|
||||
out.println(" }");
|
||||
out.println(" if (config.loggingPeriodOffset == null) {");
|
||||
out.println(" config.loggingPeriodOffset = config.loggingPeriod.div(2);");
|
||||
out.println(" }");
|
||||
out.println();
|
||||
out.println(" robot.addPeriodic(() -> {");
|
||||
out.println(" update(robot);");
|
||||
out.println(
|
||||
" }, config.loggingPeriod.in(Seconds), config.loggingPeriodOffset.in(Seconds));");
|
||||
out.println(" }");
|
||||
}
|
||||
|
||||
out.println("}");
|
||||
|
||||
@@ -35,8 +35,6 @@ class EpilogueGeneratorTest {
|
||||
|
||||
import org.wpilib.hardware.hal.HAL;
|
||||
|
||||
import org.wpilib.epilogue.ExampleLogger;
|
||||
|
||||
public final class Epilogue {
|
||||
static {
|
||||
HAL.reportUsage("Epilogue", "");
|
||||
@@ -90,8 +88,6 @@ class EpilogueGeneratorTest {
|
||||
|
||||
import org.wpilib.hardware.hal.HAL;
|
||||
|
||||
import org.wpilib.epilogue.ExampleLogger;
|
||||
|
||||
public final class Epilogue {
|
||||
static {
|
||||
HAL.reportUsage("Epilogue", "");
|
||||
@@ -115,6 +111,17 @@ class EpilogueGeneratorTest {
|
||||
public static boolean shouldLog(Logged.Importance importance) {
|
||||
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.epilogue.ExampleLogger;
|
||||
|
||||
public final class Epilogue {
|
||||
static {
|
||||
HAL.reportUsage("Epilogue", "");
|
||||
@@ -224,9 +229,6 @@ class EpilogueGeneratorTest {
|
||||
|
||||
import org.wpilib.hardware.hal.HAL;
|
||||
|
||||
import org.wpilib.epilogue.AlphaBotLogger;
|
||||
import org.wpilib.epilogue.BetaBotLogger;
|
||||
|
||||
public final class Epilogue {
|
||||
static {
|
||||
HAL.reportUsage("Epilogue", "");
|
||||
@@ -263,6 +265,17 @@ class EpilogueGeneratorTest {
|
||||
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
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.epilogue.ExampleLogger;
|
||||
import org.wpilib.epilogue.CustomLogger;
|
||||
|
||||
public final class Epilogue {
|
||||
static {
|
||||
HAL.reportUsage("Epilogue", "");
|
||||
|
||||
Reference in New Issue
Block a user