001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package org.wpilib.commands3; 006 007import edu.wpi.first.units.measure.Time; 008import java.util.List; 009import java.util.function.Consumer; 010import org.wpilib.annotation.NoDiscard; 011 012/** 013 * Generic base class to represent mechanisms on a robot. Commands can require sole ownership of a 014 * mechanism; when a command that requires a mechanism is running, no other commands may use it at 015 * the same time. 016 * 017 * <p>Even though this class is named "Mechanism", it may be used to represent other physical 018 * hardware on a robot that should be controlled with commands - for example, an LED strip or a 019 * vision processor that can switch between different pipelines could be represented as mechanisms. 020 */ 021public class Mechanism { 022 private final String m_name; 023 024 private final Scheduler m_registeredScheduler; 025 026 /** 027 * Creates a new mechanism registered with the default scheduler instance and named using the name 028 * of the class. Intended to be used by subclasses to get sane defaults without needing to 029 * manually declare a constructor. 030 */ 031 @SuppressWarnings("this-escape") 032 protected Mechanism() { 033 m_name = getClass().getSimpleName(); 034 m_registeredScheduler = Scheduler.getDefault(); 035 setDefaultCommand(idle()); 036 } 037 038 /** 039 * Creates a new mechanism, registered with the default scheduler instance. 040 * 041 * @param name The name of the mechanism. Cannot be null. 042 */ 043 public Mechanism(String name) { 044 this(name, Scheduler.getDefault()); 045 } 046 047 /** 048 * Creates a new mechanism, registered with the given scheduler instance. 049 * 050 * @param name The name of the mechanism. Cannot be null. 051 * @param scheduler The registered scheduler. Cannot be null. 052 */ 053 @SuppressWarnings("this-escape") 054 public Mechanism(String name, Scheduler scheduler) { 055 m_name = name; 056 m_registeredScheduler = scheduler; 057 setDefaultCommand(idle()); 058 } 059 060 /** 061 * Gets the name of this mechanism. 062 * 063 * @return The name of the mechanism. 064 */ 065 @NoDiscard 066 public String getName() { 067 return m_name; 068 } 069 070 /** 071 * Sets the default command to run on the mechanism when no other command is scheduled. The 072 * default command's priority is effectively the minimum allowable priority for any command 073 * requiring a mechanism. For this reason, it's recommended that a default command have a priority 074 * less than {@link Command#DEFAULT_PRIORITY} so it doesn't prevent low-priority commands from 075 * running. 076 * 077 * <p>The default command is initially an idle command that only owns the mechanism without doing 078 * anything. This command has the lowest possible priority to allow any other command to run. 079 * 080 * @param defaultCommand the new default command 081 */ 082 public void setDefaultCommand(Command defaultCommand) { 083 m_registeredScheduler.setDefaultCommand(this, defaultCommand); 084 } 085 086 /** 087 * Gets the default command that was set by the latest call to {@link 088 * #setDefaultCommand(Command)}. 089 * 090 * @return The currently configured default command 091 */ 092 public Command getDefaultCommand() { 093 return m_registeredScheduler.getDefaultCommandFor(this); 094 } 095 096 /** 097 * Starts building a command that requires this mechanism. 098 * 099 * @param commandBody The main function body of the command. 100 * @return The command builder, for further configuration. 101 */ 102 public NeedsNameBuilderStage run(Consumer<Coroutine> commandBody) { 103 return new StagedCommandBuilder().requiring(this).executing(commandBody); 104 } 105 106 /** 107 * Starts building a command that requires this mechanism. The given function will be called 108 * repeatedly in an infinite loop. Useful for building commands that don't need state or multiple 109 * stages of logic. 110 * 111 * @param loopBody The body of the infinite loop. 112 * @return The command builder, for further configuration. 113 */ 114 public NeedsNameBuilderStage runRepeatedly(Runnable loopBody) { 115 return run( 116 coroutine -> { 117 while (true) { 118 loopBody.run(); 119 coroutine.yield(); 120 } 121 }); 122 } 123 124 /** 125 * Returns a command that idles this mechanism until another command claims it. The idle command 126 * has {@link Command#LOWEST_PRIORITY the lowest priority} and can be interrupted by any other 127 * command. 128 * 129 * <p>The {@link #getDefaultCommand() default command} for every mechanism is an idle command 130 * unless a different default command has been configured. 131 * 132 * @return A new idle command. 133 */ 134 public Command idle() { 135 return run(Coroutine::park).withPriority(Command.LOWEST_PRIORITY).named(getName() + "[IDLE]"); 136 } 137 138 /** 139 * Returns a command that idles this mechanism for the given duration of time. 140 * 141 * @param duration How long the mechanism should idle for. 142 * @return A new idle command. 143 */ 144 public Command idleFor(Time duration) { 145 return idle().withTimeout(duration); 146 } 147 148 /** 149 * Gets all running commands that require this mechanism. Commands are returned in the order in 150 * which they were scheduled. The returned list is read-only. Every command in the list will have 151 * been scheduled by the previous entry in the list or by intermediate commands that do not 152 * require the mechanism. 153 * 154 * @return The currently running commands that require the mechanism. 155 */ 156 @NoDiscard 157 public List<Command> getRunningCommands() { 158 return m_registeredScheduler.getRunningCommandsFor(this); 159 } 160 161 @Override 162 public String toString() { 163 return m_name; 164 } 165}