Skip to content

how to write a new wrapper

ebiiii edited this page Oct 16, 2014 · 1 revision

##How to write a new wrapper All standard wrappers are subclasses of gsn.wrapper.AbstractWrapper. Subclasses must implement the following four methods:

  1. boolean initialize()
  2. void finalize()
  3. String getWrapperName()
  4. DataField[] getOutputFormat()

Each wrapper is a thread in the GSN. If you want to do some kind of processing in a fixed time interval, you can override the run() method. The run method is useful for time driven wrappers in which the production of a sensor data is triggered by a timer. Optionally, you may wish to override the method

boolean sendToWrapper(String action, String[] paramNames, Object[] paramValues);

initialize()

This method is called after the wrapper object creation. For more information on the life cycle of a wrapper, see (wrapper-life-cycle-v1 Wrapper life cycle). The complete method prototype is

public boolean initialize();

In this method, the wrapper should try to initialize its connection to the actual data producing/receiving device(s) (e.g., wireless sensor networks or cameras). The wrapper should return true if it can successfully initialize the connection, false otherwise. GSN provides access to the wrapper parameters through the following method call:

getActiveAddressBean().getPredicateValue("parameter-name");

For example, if you have the following fragment in the virtual sensor configuration file:

<source ... >
  <address wrapper="x">
    <predicate key="range">100</predicate>
    <predicate key="log">0</predicate>
  </address>

You can access the initialization parameter named range with the following code:

if(getActiveAddressBean().getPredicateValue("range") != null)
{...}

By default GSN assumes that the timestamps of the data produced in a wrapper are local, that is, the wrapper produced them using the system (or GSN) time. If you have cases where this assumption is not valid and GSN should assume remote timestamps for stream elements, add the following line in the initialize() method:

 setUsingRemoteTimestamp(true);

###finalize() In finalize() method, you should release all the resources you acquired during the initialization procedure or during the life cycle of the wrapper. Note that this is the last chance for the wrapper to release all its reserved resources and after this call the wrapper instance virtually won't exist anymore. For example, if you open a file in the initialization phase, you should close it in the finalization phase.

###getWrapperName() This method returns a name for the wrapper.

###getOutputFormat() The method getOutputFormat() returns a description of the data structure produced by this wrapper. This description is an array of DataField objects. A DataField object can be created with a call to the constructor

 public DataField(String name, String type, String description)

