magicbot module

class magicbot.magicrobot.MagicRobot[source]

Bases: RobotBase

Robots that use the MagicBot framework should use this as their base robot class. If you use this as your base, you must implement the following methods:

MagicRobot uses the AutonomousModeSelector to allow you to define multiple autonomous modes and to select one of them via the SmartDashboard/Shuffleboard.

MagicRobot will set the following NetworkTables variables automatically:

  • /robot/mode: one of ‘disabled’, ‘auto’, ‘teleop’, or ‘test’

  • /robot/is_simulation: True/False

  • /robot/is_ds_attached: True/False

Constructor for a generic robot program.

User code can be placed in the constructor that runs before the Autonomous or Operator Control period starts. The constructor will run to completion before Autonomous is entered.

This must be used to ensure that the communications code starts. In the future it would be nice to put this code into it’s own task that loads on boot so ensure that it runs.

autonomousInit()[source]

Initialization code for autonomous mode may go here.

Users may override this method for initialization code which will be called each time the robot enters autonomous mode, regardless of the selected autonomous mode.

This can be useful for code that must be run at the beginning of a match. :rtype: None

Note

This method is called after every component’s on_enable method, but before the selected autonomous mode’s on_enable method.

consumeExceptions(forceReport=False)[source]

This returns a context manager which will consume any uncaught exceptions that might otherwise crash the robot.

Example usage:

def teleopPeriodic(self):
    with self.consumeExceptions():
        if self.joystick.getTrigger():
            self.shooter.shoot()

    with self.consumeExceptions():
        if self.joystick.getRawButton(2):
            self.ball_intake.run()

    # and so on...
Parameters:

forceReport (bool) – Always report the exception to the DS. Don’t set this to True

See also

onException() for more details

control_loop_wait_time = 0.02

Amount of time each loop takes (default is 20ms)

createObjects()[source]

You should override this and initialize all of your wpilib objects here (and not in your components, for example). This serves two purposes: :rtype: None

  • It puts all of your motor/sensor initialization in the same place, so that if you need to change a port/pin number it makes it really easy to find it. Additionally, if you want to create a simplified robot program to test a specific thing, it makes it really easy to copy/paste it elsewhere

  • It allows you to use the magic injection mechanism to share variables between components

Note

Do not access your magic components in this function, as their instances have not been created yet. Do not create them either.

disabledInit()[source]

Initialization code for disabled mode may go here.

Users may override this method for initialization code which will be called each time the robot enters disabled mode. :rtype: None

Note

The on_disable functions of all components are called before this function is called.

disabledPeriodic()[source]

Periodic code for disabled mode should go here.

Users should override this method for code which will be called periodically at a regular rate while the robot is in disabled mode.

This code executes before the execute functions of all components are called.

endCompetition()[source]

Ends the main loop in StartCompetition().

Return type:

None

error_report_interval = 0.5

Error report interval: when an FMS is attached, how often should uncaught exceptions be reported?

logger = <Logger robot (WARNING)>

A Python logging object that you can use to send messages to the log. It is recommended to use this instead of print statements.

onException(forceReport=False)[source]

This function must only be called when an unexpected exception has occurred that would otherwise crash the robot code. Use this inside your operatorActions() function.

If the FMS is attached (eg, during a real competition match), this function will return without raising an error. However, it will try to report one-off errors to the Driver Station so that it will be recorded in the Driver Station Log Viewer. Repeated errors may not get logged.

Example usage:

def teleopPeriodic(self):
    try:
        if self.joystick.getTrigger():
            self.shooter.shoot()
    except:
        self.onException()

    try:
        if self.joystick.getRawButton(2):
            self.ball_intake.run()
    except:
        self.onException()

    # and so on...
Parameters:

forceReport (bool) – Always report the exception to the DS. Don’t set this to True

Return type:

None

robotPeriodic()[source]

Periodic code for all modes should go here.

Users must override this method to utilize it but it is not required.

This function gets called last in each mode. You may use it for any code you need to run during all modes of the robot (e.g NetworkTables updates)

The default implementation will update SmartDashboard, LiveWindow and Shuffleboard.

Return type:

None

startCompetition()[source]

This runs the mode-switching loop. :rtype: None

Warning

Internal API, don’t override

teleopInit()[source]

Initialization code for teleop control code may go here.

Users may override this method for initialization code which will be called each time the robot enters teleop mode. :rtype: None

Note

The on_enable functions of all components are called before this function is called.

teleopPeriodic()[source]

Periodic code for teleop mode should go here.

Users should override this method for code which will be called periodically at a regular rate while the robot is in teleop mode.

This code executes before the execute functions of all components are called.

Note

If you want this function to be called in autonomous mode, set use_teleop_in_autonomous to True in your robot class.

