diff --git a/elastic-layout.json b/elastic-layout.json index 61c7013..b933e4f 100644 --- a/elastic-layout.json +++ b/elastic-layout.json @@ -91,6 +91,93 @@ "period": 0.06, "sort_options": false } + }, + { + "title": "HubActive", + "x": 0.0, + "y": 0.0, + "width": 256.0, + "height": 128.0, + "type": "Boolean Box", + "properties": { + "topic": "/GameData2026/HubActive", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "HubStatus", + "x": 256.0, + "y": 0.0, + "width": 256.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/GameData2026/HubStatus", + "period": 0.06, + "data_type": "string", + "show_submit_button": false + } + }, + { + "title": "SecondsUntilHubOpens", + "x": 0.0, + "y": 128.0, + "width": 256.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/GameData2026/SecondsUntilHubOpens", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Shift", + "x": 256.0, + "y": 128.0, + "width": 256.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/GameData2026/Shift", + "period": 0.06, + "data_type": "string", + "show_submit_button": false + } + }, + { + "title": "FirstInactiveAlliance", + "x": 0.0, + "y": 256.0, + "width": 256.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/GameData2026/FirstInactiveAlliance", + "period": 0.06, + "data_type": "string", + "show_submit_button": false + } + }, + { + "title": "MatchTime", + "x": 256.0, + "y": 256.0, + "width": 256.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/GameData2026/MatchTime", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } } ] } @@ -1492,4 +1579,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/main/java/frc/robot/Robot.java b/src/main/java/frc/robot/Robot.java index a2b81e4..17bcdae 100644 --- a/src/main/java/frc/robot/Robot.java +++ b/src/main/java/frc/robot/Robot.java @@ -23,6 +23,7 @@ public class Robot extends TimedRobot { public void robotPeriodic() { CommandScheduler.getInstance().run(); m_robotContainer.tunerPeriodic(); + m_robotContainer.dashboardPeriodic(); } diff --git a/src/main/java/frc/robot/RobotContainer.java b/src/main/java/frc/robot/RobotContainer.java index 6f66f1d..4e782ad 100644 --- a/src/main/java/frc/robot/RobotContainer.java +++ b/src/main/java/frc/robot/RobotContainer.java @@ -30,6 +30,7 @@ import frc.robot.utils.LedBindings; import frc.robot.controls.ClimberControls; import frc.robot.controls.Autocommand; import frc.robot.subsystems.ClimberSubsystem; +import frc.robot.utils.GameData2026; public class RobotContainer { /* ====================== */ @@ -63,6 +64,7 @@ public class RobotContainer { //private final ClimberControls climberControls = new ClimberControls(climberSubsystem, controllerupper); private final IntakerControls intakerControls = new IntakerControls(IntakerSubsystem, shooterSubsystem, controllerupper); private final DriveGainsTuner driveGainsTuner = new DriveGainsTuner(drivetrain); + private final GameData2026 gameData2026 = new GameData2026(); private ConfigTalonFXMotorTuner configFlywheelTuner = new ConfigTalonFXMotorTuner(shooterSubsystem.flywheelMotorLeft, "flywheel", Constants.Shooter.flyWheelSlot0Configs); private ConfigTalonFXMotorTuner configConveyorTuner = new ConfigTalonFXMotorTuner(shooterSubsystem.conveyorMotor, "conveyor", Constants.Shooter.conveyorSlot0Configs); private ConfigTalonFXSMotorTuner configBackboardTuner = new ConfigTalonFXSMotorTuner(shooterSubsystem.backboardMotor, "backboard", Constants.Shooter.backboardSlot0Configs); @@ -202,6 +204,10 @@ public class RobotContainer { shooterSubsystem.setRightFollowLeft(); } + public void dashboardPeriodic() { + gameData2026.periodic(); + } + // private void configueClimber() { // climberSubsystem.setDefaultCommand(climberControls.defaultClimberCommand()); // } diff --git a/src/main/java/frc/robot/utils/GameData2026.java b/src/main/java/frc/robot/utils/GameData2026.java new file mode 100644 index 0000000..6895b3a --- /dev/null +++ b/src/main/java/frc/robot/utils/GameData2026.java @@ -0,0 +1,117 @@ +package frc.robot.utils; + +import java.util.Optional; + +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.DriverStation.Alliance; + +public class GameData2026 { + private static final double SHIFT_1_END_TIME = 105.0; + private static final double SHIFT_2_END_TIME = 80.0; + private static final double SHIFT_3_END_TIME = 55.0; + private static final double SHIFT_4_END_TIME = 30.0; + private static final double TRANSITION_END_TIME = 130.0; + + private final NetworkTable table = NetworkTableInstance.getDefault().getTable("GameData2026"); + + public void periodic() { + Optional alliance = DriverStation.getAlliance(); + double matchTime = DriverStation.getMatchTime(); + String gameData = DriverStation.getGameSpecificMessage(); + + HubWindow window = calculateHubWindow(alliance, matchTime, gameData); + + table.getEntry("HubActive").setBoolean(window.hubActive); + table.getEntry("HubStatus").setString(window.status); + table.getEntry("SecondsUntilHubOpens").setDouble(window.secondsUntilHubOpens); + table.getEntry("SecondsUntilHubChanges").setDouble(window.secondsUntilHubChanges); + table.getEntry("Shift").setString(window.shift); + table.getEntry("MatchTime").setDouble(matchTime); + table.getEntry("GameData").setString(gameData); + table.getEntry("Alliance").setString(alliance.map(Enum::name).orElse("Unknown")); + table.getEntry("FirstInactiveAlliance").setString(getFirstInactiveAlliance(gameData)); + } + + private HubWindow calculateHubWindow(Optional alliance, double matchTime, String gameData) { + if (alliance.isEmpty()) { + return new HubWindow(false, "NO_ALLIANCE", "Disabled", Double.NaN, Double.NaN); + } + + if (DriverStation.isAutonomousEnabled()) { + return new HubWindow(true, "OPEN", "Auto", 0.0, Double.NaN); + } + + if (!DriverStation.isTeleopEnabled()) { + return new HubWindow(false, "DISABLED", "Disabled", Double.NaN, Double.NaN); + } + + if (matchTime < 0.0) { + return new HubWindow(true, "OPEN_TIME_UNKNOWN", "Unknown", 0.0, Double.NaN); + } + + Boolean redInactiveFirst = getRedInactiveFirst(gameData); + if (redInactiveFirst == null) { + return new HubWindow(true, "WAITING_DATA", "WaitingData", 0.0, Double.NaN); + } + + boolean shift1Active = switch (alliance.get()) { + case Red -> !redInactiveFirst; + case Blue -> redInactiveFirst; + }; + + if (matchTime > TRANSITION_END_TIME) { + return new HubWindow(true, "OPEN", "Transition", 0.0, matchTime - TRANSITION_END_TIME); + } else if (matchTime > SHIFT_1_END_TIME) { + return fromShift("Shift1", shift1Active, matchTime, SHIFT_1_END_TIME); + } else if (matchTime > SHIFT_2_END_TIME) { + return fromShift("Shift2", !shift1Active, matchTime, SHIFT_2_END_TIME); + } else if (matchTime > SHIFT_3_END_TIME) { + return fromShift("Shift3", shift1Active, matchTime, SHIFT_3_END_TIME); + } else if (matchTime > SHIFT_4_END_TIME) { + return fromShift("Shift4", !shift1Active, matchTime, SHIFT_4_END_TIME); + } else { + return new HubWindow(true, "OPEN", "Endgame", 0.0, Math.max(matchTime, 0.0)); + } + } + + private HubWindow fromShift(String shift, boolean hubActive, double matchTime, double nextBoundary) { + double secondsUntilChange = Math.max(matchTime - nextBoundary, 0.0); + double secondsUntilOpen = hubActive ? 0.0 : secondsUntilChange; + String status = hubActive ? "OPEN" : "CLOSED"; + return new HubWindow(hubActive, status, shift, secondsUntilOpen, secondsUntilChange); + } + + private Boolean getRedInactiveFirst(String gameData) { + if (gameData.isEmpty()) { + return null; + } + + return switch (gameData.charAt(0)) { + case 'R' -> true; + case 'B' -> false; + default -> null; + }; + } + + private String getFirstInactiveAlliance(String gameData) { + if (gameData.isEmpty()) { + return "None"; + } + + return switch (gameData.charAt(0)) { + case 'R' -> "Red"; + case 'B' -> "Blue"; + default -> "Invalid"; + }; + } + + private record HubWindow( + boolean hubActive, + String status, + String shift, + double secondsUntilHubOpens, + double secondsUntilHubChanges + ) {} +}