InputPlugins

Writing a Custom InputPlugin

This page provides guidance on how to write an InputPlugin for the SensorPlatformFramework.

Warning, important To find information about existing plugins, please go to SensorPlatformFramework#Input.

Using the API

First of all, you need the SPF API (SensorPlatformFramework#API). The easiest way is to download the latest release of the SPFramework API (SensorPlatformFramework#Download). You will need the spf-api-[version].jar on the class path of your plugin project. Assuming you are using Eclipse IDE, you can manually add it to the projects build path (Right-click / Build Path / Add to Build Path).

If you need access to the sources of the API, you can check out the project from SVN. See SensorPlatformFramework#Development for details. All SPFramework projects are managed using maven. For clean integration into Eclipse the m2eclipse plugin is recommended. Follow these steps to set up your project in Eclipse:

  • In the SVN repository perspective, navigate to <project-name>/trunk (here: spf-api/trunk)
  • Right-click on trunk and choose "Find/Check Out as"
  • Choose "Check out as a project configured using the New Project Wizard"
  • Choose "Java project" and specify a name of your choice (e.g., spf-api)
  • After succesful ceckout, right-click on the project and choose "Maven / Enable Dependency Management" or "Configure / Conver to Maven Project" depending on the m2eclipse version
  • m2eclipse will do some work. After finished, right-click on the project and choose "Maven / Update Project Configuration"

Creating the InputPlugin Class

Create a new class inside your project. Let this class implement org.n52.ifgicopter.spf.input.IInputPlugin. Find below an example skeleton excerpt of an IInputPlugin implementation.
package org.n52.ifgicopter.spf.input;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.n52.ifgicopter.spf.gui.PluginGUI;

public class ExampleInputPlugin implements IInputPlugin {

    @Override
    public void init() throws Exception {
    }

    @Override
    public void shutdown() throws Exception {
    }

    @Override
    public InputStream getConfigFile() {
        return null;
    }

...

}

The init() method will be called during the framework startup. In analogy, the shutdown() method is called during the shutdown phase. These methods can be used to initialized/free needed resources such as streams, connections or serial ports.

Providing Metadata

Every InputPlugin must provide a plugin description as an XML document. The framework retrieves this information as an InputStream using the getConfigFile() method. An easy way for providing a File as an InputStream is outlined in the following.
public InputStream getConfigFile() {
   try {
      return new FileInputStream(new File(
            "config/spf/input-example.xml"));
   } catch (FileNotFoundException e) {
      e.printStackTrace();
   }
   return null;
}

Providing Data

As the framework makes use of the inversion of control paradigm, the data provided by InputPlugins gets pulled regularly. A plugin implementations must provide two methods to ensure a smooth data processing. The hasNewData() should return true, if the plugin has new data available. If true is returned, the getNewData() method gets called. Find below an example of this method.

public synchronized List<Map<String, Object>> getNewData() {
   List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(
         this.dataBuffer);
   this.dataBuffer.clear();
   return result;
}

As you can see, the data must be provided as a List of Maps. The inline map instances hold the actual key/value pair data including a timestamp identifier. A best practice is to buffer the gathered data using an internal List. Note the synchronized modifier for this method. This ensures that the buffer always has a valid state. Thus, adding data must also be encapsulated in a synchronized block. A simple implementation of the init() method which creates a worker thread to gather data is illustrated in the following.

public void init() throws Exception {
   new Thread(new Runnable() {

      @Override
      public void run() {
         while (running) {
            try {
               Thread.sleep(1000);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }

            Map<String,Object> data = new HashMap<String, Object>();
            data.put("time", System.currentTimeMillis());
            data.put("temperature", 24.3);
            synchronized (this) {
               dataBuffer.add(data);
            }
         }
      }
   }).start();
}

Other

For information on the other interface methods please consider the SPF Api Javadocs (SensorPlatformFramework#Download).

Plugin Description XML

The plugin description used by the getConfigFile() method (see #Providing_Metadata) is based on SensorML version 1.0.1. It is used to identify the observed phenomena of the InputPlugin implementation. Only those phenomena defined in the XML description can be processed by the framework. Java property key names (see #Providing_Data) are directly mapped to the inputs of the SensorML markup. Find below an example of a plugin description.
<?xml version="1.0" encoding="UTF-8"?>
<spf:plugin xmlns:spf="http://ifgi.uni-muenster.de/~m_riek02/spf/0.1" name="urn:ifgi:id:ifgicopter2">
   <spf:output>
      <spf:AvailabilityBehaviour>
         <spf:outputProperties>
            <spf:property>temperature</spf:property>
         </spf:outputProperties>
      </spf:AvailabilityBehaviour>
      <spf:mandatoryProperties>
         <spf:property>position</spf:property>
      </spf:mandatoryProperties>
   </spf:output>
   <SensorML xmlns="http://www.opengis.net/sensorML/1.0.1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:swe="http://www.opengis.net/swe/1.0.1"
      xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"
      xsi:schemaLocation="http://www.opengis.net/sensorML/1.0.1 http://schemas.opengis.net/sensorML/1.0.1/sensorML.xsd"
      version="1.0.1">
      <member>
         <System>

            <!-- if the platform is mobile use this instead of sml:position -->
            <gml:boundedBy>
               <gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326">
                  <gml:lowerCorner>51.5 6.5</gml:lowerCorner>
                  <gml:upperCorner>52.5 7.5</gml:upperCorner>
               </gml:Envelope>
            </gml:boundedBy>

            <!--~~~~~~~~~~~~~~~~~~~~~~~~ -->
            <!--Keywords -->
            <!--~~~~~~~~~~~~~~~~~~~~~~~ -->
            <keywords>
               <KeywordList>
                  <keyword>ifgicotper sensor platform</keyword>
                  <keyword>humidity</keyword>
                  <keyword>temperature</keyword>
               </KeywordList>
            </keywords>
            <!--~~~~~~~~~~~~~~~~~~~~~~~~ -->
            <!--Identification -->
            <!--~~~~~~~~~~~~~~~~~~~~~~~ -->
            <identification>
               <IdentifierList>
                  <identifier name="uniqueID">
                     <Term definition="urn:ogc:def:identifier:OGC:uniqueID">
                        <value>urn:ogc:object:feature:platform:IFGI:ifgicopter1</value>
                     </Term>
                  </identifier>
                  <identifier name="longName">
                     <Term definition="urn:ogc:def:identifier:OGC:1.0:longName">
                        <value>ifgicopter sensor platform for environmental monitoring</value>
                     </Term>
                  </identifier>
                  <identifier name="shortName">
                     <Term definition="urn:ogc:def:identifier:OGC:1.0:shortName">
                        <value>ifgicopter sensor platform</value>
                     </Term>
                  </identifier>
               </IdentifierList>
            </identification>
            <!--~~~~~~~~~~~~~~~~~~~~~~~~ -->
            <!--Classification -->
            <!--~~~~~~~~~~~~~~~~~~~~~~~ -->
            <classification>
               <ClassifierList>
                  <classifier name="intendedApplication">
                     <Term definition="urn:ogc:def:classifier:OGC:1.0:application">
                        <value>climate</value>
                     </Term>
                  </classifier>
               </ClassifierList>
            </classification>
            <!--~~~~~~~~~~~~~~~~~~~~~~~~ -->
            <!--Valid time -->
            <!--~~~~~~~~~~~~~~~~~~~~~~~ -->
            <validTime>
               <gml:TimePeriod>
                  <gml:beginPosition>2010-09-15</gml:beginPosition>
                  <gml:endPosition>2013-03-15</gml:endPosition>
               </gml:TimePeriod>
            </validTime>
            <!--~~~~~~~~~~~~~~~~~~~~~~~ -->
            <!--Contact -->
            <!--~~~~~~~~~~~~~~~~~~~~~~~ -->
            <!-- profile-specific: A "contact" element has to be present. -->
            <contact>
               <ResponsibleParty gml:id="WWU_IfGI_ifgcopter_contact">
                  <organizationName>Institute for Geoinformatics - Westfaelische
                     Wilhelms
                     Universitaet Muenster - Sensor Web and Simulation Lab
                  </organizationName>
                  <contactInfo>
                     <address>
                        <electronicMailAddress>m.rieke@uni-muenster.de
                        </electronicMailAddress>
                     </address>
                  </contactInfo>
               </ResponsibleParty>
            </contact>


            <!--~~~~~~~~~~~~~ -->
            <!--System Inputs -->
            <!--~~~~~~~~~~~~~ -->
            <inputs>
               <InputList>
                  <input name="position">
                     <swe:Position referenceFrame="urn:ogc:def:crs:EPSG::4326">
                        <swe:location>
                           <swe:Vector>
                              <swe:coordinate name="latitude">
                                 <swe:Quantity>
                                    <swe:uom code="deg" />
                                 </swe:Quantity>
                              </swe:coordinate>
                              <swe:coordinate name="longitude">
                                 <swe:Quantity>
                                    <swe:uom code="deg" />
                                 </swe:Quantity>
                              </swe:coordinate>
                              <swe:coordinate name="altitude">
                                 <swe:Quantity>
                                    <swe:uom code="m" />
                                 </swe:Quantity>
                              </swe:coordinate>
                           </swe:Vector>
                        </swe:location>
                     </swe:Position>
                  </input>
                  <input name="temperature">
                     <swe:Quantity definition="urn:ogc:def:property:OGC:1.0:temperature">
                        <swe:uom code="Cel" />
                     </swe:Quantity>
                  </input>
                  <input name="altitude">
                     <swe:Quantity>
                        <swe:uom code="m" />
                     </swe:Quantity>
                  </input>
                  <input name="time">
                     <swe:Time definition="urn:ogc:def:phenomenon:time" referenceFrame="urn:ogc:def:unit:iso8601" />
                  </input>
               </InputList>
            </inputs>
            <!--~~~~~~~~~~~~~~ -->
            <!--System Outputs -->
            <!--~~~~~~~~~~~~~~ -->
            <outputs>
               <OutputList>
                  <output name="position">
                     <swe:Position referenceFrame="urn:ogc:def:crs:EPSG::4326">
                        <swe:location>
                           <swe:Vector>
                              <swe:coordinate name="latitude">
                                 <swe:Quantity>
                                    <swe:uom code="deg" />
                                 </swe:Quantity>
                              </swe:coordinate>
                              <swe:coordinate name="longitude">
                                 <swe:Quantity>
                                    <swe:uom code="deg" />
                                 </swe:Quantity>
                              </swe:coordinate>
                              <swe:coordinate name="altitude">
                                 <swe:Quantity>
                                    <swe:uom code="m" />
                                 </swe:Quantity>
                              </swe:coordinate>
                           </swe:Vector>
                        </swe:location>
                     </swe:Position>
                  </output>
                  <output name="temperature">
                     <swe:Quantity definition="urn:ogc:def:property:OGC:1.0:temperature">
                        <swe:uom code="Cel" />
                     </swe:Quantity>
                  </output>
                  <output name="altitude">
                     <swe:Quantity>
                        <swe:uom code="m" />
                     </swe:Quantity>
                  </output>
                  <output name="time">
                     <swe:Time definition="urn:ogc:def:phenomenon:time" referenceFrame="urn:ogc:def:unit:iso8601" />
                  </output>
               </OutputList>
            </outputs>

         </System>
      </member>
   </SensorML>

</spf:plugin>

At first glance this might look quite complex. But there are only small adjustments you need to make for a quick implementation. If your InputPlugin provides positions as WGS84 lat/long (thus being mobile) you only need to adjust the other inputs and outputs. Every input must define its identifier by setting the name attribute. This must also be the key used in the Java implementation (see above).

Phenomena

The type of the phenomena is defined using Swe Common (part of the SensorML specification). Currently only numerical phenomena are supported, e.g.
<swe:Quantity definition="urn:ogc:def:property:OGC:1.0:temperature"><swe:uom code="Cel" /></swe:Quantity>

The definition should be defined as OutputPlugins could depend on it. the swe:uom is used to define the unit of measure as UCUM codes.

Note the special input for representing the time of a measurement. The SPFramework searches for inputs which use swe:Time. The identifier of this input is used to temporally order data. Thus every Map (see #) should provide an property with this key. Otherwise the SPFramework will use the current system time which could lead to unintended behaviour. Internal times can be represented as ISO 8601 strings or unix timestamp as milliseconds (as a long variable).

inputs and outputs are mostly structured in the same way and contents can be reused.

Plugin Behaviour

The first portion of the XML markup defines the dissemination and interpolation behaviour of the plugin. AvailabilityBehaviour should be used to trigger an output when certain phenomena are available (e.g., temperature). Multiple entries are allowed and result in creating multiple outputs if the timestamps differ.

   <spf:output>
      <spf:AvailabilityBehaviour>
         <spf:outputProperties>
            <spf:property>temperature</spf:property>
         </spf:outputProperties>
      </spf:AvailabilityBehaviour>
      <spf:mandatoryProperties>
         <spf:property>position</spf:property>
      </spf:mandatoryProperties>
   </spf:output>

Every property listed in the mandatoryProperties is then interpolated at the timestamp of the temperature measurement. Alternatively, the mandatoryProperties can be replaced by two other elements:

<spf:outputOnAllItems>true</spf:outputOnAllItems>

Every phenomena defined in the SensorML part of the XML must be present for generating output.

<spf:singleOutputAllowed>true</spf:singleOutputAllowed>

All outputProperties can generate single outputs (results in no data tuples).

Providing a User Interface

A Plugin can provide a GUI for enabling user interactions. Therefor the getUserInterface() method must return an instance of org.n52.ifgicopter.spf.gui.PluginGUI (see API Javadoc for details). An example of such a user interface is the MKInputPlugin.

Metadata

  • Topic created by: MatthesRieke
  • Topic created on: 2011-12-19
-- MatthesRieke - 2012-01-25

This topic: SensorWeb > WebHome > SensorPlatformFramework > InputPlugins
Topic revision: 27 Jan 2012, MatthesRieke
Legal Notice | Privacy Statement


This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki? Send feedback