testInit()[source]

Initialization code for test mode should go here.

Users should override this method for initialization code which will be called each time the robot enters disabled mode.

Return type:

None

testPeriodic()[source]

Periodic code for test mode should go here.

Return type:

None

use_teleop_in_autonomous = False

If True, teleopPeriodic will be called in autonomous mode

Component

class magicbot.magiccomponent.MagicComponent[source]

Bases: object

To automagically retrieve variables defined in your base robot object, you can add the following:

class MyComponent:

    # other variables 'imported' automatically from MagicRobot
    elevator_motor: Talon
    other_component: MyOtherComponent

    ...

    def execute(self):

        # This will be automatically set to the Talon
        # instance created in robot.py
        self.elevator_motor.set(self.value)

What this says is “find the variable in the robot class called ‘elevator_motor’, which is a Talon”. If the name and type match, then the variable will automatically be injected into your component when it is created.

Note

You don’t need to inherit from MagicComponent, it is only provided for documentation’s sake

execute()[source]

This function is called at the end of the control loop

Return type:

None

logger: Logger
on_disable()[source]

Called when the robot leaves autonomous or teleoperated

Return type:

None

on_enable()[source]

Called when the robot enters autonomous or teleoperated mode. This function should initialize your component to a “safe” state so that unexpected things don’t happen when enabling the robot. :rtype: None

Note

You’ll note that there isn’t a separate initialization function for autonomous and teleoperated modes. This is intentional, as they should be the same.

setup()[source]

This function is called after createObjects has been called in the main robot class, and after all components have been created

The setup function is optional and components do not have to define one. setup() functions are called in order of component definition in the main robot class. :rtype: None

Note

For technical reasons, variables imported from MagicRobot are not initialized when your component’s constructor is called. However, they will be initialized by the time this function is called.

Tunable

class magicbot.magic_tunable.StructSerializable(*args, **kwargs)[source]

Bases: Protocol

Any type that is a wpiutil.wpistruct.

WPIStruct: ClassVar
magicbot.magic_tunable.collect_feedbacks(component, cname, prefix='components')[source]

Finds all methods decorated with feedback() on an object and returns a list of 2-tuples (method, NetworkTables entry setter).

Note

This isn’t useful for normal use.

magicbot.magic_tunable.feedback(f=None, *, key=None)[source]

This decorator allows you to create NetworkTables values that are automatically updated with the return value of a method.

key is an optional parameter, and if it is not supplied, the key will default to the method name with a leading get_ removed. If the method does not start with get_, the key will be the full name of the method.

The key of the NetworkTables value will vary based on what kind of object the decorated method belongs to: :rtype: Callable

  • A component: /components/COMPONENTNAME/VARNAME

  • Your main robot class: /robot/VARNAME

The NetworkTables value will be auto-updated in all modes.

Warning

The function should only act as a getter, and must not take any arguments (other than self).

Example:

from magicbot import feedback

class MyComponent:
    navx: ...

    @feedback
    def get_angle(self) -> float:
        return self.navx.getYaw()

class MyRobot(magicbot.MagicRobot):
    my_component: MyComponent

    ...

In this example, the NetworkTable key is stored at /components/my_component/angle.

See also

LiveWindow may suit your needs, especially if you wish to monitor WPILib objects.

Added in version 2018.1.0.

Changed in version 2024.1.0: WPILib Struct serializable types are supported when the return type is type hinted. An int return type hint now creates an integer topic.

magicbot.magic_tunable.setup_tunables(component, cname, prefix='components')[source]

Connects the tunables on an object to NetworkTables.

Parameters:
  • component – Component object

  • cname (str) – Name of component

  • prefix (Optional[str]) – Prefix to use, or no prefix if None

Return type:

None

Note

This is not needed in normal use, only useful for testing

class magicbot.magic_tunable.tunable(default, *, writeDefault=True, subtable=None, doc=None)[source]

Bases: Generic[V]

This allows you to define simple properties that allow you to easily communicate with other programs via NetworkTables.

The following example will define a NetworkTable variable at /components/my_component/foo:

class MyRobot(magicbot.MagicRobot):

    my_component: MyComponent

...

from magicbot import tunable

class MyComponent:

    # define the tunable property
    foo = tunable(True)

    def execute(self):

        # set the variable
        self.foo = True

        # get the variable
        foo = self.foo

The key of the NetworkTables variable will vary based on what kind of object the decorated method belongs to:

  • A component: /components/COMPONENTNAME/VARNAME

  • An autonomous mode: /autonomous/MODENAME/VARNAME

  • Your main robot class: /robot/VARNAME

Note

