diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts index 1693433..3a42c9a 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -65,6 +65,16 @@ export const sidebarSections: Record = { }, ], + '/section-2-commands': [ + { + label: 'Section 2: Commands', + items: [ + { label: 'Why use Commands?', slug: 'section-2-commands/why-commands' }, + { label: 'Why use Mechanisms?', slug: 'section-2-commands/why-mechanisms' }, + { label: 'The Command Scheduler', slug: 'section-2-commands/the-command-scheduler' }, + ], + } + ] // Intro To Java section '/intro-to-java': [ { diff --git a/src/content/docs/section-2-commands/the-command-scheduler.mdx b/src/content/docs/section-2-commands/the-command-scheduler.mdx new file mode 100644 index 0000000..167a976 --- /dev/null +++ b/src/content/docs/section-2-commands/the-command-scheduler.mdx @@ -0,0 +1,33 @@ +--- +title: The Command Scheduler +description: How to use the command scheduler +prev: section-2-commands/why-mechanisms +next: false +--- +import Aside from '../../../components/Aside.astro'; + +# The Command Scheduler + +Commands within commands v3 are run and handled by the command scheduler. +The command scheduler can be fetched via ```Scheduler.getDefault()```. + +In order for commands to work, you must call ```Scheduler.getDefault().run()``` +periodically, in the ```robotPeriodic``` method of your robot class. This will work +with robot classes that extend ```TimedRobot``` and ```OpModeRobot```. + +```java +public class Robot extends TimedRobot { + @Override + public void robotPeriodic() { + Scheduler.getDefault().run(); + } +} +``` + + + diff --git a/src/content/docs/section-2-commands/why-commands.mdx b/src/content/docs/section-2-commands/why-commands.mdx new file mode 100644 index 0000000..251ae03 --- /dev/null +++ b/src/content/docs/section-2-commands/why-commands.mdx @@ -0,0 +1,116 @@ +--- +title: Why Commands +description: The what, how, and why of commands +prev: false +next: section-2-commands/why-mechanisms +--- +import Aside from '../../../components/Aside.astro'; + +# What is a Command? + +A command is an action a robot can take - which can include something as simple +as raising an arm joint or complex as an autonomous program. Think of them as powered-up +methods that can handle the requirements for controlling a robot over long periods of time. + +# What's wrong with methods? + +Let's say you're writing a method that prints "hello world!" to the console, +then sets a motor to 5 volts indefinitely. Your first instinct might be to do this: +```java +public void printHiAndRunMotor() { + System.out.println("Hello World!"); + while (true) { + motor.setVoltage(5.0); + } +} +``` +Here, the problem is with the while loop. While it's running, we are only setting the voltage of the motor; +not listening to button presses, logging data, and running other background tasks. To function properly, WPILib +requires many of these background tasks to be run periodically(at a 0.02 second or smaller interval). + +## Enter: Commands! + +Functionally, commands are superpowered methods that gives you a special statement that runs +these background tasks within a while loop: `coroutine.yield()`. +```java +System.out.println("Hello World!"); +while (true) { + motor.setVoltage(5.0); + coroutine.yield(); +} +``` +In other words, it "yields control" to any pending background tasks before resuming the while loop. + + + +# Command Syntax + +A fully constructed command looks like this: +```java +Command runMotor = + Command.noRequirements(coroutine -> { + System.out.println("Hello World!"); + while (true) { + motor.setVoltage(5.0); + coroutine.yield(); + } + }) + .named("Set to 5 volts"); +``` + +There's a lot to digest here, so let's break it down step-by-step. +- Even though commands are more like methods, they act like objects in java. +Hence, the `Command runMotor` definition. +- `Command.noRequirements` creates the command. +We'll explain what "noRequirements" means in the following section. +- Every line of code between `coroutine -> {` and `}` will be run +when the command runs: just like a method body. +- `named("Set to 5 Volts")` gives the command a name, which allows programmers +to distinguish this command from others when using debugging tools. + +# Running Commands + +The following allows you to start a command asynchronously: + +```java +Command runMotor = ...; // see previous example +Scheduler.getDefault().schedule(runMotor); +System.out.println("Hello!"); +``` + +Think of calling `schedule()` as ordering a robot to start washing the dishes +without waiting for it to finish. In this case, the `println("Hello!")` statement +will run before the `runMotor` command completes. + +But the vast majority of the time, you want a command to run when a button is pressed or held down. +To do this, we use a `CommandGamepad` instead of a `Gamepad`, like so: +```java +private CommandGamepad xbox = new CommandGamepad(0); + +public Robot() { + Command runMotor = ...; + xbox.leftTrigger().onTrue(runMotor); +} +``` + +In this case, the `runMotor` command will run once the left trigger is pressed. +If it is desired to cancel the runMotor command once the left trigger is released, +the `onTrue` statement can be replaced with `whileTrue`: +```java +public Robot() { + Command runMotor = ...; + driverController.leftTrigger().whileTrue(runMotor); +} +``` + +Generally, whileTrue is desireable over onTrue since it provides a way for a driver/operator +to quickly cancel a command if it goes awry. + + + + + + diff --git a/src/content/docs/section-2-commands/why-mechanisms.mdx b/src/content/docs/section-2-commands/why-mechanisms.mdx new file mode 100644 index 0000000..3579bdb --- /dev/null +++ b/src/content/docs/section-2-commands/why-mechanisms.mdx @@ -0,0 +1,140 @@ +--- +title: Mechanisms in Command-Based Programming +description: Why we need mechanisms in command-based +prev: section-2-commands/why-commands +next: section-2-commands/the-command-scheduler +--- +import Aside from '../../../components/Aside.astro'; + +# An overview of Mechanisms + +A `Mechanism`, on the surface level, is a part of the robot, like a double-jointed arm or a shooter. +If you've used commands V2 before, they're the exact same thing as subsystems (and in that case, feel free +to skip to the "Defining a Mechanism" section). + +# Conflicting Commands + +Let's say we have the following code: + +```java +CommandGamepad driverController = new CommandGamepad(0); +Command shootAt5Volts = ...; // assume we have a command defined here. +Command shootAt7Volts = ...; // same here. +driverController.leftTrigger().whileTrue(shootAt5Volts); +driverController.rightTrigger().whileTrue(shootAt7Volts); +``` + +When we hold down the left trigger, the shooter is ran at 5 volts, and when +we hold down the right trigger, it's set to 7 volts. Pretty simple. + +Notice here, though, that it's possible for both triggers to be held down at the same time, +which would cause `shootAt5Volts` and `shootAt7Volts` to run simultaneously. This is obviously +undefined behavior that we want to prevent - but how? + +# Using Mechanisms to fix conflicting commands + +Ideally, we could declare what motors each command requires. If 2 commands that +required the same motor were ran simultaneously, one would get cancelled. + +However, there are many varieties of motors and actuating components. As a result, the commands +v3 framework leaves it to the coder to group these motors/components into categories, called mechanisms. +Each command will then declare the mechanisms they require. + +For instance, the 4 motors that control a robot's movement on the field could be grouped into a "drivetrain" mechanism. +If we were to declare a command that drives the robot forward by 5 meters, it would require this drivetrain mechanism. + +# Defining a Mechanism + +Mechanisms are represented as classes in java, inside their own separate files. +Note that a mechanism class slightly deviates from a traditional class with the `implements Mechanism` keyword at the end. + +```java +public class Shooter implements Mechanism { + private final TalonFX motor = new TalonFX(4); +} +``` + +Then, in the `Robot.java` file, you would simply define an instance of each mechanism like so: +```java +public class Robot { + private final Shooter shooter = new Shooter(); +} +``` + +# Defining commands that require a mechanism + +In general, commands that require a mechanism should be defined inside of that mechanism's class. +If that approach is followed, you can create a command that requires the shooter mechanism like so: + +```java +public class Shooter implements Mechanism { + private final TalonFX motor = new TalonFX(4); + + public Shooter() { + Command shoot = + run(coroutine -> { + while (true) { + motor.setVoltage(5,0); + coroutine.yield(); + } + }) + .named("Shoot"); + } +} +``` + + + +However, this poses a problem: the `shoot` command isn't accessible to the Robot class, +since it only exists inside of the constructor of the Shooter class. To remedy this, we +can instead make a method that creates an instance of the `shoot` command: + +```java +public class Shooter implements Mechanism { + private final TalonFX motor = new TalonFX(4); + + public Command shoot() { + return run(coroutine -> { + while (true) { + motor.setVoltage(5,0); + coroutine.yield(); + } + }) + .named("Shoot"); + } +} +``` + +Then, in Robot.java, you can use that command like so: + +```java +public class Robot { + private final CommandGamepad xbox = new CommandGamepad(0); + private final Shooter shooter = new Shooter(); + + public Robot() { + xbox.leftTrigger().whileTrue(shooter.shoot()); + } +} +``` + + \ No newline at end of file