Skip to main content

The Scheduler itself

🚪 The Scheduler is our entry point into the API- it's our Wardrobe, our Doors of Durin, our Ellis Island.

We saw some basic usage in the Overview, but now let's really get into it.

Scheduler opmode methods​

Scheduler.launch 🚀🚀🚀​

Scheduler.launch is what starts the entire Scheduler system, and internally hosts the loop that drives our OpModes.

Params
  • opmode: LinearOpMode - The LinearOpMode to run the Scheduler in
  • afterEach: Runnable - An optional block of code to run after every cycle, after the listeners have ran
    - Defaults to: () -> {}
    @Override
    public void runOpMode() throws InterruptedException {
    instantiateListeners();

    waitForStart();

    Scheduler.launch(this);

    // or

    Scheduler.launch(this, () -> {
    // Loop code, such as PIDs
    // Run at the end of every loop
    });
    }

    Scheduler.launchOnStart​

    Equivalent to waitForStart(); Scheduler.launch(...);

    Convenient for when you aren't doing anything on a loop during initialization until the game starts.

    Params
  • opmode: LinearOpMode - The LinearOpMode to run the Scheduler in
  • afterEach: Runnable - An optional block of code to run after every cycle, after the listeners have ran
    - Defaults to: () -> {}
    @Override
    public void runOpMode() throws InterruptedException {
    instantiateListeners();

    Scheduler.launchOnStart(this); // Waits until the opMode starts to launch

    // or

    Scheduler.launchOnStart(this, () -> {
    // Loop code, such as PIDs
    // Run at the end of every loop
    });
    }

    Scheduler.launchManually​

    Manually launches the Scheduler using the given lambda which returns a boolean, stopping when it returns false.

    Has all the same functionality of Scheduler.launch()

    Params
  • condition: () -> Boolean - The lambda to evaluate at the start of every loop to determine when to stop looping
    @Override
    public void runOpMode() throws InterruptedException {
    // Using int[] b/c of Java's effectively final thing
    final int[] counter = { 1 };

    // Could just pass it in directly too if you want
    Function0<Boolean> condition = () -> counter[0] <= 3;

    Scheduler.launchManually(condition, () -> { // Prints '123' then stops
    System.out.print(counter[0]);
    counter[0]++;
    });

    // Eqiv. to Scheduler.launch
    Scheduler.launchManually(() -> opModeIsActive() && !isStopRequested());
    }

    Scheduler.beforeEach​

    Runs a block of code before all the Listeners and the afterEach block (the one passed directly into Scheduler.launch runs).

    Params
  • block: Runnable - The block of code to run at the start of each Scheduler loop
    @Override
    public void runOpMode() throws InterruptedException {
    Listener.always(() -> { // Listener that runs every loop, called by static method
    System.out.print(2);
    });

    Scheduler.beforeEach(() -> { // Runs at the start of every loop
    System.out.print(1);
    });

    Scheduler.launch(this, () -> { // Prints '123' every loop
    System.out.print(3);
    });
    }

    Scheduler.debug​

    Utility method for to help benchmark performance or debug Scheduler-related issues

    It's also like Scheduler.launchManually in that you need to explicitly pass in the loop condition (scroll up if you need to read the documentation for that method for this to make sense)

    The 'afterEach' block now takes in a SchedulerDebugInfo that contains some debug information. The class definition is as follows:

    class SchedulerDebugInfo(
    val loopTime: Double, /* raw loop time, not averaged */
    val numHookedListeners: Int,
    val numUniqueMessageSubs: Int,
    )
    caution

    Keep in mind Blacksmith may internally subscribe to a few messages or something akin.

    Params
  • opmode: LinearOpMode - The LinearOpMode to run the Scheduler in
  • afterEach: (Consumer<SchedulerDebugInfo>) -> Void - An block of code to run every tick, after the listeners have ran. Takes in a 'SchedulerDebugInfo' instance.
    - Defaults to: () -> {}
    @Override
    public void runOpMode() throws InterruptedException {
    instantiateListeners();

    waitForStart();

    Scheduler.debug(() -> opModeIsActive() && !isStopRequested(), info -> {
    telemetry.addData("LoopTime (ms)", info.getLoopTime());
    telemetry.addData("Number of hooked listeners", info.getNumHookedListeners());
    telemetry.addData("Number of (unique) msg subscribers", info.getNumUniqueMessageSubs());
    telemetry.update();
    });
    }

    Scheduler messaging methods​

    A more niche functionality of the Scheduler is to directly send "messages" to invoke actions from the message receivers.

    It can be triggered among classes and doesn't have to be used in an OpMode; it's versatile and can be used anywhere, even without a running OpMode.

    Just be careful that your messages don't get mixed up and accidentally intercepted/interfere with each other.

    // The message can be anything you want
    // Just make sure it's comparable in some way, as it uses .equals() internally
    Scheduler.on("message", () -> {
    System.out.print("Hello,");
    });

    Scheduler.on("message", () -> {
    System.out.print(" world!");
    });

    Scheduler.emit("message"); // "Hello, world!" is printed
    Scheduler.emit("message"); // "Hello, world!" is printed again

    Object msg = new Object()
    Scheduler.emit(msg); // Nothing happens, no one is listening for it

    Scheduler.emit​

    Broadcasts a message for anyone to receive with Scheduler.emit.

    Params
  • message: Any - The "message" to send, can be literally anything (doesn't have to be of any significant, just that both the sender and the receiver have to know it). NOTE: this uses .equals() to compare messages, so make sure the message is comparable in some way.
  • Scheduler.on​

    Registers a message receiver, which will be called whenever a message is sent with Scheduler.emit.

    Params
  • message: Any - The "message" to listen for, can be literally anything (doesn't have to be of any significant, just that both the sender and the receiver have to know it). NOTE: this uses .equals() to compare messages, so make sure the message is comparable in some way.
  • block: Runnable - The block of code to run when the message is received
  • Other Scheduler methods​

    Scheduler.nuke​

    Goes nuclear.

    Destroys everything Scheduler API related.

    Clears and unhooks all of the listeners, clears all of the messages, clears the beforeEach block.

    Basically just a hard reset. But with a cooler name. Thanks GHCup.

    you can still reuse all the listeners and stuff by just resubscribing actions to them

    Optionally, if you want to only nuke specific parts of the Scheduler, you can pass in Nuke.Schedulables, Nuke.Messages, or Nuke.BeforeEach (you can pass in multiple as well since it's varargs)

    danger

    Nuking the 'Schedulables' means nuking things that implement/use Schedulable, including listeners, timers, Ons, pulsars, gamepad listeners, etc.

    @Override
    public void runOpMode() throws InterruptedException {
    Scheduler.beforeEach(() -> System.out.println("Hi!"));

    Listener(() -> true).whileHigh(() -> System.out.println("I'm high!"));

    Scheduler.on("message", () -> ...)

    Scheduler.nuke() // Nukes everything

    // Scheduler.beforeEach == () -> {}
    // Scheduler.listeners.size == 0
    // Scheduler.messages.size == 0

    Scheduler.nuke(Nuke.Schedulables) // Only nukes the listeners & other schedulables

    // (also you can static import for readability)
    Scheduler.nuke(Schedulables, BeforeEach) // Nukes everything besides messages
    }
    Params
  • toNuke: vararg NukeFlag - What parts of the Scheduler to nuke (hard reset). Defaults to nuking everything.
    - Defaults to: Nuke.All