Repository navigation
feat(safeopmode): default gamepad publishing by opmode type #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| import com.aaravlabs.synapse.Orchestrator; | ||
| import com.qualcomm.robotcore.eventloop.opmode.OpMode; | ||
| import com.qualcomm.robotcore.eventloop.opmode.TeleOp; | ||
|
|
||
| /** | ||
| * Base class for {@link OpMode} subclasses that use the Synapse pub/sub bus | ||
|
|
@@ -70,13 +71,33 @@ public abstract class SafeOpMode extends OpMode { | |
| /** Wraps {@code hardwareMap} so device lookups return {@link SafeDevice}s. */ | ||
| protected SafeHardwareMap safeMap; | ||
|
|
||
| /** | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @TELEOP not @inherited causes publishGamepads default to fail for concrete subclasses The default Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||
| * Whether {@link #attachGamepad(Object, String)} should actually attach a | ||
| * {@link GamepadAdaptor} for this OpMode. | ||
| * | ||
| * <p>Defaulted in {@link #init()} based on the OpMode type: | ||
| * <ul> | ||
| * <li>{@link TeleOp}-annotated OpModes: {@code true} — gamepads are published | ||
| * by default.</li> | ||
| * <li>Autonomous / other OpModes: {@code false} — gamepads are <b>not</b> | ||
| * published by default; set this to {@code true} in {@link #onSafeInit()} | ||
| * to opt in.</li> | ||
| * </ul> | ||
| * | ||
| * <p>Subclasses may override this field in {@link #onSafeInit()} before | ||
| * calling {@link #attachGamepad} to opt in (auto) or opt out (teleop). | ||
| */ | ||
| protected boolean publishGamepads; | ||
|
|
||
| @Override | ||
| @SuppressWarnings("deprecation") | ||
| public final void init() { | ||
| orchestrator = FtcOrchestrator.create(); | ||
| orch = orchestrator; | ||
| hardware = orchestrator.hardware(); | ||
| safeMap = new SafeHardwareMap(hardwareMap, hardware); | ||
| // Gamepad publishing is on by default for TeleOp, off for Autonomous/other. | ||
| publishGamepads = getClass().isAnnotationPresent(TeleOp.class); | ||
| onSafeInit(); | ||
| } | ||
|
|
||
|
|
@@ -119,4 +140,37 @@ protected void onSafeStart() {} | |
|
|
||
| /** Called when the OpMode stops, before orchestrator is closed. Defaults to no-op. */ | ||
| protected void onSafeStop() {} | ||
|
|
||
| // ---- gamepad attachment --------------------------------------------- | ||
|
|
||
| /** | ||
| * Attach a {@link GamepadAdaptor} for {@code gamepad} only if | ||
| * {@link #publishGamepads} is true. Returns the registered node name | ||
| * (e.g. {@code "GamepadAdaptor:g1"}) or {@code null} when gamepad | ||
| * publishing is disabled for this OpMode. | ||
| * | ||
| * <p>Use this instead of calling {@link GamepadAdaptor#attach} directly so | ||
| * the auto/teleop default is respected. Example: | ||
| * | ||
| * <pre>{@code | ||
| * @Autonomous(name = "My Auto") | ||
| * public class MyAuto extends SafeOpMode { | ||
| * @Override protected void onSafeInit() { | ||
| * // publishGamepads defaults to false for Autonomous. | ||
| * attachGamepad(gamepad1, "g1"); // no-op: gamepads not published | ||
| * publishGamepads = true; // opt in | ||
| * attachGamepad(gamepad1, "g1"); // now attaches | ||
| * } | ||
| * } | ||
| * }</pre> | ||
| */ | ||
| protected String attachGamepad(Object gamepad, String parentTopic) { | ||
| if (!publishGamepads) { | ||
| if (orchestrator != null) { | ||
| orchestrator.log("GamepadAdaptor: skipping '" + parentTopic + "' — gamepad publishing disabled"); | ||
| } | ||
| return null; | ||
| } | ||
| return GamepadAdaptor.attach(orchestrator, gamepad, parentTopic); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| package com.aaravlabs.synapse.ftc; | ||
|
|
||
| import com.qualcomm.robotcore.eventloop.opmode.TeleOp; | ||
| import com.qualcomm.robotcore.hardware.Gamepad; | ||
| import org.junit.jupiter.api.AfterEach; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
||
| /** | ||
| * Verifies that {@link SafeOpMode} defaults gamepad publishing off for | ||
| * Autonomous/other opmodes (opt-in) and on for {@link TeleOp} opmodes | ||
| * (optionally disabled), and that {@link SafeOpMode#attachGamepad(Object, String)} | ||
| * respects the {@link SafeOpMode#publishGamepads} flag. | ||
| */ | ||
| class SafeOpModeTest { | ||
|
|
||
| @TeleOp(name = "TeleOp Default") | ||
|
kody-ai[bot] marked this conversation as resolved.
Resolved
|
||
| private static class TeleOpDefault extends SafeOpMode { | ||
| @Override | ||
| protected void onSafeInit() { | ||
| attachGamepad(gamepad1, "g1"); | ||
| } | ||
| } | ||
|
|
||
| private static class AutoDefault extends SafeOpMode { | ||
| @Override | ||
| protected void onSafeInit() { | ||
| attachGamepad(gamepad1, "g1"); | ||
| } | ||
| } | ||
|
|
||
| @TeleOp(name = "TeleOp Opt Out") | ||
| private static class TeleOpOptOut extends SafeOpMode { | ||
| @Override | ||
| protected void onSafeInit() { | ||
| publishGamepads = false; // opt out | ||
| attachGamepad(gamepad1, "g1"); | ||
| } | ||
| } | ||
|
|
||
| private static class AutoOptIn extends SafeOpMode { | ||
| @Override | ||
| protected void onSafeInit() { | ||
| publishGamepads = true; // opt in | ||
| attachGamepad(gamepad1, "g1"); | ||
| } | ||
| } | ||
|
|
||
| /** Tracks every OpMode initialized by a test so its orchestrator is closed. */ | ||
| private final List<SafeOpMode> initialized = new ArrayList<>(); | ||
|
|
||
| @AfterEach | ||
| void tearDown() { | ||
| for (SafeOpMode op : initialized) { | ||
| op.stop(); | ||
| } | ||
| initialized.clear(); | ||
| } | ||
|
|
||
| @Test | ||
| void teleOpDefaultsPublishGamepadsTrue() { | ||
| SafeOpMode op = new TeleOpDefault(); | ||
| op.gamepad1 = new Gamepad(); | ||
| op.init(); | ||
| initialized.add(op); | ||
| assertTrue(op.publishGamepads, | ||
| "@TeleOp opmode should default publishGamepads to true"); | ||
| assertNotNull(op.orchestrator, "orchestrator should be initialized"); | ||
| } | ||
|
|
||
| @Test | ||
| void autoDefaultsPublishGamepadsFalse() { | ||
| SafeOpMode op = new AutoDefault(); | ||
| op.gamepad1 = new Gamepad(); | ||
| op.init(); | ||
| initialized.add(op); | ||
| assertFalse(op.publishGamepads, | ||
| "Autonomous opmode should default publishGamepads to false"); | ||
| } | ||
|
|
||
| @Test | ||
| void teleOpAttachesGamepadAdaptorByDefault() { | ||
| SafeOpMode op = new TeleOpDefault(); | ||
| op.gamepad1 = new Gamepad(); | ||
| op.init(); | ||
| initialized.add(op); | ||
| assertTrue(op.publishGamepads); | ||
| assertTrue(op.orchestrator.findTopic("g1/a", Boolean.class).isPresent(), | ||
| "gamepad topics should be created for @TeleOp by default"); | ||
| } | ||
|
|
||
| @Test | ||
| void autoDoesNotAttachGamepadAdaptorByDefault() { | ||
| SafeOpMode op = new AutoDefault(); | ||
| op.gamepad1 = new Gamepad(); | ||
| op.init(); | ||
| initialized.add(op); | ||
| assertFalse(op.publishGamepads); | ||
| assertFalse(op.orchestrator.findTopic("g1/a", Boolean.class).isPresent(), | ||
| "no gamepad topics should be created for Auto by default"); | ||
| } | ||
|
|
||
| @Test | ||
| void teleOpOptOutDisablesGamepadPublishing() { | ||
| SafeOpMode op = new TeleOpOptOut(); | ||
| op.gamepad1 = new Gamepad(); | ||
| op.init(); | ||
| initialized.add(op); | ||
| assertFalse(op.publishGamepads, | ||
| "TeleOp with publishGamepads=false should not publish gamepads"); | ||
| assertFalse(op.orchestrator.findTopic("g1/a", Boolean.class).isPresent(), | ||
| "no gamepad topics should be created when opted out"); | ||
| } | ||
|
|
||
| @Test | ||
| void autoOptInEnablesGamepadPublishing() { | ||
| SafeOpMode op = new AutoOptIn(); | ||
| op.gamepad1 = new Gamepad(); | ||
| op.init(); | ||
| initialized.add(op); | ||
| assertTrue(op.publishGamepads, | ||
| "Auto opmode with publishGamepads=true should publish gamepads"); | ||
| assertTrue(op.orchestrator.findTopic("g1/a", Boolean.class).isPresent(), | ||
| "gamepad topics should be created when opted in"); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.