diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index cf25b2a..57a31f7 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -42,13 +42,13 @@ import edu.wpi.first.wpilibj2.command.button.JoystickButton; import frc4388.robot.Constants.OIConstants; import frc4388.robot.Constants.StorageConstants; import frc4388.robot.Constants.SwerveDriveConstants; -import frc4388.robot.commands.CommandSchedule; import frc4388.robot.commands.PathRecorder; import frc4388.robot.commands.RunCommandForTime; -import frc4388.robot.commands.ShooterTuner; import frc4388.robot.commands.DriveCommands.DriveWithInputForTime; import frc4388.robot.commands.ExtenderIntakeCommands.ExtenderIntakeGroup; import frc4388.robot.commands.ShooterCommands.TrackTarget; +import frc4388.robot.commands.shuffleboard.ShooterTuner; +import frc4388.robot.commands.shuffleboard.CommandSchedule; import frc4388.robot.subsystems.BoomBoom; import frc4388.robot.subsystems.Camera; import frc4388.robot.subsystems.Claws; diff --git a/src/main/java/frc4388/robot/commands/PathRecorder.java b/src/main/java/frc4388/robot/commands/PathRecorder.java index 17f0e58..b6f16bb 100644 --- a/src/main/java/frc4388/robot/commands/PathRecorder.java +++ b/src/main/java/frc4388/robot/commands/PathRecorder.java @@ -42,9 +42,9 @@ import edu.wpi.first.wpilibj2.command.InstantCommand; import edu.wpi.first.wpilibj2.command.NotifierCommand; import frc4388.robot.Constants.SwerveDriveConstants; import frc4388.robot.subsystems.SwerveDrive; -import frc4388.utility.ListeningSendableChooser; import frc4388.utility.PathPlannerUtil; import frc4388.utility.PathPlannerUtil.Path.Waypoint; +import frc4388.utility.shuffleboard.ListeningSendableChooser; public class PathRecorder extends CommandBase { private static final Logger LOGGER = Logger.getLogger(PathRecorder.class.getSimpleName()); diff --git a/src/main/java/frc4388/robot/commands/CommandSchedule.java b/src/main/java/frc4388/robot/commands/shuffleboard/CommandSchedule.java similarity index 80% rename from src/main/java/frc4388/robot/commands/CommandSchedule.java rename to src/main/java/frc4388/robot/commands/shuffleboard/CommandSchedule.java index e705da4..446a24f 100644 --- a/src/main/java/frc4388/robot/commands/CommandSchedule.java +++ b/src/main/java/frc4388/robot/commands/shuffleboard/CommandSchedule.java @@ -1,4 +1,4 @@ -package frc4388.robot.commands; +package frc4388.robot.commands.shuffleboard; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -20,14 +20,11 @@ import java.util.logging.Logger; import com.diffplug.common.base.Errors; -import edu.wpi.first.networktables.NetworkTable; -import edu.wpi.first.networktables.NetworkTableInstance; import edu.wpi.first.wpilibj.shuffleboard.BuiltInLayouts; import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardComponent; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardContainer; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout; -import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.CommandBase; import edu.wpi.first.wpilibj2.command.CommandGroupBase; @@ -36,6 +33,7 @@ import edu.wpi.first.wpilibj2.command.ParallelCommandGroup; import edu.wpi.first.wpilibj2.command.ParallelDeadlineGroup; import edu.wpi.first.wpilibj2.command.ParallelRaceGroup; import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; +import frc4388.utility.shuffleboard.ShuffleboardHelper; public final class CommandSchedule extends CommandBase { private static final Logger LOGGER = Logger.getLogger(CommandSchedule.class.getSimpleName()); @@ -51,6 +49,7 @@ public final class CommandSchedule extends CommandBase { private final int maxHeight; private final boolean showGroupStatus; + /** The maxWidth and maxHeight must be less than or equal to the number of full grid cells in the Shuffleboard tab. This amount will vary depending on window size. */ public CommandSchedule(int maxWidth, int maxHeight, boolean showGroupStatus) { this.maxWidth = maxWidth; this.maxHeight = maxHeight; @@ -74,8 +73,8 @@ public final class CommandSchedule extends CommandBase { parallelDeadlineGroupCommandsMethod = lookup.unreflectGetter(parallelDeadlineGroupCommandsField); parallelRaceGroupCommandsMethod = lookup.unreflectGetter(parallelRaceGroupCommandsField); scheduledCommands = ((LinkedHashMap) lookup.unreflectGetter(scheduledCommandsField).invoke(CommandScheduler.getInstance())); - } catch (Throwable e) { - LOGGER.log(Level.SEVERE, "Failed to reflect necessary fields to run the command schedule.", e); + } catch (Throwable exception) { + LOGGER.log(Level.SEVERE, "Failed to reflect necessary fields to run the command schedule.", exception); cancel(); return; } @@ -102,7 +101,7 @@ public final class CommandSchedule extends CommandBase { root = null; ungroupedLayout = null; scheduledCommands = null; - purgeShuffleboardTab("Command Schedule"); + ShuffleboardHelper.purgeShuffleboardTab("Command Schedule"); } @Override @@ -158,35 +157,4 @@ public final class CommandSchedule extends CommandBase { target.addBoolean(name, running); } } - - private static void purgeShuffleboardTab(String name) { - Shuffleboard.getTab(name).getComponents().clear(); - NetworkTable rootTable = NetworkTableInstance.getDefault().getTable("Shuffleboard"); - NetworkTable rootMetaTable = rootTable.getSubTable(".metadata"); - recursiveClearTable(rootMetaTable.getSubTable(name)); - recursiveClearTable(rootTable.getSubTable(name)); - rootMetaTable.getEntry("Selected").setString(""); - rootMetaTable.delete(name); - rootTable.delete(name); - try { - Field shuffleboardRootField = Shuffleboard.class.getDeclaredField("root"); - shuffleboardRootField.trySetAccessible(); - Object shuffleboardRoot = shuffleboardRootField.get(null); - Field shuffleboardTabsField = shuffleboardRoot.getClass().getDeclaredField("m_tabs"); - Field shuffleboardTabsChangedField = shuffleboardRoot.getClass().getDeclaredField("m_tabsChanged"); - shuffleboardTabsField.trySetAccessible(); - shuffleboardTabsChangedField.trySetAccessible(); - ((LinkedHashMap) shuffleboardTabsField.get(shuffleboardRoot)).remove(name); - shuffleboardTabsChangedField.set(shuffleboardRoot, true); - } catch (NoSuchFieldException | IllegalAccessException e) { - LOGGER.log(Level.SEVERE, "Failed to purge Shuffleboard tab " + name + ".", e); - } - Shuffleboard.update(); - } - - private static void recursiveClearTable(NetworkTable table) { - table.getSubTables().forEach(name -> recursiveClearTable(table.getSubTable(name))); - table.getSubTables().forEach(table::delete); - table.getKeys().forEach(table::delete); - } } diff --git a/src/main/java/frc4388/robot/commands/ShooterTuner.java b/src/main/java/frc4388/robot/commands/shuffleboard/ShooterTuner.java similarity index 94% rename from src/main/java/frc4388/robot/commands/ShooterTuner.java rename to src/main/java/frc4388/robot/commands/shuffleboard/ShooterTuner.java index bc8c8d6..0b9aabe 100644 --- a/src/main/java/frc4388/robot/commands/ShooterTuner.java +++ b/src/main/java/frc4388/robot/commands/shuffleboard/ShooterTuner.java @@ -1,4 +1,4 @@ -package frc4388.robot.commands; +package frc4388.robot.commands.shuffleboard; import java.io.File; import java.io.IOException; @@ -23,7 +23,8 @@ import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.CommandBase; import frc4388.robot.subsystems.BoomBoom; import frc4388.robot.subsystems.BoomBoom.ShooterTableEntry; -import frc4388.utility.SendableTable; +import frc4388.utility.shuffleboard.SendableTable; +import frc4388.utility.shuffleboard.ShuffleboardHelper; public class ShooterTuner extends CommandBase { private static final Logger LOGGER = Logger.getLogger(ShooterTuner.class.getSimpleName()); @@ -60,7 +61,7 @@ public class ShooterTuner extends CommandBase { tableOverrideEntry.drumVelocity = 0.0; m_boomBoom.m_shooterTable = new ShooterTableEntry[] { tableOverrideEntry }; Shuffleboard.selectTab("Shooter Tuner"); - SmartDashboard.putData("TABLE", m_tableEditor); + SmartDashboard.putData("Shooter Table", m_tableEditor); } @Override public void execute() { @@ -71,6 +72,7 @@ public class ShooterTuner extends CommandBase { public void end(boolean interrupted) { m_boomBoom.loadShooterTable(); LOGGER.info(Errors.log().wrapWithDefault(() -> Files.readString(PATH), "Failed to read CSV")); + ShuffleboardHelper.purgeShuffleboardTab("Shooter Tuner"); } @Override public final boolean isFinished() { diff --git a/src/main/java/frc4388/utility/ListeningSendableChooser.java b/src/main/java/frc4388/utility/shuffleboard/ListeningSendableChooser.java similarity index 99% rename from src/main/java/frc4388/utility/ListeningSendableChooser.java rename to src/main/java/frc4388/utility/shuffleboard/ListeningSendableChooser.java index c0f3432..cb289e3 100644 --- a/src/main/java/frc4388/utility/ListeningSendableChooser.java +++ b/src/main/java/frc4388/utility/shuffleboard/ListeningSendableChooser.java @@ -3,7 +3,7 @@ // the WPILib BSD license file in the root directory of this project. // package edu.wpi.first.wpilibj.smartdashboard; -package frc4388.utility; +package frc4388.utility.shuffleboard; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; diff --git a/src/main/java/frc4388/utility/SendableTable.java b/src/main/java/frc4388/utility/shuffleboard/SendableTable.java similarity index 97% rename from src/main/java/frc4388/utility/SendableTable.java rename to src/main/java/frc4388/utility/shuffleboard/SendableTable.java index 871546c..0cdead2 100644 --- a/src/main/java/frc4388/utility/SendableTable.java +++ b/src/main/java/frc4388/utility/shuffleboard/SendableTable.java @@ -1,4 +1,4 @@ -package frc4388.utility; +package frc4388.utility.shuffleboard; import java.nio.ByteBuffer; import java.util.Arrays; diff --git a/src/main/java/frc4388/utility/shuffleboard/ShuffleboardHelper.java b/src/main/java/frc4388/utility/shuffleboard/ShuffleboardHelper.java new file mode 100644 index 0000000..ff1b0f5 --- /dev/null +++ b/src/main/java/frc4388/utility/shuffleboard/ShuffleboardHelper.java @@ -0,0 +1,47 @@ +package frc4388.utility.shuffleboard; + +import java.lang.reflect.Field; +import java.util.LinkedHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; +import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab; + +public final class ShuffleboardHelper { + private static final Logger LOGGER = Logger.getLogger(ShuffleboardHelper.class.getSimpleName()); + + @SuppressWarnings("unchecked") + public static void purgeShuffleboardTab(String name) { + Shuffleboard.getTab(name).getComponents().clear(); + NetworkTable rootTable = NetworkTableInstance.getDefault().getTable("Shuffleboard"); + NetworkTable rootMetaTable = rootTable.getSubTable(".metadata"); + recursiveClearTable(rootMetaTable.getSubTable(name)); + recursiveClearTable(rootTable.getSubTable(name)); + rootMetaTable.getEntry("Selected").setString(""); + rootMetaTable.delete(name); + rootTable.delete(name); + try { + Field shuffleboardRootField = Shuffleboard.class.getDeclaredField("root"); + shuffleboardRootField.trySetAccessible(); + Object shuffleboardRoot = shuffleboardRootField.get(null); + Field shuffleboardTabsField = shuffleboardRoot.getClass().getDeclaredField("m_tabs"); + Field shuffleboardTabsChangedField = shuffleboardRoot.getClass().getDeclaredField("m_tabsChanged"); + shuffleboardTabsField.trySetAccessible(); + shuffleboardTabsChangedField.trySetAccessible(); + ((LinkedHashMap) shuffleboardTabsField.get(shuffleboardRoot)).remove(name); + shuffleboardTabsChangedField.set(shuffleboardRoot, true); + } catch (NoSuchFieldException | IllegalAccessException | ClassCastException exception) { + LOGGER.log(Level.SEVERE, exception, () -> "Failed to purge Shuffleboard tab " + name + "."); + } + Shuffleboard.update(); + } + + public static void recursiveClearTable(NetworkTable table) { + table.getSubTables().forEach(name -> recursiveClearTable(table.getSubTable(name))); + table.getSubTables().forEach(table::delete); + table.getKeys().forEach(table::delete); + } +}