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:

 

1.Define an interface which determines which properties and methods are available when an object defined by your Extension is created in FreeFlyer script.

2.Implement the custom interface.

3.Assign the implementation a unique GUID.

4.Override the PublishedInterfaceType property and return the name of the interface you have defined.

5.Override the ExtendedTypeName property and return the type name your Extension will have in FreeFlyer script.

6.Copy and paste the boilerplate Extension registration code.

 

Anatomy of an Extension Visualized

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.

 

FreeFlyer Extensions SDK Interfaces (i.e. Aisolutions.FreeFlyer.SDK.Interfaces.dll)

FreeFlyer Extensions SDK Utilities (i.e. Aisolutions.FreeFlyer.SDK.Utilities.dll)

 

The "using" statements themselves will look as follows.

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Runtime.InteropServices;

 

using Aisolutions.FreeFlyer.SDK;

 

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.

 

 

Namespace Declaration


The next part of our example extension is the namespace declaration:

 

namespace ExtensionExamples

{

 

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.

 

 

Interface Definition


Next, we have the interface definition:

 

public interface ISimpleExtension

{

  double MyProperty { get; set; }

  void MyMethod(double [] input, double [] output);

}

 

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:

 

Properties can be any of the following types:

double, double[]

string, string[]

bool, bool[]

int, int[]

IMatrix    

ITimeSpan, ITimeSpan[]

Properties must implement both get and set accessors

Methods can return any of the following types:

void

double

int

bool

string

IMatrix

ITimeSpan

Method arguments are restricted to types:

double, ref double, double[]

string, ref string, string[]

bool, ref bool, bool[]

int, ref int, int[]

IMatrix, IMatrix[]

ITimeSpan, ITimeSpan[]

ISpacecraft, ISpacecraft[]

ICelestialObject, ICelestialObject[]

IMyExtension, IMyExtension[] – where IMyExtension is the interface you publish for your extension

Note: Interface need to be "public"

Method overloads are not supported

Overloads are two or more methods with the same name but different argument lists

 

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.

 

 

Class Implementation


The first step toward implementing the interface is the class definition:

 

[ComVisible(true)]

[ClassInterface(ClassInterfaceType.None)]

[Guid("85cefb4a-cf67-4b50-b66b-7f4d385bc0a6")]

public class SimpleExtension : ExtensionBase,

                              IFFObjectExtension,

                              ISimpleExtension

{

 

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:

 

public override Type PublishedInterfaceType

{

  get { return typeof(ISimpleExtension); }

}

 

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:

 

public override string ExtendedTypeName

{

  get { return "SimpleExtension"; }

}

 

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:

 

It must start with a letter

It must be different from any of the built-in FreeFlyer object type names

It can only contain upper and lower case letters, numbers, and underscores

 

Next, each of the properties and methods defined in the custom interface must be implemented:

 

private double _SomeData = 7.2;

 

public double MyProperty

{

  get { return _SomeData;  }

  set { _SomeData = value; }

}

 

public void MyMethod(double [] input, double [] output)

{

  double result = 0.0;

 

  foreach (double item in input)

  {

     result += item;

  }

 

  for (int i = 0; i < output.Length; i++)

  {

     output[i] = result;

  }

}

 

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.

 

 

System Registration


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.

 

[ComRegisterFunction]

private static void ComRegisterFunction(Type t)

{

  FFExtensionUtils.RegForComUse(t);

}

 

[ComUnregisterFunction]

private static void ComUnregisterFunction(Type t)

{

  FFExtensionUtils.UnRegForComUse(t);

}