-
Notifications
You must be signed in to change notification settings - Fork 0
Actions
Actions are tasks executed by FlashLib's Scheduler. These tasks are more than a simple run
, they are executed by specific steps, and can be considered as a complex task.
Using Action
s, we can instruct the robot and its system to perform complex tasks of various types. This makes Action
s very crucial for robot control.
Actions are built on a specific execution flow, which is provided by the Scheduler
:
Once started, the task enters the initialization step, which allows users to initialize data or reset data holders in preparation for running.
Next, the task enters the execution step. During this step, users should perform the main task logic. This step continues until the task is marked to be stop.
The action can be marked to stop in several ways:
- The
Action
reports that it has finished. - An outside user calls
cancel
- The
Scheduler
interrupted theAction
.
If the action stopped because it reported a finish, the flow enters the end phase. Otherwise it enters the interruption phase.
An Action
implementation is a class which implements Action
interface. Although it's actually recommended to extend the ActionBase
instead.
When implementing an Action
, users need to implement what the action should be doing at each phase of it's execution flow.
This is done by implementing the following methods:
-
void initialize()
: (optional) initialization step. -
void execute()
: (required) execution step. -
boolean isFinished()
: (optional) reports when theAction
is finished. Should returntrue
when it is,false
otherwise. -
void end(boolean wasInterrupted)
: (optional) during the ending/interruption steps. If in interruption step,wasInterrupted
would betrue
. If in ending step,wasInterrupted
would befalse
.
An empty Action
class, would look as such:
public class SomeAction extends ActionBase {
@Override
public void initialize() {
}
@Override
public void execute() {
}
@Override
public boolean isFinished() {
return false;
}
@Override
public void end(boolean wasInterrupted) {
}
}
The following example RotateAction
is an Action
which rotates a Rotatable
at a constant speed
forever (until canceled or interrupted).
We would need two instance variables: the Rotatable
and the wanted speed.
Then we would need to implement the execution behavior.
public class RotateAction extends ActionBase {
private final Rotatable mRotatable;
private final double mSpeed;
public RotateAction(Rotatable rotatable, double speed) {
mRotatable = rotatable;
mSpeed = speed;
}
@Override
public void initialize() {
// nothing to do here, so leave empty.
}
@Override
public void execute() {
// here we actually rotate the rotatable
mRotatable.rotate(mSpeed);
}
@Override
public boolean isFinished() {
// we want the action to run forever (until interrupted/canceled) so we will just return false.
return false;
}
@Override
public void end(boolean wasInterrupted) {
// when finished, it's best to just stop the rotatable
mRotatable.stop();
}
}
Pretty straight-forward. This is actually pretty close to how RotateAction
from FlashLib is actually implemented.
An Action has several properties, which enhance its capability.
An Action has requirements. These are robot Subsystems which the Action aims to use. The Scheduler ensures only one running Action uses a certain Subsystem at any given time.
An Action can define a run timeout, which defines the maximum amount of time the Action should run. Once the timeout was reached, the Action will be stopped.
An Action has several execution steps, which define the flow of the task. These steps allow defining a more complex behavior for tasks, to handle different situations, and points in the execution cycle of the task.
The following is the flow of an Action:
Once started, the task enters the initialization step, which allows users to initialize data or reset data holders. Next, the task enters the execution step. During this step, users should perform the main task logic. This step continues until the task is marked to be stop.
One the task is marked for finishing, the task can either enter end, or interruption steps.
The end step is entered if the task reports to have finished.
Interruption step is entered in one of several condition:
- The task was canceled by the user
- The task was canceled by the scheduler due to requirement conflict
- The task has timed out
Actions are represented by the Action
class, which is abstract class. Users are required to implement the contents of each execution step:
-
initialize
: code for the initialization step. Default implementation: does nothing -
execute
: code for the execution step -
end
: code for ordered stop of the action -
interrupted
: code for interruption stop. Default implementation: callsend
. -
isFinished
: indicates whether or not the action should stop running. Default implementation: returnsfalse
, which indicates that action should not stop.
During the initialization phase, the method initialize
is called once. This is done each time the action is started.
During the execution phase, the method execute
is called periodically (the interval depends on the Scheduler, but should largely be around few milliseconds). During the end step, end
is called once. During the interruption step, interrupted
is called once.
An Action can report itself has finished, by returning true
from the isFinished
method.
To create an Action you will need to extend it, and implement the step methods:
public class SomeAction extends Action {
@Override
protected void execute() {
}
@Override
protected void end() {
}
}
execute
and end
are the only step methods which don't have a default implementation, and must be implemented. The rest can be ignored, but when needed, can be implemented.
The Action
class contains methods for defining its properties. Setting the properties should be done once the Action is created, and cannot be done while it is running.
- To set the requirements of an Action, use the
requires
method. Each call to this method will add a new requirement and not override requirements already defined. Call toresetRequirements
to clear defined requirements.Action action = new SomeAction(); action.requires(subsystem);
public class SomeAction extends Action { ... public SomeAction(Subsystem subsystem) { requires(subsystem); } ... }
- To set run timeout, use
setTimeout
method, which receivesTime
from FlashLib's Time API. You may also callcancelTimeout
to remove the defined timeout.Action action = new SomeAction(); action.setTimeout(Time.milliseconds(timeoutInMilliseconds));
public class SomeAction extends Action { ... public SomeAction() { setTimeout(Time.milliseconds(timeoutInMilliseconds)); } ... }
To start and action use the start
method. To interrupt a running action, call cancel
method. The isRunning
method indicates whether or not the Action
is running.
public class Robot extends IterativeRobot {
...
@Override
protected void modeInit(RobotMode mode) {
new SomeAction().start();
}
}
Action Groups are a specific implementation of Action
which executes a group of 1 or more actions in a specific order. There are 2:
-
ParallelActionGroup
for executing multiple actions concurrently. One should not that whether the execution is truly parallel or simply concurrent depends on the implementation on theScheduler
. In addition, it is important to be aware that multiple actions do not use the same subsystems. -
SequentialActionGroup
for executing actions in a sequential order, only starting the next, once the previous has finished.
Usage is quite simple. One must construct the wanted class, and define the wanted actions:
Action group = new ParallelActionGroup()
.add(action1, action2);
group.start();
Action group = Actions.parallel(action1, action2);
When the robot mode changes, all running actions will be interrupted.