GamePlan is 4311's custom Shuffleboard plugin for creating autonomous routines. Typically, autonomous programming in FRC involves stringing together a static list of actions that the robot performs every match. Through the use of a graphical user interface, GamePlan increases the versatility of autonomous routines while simultaneously making them easier to compose.
GamePlan is incredibly easy to use! In essence, GamePlan allows one to easily create a list of actions (as seen in the screenshot to the right) that the robot will sequentially perform.
The simplest commands are drive commands, which tell the robot that it should turn toward and drive to a specific location. To create a new drive command, simply click the onscreen location to which the robot should drive, and an arrow will appear. To edit an existing arrow, alt-click on the arrow, and it will become repositionable.
To add game specific actions, remove actions, edit actions, show/hide actions, or rearrange actions, use the buttons on the left.
GamePlans can be saved and loaded as files so that they may be reused. To save/load a GamePlan, use the buttons on the right.
The robot can be programmed to use the open GamePlan as its autonomous. For this to work properly, GamePlan should be open at the start of each FRC match.
GamePlan Widget Programming
Though it should not be necessary to make any major changes to GamePlan each year, certain features will need to be updated, like the field image or the actions that the robot can perform. Therefore, it is important to have a knowledge of how GamePlan is structured and how to make changes to it.
GamePlan is a custom Shuffleboard widget, written in Java. Its source code is stored in a Github Repository under the 4311 Swampscott Currents Github organization. To edit/rebuild GamePlan, simply clone the repository and open the root folder in VSCode. To build the project, open the WPILib command palette, select "build robot code," and select "Java" as the language. This will invoke a Gradle command which outputs the GamePlan binaries as a JAR file to build\libs\GamePlan.jar. This is the standalone Java executable file which may be imported into Shuffleboard.
Editing the GUI
FRC Shuffleboard is based on JavaFX. JavaFX allows for the creation/loading of GUI interfaces through the use of FXML files, which use the XML file format to describe combinations of windows, panes, buttons, and labels. GamePlan has its own FXML file located at src\main\resources\org\swampscottcurrents\serpentframework\gameplan\GamePlanWidget.fxml. This file can be edited manually with a text editor, or with the graphical editor Scene Builder, to modify things like the field image or button text. GamePlan also stores each action that the robot can perform as its own separate FXML file. These are located in the folder src\main\resources\org\swampscottcurrents\serpentframework\gameplan\actionviews.
GamePlan code structure
GamePlan is written in accordance with standard object-oriented programming principles. The following aspects are of note:
Widget functionality is implemented through GamePlanWidget class, which inherits from Shuffleboard's SimpleAnnotatedWidget type. The widget class controls the GUI, and is configured to automatically update every 33 milliseconds (30 FPS). Each frame, it iterates over the current GamePlan (held in the currentPlan static field), and creates/updates GameActionViews for every GameAction in the GamePlan action list. In addition, the widget class implements behavior for all of the buttons (save, load, add action, etc.), and contains a list of action generators (the availableActions array) which are used for adding new actions. Whenever the add action button is clicked, the user is presented with a list of all action generators. When the user selects one, a function is called on the action generator to create a new action. This allows for the implementation of custom behavior when adding actions. By default, the action generator list is populated in the widget's constructor.
The GamePlan class is used to describe GamePlans in-memory. Fields can be added and removed for the class to store whatever data necessary for a given year, but by default, the class only has a list of GameActions, which each describe one action that the robot should complete. This is the list of actions that appears on the bottom of the widget GUI. Because GamePlans need to be stored on-disk and sent over NetworkTables, GamePlan contains two functions that allow for the serialization/deserialization of GamePlan instances. These functions convert instances to/from JSON strings using the RoyalJson library. The JSON text may then be easily transferred. It is important to note that RoyalJson only serializes objects in public fields, so any data that needs to be serialized must be marked public.
Classes derived from GameAction describe specific, sequential actions that the robot should perform. They can contain arbitrary data - however, like GamePlan, any data that should be saved during serialization must be marked public. To describe how the widget should display each action onscreen, derived classes must override the createView method, which returns a GameActionView. Most actions that the robot performs while standing still require a simple marker at their location for the actionview - in this case, createView may return a new StationaryActionView, which is the class used to display the colored action dots.
Classes derived from GameActionView interface with GamePlanWidget to display GameActions. When the widget attempts to display a new GameAction, it calls createView on the action. This method creates a new actionview, which adds necessary JavaFX components to the widget's fieldView during the execution of its constructor. For a GameActionView to work properly, multiple methods must be overridden, such as update (which is called once per frame) and destroy (which is called when the actionview should remove its JavaFX components from the fieldView).
GamePlan Robot Programming
For the robot to execute GamePlans, it must be able to receive and interpret them from the driver station. The GamePlan widget uses NetworkTables to send serialized GamePlans to the robot under the key GamePlan/Plan. Once received, the GamePlan JSON must be deserialized and used for robot control.
When RoyalJson serializes an object, it stores the object's package-qualified type and converts all of its public fields to strings. This means that, during deserialization, the JRE classloader must be able to find types with matching package names, class names, and public fields. Thus, identical classes (with the same package name and public field declarations) must be defined in robot-side code for GamePlan, GameAction, and any of their derived types. Robot-side classes may contain unique method declarations.
Using a deserialized GamePlan
As described above, a GamePlan is just a specific set of actions which the robot is meant to perform. GamePlans do not provide any behavior for actually executing the commands - how to interpret the GamePlan is left up to robot code. To execute the plan using the standard command-based model for writing robot code, one can simply define commands for each GameAction. Then, at runtime, the code can iterate over the GamePlan's actions and construct a SequentialCommandGroup with their corresponding commands. This command group may then be executed as the robot's autonomous.