From 95e1ec76b664d7585af222b811a62b831824f8f8 Mon Sep 17 00:00:00 2001 From: nathanrsxtn <37890449+nathanrsxtn@users.noreply.github.com> Date: Wed, 6 Apr 2022 02:32:17 -0600 Subject: [PATCH] Fix missing path file Re-enable PhotonCamera version check Revert PathPlanner auto preloading Complete working copy of Shuffleboard command schedule --- .gitignore | 2 + src/main/java/frc4388/robot/Robot.java | 7 +- .../java/frc4388/robot/RobotContainer.java | 10 +- .../robot/commands/CommandSchedule.java | 192 ++++++++++++++++++ .../robot/subsystems/VisionOdometry.java | 1 - src/main/java/frc4388/utility/Commander.java | 130 ------------ 6 files changed, 200 insertions(+), 142 deletions(-) create mode 100644 src/main/java/frc4388/robot/commands/CommandSchedule.java delete mode 100644 src/main/java/frc4388/utility/Commander.java diff --git a/.gitignore b/.gitignore index 75d10cd..af34b36 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,5 @@ bin/ simgui*.json src/main/deploy/config.json + +.OutlineViewer \ No newline at end of file diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index 7bc646f..dcbcf9c 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -18,7 +18,6 @@ import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.CommandScheduler; import frc4388.robot.commands.ExtenderIntakeCommands.ExtenderIntakeGroup; -import frc4388.utility.Commander; import frc4388.utility.RobotTime; import frc4388.utility.Vector2D; @@ -58,7 +57,6 @@ public class Robot extends TimedRobot { LOGGER.log(Level.FINE, "Logging Test 6/8"); LOGGER.log(Level.FINER, "Logging Test 7/8"); LOGGER.log(Level.FINEST, "Logging Test 8/8"); - Commander.initialize(); // var path = // PathPlannerUtil.Path.read(Filesystem.getDeployDirectory().toPath().resolve("pathplanner").resolve("Move // Forward.path").toFile()); @@ -94,7 +92,6 @@ public class Robot extends TimedRobot { @Override public void robotPeriodic() { m_robotTime.updateTimes(); - Commander.periodic(); Vector2D firstBallPosition = new Vector2D(15.56 - (82.83 / 2.00), 11.21 - 162.00); Vector2D secondBallPosition = new Vector2D(-(40.44 * (Math.sqrt(2.00) / 2.00)) - ((82.83 - 7.58) * (Math.sqrt(2.00) / 2.00)) - (82.83 / 2.00), -(40.44 * (Math.sqrt(2.00) / 2.00)) + ((82.83 - 7.58) * (Math.sqrt(2.00) / 2.00)) - (219.25 / 2.00)); // * position of second ball, relative to hub. Vector2D firstToSecond = Vector2D.subtract(secondBallPosition, firstBallPosition); @@ -169,9 +166,7 @@ public class Robot extends TimedRobot { * This function is called periodically during autonomous. */ @Override - public void autonomousPeriodic() { - final int a = 1; - } + public void autonomousPeriodic() {} @Override public void teleopInit() { diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index c14d389..cf25b2a 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -42,6 +42,7 @@ 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; @@ -97,6 +98,7 @@ public class RobotContainer { private final PathRecorder m_pathChooser = new PathRecorder(m_robotSwerveDrive); private final ShooterTuner m_shooterTuner = new ShooterTuner(m_robotBoomBoom); + private final CommandSchedule m_commandSchedule = new CommandSchedule(13, 6, false); // Controllers private final static DeadbandedXboxController m_driverXbox = new DeadbandedXboxController(OIConstants.XBOX_DRIVER_ID); private final static DeadbandedXboxController m_operatorXbox = new DeadbandedXboxController(OIConstants.XBOX_OPERATOR_ID); @@ -122,9 +124,6 @@ public class RobotContainer { private SendableChooser quickAutoChooser = new SendableChooser<>(); - private final SequentialCommandGroup autoJMove1 = buildAuto(3.0, 3.0, "JMove1"); - private final SequentialCommandGroup autoJMove2 = buildAuto(3.0, 3.0, "JMove2"); - /** * SmartDash * - Limelight cam X @@ -283,6 +282,7 @@ public class RobotContainer { ); SmartDashboard.putData("Shooter Tuner", m_shooterTuner); + SmartDashboard.putData("Command Schedule", m_commandSchedule); } /** @@ -628,11 +628,11 @@ public class RobotContainer { // ! PathPlanner Testing ParallelDeadlineGroup intakeWithPath1 = new ParallelDeadlineGroup(new RunCommandForTime(new RunCommand(() -> m_robotIntake.runAtOutput(-1.0), m_robotIntake), 3.0, true), new RunCommand(() -> m_robotSerializer.setSerializer(0.8), m_robotSerializer), - autoJMove1); + buildAuto(3.0, 3.0, "JMove1")); ParallelDeadlineGroup intakeWithPath2 = new ParallelDeadlineGroup(new RunCommandForTime(new RunCommand(() -> m_robotIntake.runAtOutput(-1.0), m_robotIntake), 5.0, true), new RunCommand(() -> m_robotSerializer.setSerializer(0.8), m_robotSerializer), - autoJMove2); + buildAuto(3.0, 3.0, "JMove2")); ParallelCommandGroup extendWhileTurretIsAiming = new ParallelCommandGroup(new RunCommandForTime(new RunCommand(() -> m_robotTurret.runShooterRotatePID((180.0 / Math.PI) * Math.atan2(-(82.83 / 2.00) + 15.56, -(219.25 / 2.00) - 40.44 + 10.00)), m_robotTurret), 1.0, true), new ExtenderIntakeGroup(m_robotIntake, m_robotExtender)); ParallelCommandGroup intakeWithPathAndTrackTarget = new ParallelCommandGroup(intakeWithPath1, weirdAutoShootingGroup2); diff --git a/src/main/java/frc4388/robot/commands/CommandSchedule.java b/src/main/java/frc4388/robot/commands/CommandSchedule.java new file mode 100644 index 0000000..e705da4 --- /dev/null +++ b/src/main/java/frc4388/robot/commands/CommandSchedule.java @@ -0,0 +1,192 @@ +package frc4388.robot.commands; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.logging.Level; +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; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +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; + +public final class CommandSchedule extends CommandBase { + private static final Logger LOGGER = Logger.getLogger(CommandSchedule.class.getSimpleName()); + private MethodHandle sequentialCommandGroupCommandsMethod; + private MethodHandle sequentialCommandGroupCurrentCommandIndexMethod; + private MethodHandle parallelCommandGroupCommandsMethod; + private MethodHandle parallelDeadlineGroupCommandsMethod; + private MethodHandle parallelRaceGroupCommandsMethod; + private ShuffleboardLayout root; + private ShuffleboardLayout ungroupedLayout; + private LinkedHashMap scheduledCommands; + private final int maxWidth; + private final int maxHeight; + private final boolean showGroupStatus; + + public CommandSchedule(int maxWidth, int maxHeight, boolean showGroupStatus) { + this.maxWidth = maxWidth; + this.maxHeight = maxHeight; + this.showGroupStatus = showGroupStatus; + } + + @Override + public void initialize() { + try { + Field scheduledCommandsField = CommandScheduler.class.getDeclaredField("m_scheduledCommands"); + Field sequentialCommandGroupCommandsField = SequentialCommandGroup.class.getDeclaredField("m_commands"); + Field sequentialCommandGroupCurrentCommandIndexField = SequentialCommandGroup.class.getDeclaredField("m_currentCommandIndex"); + Field parallelCommandGroupCommandsField = ParallelCommandGroup.class.getDeclaredField("m_commands"); + Field parallelDeadlineGroupCommandsField = ParallelDeadlineGroup.class.getDeclaredField("m_commands"); + Field parallelRaceGroupCommandsField = ParallelRaceGroup.class.getDeclaredField("m_commands"); + AccessibleObject.setAccessible(new Field[] { scheduledCommandsField, sequentialCommandGroupCommandsField, sequentialCommandGroupCurrentCommandIndexField, parallelCommandGroupCommandsField, parallelDeadlineGroupCommandsField, parallelRaceGroupCommandsField }, true); + Lookup lookup = MethodHandles.lookup(); + sequentialCommandGroupCommandsMethod = lookup.unreflectGetter(sequentialCommandGroupCommandsField); + sequentialCommandGroupCurrentCommandIndexMethod = lookup.unreflectGetter(sequentialCommandGroupCurrentCommandIndexField); + parallelCommandGroupCommandsMethod = lookup.unreflectGetter(parallelCommandGroupCommandsField); + 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); + cancel(); + return; + } + root = Shuffleboard.getTab("Command Schedule").getLayout("Command Schedule", BuiltInLayouts.kGrid).withSize(maxWidth, maxHeight); + Shuffleboard.selectTab("Command Schedule"); + } + + @Override + public void execute() { + int size = scheduledCommands.size(); + root.withProperties(Map.of("Number of columns", size, "Number of rows", 1, "Label position", "TOP")); + for (Command command : scheduledCommands.keySet()) { + putCommand(command, root, size, command::isScheduled); + } + } + + @Override + public void end(boolean interrupted) { + sequentialCommandGroupCommandsMethod = null; + sequentialCommandGroupCurrentCommandIndexMethod = null; + parallelCommandGroupCommandsMethod = null; + parallelDeadlineGroupCommandsMethod = null; + parallelRaceGroupCommandsMethod = null; + root = null; + ungroupedLayout = null; + scheduledCommands = null; + purgeShuffleboardTab("Command Schedule"); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public String getName() { + return isScheduled() ? "Enabled" : "Disabled"; + } + + @Override + public boolean runsWhenDisabled() { + return true; + } + + private void putCommand(Command command, ShuffleboardContainer layout, int siblings, BooleanSupplier running) { + boolean isRoot = root == layout; + String name = command.getName() + "@" + Integer.toHexString(command.hashCode()); + if (command instanceof CommandGroupBase) { + Collection commands = List.of(); + Function nestedRunningMaker = c -> () -> !c.isFinished(); + if (command instanceof SequentialCommandGroup) { + ArrayList commandsList = Errors.log().getWithDefault(() -> (ArrayList) sequentialCommandGroupCommandsMethod.invoke(command), new ArrayList<>()); + commands = commandsList; + nestedRunningMaker = c -> () -> Errors.log().getWithDefault(() -> (int) sequentialCommandGroupCurrentCommandIndexMethod.invoke(command) == commandsList.indexOf(c), false); + } else if (command instanceof ParallelCommandGroup) { + HashMap commandsMap = Errors.log().getWithDefault(() -> (HashMap) parallelCommandGroupCommandsMethod.invoke(command), new HashMap()); + commands = commandsMap.keySet(); + nestedRunningMaker = c -> () -> commandsMap.get(c); + } else if (command instanceof ParallelDeadlineGroup) { + commands = Errors.log().getWithDefault(() -> (HashMap) parallelDeadlineGroupCommandsMethod.invoke(command), new HashMap()).keySet(); + nestedRunningMaker = c -> () -> !command.isFinished(); + } else if (command instanceof ParallelRaceGroup) { + commands = Errors.log().getWithDefault(() -> (HashSet) parallelRaceGroupCommandsMethod.invoke(command), new HashSet<>()); + nestedRunningMaker = c -> () -> !command.isFinished(); + } + ShuffleboardLayout nestedLayout; + int size = commands.size() + (showGroupStatus ? 1 : 0); + if (isRoot) + nestedLayout = layout.getLayout(name, BuiltInLayouts.kList).withSize(maxWidth / siblings, maxHeight); + else + nestedLayout = layout.getLayout(name, BuiltInLayouts.kGrid).withProperties(Map.of("Number of columns", size, "Number of rows", 1)); + if (showGroupStatus && nestedLayout.getComponents().stream().map(ShuffleboardComponent::getTitle).noneMatch("_self_"::equals)) + nestedLayout.addBoolean("_self_", running); + for (Command nestedCommand : commands) { + putCommand(nestedCommand, nestedLayout, size, nestedRunningMaker.apply(nestedCommand)); + } + } else if (command instanceof CommandBase) { + ShuffleboardContainer target = isRoot ? Objects.requireNonNullElseGet(ungroupedLayout, () -> ungroupedLayout = root.getLayout("Ungrouped", BuiltInLayouts.kList)) : layout; + if (target.getComponents().stream().map(ShuffleboardComponent::getTitle).noneMatch(name::equals)) + 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/subsystems/VisionOdometry.java b/src/main/java/frc4388/robot/subsystems/VisionOdometry.java index 861ded3..fda2586 100644 --- a/src/main/java/frc4388/robot/subsystems/VisionOdometry.java +++ b/src/main/java/frc4388/robot/subsystems/VisionOdometry.java @@ -51,7 +51,6 @@ public class VisionOdometry extends SubsystemBase { setLEDs(false); setDriverMode(false); - PhotonCamera.setVersionCheckEnabled(false); } /** Gets the vision points from the limelight diff --git a/src/main/java/frc4388/utility/Commander.java b/src/main/java/frc4388/utility/Commander.java deleted file mode 100644 index 1e37e24..0000000 --- a/src/main/java/frc4388/utility/Commander.java +++ /dev/null @@ -1,130 +0,0 @@ -package frc4388.utility; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.Map.Entry; -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import com.diffplug.common.base.Errors; - -import edu.wpi.first.math.Pair; -import edu.wpi.first.wpilibj.shuffleboard.BuiltInLayouts; -import edu.wpi.first.wpilibj.shuffleboard.BuiltInWidgets; -import edu.wpi.first.wpilibj.shuffleboard.LayoutType; -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.wpilibj.smartdashboard.SmartDashboard; -import edu.wpi.first.wpilibj2.command.Command; -import edu.wpi.first.wpilibj2.command.CommandBase; -import edu.wpi.first.wpilibj2.command.CommandGroupBase; -import edu.wpi.first.wpilibj2.command.CommandScheduler; -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; - -public final class Commander { - private static MethodHandle m_scheduledCommandsHandle; - private static MethodHandle m_sequentialCommandGroupCommandsHandle; - private static MethodHandle m_sequentialCommandGroupCurrentCommandIndexHandle; - private static MethodHandle m_parallelCommandGroupCommandsHandle; - private static MethodHandle m_parallelDeadlineGroupCommandsHandle; - private static MethodHandle m_parallelRaceGroupCommandsHandle; - private static ShuffleboardTab tab; - - public static void initialize() { - try { - Field m_scheduledCommandsField = CommandScheduler.class.getDeclaredField("m_scheduledCommands"); - m_scheduledCommandsField.trySetAccessible(); - m_scheduledCommandsHandle = MethodHandles.lookup().unreflectGetter(m_scheduledCommandsField); - Field m_sequentialCommandGroupCommandsField = SequentialCommandGroup.class.getDeclaredField("m_commands"); - m_sequentialCommandGroupCommandsField.trySetAccessible(); - m_sequentialCommandGroupCommandsHandle = MethodHandles.lookup().unreflectGetter(m_sequentialCommandGroupCommandsField); - Field m_sequentialCommandGroupCurrentCommandIndexField = SequentialCommandGroup.class.getDeclaredField("m_currentCommandIndex"); - m_sequentialCommandGroupCurrentCommandIndexField.trySetAccessible(); - m_sequentialCommandGroupCurrentCommandIndexHandle = MethodHandles.lookup().unreflectGetter(m_sequentialCommandGroupCurrentCommandIndexField); - Field m_parallelCommandGroupCommandsField = ParallelCommandGroup.class.getDeclaredField("m_commands"); - m_parallelCommandGroupCommandsField.trySetAccessible(); - m_parallelCommandGroupCommandsHandle = MethodHandles.lookup().unreflectGetter(m_parallelCommandGroupCommandsField); - Field m_parallelDeadlineGroupCommandsField = ParallelDeadlineGroup.class.getDeclaredField("m_commands"); - m_parallelDeadlineGroupCommandsField.trySetAccessible(); - m_parallelDeadlineGroupCommandsHandle = MethodHandles.lookup().unreflectGetter(m_parallelDeadlineGroupCommandsField); - Field m_parallelRaceGroupCommandsField = ParallelRaceGroup.class.getDeclaredField("m_commands"); - m_parallelRaceGroupCommandsField.trySetAccessible(); - m_parallelRaceGroupCommandsHandle = MethodHandles.lookup().unreflectGetter(m_parallelRaceGroupCommandsField); - } catch (IllegalArgumentException | NoSuchFieldException | SecurityException | IllegalAccessException e) { - e.printStackTrace(); - } - tab = Shuffleboard.getTab("Commander"); - try { - scheduledCommands = ((LinkedHashMap) m_scheduledCommandsHandle.invoke(CommandScheduler.getInstance())); - } catch (Throwable e) { - e.printStackTrace(); - } - } - private static LinkedHashMap scheduledCommands; - - public static void periodic() { - int count = scheduledCommands.size(); - for (Command command : scheduledCommands.keySet()) { - putCommand(command, null, count, command::isScheduled); - } - } - private static int count = 0; - private static void putCommand(Command command, ShuffleboardContainer layout, int siblings, BooleanSupplier running) { - String name = (count++) + ":" + command.getClass().getSimpleName()/* + "@" + Integer.toHexString(command.hashCode()) */; - if (Objects.requireNonNullElse(layout, tab).getComponents().stream().map(ShuffleboardComponent::getTitle).anyMatch(name::equals)) return; - if (command instanceof CommandGroupBase) { - Collection commands = List.of(); - String glyph = "CUBE"; - Function nestedRunningMaker = c -> () -> !c.isFinished(); - if (command instanceof SequentialCommandGroup) { - ArrayList commandsList = Errors.log().getWithDefault(() -> (ArrayList) m_sequentialCommandGroupCommandsHandle.invoke(command), new ArrayList<>()); - commands = commandsList; - nestedRunningMaker = c -> () -> Errors.log().getWithDefault(() -> (int) m_sequentialCommandGroupCurrentCommandIndexHandle.invoke(command) == commandsList.indexOf(c), false); - glyph = "LIST_OL"; - } else if (command instanceof ParallelCommandGroup) { - HashMap commandsMap = Errors.log().getWithDefault(() -> (HashMap) m_parallelCommandGroupCommandsHandle.invoke(command), new HashMap()); - commands = commandsMap.keySet(); - nestedRunningMaker = c -> () -> commandsMap.get(c); - glyph = "LIST_UL"; - } else if (command instanceof ParallelDeadlineGroup) { - HashMap commandsMap = Errors.log().getWithDefault(() -> (HashMap) m_parallelDeadlineGroupCommandsHandle.invoke(command), new HashMap()); - commands = commandsMap.keySet(); - nestedRunningMaker = c -> () -> commandsMap.get(c); - glyph = "CROSSHAIRS"; - } else if (command instanceof ParallelRaceGroup) { - commands = Errors.log().getWithDefault(() -> (HashSet) m_parallelRaceGroupCommandsHandle.invoke(command), new HashSet<>()); - nestedRunningMaker = c -> () -> !command.isFinished(); - glyph = "RANDOM"; - } - ShuffleboardLayout nestedLayout = Objects.requireNonNullElse(layout, tab).getLayout(name, layout == null ? BuiltInLayouts.kList : BuiltInLayouts.kGrid).withSize(11 / siblings, layout == null ? 6 : 1).withProperties(Map.of("Number of columns", 11 / siblings, "Number of rows", 1, "Label position", "BOTTOM", "Show Glyph", true, "Glyph", glyph)); - int count = commands.size(); - for (Command nestedCommand : commands) { - putCommand(nestedCommand, nestedLayout, count, nestedRunningMaker.apply(nestedCommand)); - } - } else if (command instanceof CommandBase) { - Objects.requireNonNullElse(layout, tab).addBoolean(name, running); - } - } -}