From 36ace22ce2bfe148e41e7a070ba640696caa5b82 Mon Sep 17 00:00:00 2001 From: gaming <48131223+TheGamer1002@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:47:18 -0500 Subject: [PATCH] feat(logging): create new telemetry subsystem, add NetworkAlerts interface --- .gitignore | 5 + .../robot/subsystems/TelemetrySubsystem.java | 41 ++++ src/main/java/frc/robot/util/Alert.java | 195 ++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 src/main/java/frc/robot/subsystems/TelemetrySubsystem.java create mode 100644 src/main/java/frc/robot/util/Alert.java diff --git a/.gitignore b/.gitignore index b2947a6..4edbf7c 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,8 @@ src/main/java/frc/robot/BuildConstants.java .gradle/8.5/fileHashes/fileHashes.lock .gradle/buildOutputCleanup/buildOutputCleanup.lock .gradle/ +.gradle/8.5/checksums/checksums.lock +.gradle/8.5/checksums/md5-checksums.bin +.gradle/8.5/checksums/sha1-checksums.bin +.gradle/8.5/fileHashes/fileHashes.lock +.gradle/buildOutputCleanup/buildOutputCleanup.lock diff --git a/src/main/java/frc/robot/subsystems/TelemetrySubsystem.java b/src/main/java/frc/robot/subsystems/TelemetrySubsystem.java new file mode 100644 index 0000000..7a4a712 --- /dev/null +++ b/src/main/java/frc/robot/subsystems/TelemetrySubsystem.java @@ -0,0 +1,41 @@ +package frc.robot.subsystems; + + +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +public class TelemetrySubsystem extends SubsystemBase { + + // With eager singleton initialization, any static variables/fields used in the + // constructor must appear before the "INSTANCE" variable so that they are initialized + // before the constructor is called when the "INSTANCE" variable initializes. + + /** + * The Singleton instance of this TelemetrySubsystem. Code should use + * the {@link #getInstance()} method to get the single instance (rather + * than trying to construct an instance of this class.) + */ + private final static TelemetrySubsystem INSTANCE = new TelemetrySubsystem(); + + /** + * Returns the Singleton instance of this TelemetrySubsystem. This static method + * should be used, rather than the constructor, to get the single instance + * of this class. For example: {@code TelemetrySubsystem.getInstance();} + */ + @SuppressWarnings("WeakerAccess") + public static TelemetrySubsystem getInstance() { + return INSTANCE; + } + + /** + * Creates a new instance of this TelemetrySubsystem. This constructor + * is private since this class is a Singleton. Code should use + * the {@link #getInstance()} method to get the singleton instance. + */ + private TelemetrySubsystem() { + // TODO: Set the default command, if any, for this subsystem by calling setDefaultCommand(command) + // in the constructor or in the robot coordination class, such as RobotContainer. + // Also, you can call addChild(name, sendableChild) to associate sendables with the subsystem + // such as SpeedControllers, Encoders, DigitalInputs, etc. + } +} + diff --git a/src/main/java/frc/robot/util/Alert.java b/src/main/java/frc/robot/util/Alert.java new file mode 100644 index 0000000..3462e1a --- /dev/null +++ b/src/main/java/frc/robot/util/Alert.java @@ -0,0 +1,195 @@ +// Copyright (c) 2023 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found below. + +// MIT License +// +// Copyright (c) 2023 FRC 6328 +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package frc.robot.util; + +import edu.wpi.first.util.sendable.Sendable; +import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +/** Class for managing persistent alerts to be sent over NetworkTables. */ +public class Alert { + private static Map groups = new HashMap(); + + private final AlertType type; + private boolean active = false; + private double activeStartTime = 0.0; + private String text; + + /** + * Creates a new Alert in the default group - "Alerts". If this is the first to be instantiated, + * the appropriate entries will be added to NetworkTables. + * + * @param text Text to be displayed when the alert is active. + * @param type Alert level specifying urgency. + */ + public Alert(String text, AlertType type) { + this("Alerts", text, type); + } + + /** + * Creates a new Alert. If this is the first to be instantiated in its group, the appropriate + * entries will be added to NetworkTables. + * + * @param group Group identifier, also used as NetworkTables title + * @param text Text to be displayed when the alert is active. + * @param type Alert level specifying urgency. + */ + public Alert(String group, String text, AlertType type) { + if (!groups.containsKey(group)) { + groups.put(group, new SendableAlerts()); + SmartDashboard.putData(group, groups.get(group)); + } + + this.text = text; + this.type = type; + groups.get(group).alerts.add(this); + } + + /** + * Sets whether the alert should currently be displayed. When activated, the alert text will also + * be sent to the console. + */ + public void set(boolean active) { + if (active && !this.active) { + activeStartTime = Timer.getFPGATimestamp(); + switch (type) { + case ERROR: + DriverStation.reportError(text, false); + break; + case ERROR_TRACE: + DriverStation.reportError(text, true); + break; + case WARNING: + DriverStation.reportWarning(text, false); + break; + case WARNING_TRACE: + DriverStation.reportWarning(text, true); + break; + case INFO: + System.out.println(text); + break; + } + } + this.active = active; + } + + /** Updates current alert text. */ + public void setText(String text) { + if (active && !text.equals(this.text)) { + switch (type) { + case ERROR: + DriverStation.reportError(text, false); + break; + case ERROR_TRACE: + DriverStation.reportError(text, true); + break; + case WARNING: + DriverStation.reportWarning(text, false); + break; + case WARNING_TRACE: + DriverStation.reportWarning(text, true); + break; + case INFO: + System.out.println(text); + break; + } + } + this.text = text; + } + + private static class SendableAlerts implements Sendable { + public final List alerts = new ArrayList<>(); + + public String[] getStrings(AlertType type) { + Predicate activeFilter = (Alert x) -> x.type == type && x.active; + Comparator timeSorter = + (Alert a1, Alert a2) -> (int) (a2.activeStartTime - a1.activeStartTime); + return alerts.stream() + .filter(activeFilter) + .sorted(timeSorter) + .map((Alert a) -> a.text) + .toArray(String[]::new); + } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("Alerts"); + builder.addStringArrayProperty("errors", () -> getStrings(AlertType.ERROR), null); + builder.addStringArrayProperty("errors", () -> getStrings(AlertType.ERROR_TRACE), null); + builder.addStringArrayProperty("warnings", () -> getStrings(AlertType.WARNING), null); + builder.addStringArrayProperty("warnings", () -> getStrings(AlertType.WARNING_TRACE), null); + builder.addStringArrayProperty("infos", () -> getStrings(AlertType.INFO), null); + } + } + + /** Represents an alert's level of urgency. */ + public static enum AlertType { + /** + * High priority alert - displayed first on the dashboard with a red "X" symbol. Use this type + * for problems which will seriously affect the robot's functionality and thus require immediate + * attention. + */ + ERROR, + /** + * High priority alert - displayed first on the dashboard with a red "X" symbol. Use this type + * for problems which will seriously affect the robot's functionality and thus require immediate + * attention. + * Trace printed to driver station console. + */ + ERROR_TRACE, + + /** + * Medium priority alert - displayed second on the dashboard with a yellow "!" symbol. Use this + * type for problems which could affect the robot's functionality but do not necessarily require + * immediate attention. + */ + WARNING, + /** + * Medium priority alert - displayed second on the dashboard with a yellow "!" symbol. Use this + * type for problems which could affect the robot's functionality but do not necessarily require + * immediate attention. + * Trace printed to driver station console. + */ + WARNING_TRACE, + /** + * Low priority alert - displayed last on the dashboard with a green "i" symbol. Use this type + * for problems which are unlikely to affect the robot's functionality, or any other alerts + * which do not fall under "ERROR" or "WARNING". + */ + INFO + } +} \ No newline at end of file