The name is the field name, the type is one of GSN data types (TINYINT, SMALLINT, INTEGER, BIGINT, CHAR(#), BINARY[(#)], VARCHAR(#), DOUBLE, TIME), See gsn.beans.DataTypes, and description is a text describing the field. The following examples should help you get started.

####Wireless Sensor Network Example Assuming that you have a wrapper for a wireless sensor network which produces the average temperature and light value of the nodes in the network, you can implement getOutputFormat() as follows:

public DataField[] getOutputFormat() {
	DataField[] outputFormat = new DataField[2];
	outputFormat[0] = new DataField("Temperature", "double",
		"Average of temperature readings from the sensor network");
	outputFormat[1] = new DataField("light", "double",
		"Average of light readings from the sensor network");
	return outputFormat;
}

####Webcam Example If you have a wrapper producing jpeg images as output (e.g., from wireless camera), the method is similar to below :

public DataField[] getOutputFormat() {
	DataField[] outputFormat = new DataField[1];
	outputFormat[0] = new DataField("Picture", "binary:jpeg",
		"Picture from the Camera at room BC143");
	return outputFormat;
}

###run() As described before, the wrapper acts as a bridge between the actual hardware device(s) or other kinds of stream data sources and GSN, thus in order for the wrapper to produce data, it should keep track of the newly produced data items. This method is responsible for forwarding the newly received data to the GSN engine. You should not try to start the thread by yourself: GSN takes care of this.

The method should be implemented as below :

while(isActive()) {
	{
		// The thread should wait here until arrival of a new data notifies it
                // or the thread should sleep for some finite time before polling the data source or producing the next data
	}

        //Application dependent processing ...
	StreamElement streamElement = new StreamElement ( ...);
	postStreamElement( streamElement ); // This method in the AbstractWrapper sends the data to the registered StreamSources
}

####Webcam example Assume that we have a wireless camera which runs a HTTP server and provides pictures whenever it receives a GET request. In this case we are in a data on demand scenario (most of the network cameras are like this). To get the data at the rate of 1 picture every 5 seconds we can do the following :

while(isActive()) {
	byte[] received_image = getPictureFromCamera();
	postStreamElement(System.currentTimeMillis(), new Serializable[] {received_image});
	Thread.sleep(5*1000); // Sleeping 5 seconds
}

####Data driven systems Compared to the previous example, we do sometimes deal with devices that are data driven. This means that we don't have control of either when the data is produced by them (e.g., when they do the capturing) or the rate at which data is received from them. For example, having an alarm system, we don't know when we are going to receive a packet, or how frequently the alarm system will send data packets to GSN. These kind of systems are typically implemented using a callback interface. In the callback interface, one needs to set a flag indicating the data reception state of the wrapper and control that flag in the run method to process the received data.

###sendToWrapper()

In GSN, the wrappers can not only receive data from a source, but also send data to it. Thus wrappers are actually two-way bridges between GSN and the data source. In the wrapper interface, the method sendToWrapper() is called whenever there is a data item which should be send to the source. A data item could be as simple as a command for turning on a sensor inside the sensor network, or it could be as complex as a complete routing table which should be used for routing the packets in the sensor network. The full syntax of sendToWrapper() is as follows:

public boolean sendToWrapper(String action, String[] paramNames, Object[] paramValues) throws OperationNotSupportedException;

The default implementation of the above-mentioned method throws an OperationNotSupportedException exception because the wrapper doesn't support this operation. This design choice is justified by the observation that not all kind of devices (sensors) can accept data from a computer. For instance, a typical wireless camera doesn't accept commands from the wrapper. If the sensing device supports this operation, one needs to override this method so that instead of the default action (throwing the exception), the wrapper sends the data to the sensor. You can consult the gsn.wrappers.general.SerialWrapper class for an example.

A fully functional wrapper

/**
 * This wrapper presents a MultiFormat protocol in which the data comes from the
 * system clock. Think about a sensor network which produces packets with
 * several different formats. In this example we have 3 different packets
 * produced by three different types of sensors. Here are the packet structures
 * : [temperature:double] , [light:double] , [temperature:double, light:double]
 * The first packet is for sensors which can only measure temperature while the
 * latter is for the sensors equipped with both temperature and light sensors.
 * 
 */
public class MultiFormatWrapper extends AbstractWrapper {
  private DataField[] collection = new DataField[] { new DataField("packet_type", "int", "packet type"),
      new DataField("temperature", "double", "Presents the temperature sensor."), new DataField("light", "double", "Presents the light sensor.") };
  private final transient Logger logger = Logger.getLogger(MultiFormatWrapper.class);
  private int counter;
  private AddressBean params;
  private long rate = 1000;

  public boolean initialize() {
    setName("MultiFormatWrapper" + counter++);
    
    params = getActiveAddressBean();
    
    if ( params.getPredicateValue( "rate" ) != null ) {
      rate = (long) Integer.parseInt( params.getPredicateValue( "rate"));
      
      logger.info("Sampling rate set to " + params.getPredicateValue( "rate") + " msec.");
    }
    
    return true;
  }

  public void run() {
    Double light = 0.0, temperature = 0.0;
    int packetType = 0;
    
    while (isActive()) {
      try {
        // delay 
        Thread.sleep(rate);
      } catch (InterruptedException e) {
        logger.error(e.getMessage(), e);
      }
      
      // create some random readings
      light = ((int) (Math.random() * 10000)) / 10.0;
      temperature = ((int) (Math.random() * 1000)) / 10.0;
      packetType = 2;

      // post the data to GSN
      postStreamElement(new Serializable[] { packetType, temperature, light });       
    }
  }

  public DataField[] getOutputFormat() {
    return collection;
  }

  public String getWrapperName() {
    return "MultiFormat Sample Wrapper";
  }  

  public void finalize() {
    counter--;
  }
}
Clone this wiki locally