Mono is an internal library that handles FTC control systems.
- Autonomous structuring/state machines
- Driver control gamepad button mapping
- Robot hardware modularization with Subsystems
State machines are used in autonomous implementations to plan out chains of tasks our robot takes (execution groups). You need to know three key terms to understand our autonomous programs:
single
: A single task that is executedsimultaneous
: A group of tasks executed simultaneously.consecutive
: A group of tasks executed consecutively.
You can start writing your execution group tasks in the body of a Mono.buildExecutionGroup { /* here */ }
call. Here is a simple example:
Mono.buildExecutionGroup {
single("Forward to tape") {
move(-BlueLeft.MoveForwardToTape)
}
}
And we can make it more complex...
Mono.buildExecutionGroup {
simultaneous("move into tape") {
single("Forward to tape") {
move(-BlueLeft.MoveForwardToTape)
}
single("set extender to intake") {
clawSubsystem.toggleExtender(
ExtendableClaw.ExtenderState.Intake,
force = true
)
}
}
}
And even more complex...
simultaneous("strafe into drop position") {
consecutive("strafe") {
single("strafe into position") {
strafe(-BlueLeft.StrafeIntoPosition)
}
single("sync into heading") {
turn(BlueLeft.TurnTowardsBackboard)
}
}
single("heighten elevator") {
elevatorSubsystem.configureElevatorManually(BlueLeft.ZElevatorDropExpectedHeight)
}
}
Gamepad mappings are used in our TeleOp code to make writing command executions triggered by the driver 1/2 gamepads easier. These gamepad mappings are created with a builder-style system. A simple example:
val gamepad = Mono.commands(gamepad1) // declare your GamepadCommands instance
// launch the airplane when you click the square button, and
// set it back to armed when you release the square button.
gp1Commands
.where(ButtonType.PlayStationSquare)
.triggers {
paperPlaneLauncher.launch()
}
.andIsHeldUntilReleasedWhere {
paperPlaneLauncher.reset()
}
This builder system makes it very easy for us to build deposit presets in TeleOp:
//depositPresetReleaseOnElevatorHeight is hidden
gp2Commands
.where(ButtonType.DPadLeft)
.depositPresetReleaseOnElevatorHeight(-630)
gp2Commands
.where(ButtonType.DPadUp)
.depositPresetReleaseOnElevatorHeight(-850)
gp2Commands
.where(ButtonType.DPadRight)
.depositPresetReleaseOnElevatorHeight(-1130)
Our robot code is split up into multiple classes through Subsystems. Subsystems are independent components of the robot. An example of a subsystem is: AirplaneLauncher
.
- Subsystem is registered in the subsystem registry (essentially a list of available subsystems we keep control over).
- When you press init on the opmode, subsystems are initialized.
- Calls the
doInitialize()
function within the Subsystem implementation.
- Calls the
- When the opmode is stopped, the subsystems are disposed.
- Calls the
dispose()
function within the Subsystem implementation.
- Calls the
Here is the real implementation of the AirplaneLauncher
:
class AirplaneLauncher(private val opMode: LinearOpMode) : AbstractSubsystem()
{
private val backingServo by lazy {
opMode.hardware<Servo>("launcher")
}
/**
* Puts the airplane launcher servo to the LAUNCHED position.
*/
fun launch()
{
backingServo.position = ClawExpansionConstants.MAX_PLANE_POSITION
}
/**
* Puts the airplane launcher servo to the ARMED position.
*/
fun arm()
{
backingServo.position = ClawExpansionConstants.DEFAULT_PLANE_POSITION
}
/**
* Initialize the servo when the subsystem is initalized, and arm the airplane launcher.
*/
override fun doInitialize()
{
arm()
}
// do nothing since servos don't need to be reset on end
override fun dispose()
{
}
}