Adding a Data Provider

VWoeltjen edited this page Nov 7, 2013 · 7 revisions

To connect to your data source, MCT just needs to be able to access your data through a known interface. To facilitate this, you will need to provide an implementation of the gov.nasa.arc.mct.api.feed.DataProvider interface. This is fairly simple - it requires only four methods:

public boolean isFullyWithinTimeSpan(String feedID, long startTime, TimeUnit timeUnit);
public LOS getLOS();
public Map<String, SortedMap<Long, Map<String, String>>> getData(Set<String> feedIDs, long startTime, long endTime, TimeUnit timeUnit);
public Map<String, List<Map<String, String>>> getData(Set<String> feedIDs, TimeUnit timeUnit, long startTime, long endTime);

The first two methods are fairly simple. isFullyWithinTimeSpan just confirms that some data request can be serviced by this data provider. getLOS provides a hint about the level of service expected from this data provider, and can be one of the enumerated values LOS.fast, LOS.medium, or LOS.slow.

A note about the feedID arguments that appear here: Typically, these will be whatever sort of external key is used to identify your data points, prefixed by some appropriate signifier. (MCT supports multiple DataProviders side-by-side; having such a prefix helps to avoid naming clashes between them.) From MCT's perspective, these feed IDs originate with the FeedProvider capability of an AbstractComponent, via the getSubscriptionId() method. A typical pattern would be to implement components to describe telemetry points (see Adding a Component Plugin), and to implement a DataProvider to recognize, and provide data for, the feed IDs to which those telemetry points subscribe.

The two getData methods are essentially the same, and are distinguished only by the return type of their data structure: In one case, data elements are returned in a SortedMap (to preserve their time stamp), whereas in the other they are simply returned in a List (where their specific time stamp is lost, but their ordinality is preserved.) Your implementations of these methods may look very similar, but the List-based option may permit some local optimization when specific time stamp information is not needed.

When data is brought into MCT, it is structured using standard Java collections. Breaking down these collections:

Map<							The map contains all data required to service the request.
	String,				    	Each feed ID serves as a key,
	List< or, SortedMap<Long,	which refers to either a list or sorted map of specific incoming data values,
		Map<					which are themselves represented as maps
			String, String>	    of (potentially arbitrary) key / value pairs.

The keys and values for your individual data points may vary. You are free to pack any information you'd like in their (you can implement your views such that they pull this information back out.) The most standard way is to package them using the keys provided in FeedProvider.RenderingInfo. The TestDataFeed in the example plugin provides an example of how to construct such a map. Populating your data point in this manner allows the data to be recognized and displayed correctly by MCT's existing views. The boilerplate for packing a datum given a value and a status code is:

void Map<String, String> toDatum(String value, String status, long timestamp) { 
    Map<String, String> datum = new HashMap<String, String>();		

    // Set up the RenderingInfo, describing how to show this value
    RenderingInfo ri = new RenderingInfo(				
        value,       // The value; often Double.toString(numericValue)
        Color.GREEN, // Value color; any color you like
        status,      // Status code; typically a one-character string, or ""
        Color.GREEN, // Status color; any color you like
        true         // Valid
    );
    ri.setPlottable(true);

    // Fill in the normally expected key/value pairs
    datum.put(FeedProvider.NORMALIZED_IS_VALID_KEY, Boolean.TRUE.toString());	
    datum.put(FeedProvider.NORMALIZED_RENDERING_INFO, ri.toString());
    datum.put(FeedProvider.NORMALIZED_TIME_KEY, String.valueOf(time));
    datum.put(FeedProvider.NORMALIZED_VALUE_KEY, value);

    return datum;
}

Once implemented, your data provider can be made available to MCT through OSGi's declarative services. The easiest way to do this is to package your component in a Jar archive along with an XML document that describes what service you are offering (typically OSGI-INF/services.xml). An example of how such an XML file should look:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
    <scr:component name="org.acme.example.telemetry.ExampleDataProvider"
    	    immediate="true">
		<implementation class="org.acme.example.telemetry.ExampleDataProvider" />
	    <service>
		    <provide interface="gov.nasa.arc.mct.api.feed.DataProvider" />
	    </service>
    </scr:component>
</root>

Finally, you should add a line to your Jar's manifest to identify this service description:

Service-Component: OSGI-INF/services.xml