A Command represents sets of instruction(s) to perform a certain task in GMAT. A Command is associated with one or more Resources and describes how associated Resources will be utilized and evolved over time. Commands are sequential and they run in sequential order, which is called the Mission Control Sequence. Commands in GMAT are created in order, via script or GUI, for running a mission.
The following text shows sample scripting of a mission sequence:
The following shows the mission sequence of the above script in GUI mode.
There are many types of built-in Commands for use in spacecraft missions. However, new Commands can be created to extend GMAT and perform a new desired task. This document will describe a general procedure how to add a new Command to the baseline GMAT code. If you would prefer to add your new Command as a plugin, please see instead the document "Building a GMAT Plug-in: A Walkthrough Using Visual Studio 2010".
All configurable Command types must be derived from GmatCommand. The GmatCommand class is derived from the base GMAT class, GmatBase. There are often intermediate classes as well, to encapsulate common data and methods for the Command type. For example, there is a PlotCommand class, which derives from GmatCommand, and from which ClearPlot, MarkPoint, PenDown, and PenUp Commands are derived. Data and method implementations that are common to all of these subclasses are placed in the PlotCommand class, and only those that need specific modifications are added or overridden in the subclasses. Only leaf Commands can be created and used in GMAT mission sequence.
Most Commands have one or more associated Resources and options. Though not shown in the above diagram, the associated Resource type of the PlotCommand is the Subscriber/XYPlot, and there are no command options. The example script syntax is
A more complicated command is shown below.
The associated Resource type is the Propagator type named "DefaultProp" and the Spacecraft type named "DefaultSC." The command option is "DefaultSC.ElapsedSecs = 12000.0." This script tells the Propagate command to propagate the Spacecraft object "DefaultSC" for 12000 seconds using the Propagator object "DefaultProp."
A maneuver command example is shown below.
The associated Resource type is the ImpulsiveBurn type named "DefaultIB" and the Spacecraft type named "DefaultSC." This script snippet tells the Maneuver Command to maneuver the Spacecraft object "DefaultSC" using the ImpulsiveBurn object "DefaultIB."
Commands in GMAT are executed in order, whether created via script or GUI, when running a mission. When a Command is created via GUI, it is set to use its default Resource and options. When a Command is created via script, the associated Resource must be created before the Commands are created, otherwise errors will occur during script parsing. GMAT uses a late binding scheme to provide interconnection between Resource objects and Commands. Therefore, Commands store names of associated Resource objects during script parsing. Actual object instances are connected before the execution of Commands when the Sandbox is initialized. See the "GMAT Architectural Specification" document for more details on late binding scheme.
Required First Steps
In order to add a new Command to GMAT, some preliminary steps must be taken to set up your build environment:
- Download the configured GMAT source
- Download, or refer to, the GMAT C++ Code Style Guide from the GMAT Wiki – standard GMAT coding style must be followed for any new/modified code
- Build GMAT on your platform with no code changes (see instructions on building the source on the GMAT Wiki) [NOTE: to maintain cross-platform compatibility, code must build successfully using the GCC compiler in order to be considered for inclusion into GMAT]
- Run Sample Missions (or SmokeTests* and possibly a subset of relevant system test folders, if you have access to those) before code modification, to
- Confirm successful execution of the unmodified GMAT, and
- Obtain an initial set of 'truth' data for later testing
Once these steps have been completed and GMAT runs successfully as-is, you can start the work to create a new Command.
Though GMAT is a modular system, adding a new Command is not as simple as just compiling and linking a new class. There are requirements that must be met regarding, among other things:
- Deriving from base classes (either an existing Command class, or GmatCommand)
- Implementing, at a minimum, specific methods
- Adding a new type to the appropriate factory (or creating a new factory)
- Updating base type lists and related enumerated types
- Modification of types and addition of methods in GUI management classes
- Optionally, creating GUI components to edit your new Command
Among other generic methods provided by the GmatBase class, the following list details methods that should usually be implemented for a new Command. Note that InterpretAction() and Execute() methods are specific to the Command class and must be implemented.
InterpretAction(): Parses the command string and builds the corresponding command structures. During the script interpretation, this method is called to parse the string and to set Resource object names and options.
GetWrapperObjectNameArray(), SetElementWrapper(), ClearWrappers(): If a Command access configured Parameters or other Resource properties, these methods should be implemented.
RenameRefObject(): If a Command access configured Resource, this method should be implemented.
Initialize(): Initializes the internal command data. During the Sandbox initialization, this method is called to set Resource object pointers to the objects in the Sandbox.
Execute(): Performs actions on associated Resource objects or just performs actions without associated Resource objects. During the Sandbox execution, this method is called to perform actions.
TakeAction(): Performs an action specific to a Command. This method is usually called from other Commands.
GetGeneratingString():Generates a command string as it appears in the script. This method is called when saving GUI resources and commands to script file or showing scripts from the individual resource panel.
There are practices and/or strategies that may be helpful to a developer in creating a new Command, e.g., sometimes it is helpful to:
- First, determine scripting of the new component
- Next, modify/add the minimal set of code for the new Command until the script parses
- Then, add validation of the input data
- Then, begin to add functionality (may be done in steps)
Procedures for Adding a New Command
Base Code modifications:
- Decide which GMAT Command base class will be the parent of your new class. Most Commands derive from the GmatCommand class located in src/base/command.
- Add additional data or methods to the new class as needed.
- Implement the pure virtual methods of the parent class, and other public methods whose base/default implementation are not correct or sufficient for your new class. The only pure virtual method for Command is Execute() and must be implemented. For example, you may need to add some validation to the Initialize() method, so you would include new code in your Initialize() method and (almost certainly) call the parent Initialize() method as well.
- If your class is a leaf class, implement the Clone() and Copy() methods. Implement RenameRefobjects() method to handle renaming Resource names from the GUI. If your command does not reference any objects, add the macro "DEFAULT_TO_NO_REFOBJECTS" in the public part of your header file. This macro is replaced by RenameRefobjects() returning true during compilation time.
- If your class has cloned objects, implement cloning of objects in the copy constructor and the assignment operator appropriately. The assignment operator should delete owned cloned objects first before creating new owned cloned objects. If your class does not own any cloned objects, add the macro "DEFAULT_TO_NO_CLONES" in the public part of your header file. This macro is replaced by HasLocalClones() returning false.
- Update /src/base/factory/CommandFactory.cpp to add your new Command type. Add a new Command type to the data member "creatables" list in the CommandFactory constructor so that new Command type is recognized from the Interpreter. Update CreateCommand() method to return an instance of a new Command type when new Command instance is requested.
- Add your new class(es) to the /src/base/MakeBase.eclipse file to make sure it is compiled.
- Add new Command class to Microsoft Visual C++ 2010 Express libGmatBase project if you prefer to build GMAT using MS Visual Studio.
GUI Code modifications:
- If you want to show customized icon for a new Command from the Mission tree, insert your new Command type to MissionIconType in /src/gui/app/GuiTreeItem.hpp. The new Command type must to be inserted between MISSION_ICON_OPENFOLDER and MISSION_ICON_DEFAULT. Add a statement to load the new Command icon file at the same position where new Type was inserted to MissionIconType in MissionTree.cpp in /src/gui/mission/MissionTree::AddIcons() since bitmaps index should match with the position in MissionIconType.
- If you want to specially handle the new Command type, for example creating different popup menu, add your new type in ItemType in /src/gui/app/GuiTreeItem.hpp and implement appropriately in MissionTree.cpp.
- Check CreateNewCommand() in /src/gui/app/GmatMainFrame.cpp to see if you need to add or modify the list (i.e. look in the switch statement), to match the modifications to GmatTreeItemData.
- Decide whether you can use the GmatCommandPanel which is a generic panel for a Command, or if you will need a custom GUI panel for your new Command. See "How to Create GMAT panels" for further instructions on creating a new GUI panel.
- Add your new class(es) to the /src/gui/MakeGui.eclipse file to make sure it is compiled
- Add your new Command class(es) to Microsoft Visual C++ 2010 Express GMAT_wxGui project if you prefer to build GMAT using MS Visual Studio.
System Test Readiness Criteria
Once you have completed your code modifications and additions, there are several criteria that must be met before the GMAT team will allow the code to be included into the GMAT base. Note that meeting these criteria does not guarantee that your commands will be added to GMAT.
- Update your code base with the latest configured GMAT code, merging the configured code into your code base when necessary.
- Build with the GCC compiler to make sure that it builds on other platforms. If you prefer, build with Visual Studio C++ Compiler.
Then, for those with code modification and system test privileges:
- Run SmokeTests and the applicable Command and/or command system test folder(s) successfully
- Inform testers of changes to GUI components, when applicable
Or, for those without code modification and system test privileges:
- Run unit-tests with the new code – these unit-tests must be thorough and should use input test data obtained from GMAT engineers when available
- Coordinate with GMAT team members to deliver code modifications
This section shows an example how to create a new Command. The actual code for the command we are going to create is in Appendix B.
- Come up with Command syntax and actions. For this example, we are going to add a new Display command. The Display command will display GMAT Parameters (Calculated Parameters, Array, Variable, and String) in the Message Window. The script syntax will be: "Display ParameterName" where ParameterName is a configured Parameter object name.
- There are a couple of ways to create a new Command class. You can create it from scratch or copying existing class and modify it. To copy an existing class and modify it, look through built-in Commands in/src/base/command and see if you can find a similar Command that you will use for the new Command. If you don't see any similar Commands, just copy SaveMission class which provides basic methods and is simple enough to modify for a new Command. The SaveMission command writes the whole mission to a file.Our Display class will be pretty similar to the Report class except it will write data to the Message Window instead of to a file. So Report.hpp and Report.cpp would be a good choice to copy and modify for our Display class. However, for this example, we will copy and modify SaveMission for Display command class to illustrate the steps needed to create the new Command. Copy the SaveMission.cpp and SaveMission.hpp files as Display.cpp and Display.hpp and save them in the /src/base/command directory. After copying the SaveMission C++ files, globally replace SaveMission with Display. Update only the author, date, description from the file prolog. Then, globally replace FILE_NAME with PARAM_NAME and fileName with paramName.In the Display.cpp file, change "Filename" to "Parameter" in PARAMETER_TEXT. This informs the GMAT code that the Display command has one associated object type of PARAMETER. Change FILENAME_TYPE to OBJECT_TYPE since "Parameter" is an object type. See /base/include/gmatdefs.hpp for the kinds of object types defined.
- Next, go to InterpretAction() method. This method parses the script line that was read from the Interpreter and does some syntax checking. In the InterpretAction() method, update the function prolog to match the script syntax which is "Display ParameterName". This method parses generatingString which is set from the Interpreter and builds internal command data. Basically the Interpreter reads a script line and creates an instance of Display command and then calls Display::InterpretAction() to build the command data. The Display command expects two chunks, "Display" and "ParameterName", so add a check for the correct number of chunks. We can remove the code for removing single quotes since "ParameterName" does not require single quotes.
- Next, go to the GetGeneratingString() method. This method generates a syntactically-correct command string as it would be read from the script file. This method is called when saving a mission to a script file or showing a script from the GUI panel. The single quote is not needed around paramName, so remove it.
- Next, the Initialize() method needs to be implemented. The SaveMission class did not need additional code for Initialize(), but our Display class needs to add additional code to set Parameter object pointers. In order to handle various types of optional inputs to Commands in a generic way, Commands use ElementWrapper classes. For detailed description on how ElementWrapper classes are used in Command, see "Data Elements in Commands" section in "GMAT Architectural Specification." Since a Parameter type is required to be wrapped with ElementWrapper, include "ElementWrapper.hpp" and add ElementWrapper *paramWrapper to header. ElementWrapper access methods are also needed to set paramWrapper. These are the GetWrapperObjectNameArray(), SetElementWrapper(), and ClearWrappers() methods. When the Interpreter parses a command script line, it creates a wrapper for paramName and calls SetElementWrapper(). In the Initialize() method, call parent GmatCommand::SetWrapperReferences() to set wrapper references which is Parameter object. See Appendix B for actual code.
- Next, go to the Execute() method. In the Execute() method, we need to evaluate the paramWrapper and write a string value to MessageWindow using MessageInterface::ShowMessage().
- Next, we need to add our new command to factory class so that GMAT can properly create it. Open the /src/base/factory/CommandFactory.cpp to add the Display command. Modify the CreateCommand() method so that it can return a new instance of Display class. Modify the CommandFactory() constructor to add "Display" to the creatable command list.
- All of our code updates are now done. Assuming you are using Microsoft Visual Studio C++ 2010 Express to build GMAT, open the solution file for GMAT located at /build/windows-VS2010/GmatVS2010.sln. Add the new Display class to the project. Expand the libGmatBase -> Source Files -> command, and then add existing item Display.hpp and Display.cpp from /src/base/command folder. Build the project and fix any compilation errors.
- For GCC build, open /src/base/MakeBase.eclipse and add "command/Display.o" in OBJECTS section.
- The next step is testing. Our Display command does not use any math algorithms, so we will just test it using a script. Create the following script.
Open GMAT and open the script. If you click on Mission tab, the Mission Sequence will look like this:
Press the Run button, then you will see the following text in the MessageWindow.
You can also add a new Display Command via the GUI. Append a Display Command to MissionSequence and double click on the new Display Command and you will see the following dialog opens. This dialog is the default dialog for the new Commands. If you want you can create a customized Command dialog and connect it to GMAT main menu item. See "How to Create GMAT Panels" for detail instructions.
Congratulations! You have created your first GMAT command.
Tutorial Sample Code