Using the Runtime API
|Top Previous Next|
A regular use-case for FreeFlyer is as a component within a larger ground system or set of tools built in support of end-to-end space-based mission design and operations. The runtime API, available only in the Mission tier of FreeFlyer, allows FreeFlyer instances to be closely integrated into these larger systems built using other programming languages, which ultimately enables developers to build their overarching driver applications around commonly used and familiar software engineering libraries. Four programming language interfaces are provided with the runtime API, and they are listed below.
Note: The Java API interface (freeflyer-runtimeapi-3.0.0.jar) can be used directly or can be retrieved from Maven Central.
Note: The FreeFlyer runtime API can be used in MATLAB through use of the C# or Java interfaces. When used in this fashion, you will want to ensure that you call close in MATLAB to end the ff.exe process generated by the engine. When encountering an error in their MATLAB scripts, oftentimes users will forget this call and may unintentionally leave behind unhandled ff.exe processes.
Similar applications can be built with FreeFlyer using its command line interface, but whereas that interface was specifically designed for convenience while being run from the command line, the runtime API is specifically designed for convenience when being a part of a bigger application. Some challenges to overcome if you were to attempt to build a ground system using the command line interface follow.
The runtime API addresses these challenges by allowing the user to directly move information between a Mission Plan and the calling application through expressions, allowing a user to pause a Mission Plan's execution by use of the ApiLabel keyword in script, and allowing a user to retrieve data from a Mission Plan even after it has finished execution. Additionally, the runtime API has functionality built into it to support easy asynchronous operations, allowing a user to operate multiple instances of FreeFlyer in tandem and then synchronize them at certain key points to pass data between instances.
Installation & Usage
The runtime API comes installed with FreeFlyer, and can be found in a user's My Documents folder. Provided within the Runtime API folder are all of the references for all supported language interfaces as well as examples for each. These samples are described in much greater detail in the Sample Applications page of the help file.
Instances of one's FreeFlyer license are used when using the runtime API similarly to when just running FreeFlyer. The instance in the case of the runtime API instance, however, is tied to the RuntimeApiEngine class, described in greater detail below. When an instance of the RuntimeApiEngine class is instantiated, FreeFlyer is spun up and a single instance of the active FreeFlyer license is consumed. This action will also check out a seat from an associated server license if such a license configuration is in use. FreeFlyer will only relinquish that license instance when the RuntimeApiEngine class's memory is deallocated, such as follows in C#.
To get started with the API, we will create a simple interaction between a C# application and a Mission Plan in FreeFlyer. Although the C# interface is being used exclusively for this example, all of the concepts discussed work identically with the other interfaces, and even the code will look very similar because the algorithms we'll use will be identical. The Mission Plan will simply create a Spacecraft and propagate it, although we will use a few ApiLabel keywords so that the API can retrieve and supply data from and to the Mission Plan at different stages. The Mission Plan will look like the following snippet.
Note: All FreeFlyer functionality is available for use from within the runtime API, though the runtime API itself interfaces with FreeFler Mission Plans rather than exposing objects, functions, methods, and properties to the driver application itself.
With that Mission Plan created and saved to a location on your machine, we will then open up our integrated development environment for C# and create a new C# project. First, we will create an Example namespace with a program class and Main method all with the necessary using statements.
Note: Ensure that the bitness of the application you're working in matches the bitness of the FreeFlyer installation and API you're referencing.
Note the using line for Aisolutions.FreeFlyer.RuntimeApi. This line is where the code will get access to the runtime API's functionality. You will want to add the reference to the runtime API's source file, which is located in the FreeFlyer folder of your My Documents. A generalized path for Windows 7 is provided below that you can reference to find the necessary file.
Once this reference is added, we can begin to code using the runtime API inside the Main function. We will first want to declare several variables in the C# script that will allow us to find the Mission Plan and choose the initial state of the FreeFlyer Spacecraft.
Note: When declaring paths and locating the FreeFlyer installation directory, you can take advantage of the environment variables and files that FreeFlyer generates when installed. In Windows, this takes the form of a FREEFLYER_64_INSTALL_DIRECTORY environment variable. In Linux, FreeFlyer creates a file called "active_ff_install" that contains the installation path of the active FreeFlyer version. Be mindful that the check we perform in the below code snippet requires that the bitness of the C# project that we're working on be set to match the bitness of FreeFlyer, otherwise it may look for an environment variable that is not present and return null.
Note: If you are installing and uninstalling FreeFlyer versions on your machine, be aware that the environment variables used above will point to your most recent installation. In managed operational contexts, you should create your own environment variable and use that to point to the FreeFlyer installation directory you want to reference.
Now you will actually perform the API actions. In general, the following list of actions is what you will perform when using the API to work with a Mission Plan.
As noted above, the runtime API allows the user to either load a Mission Plan file directly or to provide flat FreeFlyer script that will be executed as though it were a Mission Plan. The option to run script is important because it enables your application to dynamically build up FreeFlyer scripts to execute as needed based on application context, whereas dynamically building a Mission Plan file would be significantly more difficult. When script is run in this fashion, the engine doesn't have a Mission Plan to tell it what timing precision mode to use and so defaults to nanosecond precision. Millisecond timing precision mode can be used in this script context still by specifying it through the load script overload that takes a TimeSpanMode class as follows.
Note: When loading into an engine with FreeFlyer script instead of a Mission Plan (either via a file or string), the default data files that come with your FreeFlyer installation will be used, regardless of whether these have been changed through User Preferences. See the Data Files page for descriptions of these default data files.
In this specific case, we will be executing the Mission Plan in segments to reach the different ApiLabel keywords that we defined in the Mission Plan. We will input the state from the variables we defined at the beginning of this exercise once we reach the "Set State" ApiLabel, and will then retrieve the state and print it to standard out once we reach the "Get State" ApiLabel.
Note: Relative paths passed to the API are relative to the current directory of the host application. Relative paths in the Mission Plan itself are relative to the Mission Plan file location. If you are not using a Mission Plan and are instead using a script input to the engine via string, then the relative paths in that script will be relative to the specified directory.
Note: In the place of the second ExecuteUntilApiLabel() call, we could have instead called ExecuteRemainingStatements() and still retrieved the state afterward. With the API, expressions can still be retrieved and handled even after the Mission Plan has finished executing all statements up until the point where CleanupMissionPlan() is called.
Note: A regular expression can be provided as the argument for ExecuteUntilApiLabel(). In this case, you can use GetLocation() to retrieve the ApiLabel that was reached.
At this point, you can build the example and run it and you will see the final state being written to console. If an error case is experienced, then the try block will catch it as a RuntimeApiException and will write the error message to the screen. For a more in-depth discussion of error handling with the runtime API, see the Error Handling page.
The RuntimeApiEngine class has a number of constructors available for use, each resulting in different behaviors in the runtime API. The general constructor used in earlier examples in this guide simply requires a file path to the FreeFlyer installation directory, while the other constructors, discussed below, ask for more information in order to perform specific functionality.
In many cases when using FreeFlyer Mission Plans, ConsoleWindow objects get used in order to provide diagnostic and upkeep information to the users executing the Mission Plans. If you were running these Mission Plans from the FreeFlyer GUI, you could add script to export the ConsoleWindow text to a file, or you could have it echo to standard out. When using the runtime API, however, it becomes useful to have access to the ConsoleWindow output from within the host application itself, hence the existence of this constructor. With this constructor, any ConsoleWindow object configured to echo its contents to standard out will have its contents captured by the RuntimeApiEngine as well.
The FreeFlyer runtime API supports the generation of output from host applications using this constructor. This constructor, when used and set into appropriate modes for Windows applications, can generate output in a workspace that is fully configurable like if you were running it from the FreeFlyer GUI instead of the runtime API. The varying types of modes that can be used include a mode with a fully configurable workspace, a mode that only shows the OutputWindow objects unconstrained via the OutputLayoutManager, a mode that shows no output but enables the saving of images via FreeFlyer script, and a mode that doesn't have any output support at all. When running FreeFlyer in any of the output modes that generate output, all visualization libraries will be loaded and the start time of the FreeFlyer engine will be increased.
For runtime API applications designed to work on the Linux operating system, it does not matter which constructor argument is used to generate output as the behavior is always the same. For Linux, the complete output workspace is unavailable and all windows will always be generated in an unconstrained fashion with remaining options ignored.
Note: Another constructor is present that allows the user to select both a ConsoleOutputProcessingMethod and a WindowedOutputMode for the same RuntimeApiEngine class with the full features of both.
Engine states refer to the different states that a runtime API engine can be in, which plays a role in whether or not certain methods will function correctly or return the ErrorInvalidEngineStateForOperation exception type. There are four unique engine states, described in detail in the following table, along with the methods that can be used to move an engine from one state to another. Each of these states has some methods that are invalid in these states, and a few examples of such methods are provided. For a full listing of the methods that can be used from each state, see the autogenerated help documentation provided for each interface.
There are some commonly encountered user mistakes that occur more frequently than others, and so if something isn't working in your application while using the runtime API and the error messages aren't helping you as much as you need, consider referencing the following list to ensure that your setup is properly configured.
If you cannot determine the issue with your application even after debugging it yourself, comparing the exceptions you're seeing to the exception result list, and checking against these common errors, please send an email to email@example.com in order to get additional help.