Bindings

A registry of named inputs that decouples where a control comes from (a gamepad button, a stick axis, a robot state) from the code that reacts to it. You wire inputs once by id, then query them by id from anywhere.

The bindings system lives in com.cdarobotics.cdalib.bindings. There are two flavors of control:

  • Boolean controls — a Binding wrapping a BooleanSupplier (e.g. a button press or a state condition).
  • Analog controls — an AnalogBinding wrapping a DoubleSupplier (e.g. a trigger pull or a joystick axis).

Both are registered with, and queried through, a BindingManager. Because reactive code only knows the id string — never which physical control is behind it — you can rebind a control in one place without touching the logic that consumes it. This is exactly how the reusable modules find their inputs: TeleOpPedroModule reads the ids "drive", "strafe", "turn", and "slowMode".

BindingManager

Holds a registry of boolean bindings and a registry of analog bindings, each keyed by id (internally sorted maps). The add* methods are non-destructive — they refuse to overwrite an existing id — while the replace* methods always overwrite.

MethodReturnsBehavior
addBinding(String id, BooleanSupplier condition)boolean Registers a new boolean binding built from the supplier. Returns true if added, false if the id was already taken (existing binding left untouched).
addBinding(Binding binding)boolean Registers a pre-built Binding under its own id. Returns false if that id was already taken. Use this overload to add a ToggleBinding.
replaceBinding(String id, BooleanSupplier condition)void Registers a boolean binding under the id, overwriting any existing one.
replaceBinding(Binding binding)void Registers the given binding under its own id, overwriting any existing one.
addAnalog(String id, DoubleSupplier provider)boolean Registers a new analog binding. Returns true if added, false if the id was already taken.
replaceAnalog(String id, DoubleSupplier provider)void Registers an analog binding under the id, overwriting any existing one.
replaceAnalog(AnalogBinding analog)void Registers the given analog binding under its own id, overwriting any existing one.
checkBinding(String id)boolean Evaluates the boolean binding registered under the id and returns its current status. Returns false if no non-null binding is registered under that id.
checkAnalog(String id)double Reads the analog binding registered under the id. Returns 0 if no non-null analog binding is registered under that id.
Missing ids are safe. checkBinding returns false and checkAnalog returns 0 for an unknown id, so consumers never NPE on a control that was never wired. A module that expects "slowMode" simply behaves as if the button is unpressed until you bind it.

Binding

A named boolean condition. It pairs a BooleanSupplier with an id and evaluates the condition on demand.

MemberSignatureDescription
constructorBinding(BooleanSupplier condition, String id)Wraps a condition under an id.
getStatusboolean getStatus()Evaluates the condition right now.
getIdString getId()Returns the id.
getConditionBooleanSupplier getCondition()Returns the underlying supplier.

AnalogBinding

A named analog input. It pairs a DoubleSupplier with an id and reads the value on demand.

MemberSignatureDescription
constructorAnalogBinding(DoubleSupplier provider, String id)Wraps a value source under an id.
getValuedouble getValue()Reads the current value right now.
getIdString getId()Returns the id.
getProviderDoubleSupplier getProvider()Returns the underlying supplier.

ToggleBinding

A Binding subclass that treats its condition as a momentary button and flips a latched state on every rising edge (a transition from false to true). getStatus() returns the latched toggle state, not the raw button.

ToggleBinding(BooleanSupplier condition, String id)

The implementation tracks the previous raw value: when the raw condition is currently pressed and was not pressed last time, it inverts the toggle. So a single press-and-release turns it on, the next turns it off, and holding the button does not repeatedly flip it.

Poll a toggle exactly once per loop. Edge detection is stateful — each call to getStatus() updates the "last pressed" memory. Calling it more than once per loop (for instance, checking the same id twice) can miss or double-count edges. Register it once and read it once via checkBinding.

End-to-end example

Wire physical controls in your OpMode's preload(), then read them by id anywhere — including inside library modules that never reference your gamepads directly.

@Override
public void preload() {
    // Boolean controls: id -> button
    bindingManager.addBinding("slowMode", () -> gamepad1.left_bumper);

    // A latching control: press once to toggle field-centric on/off
    bindingManager.addBinding(
        new ToggleBinding(() -> gamepad1.y, "fieldCentric"));

    // Analog controls: id -> stick axis (note the negated Y for forward = +)
    bindingManager.addAnalog("drive",  () -> -gamepad1.left_stick_y);
    bindingManager.addAnalog("strafe", () ->  gamepad1.left_stick_x);
    bindingManager.addAnalog("turn",   () ->  gamepad1.right_stick_x);
}

Elsewhere — in a subsystem, a module, or the loop — consume them by id:

double drive  = bindingManager.checkAnalog("drive");    // 0 if unbound
double strafe = bindingManager.checkAnalog("strafe");
double turn   = bindingManager.checkAnalog("turn");

boolean slow  = bindingManager.checkBinding("slowMode"); // false if unbound
if (slow) {
    drive  *= 0.4;
    strafe *= 0.4;
    turn   *= 0.4;
}

Because TeleOpPedroModule reads exactly the ids "drive", "strafe", "turn", and "slowMode", wiring those four in preload() is all it takes to drive the robot — see OpMode and Modules.