Anatomy of an Extension
|Top Previous Next|
To understand the components that make up a FreeFlyer Extension, let’s examine the SimpleExtension example provided alongside the Extensions SDK. The source code for this extension can be found in the Examples\SimpleExtension subdirectory in the SDK install location. Open the C# file titled "SimpleExtension.cs" before moving forward with this guide. The necessary steps to create a FreeFlyer Extension are as follows:
Anatomy of an Extension Visualized
References and Using Statements
The first component of our example extension is a number of "using" statements. These are dependent on successfully adding references to your Visual Studio project for the FreeFlyer Extensions libraries. In the solution explorer, right-click References and choose Add-Reference. As an example, select the following assemblies from the .NET tab and press OK.
The "using" statements themselves will look as follows.
The "using" statements let us simplify our code by letting the compiler infer the identity of a type name. For example, instead of having to write Aisolutions.FreeFlyer.SDK.ExtensionBase, we can instead simply write ExtensionBase if we include the last “using” statement listed above.
The next part of our example extension is the namespace declaration:
A namespace provides a naming context for all types which are declared within its braces. For example, if you declared a type called RocketShip within the above namespace declaration its full name would be ExtensionExamples.RocketShip. Namespaces can be useful for organizing your code and serve to avoid type naming conflicts. While namespaces are a best practice, they are not required to create a valid FreeFlyer Extension.
Next, we have the interface definition:
The interface definition defines what properties and methods beyond those provided by ExtensionBase will be available through the custom object when it's used within FreeFlyer script. Our example interface contains one property of type double (a double precision floating point number) and one method which takes two arrays of double as arguments.
The interface definition has the following restrictions:
At this point in the extension we now have an interface, but no logic behind the property MyProperty or the method MyMethod. The next step is to actually implement the interface within a class.
The first step toward implementing the interface is the class definition:
The first two lines are required boilerplate that can be copied and pasted into your code. They are necessary for the COM interfacing that's used by FreeFlyer to recognize and access the extension, but they are somewhat irrelevant to the actual functionality of the extension itself. The third line tells the compiler that the class definition which follows should be assigned the specified Globally Unique Identifier (GUID). Though it looks ugly and complicated, a GUID is simply a large number which uniquely identifies the class. This is analogous to how a social security number uniquely identifies citizens of the United States. There may be multiple citizens named John Smith, but each has a unique social security number. Newly generated GUIDs are guaranteed to be unique across all machines. When creating an extension, a new GUID should be generated using the provided GUID Generator. This GUID should then be copied and pasted into your code.
The next line creates a class named SimpleExtension which derives from the class ExtensionBase and implements the interfaces IFFObjectExtension and ISimpleExtension (the interface we've previously defined). When you derive from a class you inherit its properties and methods. When you implement an interface, you are making a promise to fully implement all properties and methods in that interface. The compiler subsequently forces you to keep that promise and all properties and methods of the interface must be implemented in the class in order to build the extension.
The next part of the interface implementation is to override the published interface type for the extension:
The PublishedInterfaceType property tells FreeFlyer which interface will be available when a FreeFlyer Extension object is created in FreeFlyer script. This example interface type declaration can be copied and pasted into your custom extensions, replacing ISimpleExtension with the name of the interface you've created for your custom FreeFlyer Extension.
The next step towards implementing the interface is to override the extended type name for the extension object:
The ExtendedTypeName property tells FreeFlyer what the type name of your extension object will be when created in FreeFlyer script. Again, the example extended type name declaration can be copied and pasted into your custom extensions, replacing "SimpleExtension" with the type name you choose for your Extension. The type name has several rules that it must follow:
Next, each of the properties and methods defined in the custom interface must be implemented:
Here we have implemented the interface we defined. In this example the property MyProperty simply stores and returns a number, while the method MyMethod adds up each item in the input array, and stores the result in each item of the output array. A FreeFlyer Extension you create would presumably contain something much more substantial, but this simple example illustrates the main principles.
Note: The use of undefined TimeSpans is not supported for extension properties. To put it another way, you cannot set an extension TimeSpan property to be an undefined TimeSpan (i.e. in FreeFlyer script: "myExtension.EpochTimeSpan = TimeSpan.Undefined;"). For example, the batch solution epoch is undefined until after either the observations are loaded or the user explicitly sets the epoch, and you would get an error if you tried to set an extension TimeSpan property from the BatchLeastSquaresOD.SolutionEpoch property.
The last part of a valid FreeFlyer extension is the boilerplate Extension registration code. This code is used by the Extensions Manager to create Windows registry entries which FreeFlyer then uses to detect your extension. You should copy and paste it into your code without modification.