Skip to content

Fix/iterative init sleep - #30

Open
saltyeg wants to merge 2 commits into
Horizon-36596:mainfrom
saltyeg:fix/iterative-init-sleep
Open

saltyeg wants to merge 2 commits into
Horizon-36596:mainfrom
saltyeg:fix/iterative-init-sleep

Conversation

@saltyeg

@saltyeg saltyeg commented Oct 28, 2026

Copy link
Copy Markdown

What

An iterative OpMode's init() may now wait under forTeamClass. A Thread.sleep (or TimeUnit.sleep, SystemClock.sleep) there holds init() for ceil(ms / tick) ticks of sim time, so Thread.sleep(500) takes exactly 25 ticks of 20 ms. Before, any wait in an iterative OpMode was refused and failed the run. Waits in init_loop(), start(), loop() and stop() are still refused, and so are waits on threads the OpMode starts.

No public API changes. Only the SimClockScheduler javadoc changed, to describe the new case. This is a behaviour change: code that used to fail the run with a refusal now runs.

Why

The SDK lets init() block: OpMode.internalRunOpMode() calls it on the OpMode's own thread and starts init_loop() only once it returns. Libraries rely on that. Pedro Pathing's Pinpoint localizer sleeps 500 ms in its constructor while the device calibrates, and teams build the follower in init(). Such an OpMode works on the robot but could not run in SimLoop.

While init() waits, the sim matches the SDK:

  • Sim time moves, and devices and plants keep updating, so a motor started in init() keeps turning. No other OpMode method runs.
  • init_loop() starts on the tick after init() returns.
  • If PLAY came during the wait, init_loop() is skipped and start() and the first loop() run on the tick after init() returns.
  • If STOP came during the wait, the wait returns normally, not interrupted, because the SDK does not interrupt an iterative OpMode at STOP. Sim time ends at STOP, so the rest of the wait takes none. Then init() finishes, start() runs if PLAY had come, and stop() runs.
  • Gamepads and time are not updated during the wait, as the SDK updates them only before a call. getRuntime() follows the sim clock, including across a resetRuntime() made just before the wait.

How I tested it

  • New IterativeInitWaitTest (9 tests) pins each rule above. It covers the wait length and the tick init_loop() starts on, PLAY and STOP during the wait, a Pedro-style localizer that sleeps in its constructor (with SimClockLoader.PEDRO_PATHING in the team packages), the same telemetry on two runs, getRuntime() after resetRuntime() and then a sleep, and refusals in init_loop() and on a helper thread started in init().

  • ./gradlew test, ./gradlew :SimLoop:docsSite and bash tools/leak-gate/leak-gate.sh all pass.

  • ./gradlew test passes on my machine

  • New or changed behaviour has a test

  • If I touched docs or public javadoc: ./gradlew :SimLoop:docsSite passes

Checklist

  • No robot-specific or season-specific code (SimLoop stays season-agnostic)
  • Every new number says its unit, and its frame if it is a pose
  • Any public API change is called out above
  • If an AI agent wrote some of this, a human has read the change and run the tests

The SDK lets init() block, and libraries do: Pedro Pathing's Pinpoint
localizer sleeps 500 ms in its constructor, which teams build in init().
Under forTeamClass such a wait was refused. It now holds init() for whole
ticks of sim time, and init_loop() (or start(), if PLAY came first) runs
on the tick after it returns. STOP does not interrupt it, as on the robot.
Waits in init_loop(), start(), loop() and stop() are still refused.
getRuntime() read one tick short after resetRuntime() then a sleep in an
iterative OpMode's init(): the reset was only noticed when the wait
resumed on the next tick. Notice it when the wait starts instead.
@saltyeg

saltyeg commented Oct 28, 2026

Copy link
Copy Markdown
Author

Needed because we build PinpointLocalizer in init() which sleeps 500ms

saltyeg added a commit to Aragon-Robotics-Team/ftc-26-27-BIOBUZZ that referenced this pull request Nov 4, 2026
…tPack's build of the init() fix

setup-sim.bat installs JDK 17, Python (numpy, scipy, trimesh) and AdvantageScope, builds the sim field into
AdvantageScope and the sim tests once (doc/sim-windows.md). simloop.gradle takes SimLoop from saltyeg/simloop at the
fix for Horizon-36596/simloop#30, so nobody has to build it. replay_auto.py builds the field into the right folder on
Windows and Linux (it always wrote the Mac's) and finds JDK 17 on Windows.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant