magicbot module

class magicbot.magicrobot.MagicRobot[source]

Bases: wpilib.samplerobot.SampleRobot

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/SFX.

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
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 – 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:

  • 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.

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.

error_report_interval = 0.5

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

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 – Always report the exception to the DS. Don’t set this to True
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 and LiveWindow.

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.

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.

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.

testPeriodic()[source]

Periodic code for test mode should go here.

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

on_disable()[source]

Called when the robot leaves autonomous or teleoperated

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.

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.

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

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

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

Note

This isn’t useful for normal use.

magicbot.magic_tunable.feedback(f=None, *, key: str = 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:

  • 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):
        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.

New in version 2018.1.0.

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 (str) – Prefix to use, or no prefix if None

Note

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

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

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.

Resettable

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

Bases: object

This marker indicates that this variable on a component will be reset to a default value after each time that execute is called.

Example usage:

class Component:

    foo = will_reset_to(False)
    
    def control_fn(self):
        self.foo = True
        
    def execute(self):
        if self.foo:
            # ... 
            
        # after this function is executed, foo is reset
        # back to the default value (False)

Note

This will only work for MagicRobot components

State machines

class magicbot.state_machine.AutonomousStateMachine[source]

Bases: magicbot.state_machine.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.

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

on_iteration(tm)[source]
exception magicbot.state_machine.IllegalCallError[source]

Bases: Exception

exception magicbot.state_machine.InvalidStateName[source]

Bases: Exception

exception magicbot.state_machine.InvalidWrapperError[source]

Bases: Exception

exception magicbot.state_machine.MultipleDefaultStatesError[source]

Bases: Exception

exception magicbot.state_machine.MultipleFirstStatesError[source]

Bases: Exception

exception magicbot.state_machine.NoFirstStateError[source]

Bases: Exception

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 component, you should ensure that your component is defined 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:

    # 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):

    ...
    
    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.

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 – If specified and execution is not currently occurring, start in this state instead of in the ‘first’ state
  • force – If True, will transition even if the state machine is currently active.
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.

is_executing
Returns:True if the state machine is executing states
next_state(name)[source]

Call this function to transition to the next state

Parameters:name – Name of the state to transition to

Note

This should only be called from one of the state functions

next_state_now(name)[source]

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

Parameters:name – Name of the state to transition to

Note

This should only be called from one of the state functions

on_disable()[source]

magicbot component API: called when autonomous/teleop is disabled

on_enable()[source]

magicbot component API: called when autonomous/teleop is enabled

magicbot.state_machine.default_state(f=None)[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:

  • 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.
magicbot.state_machine.timed_state(f=None, *, duration=None, 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 (str) – 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.