When executing unit tests on objects that create tunables, you will want to use setup_tunables to set the object up. In normal usage, MagicRobot does this for you, so you don’t have to do anything special.

Changed in version 2024.1.0: Added support for WPILib Struct serializable types. Integer defaults now create integer topics instead of double topics.

Resettable

magicbot.magic_reset.collect_resets(cls)[source]

Get all the will_reset_to variables and their values from a class. :rtype: dict[str, Any]

Note

This isn’t useful for normal use.

class magicbot.magic_reset.will_reset_to(default)[source]

Bases: Generic[V]

This marker indicates that this variable on a component will be reset to a default value at the very end of each control loop.

Example usage:

class Component:

    foo = will_reset_to(False)

    def control_fn(self):
        self.foo = True

    def execute(self):
        if self.foo:
            # ...

        # after all components are executed, foo is reset
        # back to the default value (False)

Note

This will only work for MagicRobot components

Warning

This will not work on classes that set __slots__.

default

State machines

class magicbot.state_machine.AutonomousStateMachine[source]

Bases: StateMachine

This is a specialized version of the StateMachine that is designed to be used as an autonomous mode. There are a few key differences:

  • The engage() function is always called, so the state machine will always run to completion unless done() is called

  • VERBOSE_LOGGING is set to True, so a log message will be printed out upon each state transition

VERBOSE_LOGGING = True
done()[source]

Call this function to end execution of the state machine.

This function will always be called when a state machine ends. Even if the engage function is called repeatedly, done() will be called. :rtype: None

Note

If you wish to do something each time execution ceases, override this function (but be sure to call super().done()!)

on_enable()[source]

magicbot component API: called when autonomous/teleop is enabled

Return type:

None

on_iteration(tm)[source]
Return type:

None

exception magicbot.state_machine.IllegalCallError[source]

Bases: TypeError

exception magicbot.state_machine.InvalidStateName[source]

Bases: ValueError

exception magicbot.state_machine.MultipleDefaultStatesError[source]

Bases: ValueError

exception magicbot.state_machine.MultipleFirstStatesError[source]

Bases: ValueError

exception magicbot.state_machine.NoFirstStateError[source]

Bases: ValueError

class magicbot.state_machine.StateMachine[source]

Bases: object

The StateMachine class is used to implement magicbot components that allow one to easily define a finite state machine (FSM) that can be executed via the magicbot framework.

You create a component class that inherits from StateMachine. Each state is represented as a single function, and you indicate that a function is a particular state by decorating it with one of the following decorators:

As the state machine executes, the decorated function representing the current state will be called. Decorated state functions can receive the following parameters (all of which are optional):

  • tm - The number of seconds since autonomous has started

  • state_tm - The number of seconds since this state has been active (note: it may not start at zero!)

  • initial_call - Set to True when the state is initially called, False otherwise. If the state is switched to multiple times, this will be set to True at the start of each state.

To be consistent with the magicbot philosophy, in order for the state machine to execute its states you must call the engage() function upon each execution of the main robot control loop. If you do not call this function, then execution of the FSM will cease.

Note

If you wish for the FSM to continue executing state functions regardless whether engage() is called, you must set the must_finish parameter in your state decorator to be True.

When execution ceases (because engage() was not called), the done() function will be called and the FSM will be reset to the starting state. The state functions will not be called again unless engage is called.

As a magicbot component, StateMachine contains an execute function that will be called on each control loop. All state execution occurs from within that function call. If you call other components from a StateMachine, you should ensure that your StateMachine is declared before the other components in your Robot class.

Warning

As StateMachine already contains an execute function, there is no need to define your own execute function for a state machine component – if you override execute, then the state machine may not work correctly. Instead, use the @default_state decorator.

Here’s a very simple example of how you might implement a shooter automation component that moves a ball into a shooter when the shooter is ready:

class ShooterAutomation(magicbot.StateMachine):

    # Some other component
    shooter: Shooter
    ball_pusher: BallPusher

    def fire(self):
        """This is called from the main loop."""
        self.engage()

    @state(first=True)
    def begin_firing(self):
        """
        This function will only be called IFF fire is called and
        the FSM isn't currently in the 'firing' state. If fire
        was not called, this function will not execute.
        """
        self.shooter.enable()
        if self.shooter.ready():
            self.next_state('firing')

    @timed_state(duration=1.0, must_finish=True)
    def firing(self):
        """
        Because must_finish=True, once the FSM has reached this state,
        this state will continue executing even if engage isn't called.
        """
        self.shooter.enable()
        self.ball_pusher.push()

    #
    # Note that there is no execute function defined as part of
    # this component
    #

...

class MyRobot(magicbot.MagicRobot):
    shooter_automation: ShooterAutomation

    shooter: Shooter
    ball_pusher: BallPusher

    def teleopPeriodic(self):

        if self.joystick.getTrigger():
            self.shooter_automation.fire()

This object has a lot of really useful NetworkTables integration as well:

  • tunables are created in /components/NAME/state - state durations can be tuned here - The ‘current state’ is output as it happens - Descriptions and names of the states are here (for dashboard use)

Warning

This object is not intended to be threadsafe and should not be accessed from multiple threads

VERBOSE_LOGGING = False
current_state

NT variable that indicates which state will be executed next (though, does not guarantee that it will be executed). Will return an empty string if the state machine is not currently engaged.

done()[source]

Call this function to end execution of the state machine.

This function will always be called when a state machine ends. Even if the engage function is called repeatedly, done() will be called. :rtype: None

Note

If you wish to do something each time execution ceases, override this function (but be sure to call super().done()!)

engage(initial_state=None, force=False)[source]

This signals that you want the state machine to execute its states.

Parameters:
  • initial_state (Union[str, _State, None]) – If specified and execution is not currently occurring, start in this state instead of in the ‘first’ state

  • force (bool) – If True, will transition even if the state machine is currently active.

Return type:

None

execute()[source]

magicbot component API: This is called on each iteration of the control loop. Most of the time, you will not want to override this function. If you find you want to, you may want to use the @default_state mechanism instead.

Return type:

None

property is_executing: bool
Returns:

True if the state machine is executing states

logger: Logger

A Python logging object automatically injected by magicbot. It can be used to send messages to the log, instead of using print statements.

next_state(state)[source]

Call this function to transition to the next state

Parameters:

state (Union[str, _State]) – Name of the state to transition to

Return type:

None

Note

This should only be called from one of the state functions

next_state_now(state)[source]

Call this function to transition to the next state, and call the next state function immediately. Prefer to use next_state() instead.

Parameters:

state (Union[str, _State]) – Name of the state to transition to

Return type:

None

Note

This should only be called from one of the state functions

on_disable()[source]

magicbot component API: called when autonomous/teleop is disabled

Return type:

None

on_enable()[source]

magicbot component API: called when autonomous/teleop is enabled

Return type:

None

state_descriptions: ClassVar[tunable[Sequence[str]]]
state_names: ClassVar[tunable[Sequence[str]]]
magicbot.state_machine.default_state(f)[source]

If this decorator is applied to a method in an object that inherits from StateMachine, it indicates that the method is a default state; that is, if no other states are executing, this state will execute. If the state machine is always executing, the default state will never execute.

There can only be a single default state in a StateMachine object.

The decorated function can have the following arguments in any order: :rtype: _State

  • tm - The number of seconds since the state machine has started

  • state_tm - The number of seconds since this state has been active (note: it may not start at zero!)

  • initial_call - Set to True when the state is initially called, False otherwise. If the state is switched to multiple times, this will be set to True at the start of each state execution.

magicbot.state_machine.state(f=None, *, first=False, must_finish=False)[source]

If this decorator is applied to a function in an object that inherits from StateMachine, it indicates that the function is a state. The state will continue to be executed until the next_state function is executed.

The decorated function can have the following arguments in any order:

  • tm - The number of seconds since the state machine has started

  • state_tm - The number of seconds since this state has been active (note: it may not start at zero!)

  • initial_call - Set to True when the state is initially called, False otherwise. If the state is switched to multiple times, this will be set to True at the start of each state execution.

Parameters:
  • first (bool) – If True, this state will be ran first

  • must_finish (bool) – If True, then this state will continue executing even if engage() is not called. However, if done() is called, execution will stop regardless of whether this is set.

Return type:

Union[Callable[[Callable[..., None]], _State], _State]

magicbot.state_machine.timed_state(*, duration, next_state=None, first=False, must_finish=False)[source]

If this decorator is applied to a function in an object that inherits from StateMachine, it indicates that the function is a state that will run for a set amount of time unless interrupted.

It is guaranteed that a timed_state will execute at least once, even if it expires prior to being executed.

The decorated function can have the following arguments in any order:

  • tm - The number of seconds since the state machine has started

  • state_tm - The number of seconds since this state has been active (note: it may not start at zero!)

  • initial_call - Set to True when the state is initially called, False otherwise. If the state is switched to multiple times, this will be set to True at the start of each state execution.

Parameters:
  • duration (float) – The length of time to run the state before progressing to the next state

  • next_state (Union[str, _State, None]) – The name of the next state. If not specified, then this will be the last state executed if time expires

  • first (bool) – If True, this state will be ran first

  • must_finish (bool) – If True, then this state will continue executing even if engage() is not called. However, if done() is called, execution will stop regardless of whether this is set.

Return type:

Callable[[Callable[..., None]], _State]