Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "SensiNact/Tutorial Bridge"

(Formatting in progress)
(Required knowledge)
(13 intermediate revisions by one other user not shown)
Line 1: Line 1:
= A sensiNact Bridge tutorial - the weather station example =
+
= A sensiNact bridge tutorial - the weather station example =
  
 
== Required knowledge ==
 
== Required knowledge ==
Line 6: Line 6:
  
 
* Java
 
* Java
* [[SensiNact/sensiNact Full Start|Architecture and data model]] of sensiNact
+
* Architecture and data model of sensiNact
  
 
== The project skeleton ==
 
== The project skeleton ==
  
Let start by defining a simple weather station providing four kind of information :
+
Let start by defining a simple weather station providing four kind of information:
  
 
* temperature
 
* temperature
Line 20: Line 20:
  
 
<code>
 
<code>
> mvn archetype:generate -DarchetypeGroupId=fr.cea.sna.archetypes -DarchetypeArtifactId=sensinact-archetype -DarchetypeVersion=1.0 -DgroupId=fr.cea.sna.gateway.simulated -DartifactId=weather-station
+
> mvn archetype:generate -DarchetypeGroupId=org.eclipse.sensinact.archetypes -DarchetypeArtifactId=sensinact-archetype -DarchetypeVersion=1.0 -DgroupId=org.eclipse.sensinact.gateway.simulated -DartifactId=weather-station
 
</code>
 
</code>
  
Line 27: Line 27:
 
A new maven project has been created containing :
 
A new maven project has been created containing :
  
* <code>weather-station/pom.xml</code>: this is the maven project description file in which dependencies are referenced, as well as the configuration of the [maven-bundle-plugin](http://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html) that will be used to create our executable [bundle](https://fr.wikipedia.org/wiki/OSGi).  
+
* <code>weather-station/pom.xml</code>: this is the maven project description file in which dependencies are referenced, as well as the configuration of the [http://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html maven-bundle-plugin] that will be used to create our executable [https://fr.wikipedia.org/wiki/OSGi bundle].  
 
* <code>weather-station/schema/sensinact-resource.xsd</code>: this is the schema on which the XML description of the resources of our weather station is based on. It will not be added to the compiled module, but is provided by the <code>sensinact-archetype</code> while not accessible remotely.
 
* <code>weather-station/schema/sensinact-resource.xsd</code>: this is the schema on which the XML description of the resources of our weather station is based on. It will not be added to the compiled module, but is provided by the <code>sensinact-archetype</code> while not accessible remotely.
* <code>weather-station/src/main/java/fr/cea/sna/gateway/simulated/osgi/Activator.java</code>: this java class is the [Bundle](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/Bundle.html)'s [Activator](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleActivator.html). We will have to complete it later.
+
* <code>weather-station/src/main/java/fr/cea/sna/gateway/simulated/osgi/Activator.java</code>: this java class is the [https://osgi.org/javadoc/r4v43/core/org/osgi/framework/Bundle.html Bundle]'s [https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleActivator.html Activator]. We will have to complete it later.
 
* <code>weather-station/src/main/resources/sensiNact-conf.xml</code>: this java xml properties file will be loaded at initialization time and allows to configure our module by adding properties.
 
* <code>weather-station/src/main/resources/sensiNact-conf.xml</code>: this java xml properties file will be loaded at initialization time and allows to configure our module by adding properties.
 
* <code>weather-station/src/main/resources/resource.xml</code>: we will later amend this xml file in which are described the resources provided by the weather station.
 
* <code>weather-station/src/main/resources/resource.xml</code>: we will later amend this xml file in which are described the resources provided by the weather station.
Line 42: Line 42:
 
* a '''ProtocolStackEndpoint''' which is the connecting  point to the remote counterpart of the resource model instance.
 
* a '''ProtocolStackEndpoint''' which is the connecting  point to the remote counterpart of the resource model instance.
 
   
 
   
[[File:sensinact-generic-resource-model.png|sensiNact resource model generic]]
+
[[File:sensinact-generic-resource-model.png|700px|sensiNact resource model generic]]
 
    
 
    
 
To feed our weather station we also have to define the kind of data that will be used and how to consume them. To manage processing data, the sensiNact resource model implementation expects a data structure implementing the <code>Packet</code> interface, representing a communication object wrapping the raw transmitted data. Moreover we have to provide an appropriate <code>PacketReader</code> able to identify in an handled <code>Packet</code> type the relevant information for the sensiNact system. In our bridge example we will cope with JSON formated data structures, in which the properties' keys and values will refer respectively to our resources' identifiers and values. We will so define our own <code>Packet</code> type that we will call <code>WeatherPacket</code>, and the <code>PacketReader</code> in charge of parsing it, the <code>WeatherPacketReader</code>.
 
To feed our weather station we also have to define the kind of data that will be used and how to consume them. To manage processing data, the sensiNact resource model implementation expects a data structure implementing the <code>Packet</code> interface, representing a communication object wrapping the raw transmitted data. Moreover we have to provide an appropriate <code>PacketReader</code> able to identify in an handled <code>Packet</code> type the relevant information for the sensiNact system. In our bridge example we will cope with JSON formated data structures, in which the properties' keys and values will refer respectively to our resources' identifiers and values. We will so define our own <code>Packet</code> type that we will call <code>WeatherPacket</code>, and the <code>PacketReader</code> in charge of parsing it, the <code>WeatherPacketReader</code>.
Line 50: Line 50:
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
 
package org.eclipse.sensinact.gateway.simulated.osgi;
 
package org.eclipse.sensinact.gateway.simulated.osgi;
  
Line 64: Line 64:
 
  * The weather station bundle activator
 
  * The weather station bundle activator
 
  */
 
  */
public class Activator extends AbstractActivator<Mediator>
+
public class Activator extends AbstractActivator<Mediator> {
{    
+
    private ExtProtocolStackEndpoint<WeatherPacket> connector;
private ExtProtocolStackEndpoint<WeatherPacket> connector;
+
    private ExtXmlModelConfiguration<WeatherPacket> manager;
private ExtXmlModelConfiguration<WeatherPacket> manager;
+
 
+
    /**
      /**
+
    * @inheritDoc
* @inheritDoc
+
    *
*
+
    * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStart()
* @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStart()
+
    */
*/
+
    @Override
@Override
+
    public void doStart() throws Exception {
public void doStart() throws Exception
+
        manager = new XmlModelInstanceBuilder<ExtXmlModelConfiguration,
{      
+
                XmlModelInstance>(super.mediator,XmlModelInstance.class,
  manager = new XmlModelInstanceBuilder<ExtXmlModelConfiguration,
+
                ExtXmlModelConfiguration.class
        XmlModelInstance>(super.mediator,XmlModelInstance.class,
+
        ).withPacketType(WeatherPacket.class
        ExtXmlModelConfiguration.class
+
        ).withBuildDynamically(true
        ).withPacketType(WeatherPacket.class
+
        ).withDataResourceType(SensorDataResource.class
                ).withBuildDynamically(true
+
        ).withDataType(float.class
                ).withDataResourceType(SensorDataResource.class
+
        ).withStartAtInitializationTime(true
                ).withDataType(float.class
+
        ).buildConfiguration("resource.xml", Collections.<String,String>emptyMap());
                ).withStartAtInitializationTime(true
+
        connector = new ExtProtocolStackEndpoint<WeatherPacket>(super.mediator);
                ).buildConfiguration("resource.xml", Collections.<String,String>emptyMap());            
+
        connector.connect(manager);
      connector = new ExtProtocolStackEndpoint<WeatherPacket>(super.mediator);
+
    }
      connector.connect(manager);
+
 
}
+
    /**
+
    * @inheritDoc
  /**
+
    *
* @inheritDoc
+
    * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStop()
*
+
    */
* @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStop()
+
    @Override
*/
+
    public void doStop() throws Exception {
@Override
+
        connector.stop();
public void doStop() throws Exception
+
    }
{
+
 
  connector.stop();
+
    /**
}
+
    * @inheritDoc
+
    *
  /**
+
    * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#
* @inheritDoc
+
    * doInstantiate(org.osgi.framework.BundleContext)
*
+
    */
* @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#
+
    @Override
* doInstantiate(org.osgi.framework.BundleContext)
+
    public Mediator doInstantiate(BundleContext context) throws InvalidSyntaxException {
*/
+
        return new Mediator(context);
@Override
+
    }
public Mediator doInstantiate(BundleContext context)  
+
throws InvalidSyntaxException
+
{
+
  return new Mediator(context);
+
}  
+
 
}
 
}
 
</source>
 
</source>
Line 118: Line 113:
 
----
 
----
  
The <code>mediator</code> argument, needed by both <code>XmlModelConfiguration</code> and <code>ProtocolStackEndpoint</code> constructors allows to interact with the OSGi host environment by the way of the [BundleContext](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleContext.html) from one hand, and on the other hand provides logging feature.
+
The <code>mediator</code> argument, needed by both <code>XmlModelConfiguration</code> and <code>ProtocolStackEndpoint</code> constructors allows to interact with the OSGi host environment by the way of the [https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleContext.html BundleContext] from one hand, and on the other hand provides logging feature.
  
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
 
package org.eclipse.sensinact.gateway.simulated.weather;
 
package org.eclipse.sensinact.gateway.simulated.weather;
  
Line 128: Line 123:
  
 
/**
 
/**
  * Extended {@link Packet} implementation wrapping  
+
  * Extended {@link Packet} implementation wrapping
 
  * a communication object targeting the weather station
 
  * a communication object targeting the weather station
 
  */
 
  */
public class WeatherPacket implements Packet
+
public class WeatherPacket implements Packet {
{  
+
 
  /**
+
    /**
* the bytes array content of this WeatherPacket
+
    * the bytes array content of this WeatherPacket
*/
+
    */
private byte[] content;
+
    private byte[] content;
+
 
  /**
+
    /**
* Constructor
+
    * Constructor
*
+
    *
* @param content
+
    * @param content
*      the bytes array content of the WeatherPacket to
+
    *      the bytes array content of the WeatherPacket to
*      instantiate
+
    *      instantiate
*/
+
    */
public WeatherPacket(byte[] content)
+
    public WeatherPacket(byte[] content) {
{
+
        int length = content==null?0:content.length;
int length = content==null?0:content.length;
+
        this.content = new byte[length];
this.content = new byte[length];
+
 
if(length > 0)
+
        if(length > 0) {
{
+
            System.arraycopy(content, 0, this.content, 0, length);
System.arraycopy(content, 0, this.content, 0, length);
+
        }
}
+
    }
}
+
 
+
    /**
  /**
+
    * @inheritDoc
* @inheritDoc
+
    *
*
+
    * @see org.eclipse.sensinact.gateway.generic.core.packet.Packet#getBytes()
* @see org.eclipse.sensinact.gateway.generic.core.packet.Packet#getBytes()
+
    */
*/
+
    @Override
@Override
+
    public byte[] getBytes() {
public byte[] getBytes()
+
        byte[] content = new byte[this.content.length];
{
+
 
byte[] content = new byte[this.content.length];
+
        if(this.content.length> 0) {
if(this.content.length> 0)
+
            System.arraycopy(this.content, 0, content, 0, this.content.length);
{
+
        }
System.arraycopy(this.content, 0, content, 0, this.content.length);
+
       
}
+
        return content;
return content;
+
    }
}
+
 
}
 
}
 
</source>
 
</source>
Line 177: Line 171:
 
the '''getBytes''' method is the only one specified in the <code>Packet</code> interface. We could have to define other methods to extract protocol specific information from the wrapped communication object, but in our case it is not needed.
 
the '''getBytes''' method is the only one specified in the <code>Packet</code> interface. We could have to define other methods to extract protocol specific information from the wrapped communication object, but in our case it is not needed.
  
The <code>WeatherPacketReader</code> will extend the <code>SimplePacketReader</code> abstract class which requires to only implement the **parse** method. The <code>SimplePacketReader</code> parsing mechanism consists in building a hierarchical data structure of contained information to map to the hierarchical sensiNact's resource model :  
+
The <code>WeatherPacketReader</code> will extend the <code>SimplePacketReader</code> abstract class which requires to only implement the '''parse''' method. The <code>SimplePacketReader</code> parsing mechanism consists in building a hierarchical data structure of contained information to map to the hierarchical sensiNact's resource model :  
  
 
[[File:sna_reader.png|Packet reader mechanism]]
 
[[File:sna_reader.png|Packet reader mechanism]]
Line 185: Line 179:
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
 
package org.eclipse.sensinact.gateway.simulated.weather;
 
package org.eclipse.sensinact.gateway.simulated.weather;
  
Line 196: Line 190:
 
  * {@link PacketReader} dedicated to {@link WeatherPacket} parsing
 
  * {@link PacketReader} dedicated to {@link WeatherPacket} parsing
 
  */
 
  */
public class WeatherPacketReader extends SimplePacketReader<WeatherPacket>
+
public class WeatherPacketReader extends SimplePacketReader<WeatherPacket> {
{
+
    /**
  /**
+
    * Constructor
* Constructor
+
    *
*  
+
    * @param mediator
* @param mediator
+
    */
*/
+
     protected WeatherPacketReader(Mediator mediator) {
     protected WeatherPacketReader(Mediator mediator)
+
        super(mediator);
    {
+
    super(mediator);
+
 
     }
 
     }
  
  /**
+
    /**
* @inheritDoc
+
    * @inheritDoc
*
+
    *
* @see org.eclipse.sensinact.gateway.generic.core.packet.PacketReader#
+
    * @see org.eclipse.sensinact.gateway.generic.core.packet.PacketReader#
* parse(org.eclipse.sensinact.gateway.generic.core.packet.Packet)
+
    * parse(org.eclipse.sensinact.gateway.generic.core.packet.Packet)
*/
+
    */
 
     @Override
 
     @Override
     public void parse(WeatherPacket packet) throws InvalidPacketException
+
     public void parse(WeatherPacket packet) throws InvalidPacketException {
    {
+
        JSONObject jsonObject = new JSONObject(new String(packet.getBytes()));
      JSONObject jsonObject = new JSONObject(new String(packet.getBytes()));
+
     
+
      super.setServiceProviderId("station");
+
      super.setServiceId("weather");   
+
      super.setResourceId("temperature");
+
      super.setData(jsonObject.opt("temperature"));
+
      super.configure();
+
   
+
      super.setServiceProviderId("station");
+
      super.setServiceId("weather");   
+
      super.setResourceId("humidity");
+
      super.setData(jsonObject.opt("humidity"));
+
      super.configure();
+
  
      super.setServiceProviderId("station");
+
        super.setServiceProviderId("station");
      super.setServiceId("weather");  
+
        super.setServiceId("weather");
      super.setResourceId("pressure");
+
        super.setResourceId("temperature");
      super.setData(jsonObject.opt("pressure"));
+
        super.setData(jsonObject.opt("temperature"));
      super.configure();
+
        super.configure();
  
      super.setServiceProviderId("station");
+
        super.setServiceProviderId("station");
      super.setServiceId("weather");  
+
        super.setServiceId("weather");
      super.setResourceId("wind");
+
        super.setResourceId("humidity");
      super.setData(jsonObject.opt("wind"));
+
        super.setData(jsonObject.opt("humidity"));
      super.configure();
+
        super.configure();
  }
+
 
 +
        super.setServiceProviderId("station");
 +
        super.setServiceId("weather");
 +
        super.setResourceId("pressure");
 +
        super.setData(jsonObject.opt("pressure"));
 +
        super.configure();
 +
 
 +
        super.setServiceProviderId("station");
 +
        super.setServiceId("weather");
 +
        super.setResourceId("wind");
 +
        super.setData(jsonObject.opt("wind"));
 +
        super.configure();
 +
    }
 
}
 
}
 
</source>
 
</source>
Line 259: Line 250:
 
To simulate a communication with a distant weather station we just have to wrap a JSONObject whose format is as described above into a WeatherPacket and ask the <code>ProtocolStackEndpoint</code> to '''process''' it.
 
To simulate a communication with a distant weather station we just have to wrap a JSONObject whose format is as described above into a WeatherPacket and ask the <code>ProtocolStackEndpoint</code> to '''process''' it.
  
We could have adopted a more efficient data structure, but this one has the advantage to be easily understandable. To allow our sensiNact's resource model instance to use our  <code>WeatherPacketReader</code> we still have to provide an appropriate factory and to reference it for the java's [ServiceLoader](https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html). The searched factory will be one implementing the <code>PacketReaderFactory</code> interface which defines two methods :
+
We could have adopted a more efficient data structure, but this one has the advantage to be easily understandable. To allow our sensiNact's resource model instance to use our  <code>WeatherPacketReader</code> we still have to provide an appropriate factory and to reference it for the java's [https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html ServiceLoader]. The searched factory will be one implementing the <code>PacketReaderFactory</code> interface which defines two methods :
 
   
 
   
 
* the '''handle''' method taking a <code>Packet</code> type as parameter and returning a boolean defining whether the specified <code>Packet</code> type is handled by the factory ;
 
* the '''handle''' method taking a <code>Packet</code> type as parameter and returning a boolean defining whether the specified <code>Packet</code> type is handled by the factory ;
Line 266: Line 257:
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
package fr.cea.sna.gateway.simulated.weather;
+
package org.eclipse.sensinact.gateway.simulated.weather;
  
import fr.cea.sna.gateway.generic.core.InvalidPacketException;
+
import org.eclipse.sensinactgateway.generic.core.InvalidPacketException;
import fr.cea.sna.gateway.generic.core.impl.ExtXmlModelConfiguration;
+
import org.eclipse.sensinact.gateway.generic.core.impl.ExtXmlModelConfiguration;
import fr.cea.sna.gateway.generic.core.XmlModelConfiguration;
+
import org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration;
import fr.cea.sna.gateway.generic.core.impl.PacketReaderFactory;
+
import org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory;
import fr.cea.sna.gateway.generic.core.packet.Packet;
+
import org.eclipse.sensinact.gateway.generic.core.packet.Packet;
import fr.cea.sna.gateway.generic.core.packet.PacketReader;
+
import org.eclipse.sensinact.gateway.generic.core.packet.PacketReader;
import fr.cea.sna.gateway.common.bundle.Mediator;
+
import org.eclipse.sensinact.gateway.common.bundle.Mediator;
  
 
public class WeatherPacketReaderFactory implements PacketReaderFactory<WeatherPacket> {
 
public class WeatherPacketReaderFactory implements PacketReaderFactory<WeatherPacket> {
  /**
+
    /**
* @inheritDoc
+
    * @inheritDoc
*
+
    *
* @see fr.cea.sna.gateway.generic.core.impl.PacketReaderFactory#
+
    * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
* handle(java.lang.Class)
+
    * handle(java.lang.Class)
*/
+
    */
 
     @Override
 
     @Override
 
     public boolean handle(Class<? extends Packet> packetType) {
 
     public boolean handle(Class<? extends Packet> packetType) {
    return WeatherPacket.class.isAssignableFrom(packetType);
+
        return WeatherPacket.class.isAssignableFrom(packetType);
 
     }
 
     }
      
+
 
  /**
+
     /**
    * @inheritDoc
+
    * @inheritDoc
    *
+
    *
    * @see fr.cea.sna.gateway.generic.core.impl.PacketReaderFactory#
+
    * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
    * newInstance(fr.cea.sna.gateway.common.bundle.Mediator,  
+
    * newInstance(org.eclipse.sensinact.gateway.common.bundle.Mediator,  
    * fr.cea.sna.gateway.generic.core.XmlModelConfiguration,  
+
    * org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration,  
    * fr.cea.sna.gateway.generic.core.packet.Packet)
+
    * org.eclipse.sensinact.gateway.generic.core.packet.Packet)
    */
+
    */
 
     @Override
 
     @Override
     public PacketReader<WeatherPacket> newInstance(
+
     public PacketReader<WeatherPacket> newInstance(Mediator mediator, XmlModelConfiguration<WeatherPacket> manager,
      Mediator mediator, XmlModelConfiguration<WeatherPacket> manager,
+
            WeatherPacket packet) throws InvalidPacketException {
      WeatherPacket packet) throws InvalidPacketException
+
        WeatherPacketReader packetReader = new WeatherPacketReader(mediator);
    {  
+
        packetReader.parse(packet);
    WeatherPacketReader packetReader = new WeatherPacketReader(mediator);
+
        return packetReader;
    packetReader.parse(packet);
+
    return packetReader;
+
 
     }
 
     }
 
}
 
}
Line 311: Line 300:
 
----
 
----
  
Finally, in the <code>META-INF/services/fr.cea.sna.gateway.generic.core.impl.PacketReaderFactory</code> text file we specify the canonical name of our factory class to make it accessible by the java's <code>ServiceLoader</code>: <code>fr.cea.sna.gateway.simulated.weather.WeatherPacketReaderFactory</code>.
+
Finally, in the <code>META-INF/services/org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory</code> text file we specify the canonical name of our factory class to make it accessible by the java's <code>ServiceLoader</code>: <code>org.eclipse.sensinact.gateway.simulated.weather.WeatherPacketReaderFactory</code>.
  
 
Let see below what our project looks like now:
 
Let see below what our project looks like now:
Line 323: Line 312:
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
 
package org.eclipse.sensinact.gateway.simulated.weather;
 
package org.eclipse.sensinact.gateway.simulated.weather;
  
Line 334: Line 323:
  
 
public class WeatherStation implements Runnable {
 
public class WeatherStation implements Runnable {
private static final Logger LOGGER = Logger.getLogger(WeatherStation.class.getName());
+
    private static final Logger LOGGER = Logger.getLogger(WeatherStation.class.getName());
+
private static final Random RANDOM = new Random();
+
+
private double temperature = 20.0d;
+
private double humidity = 53.0d;
+
private double pressure = 1024.0d;
+
private double wind = 22.0d;
+
  
private ProtocolStackEndpoint<WeatherPacket> endpoint;
+
    private static final Random RANDOM = new Random();
private boolean running;
+
+
/**
+
* Constructor
+
*/
+
public WeatherStation(ProtocolStackEndpoint<WeatherPacket> endpoint)
+
{
+
this.endpoint = endpoint;
+
this.running = false;
+
}
+
  
/**
+
    private double temperature = 20.0d;
* @inheritDoc
+
    private double humidity = 53.0d;
*
+
    private double pressure = 1024.0d;
* @see java.lang.Runnable#run()
+
    private double wind = 22.0d;
*/
+
public void run()
+
{
+
this.running = true;
+
while(running)
+
{
+
temperature = RANDOM.nextBoolean()
+
?addition(temperature):subtraction(temperature);
+
humidity = RANDOM.nextBoolean()
+
?addition(humidity):subtraction(humidity);
+
pressure = RANDOM.nextBoolean()
+
?addition(pressure):subtraction(pressure);
+
wind = RANDOM.nextBoolean()
+
?addition(wind):subtraction(wind);
+
+
JSONObject content = new JSONObject();
+
content.put("temperature", temperature);
+
content.put("humidity", humidity);
+
content.put("pressure", pressure);
+
content.put("wind", wind);
+
+
WeatherPacket packet= new WeatherPacket(
+
content.toString().getBytes());
+
try
+
{
+
this.endpoint.process(packet);
+
}
+
catch (InvalidPacketException e)
+
{
+
LOGGER.log(Level.SEVERE, e.getMessage(), e);
+
+
} finally
+
{
+
try
+
{
+
Thread.sleep(60000);
+
+
} catch(InterruptedException e)
+
{
+
Thread.interrupted();
+
this.stop();
+
}
+
}
+
}
+
}
+
  
public void stop()
+
    private ProtocolStackEndpoint<WeatherPacket> endpoint;
{
+
    private boolean running;
this.running = false;
+
}
+
+
private double addition(double value)
+
{
+
value +=((value/10d)*RANDOM.nextDouble());
+
return value;
+
}
+
  
private double subtraction(double value)
+
    /**
{
+
    * Constructor
value -=((value/10d)*RANDOM.nextDouble());
+
    */
return value;
+
    public WeatherStation(ProtocolStackEndpoint<WeatherPacket> endpoint) {
}
+
        this.endpoint = endpoint;
 +
        this.running = false;
 +
    }
 +
 
 +
    /**
 +
    * @inheritDoc
 +
    *
 +
    * @see java.lang.Runnable#run()
 +
    */
 +
    public void run() {
 +
        this.running = true;
 +
        while(running) {
 +
            temperature = RANDOM.nextBoolean()
 +
                    ?addition(temperature):subtraction(temperature);
 +
            humidity = RANDOM.nextBoolean()
 +
                    ?addition(humidity):subtraction(humidity);
 +
            pressure = RANDOM.nextBoolean()
 +
                    ?addition(pressure):subtraction(pressure);
 +
            wind = RANDOM.nextBoolean()
 +
                    ?addition(wind):subtraction(wind);
 +
 
 +
            JSONObject content = new JSONObject();
 +
            content.put("temperature", temperature);
 +
            content.put("humidity", humidity);
 +
            content.put("pressure", pressure);
 +
            content.put("wind", wind);
 +
 
 +
            WeatherPacket packet= new WeatherPacket(
 +
                    content.toString().getBytes());
 +
            try {
 +
                this.endpoint.process(packet);
 +
            } catch (InvalidPacketException e) {
 +
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
 +
            } finally {
 +
                try {
 +
                    Thread.sleep(60000);
 +
                } catch(InterruptedException e) {
 +
                    Thread.interrupted();
 +
                    this.stop();
 +
                }
 +
            }
 +
        }
 +
    }
 +
 
 +
    public void stop() {
 +
        this.running = false;
 +
    }
 +
 
 +
    private double addition(double value) {
 +
        value +=((value/10d)*RANDOM.nextDouble());
 +
        return value;
 +
    }
 +
   
 +
    private double subtraction(double value) {
 +
        value -=((value/10d)*RANDOM.nextDouble());
 +
        return value;
 +
    }
 
}
 
}
 
</source>
 
</source>
Line 426: Line 401:
 
----  
 
----  
  
Download the simulator [here](documents/tutorial/WeatherStation.java).
 
 
 
Download this first complete example [here](documents/tutorial/weather-station-sample.jar).
 
 
 
 
== Operate finer configuration features ==
 
== Operate finer configuration features ==
  
Line 436: Line 407:
 
----
 
----
  
<source lang="xml">
+
<source lang="xml" line start="1">
 
<complexType name="metadata">
 
<complexType name="metadata">
<complexContent>
+
    <complexContent>
<extension base="sensinact:nameTypeValue">
+
        <extension base="sensinact:nameTypeValue">
<sequence>
+
            <sequence>
<element name="type" type="sensinact:restrictedTypeType" minOccurs="1" maxOccurs="1"/>
+
                <element name="type" type="sensinact:restrictedTypeType" minOccurs="1" maxOccurs="1"/>
<element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded"/>
+
                <element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
+
            </sequence>
<attribute name="name" use="required" type="sensinact:modifiableHiddenKeyWordsExcludedString"/>
+
            <attribute name="name" use="required" type="sensinact:modifiableHiddenKeyWordsExcludedString"/>
</extension>
+
        </extension>
</complexContent>
+
    </complexContent>
 
</complexType>
 
</complexType>
+
 
 
<complexType name="attribute">
 
<complexType name="attribute">
<complexContent>
+
    <complexContent>
<extension base="sensinact:nameTypeValue">
+
        <extension base="sensinact:nameTypeValue">
<sequence>
+
            <sequence>
<element name="type" type="sensinact:typeType" minOccurs="1" maxOccurs="unbounded"/>
+
                <element name="type" type="sensinact:typeType" minOccurs="1" maxOccurs="unbounded"/>
<element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded"/>
+
                <element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded"/>
<element name="metadata"  minOccurs="0" maxOccurs="unbounded" type="sensinact:metadata">
+
                <element name="metadata"  minOccurs="0" maxOccurs="unbounded" type="sensinact:metadata">
<unique name="uniqueMetadataValueTarget">
+
                    <unique name="uniqueMetadataValueTarget">
<selector xpath="./sensinact:value"/>
+
                        <selector xpath="./sensinact:value"/>
<field xpath="@target"/>
+
                        <field xpath="@target"/>
</unique>
+
                    </unique>
</element>
+
                </element>
<element name="constraints" type="sensinact:constraints" minOccurs="0" maxOccurs="1"/>
+
                <element name="constraints" type="sensinact:constraints" minOccurs="0" maxOccurs="1"/>
</sequence>
+
            </sequence>
<attribute name="name" use="required" type="sensinact:nameTypeValueKeyWordsExcludedString"/>
+
            <attribute name="name" use="required" type="sensinact:nameTypeValueKeyWordsExcludedString"/>
<attribute name="modifiable" type="boolean" default="true" use="optional"/>
+
            <attribute name="modifiable" type="boolean" default="true" use="optional"/>
<attribute name="hidden" type="boolean" default="false" use="optional"/>
+
            <attribute name="hidden" type="boolean" default="false" use="optional"/>
</extension>
+
        </extension>
</complexContent>
+
    </complexContent>
 
</complexType>
 
</complexType>
 
</source>
 
</source>
Line 479: Line 450:
 
----
 
----
  
<source lang="xml">
+
<source lang="xml" line start="1">
 
<complexType name="resourceInfo" abstract="true">
 
<complexType name="resourceInfo" abstract="true">
<sequence maxOccurs="1" minOccurs="1">
+
    <sequence maxOccurs="1" minOccurs="1">
<element name="policy" type="sensinact:policy" minOccurs="0" maxOccurs="1"/>
+
        <element name="policy" type="sensinact:policy" minOccurs="0" maxOccurs="1"/>
<element name="subscriptionModes" type="sensinact:subscriptionModes" minOccurs="0" maxOccurs="1"/>
+
        <element name="subscriptionModes" type="sensinact:subscriptionModes" minOccurs="0" maxOccurs="1"/>
<element name="identifier" type="sensinact:simpleContent" minOccurs="1" maxOccurs="1"/>
+
        <element name="identifier" type="sensinact:simpleContent" minOccurs="1" maxOccurs="1"/>
<element name="attribute" type="sensinact:attribute" minOccurs="0" maxOccurs="unbounded">
+
        <element name="attribute" type="sensinact:attribute" minOccurs="0" maxOccurs="unbounded">
<unique name="uniqueAttributeTypeTarget">
+
            <unique name="uniqueAttributeTypeTarget">
<selector xpath="./sensinact:type"/>
+
                <selector xpath="./sensinact:type"/>
<field xpath="@target"/>
+
                <field xpath="@target"/>
</unique>
+
            </unique>
<unique name="uniqueAttributeValueTarget">
+
            <unique name="uniqueAttributeValueTarget">
<selector xpath="./sensinact:value"/>
+
                <selector xpath="./sensinact:value"/>
<field xpath="@target"/>
+
                <field xpath="@target"/>
</unique>
+
            </unique>
</element>
+
        </element>
</sequence>
+
    </sequence>
<attribute name="name" type="string" use="required"/>
+
    <attribute name="name" type="string" use="required"/>
<attribute name="target" type="sensinact:targets" use="optional"/>
+
    <attribute name="target" type="sensinact:targets" use="optional"/>
<attribute name="profile" type="sensinact:targets" use="optional"/>
+
    <attribute name="profile" type="sensinact:targets" use="optional"/>
 
</complexType>
 
</complexType>
 
</source>
 
</source>
Line 508: Line 479:
 
----
 
----
  
<source lang="xml">
+
<source lang="xml" line start="1">
<complexType name="resourceInfoData" abstract="true">
+
<complexType name="resourceInfoData" abstract="true">
<complexContent>
+
    <complexContent>
<extension base="sensinact:resourceInfo">
+
        <extension base="sensinact:resourceInfo">
<sequence>
+
            <sequence>
<element name="type" type="sensinact:typeType" minOccurs="1" maxOccurs="unbounded">
+
                <element name="type" type="sensinact:typeType" minOccurs="1" maxOccurs="unbounded">
</element>
+
                </element>
<element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded">
+
                <element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded">
</element>
+
                </element>
<element name="metadata" type="sensinact:metadata" minOccurs="0" maxOccurs="unbounded">
+
                <element name="metadata" type="sensinact:metadata" minOccurs="0" maxOccurs="unbounded">
<unique name="uniqueResourceInfoMetadataValueTarget">
+
                    <unique name="uniqueResourceInfoMetadataValueTarget">
<selector xpath="./sensinact:value"/>
+
                        <selector xpath="./sensinact:value"/>
<field xpath="@target"/>
+
                        <field xpath="@target"/>
</unique>
+
                    </unique>
</element>
+
                </element>
<element name="constraints" type="sensinact:constraints" minOccurs="0" maxOccurs="1"/>
+
                <element name="constraints" type="sensinact:constraints" minOccurs="0" maxOccurs="1"/>
<element name="parameters" type="sensinact:dataResourceParameters" minOccurs="0" maxOccurs="unbounded">
+
                <element name="parameters" type="sensinact:dataResourceParameters" minOccurs="0" maxOccurs="unbounded">
<key name="uniqueDataParameterName">
+
                    <key name="uniqueDataParameterName">
<selector xpath="./sensinact:parameter"/>
+
                        <selector xpath="./sensinact:parameter"/>
<field xpath="@name"/>
+
                        <field xpath="@name"/>
</key>
+
                    </key>
</element>
+
                </element>
</sequence>
+
            </sequence>
<attribute name="modifiable" type="sensinact:modifiable_enum" use="optional" />
+
            <attribute name="modifiable" type="sensinact:modifiable_enum" use="optional" />
<attribute name="hidden" type="boolean" use="optional" default="false" />
+
            <attribute name="hidden" type="boolean" use="optional" default="false" />
</extension>
+
        </extension>
</complexContent>
+
    </complexContent>
</complexType>
+
</complexType>
  
 
<complexType name="resourceInfoVariable" >
 
<complexType name="resourceInfoVariable" >
<complexContent>
+
    <complexContent>
<extension base="sensinact:resourceInfoData">
+
        <extension base="sensinact:resourceInfoData">
<attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="STATE_VARIABLE"/>
+
            <attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="STATE_VARIABLE"/>
</extension>
+
        </extension>
</complexContent>
+
    </complexContent>
 
</complexType>
 
</complexType>
  
 
<complexType name="resourceInfoProperty" >
 
<complexType name="resourceInfoProperty" >
<complexContent>
+
    <complexContent>
<extension base="sensinact:resourceInfoData">
+
        <extension base="sensinact:resourceInfoData">
<attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="PROPERTY"/>
+
            <attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="PROPERTY"/>
</extension>
+
        </extension>
</complexContent>
+
    </complexContent>
 
</complexType>
 
</complexType>
  
 
<complexType name="resourceInfoSensor" >
 
<complexType name="resourceInfoSensor" >
<complexContent>
+
    <complexContent>
<extension base="sensinact:resourceInfoData">
+
        <extension base="sensinact:resourceInfoData">
<attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="SENSOR"/>
+
            <attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="SENSOR"/>
</extension>
+
        </extension>
</complexContent>
+
    </complexContent>
 
</complexType>
 
</complexType>
 
</source>
 
</source>
Line 572: Line 543:
 
----
 
----
  
<source lang="xml">
+
<source lang="xml" line start="1">
<resourceInfos xmlns="http://fr.cea.sensinact/resource"  
+
<resourceInfos xmlns="http://org.eclipse.sensinact/resource"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://fr.cea.sensinact/resource ../../../schema/sensinact-resource.xsd">  
+
              xsi:schemaLocation="http://org.eclipse.sensinact/resource ../../../schema/sensinact-resource.xsd">
  <resourceInfo xsi:type="resourceInfoSensor" name="TEMPERATURE" modifiable="UPDATABLE">
+
    <resourceInfo xsi:type="resourceInfoSensor" name="TEMPERATURE" modifiable="UPDATABLE">
    <identifier xsi:type="stringContent">temp</identifier>
+
        <identifier xsi:type="stringContent">temp</identifier>
    <type>float</type>
+
        <type>float</type>
    <metadata name="unit">
+
        <metadata name="unit">
        <type>string</type>
+
            <type>string</type>
        <value>celcius (C°)</value>
+
            <value>celcius (C°)</value>
    </metadata>
+
        </metadata>
    <metadata name="description">
+
        <metadata name="description">
        <type>string</type>
+
            <type>string</type>
        <value>temperature</value>
+
            <value>temperature</value>
    </metadata>
+
        </metadata>
  </resourceInfo>
+
    </resourceInfo>
  <resourceInfo xsi:type="resourceInfoSensor" name="HUMIDITY" modifiable="UPDATABLE">
+
    <resourceInfo xsi:type="resourceInfoSensor" name="HUMIDITY" modifiable="UPDATABLE">
    <identifier xsi:type="stringContent">humidity</identifier>
+
        <identifier xsi:type="stringContent">humidity</identifier>
    <type>int</type>
+
        <type>int</type>
    <metadata name="unit">
+
        <metadata name="unit">
        <type>string</type>
+
            <type>string</type>
        <value>percent (%)</value>
+
            <value>percent (%)</value>
    </metadata>
+
        </metadata>
    <metadata name="description">
+
        <metadata name="description">
        <type>string</type>
+
            <type>string</type>
        <value>relative humidity</value>
+
            <value>relative humidity</value>
    </metadata>
+
        </metadata>
  </resourceInfo>
+
    </resourceInfo>
  <resourceInfo xsi:type="resourceInfoSensor" name="ATMOSPHERIC_PRESSURE" modifiable="UPDATABLE">
+
    <resourceInfo xsi:type="resourceInfoSensor" name="ATMOSPHERIC_PRESSURE" modifiable="UPDATABLE">
    <identifier xsi:type="stringContent">pressure</identifier>
+
        <identifier xsi:type="stringContent">pressure</identifier>
    <type>float</type>
+
        <type>float</type>
    <metadata name="unit">
+
        <metadata name="unit">
        <type>string</type>
+
            <type>string</type>
        <value>hecto pascal (hpa)</value>
+
            <value>hecto pascal (hpa)</value>
    </metadata>
+
        </metadata>
    <metadata name="description">
+
        <metadata name="description">
        <type>string</type>
+
            <type>string</type>
        <value>atmospheric pressure</value>
+
            <value>atmospheric pressure</value>
    </metadata>
+
        </metadata>
  </resourceInfo>
+
    </resourceInfo>
  <resourceInfo xsi:type="resourceInfoSensor" name="WIND_SPEED" modifiable="UPDATABLE">
+
    <resourceInfo xsi:type="resourceInfoSensor" name="WIND_SPEED" modifiable="UPDATABLE">
    <identifier xsi:type="stringContent">wind</identifier>
+
        <identifier xsi:type="stringContent">wind</identifier>
    <type>float</type>
+
        <type>float</type>
    <metadata name="unit">
+
        <metadata name="unit">
        <type>string</type>
+
            <type>string</type>
        <value>meters by second (m/s)</value>
+
            <value>meters by second (m/s)</value>
    </metadata>
+
        </metadata>
    <metadata name="description">
+
        <metadata name="description">
        <type>string</type>
+
            <type>string</type>
        <value>wind speed</value>
+
            <value>wind speed</value>
    </metadata>
+
        </metadata>
  </resourceInfo>
+
    </resourceInfo>
 
</resourceInfos>
 
</resourceInfos>
 
</source>
 
</source>
Line 635: Line 606:
 
----
 
----
  
<source lang="xml">
+
<source lang="xml" line start="1">
 
<devices>
 
<devices>
 
     <device identifier="station">
 
     <device identifier="station">
Line 645: Line 616:
 
----
 
----
  
It is enough to create a new <code>ServiceProvider</code> in the system, holding two <code>Services</code> including the weather one, and providing the four <code>Resources</code> described in the '''resource.xml''' file.  
+
It is enough to create a new <code>ServiceProvider</code> in the system, holding two <code>Services</code> including the weather one, and providing the four <code>Resources</code> described in the '''resource.xml''' file.
  
 
== The connected counterpart - OpenWeather service ==
 
== The connected counterpart - OpenWeather service ==
Line 655: Line 626:
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
package fr.cea.sna.gateway.simulated.weather;
+
package org.eclipse.sensinact.gateway.simulated.weather;
  
 
import org.json.JSONArray;
 
import org.json.JSONArray;
 
import org.json.JSONObject;
 
import org.json.JSONObject;
 
+
import org.eclipse.sensinact.gateway.common.bundle.Mediator;
import fr.cea.sna.gateway.common.bundle.Mediator;
+
import org.eclipse.sensinact.gateway.core.LocationResource;
import fr.cea.sna.gateway.core.LocationResource;
+
import org.eclipse.sensinact.gateway.core.ServiceProvider;
import fr.cea.sna.gateway.core.ServiceProvider;
+
import org.eclipse.sensinact.gateway.generic.core.InvalidPacketException;
import fr.cea.sna.gateway.generic.core.InvalidPacketException;
+
import org.eclipse.sensinact.gateway.generic.core.Task.CommandType;
import fr.cea.sna.gateway.generic.core.Task.CommandType;
+
import org.eclipse.sensinact.gateway.generic.core.packet.SimplePacketReader;
import fr.cea.sna.gateway.generic.core.packet.SimplePacketReader;
+
import org.eclipse.sensinact.gateway.sthbnd.http.HttpPacket;
import fr.cea.sna.gateway.sthbnd.http.HttpPacket;
+
import org.eclipse.sensinact.gateway.util.JSONUtils;
import fr.cea.sna.gateway.util.JSONUtils;
+
  
 
/**
 
/**
Line 674: Line 644:
 
  * Http request send to the OpenWeatherMap service
 
  * Http request send to the OpenWeatherMap service
 
  */
 
  */
public class WeatherPacketReader extends SimplePacketReader<HttpPacket>
+
public class WeatherPacketReader extends SimplePacketReader<HttpPacket> {
{
+
    /**
/**
+
    * @param mediator
* @param mediator
+
    */
*/
+
    protected WeatherPacketReader(Mediator mediator) {
protected WeatherPacketReader(Mediator mediator)
+
        super(mediator);
{
+
    }
super(mediator);
+
}
+
  
/**
+
    /**
* @inheritDoc
+
    * @inheritDoc
*
+
    *
* @see fr.cea.sna.gateway.generic.core.packet.PacketReader#parse(fr.cea.sna.gateway.generic.core.packet.Packet)
+
    * @see org.eclipse.sensinact.gateway.generic.core.packet.PacketReader#parse(org.eclipse.sensinact.gateway.generic.core.packet.Packet)
*/
+
    */
public void parse(HttpPacket packet) throws InvalidPacketException
+
    public void parse(HttpPacket packet) throws InvalidPacketException {
{
+
        JSONObject object = new JSONObject(new String(packet.getBytes()));
      JSONObject object = new JSONObject(new String(packet.getBytes()));
+
     
+
      long timestamp = object.optLong("dt")*1000L;
+
JSONObject coord = object.optJSONObject("coord");
+
if(coord != null)
+
{
+
super.setServiceProviderId("station");
+
super.setServiceId(ServiceProvider.ADMINISTRATION_SERVICE_NAME);
+
super.setResourceId(LocationResource.LOCATION);
+
super.setData(new StringBuilder().append(coord.optDouble("lat")).append(JSONUtils.COLON
+
).append(coord.optDouble("lon")).toString());
+
super.setCommand(CommandType.GET);
+
super.configure();
+
}
+
JSONArray weather = object.optJSONArray("weather");
+
JSONObject content = null;
+
if(weather != null && (content = weather.optJSONObject(0))!=null)
+
{
+
super.setServiceProviderId("station");
+
super.setServiceId("weather");
+
super.setResourceId("state");
+
super.setData(content.opt("main"));
+
super.setTimestamp(timestamp);
+
super.setCommand(CommandType.GET);
+
super.configure();
+
+
super.setServiceProviderId("station");
+
super.setServiceId("weather");
+
super.setResourceId("description");
+
super.setData(content.opt("description"));
+
super.setTimestamp(timestamp);
+
super.setCommand(CommandType.GET);
+
super.configure();
+
}
+
JSONObject wind = object.optJSONObject("wind");
+
if(wind != null)
+
{
+
super.setServiceProviderId("station");
+
super.setServiceId("weather");
+
super.setResourceId("wind");
+
super.setData(wind.opt("speed"));
+
super.setTimestamp(timestamp);
+
super.setCommand(CommandType.GET);
+
super.configure();
+
+
super.setServiceProviderId("station");
+
super.setServiceId("weather");
+
super.setResourceId("orientation");
+
super.setData(wind.opt("deg"));
+
super.setTimestamp(timestamp);
+
super.setCommand(CommandType.GET);
+
super.configure();
+
}
+
JSONObject main = object.optJSONObject("main");
+
if(main != null)
+
{
+
super.setServiceProviderId("station");
+
super.setServiceId("weather");
+
super.setResourceId("temperature");
+
super.setData(main.opt("temp"));
+
super.setTimestamp(timestamp);
+
super.setCommand(CommandType.GET);
+
super.configure();
+
  
super.setServiceProviderId("station");
+
        long timestamp = object.optLong("dt")*1000L;
super.setServiceId("weather");
+
        JSONObject coord = object.optJSONObject("coord");
super.setResourceId("humidity");
+
        if(coord != null)
super.setData(main.opt("humidity"));
+
        {
super.setTimestamp(timestamp);
+
            super.setServiceProviderId("station");
super.setCommand(CommandType.GET);
+
            super.setServiceId(ServiceProvider.ADMINISTRATION_SERVICE_NAME);
super.configure();
+
            super.setResourceId(LocationResource.LOCATION);
+
            super.setData(new StringBuilder().append(coord.optDouble("lat")).append(JSONUtils.COLON
super.setServiceProviderId("station");
+
            ).append(coord.optDouble("lon")).toString());
super.setServiceId("weather");
+
            super.setCommand(CommandType.GET);
super.setResourceId("pressure");
+
            super.configure();
super.setData(main.opt("pressure"));
+
        }
super.setTimestamp(timestamp);
+
        JSONArray weather = object.optJSONArray("weather");
super.setCommand(CommandType.GET);
+
        JSONObject content = null;
super.configure();
+
        if(weather != null && (content = weather.optJSONObject(0))!=null)
}
+
        {
}
+
            super.setServiceProviderId("station");
 +
            super.setServiceId("weather");
 +
            super.setResourceId("state");
 +
            super.setData(content.opt("main"));
 +
            super.setTimestamp(timestamp);
 +
            super.setCommand(CommandType.GET);
 +
            super.configure();
 +
 
 +
            super.setServiceProviderId("station");
 +
            super.setServiceId("weather");
 +
            super.setResourceId("description");
 +
            super.setData(content.opt("description"));
 +
            super.setTimestamp(timestamp);
 +
            super.setCommand(CommandType.GET);
 +
            super.configure();
 +
        }
 +
        JSONObject wind = object.optJSONObject("wind");
 +
        if(wind != null)
 +
        {
 +
            super.setServiceProviderId("station");
 +
            super.setServiceId("weather");
 +
            super.setResourceId("wind");
 +
            super.setData(wind.opt("speed"));
 +
            super.setTimestamp(timestamp);
 +
            super.setCommand(CommandType.GET);
 +
            super.configure();
 +
 
 +
            super.setServiceProviderId("station");
 +
            super.setServiceId("weather");
 +
            super.setResourceId("orientation");
 +
            super.setData(wind.opt("deg"));
 +
            super.setTimestamp(timestamp);
 +
            super.setCommand(CommandType.GET);
 +
            super.configure();
 +
        }
 +
        JSONObject main = object.optJSONObject("main");
 +
        if(main != null)
 +
        {
 +
            super.setServiceProviderId("station");
 +
            super.setServiceId("weather");
 +
            super.setResourceId("temperature");
 +
            super.setData(main.opt("temp"));
 +
            super.setTimestamp(timestamp);
 +
            super.setCommand(CommandType.GET);
 +
            super.configure();
 +
 
 +
            super.setServiceProviderId("station");
 +
            super.setServiceId("weather");
 +
            super.setResourceId("humidity");
 +
            super.setData(main.opt("humidity"));
 +
            super.setTimestamp(timestamp);
 +
            super.setCommand(CommandType.GET);
 +
            super.configure();
 +
 
 +
            super.setServiceProviderId("station");
 +
            super.setServiceId("weather");
 +
            super.setResourceId("pressure");
 +
            super.setData(main.opt("pressure"));
 +
            super.setTimestamp(timestamp);
 +
            super.setCommand(CommandType.GET);
 +
            super.configure();
 +
        }
 +
    }
 
}
 
}
 
</source>
 
</source>
Line 785: Line 752:
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
package fr.cea.sna.gateway.simulated.osgi;
+
package org.eclipse.sensinact.gateway.simulated.osgi;
  
 
import java.util.Collections;
 
import java.util.Collections;
 
 
import org.osgi.framework.BundleContext;
 
import org.osgi.framework.BundleContext;
 
import org.osgi.framework.InvalidSyntaxException;
 
import org.osgi.framework.InvalidSyntaxException;
 
+
import org.eclipse.sensinact.gateway.common.bundle.Mediator;
import fr.cea.sna.gateway.common.bundle.Mediator;
+
import org.eclipse.sensinact.gateway.generic.core.Task.CommandType;
import fr.cea.sna.gateway.generic.core.Task.CommandType;
+
import org.eclipse.sensinact.gateway.generic.core.XmlModelInstance;
import fr.cea.sna.gateway.generic.core.XmlModelInstance;
+
import org.eclipse.sensinact.gateway.generic.core.XmlModelInstanceBuilder;
import fr.cea.sna.gateway.generic.core.XmlModelInstanceBuilder;
+
import org.eclipse.sensinact.gateway.sthbnd.http.HttpModelConfiguration;
import fr.cea.sna.gateway.sthbnd.http.HttpModelConfiguration;
+
import org.eclipse.sensinact.gateway.sthbnd.http.HttpPacket;
import fr.cea.sna.gateway.sthbnd.http.HttpPacket;
+
import org.eclipse.sensinact.gateway.sthbnd.http.impl.HttpActivator;
import fr.cea.sna.gateway.sthbnd.http.impl.HttpActivator;
+
import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpProtocolStackEndpoint;
import fr.cea.sna.gateway.sthbnd.http.impl.SimpleHttpProtocolStackEndpoint;
+
import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpTask;
import fr.cea.sna.gateway.sthbnd.http.impl.SimpleHttpTask;
+
import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpTaskQuery;
import fr.cea.sna.gateway.sthbnd.http.impl.SimpleHttpTaskQuery;
+
import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpTaskConfigurationAdapter;
import fr.cea.sna.gateway.sthbnd.http.impl.SimpleHttpTaskConfigurationAdapter;
+
  
 
/**
 
/**
 
  * Bundle Activator
 
  * Bundle Activator
 
  */
 
  */
public class Activator extends HttpActivator<Mediator>
+
public class Activator extends HttpActivator<Mediator> {
{
+
    private SimpleHttpProtocolStackEndpoint connector;
private SimpleHttpProtocolStackEndpoint connector;
+
    private HttpModelConfiguration manager;
private HttpModelConfiguration manager;
+
 
+
    @SimpleHttpTask(command = CommandType.GET,
@SimpleHttpTask(command = CommandType.GET,  
+
            host = "api.openweathermap.org",
host = "api.openweathermap.org",  
+
            path = "data/2.5/weather",
path = "data/2.5/weather",
+
            query = { @SimpleHttpTaskQuery(key = "lat", value = "<latitude>"),
query =  
+
                            @SimpleHttpTaskQuery(key = "lon", value = "<longitude>"),
{ @SimpleHttpTaskQuery(key = "lat", value = "<latitude>"),
+
                            @SimpleHttpTaskQuery(key = "APPID", value = "<your-key>")
@SimpleHttpTaskQuery(key = "lon", value = "<longitude>"),
+
                    })
@SimpleHttpTaskQuery(key = "APPID", value = "<your-key>")
+
    private SimpleHttpTaskConfigurationAdapter adapter;
})
+
 
private SimpleHttpTaskConfigurationAdapter adapter;
+
 
+
+
 
     /**
 
     /**
* @inheritDoc
+
    * @inheritDoc
*
+
    *
* @see fr.cea.sna.gateway.common.bundle.AbstractActivator#doStart()
+
    * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStart()
*/
+
    */
@Override
+
    @Override
public void doStart() throws Exception
+
    public void doStart() throws Exception {
{      
+
        adapter = new SimpleHttpTaskConfigurationAdapter();
  adapter = new SimpleHttpTaskConfigurationAdapter();
+
        manager = new XmlModelInstanceBuilder<HttpModelConfiguration,
  manager = new XmlModelInstanceBuilder<HttpModelConfiguration,
+
                XmlModelInstance>(super.mediator,XmlModelInstance.class,
        XmlModelInstance>(super.mediator,XmlModelInstance.class,
+
                HttpModelConfiguration.class
        HttpModelConfiguration.class
+
        ).withPacketType(HttpPacket.class
        ).withPacketType(HttpPacket.class
+
        ).withStartAtInitializationTime(true
                ).withStartAtInitializationTime(true
+
        ).buildConfiguration("resource.xml", Collections.<String,String>emptyMap());
                ).buildConfiguration("resource.xml", Collections.<String,String>emptyMap());            
+
     
+
      super.configureAdapter();
+
     
+
  connector = new SimpleHttpProtocolStackEndpoint(super.mediator, adapter);
+
  connector.connect(manager);
+
}
+
  
/**
+
        super.configureAdapter();
* @inheritDoc
+
*
+
* @see fr.cea.sna.gateway.common.bundle.AbstractActivator#
+
* doStop()
+
*/
+
@Override
+
public void doStop() throws Exception
+
{
+
  connector.stop();
+
}
+
  
/**
+
        connector = new SimpleHttpProtocolStackEndpoint(super.mediator, adapter);
* @inheritDoc
+
        connector.connect(manager);
*
+
    }
* @see fr.cea.sna.gateway.common.bundle.AbstractActivator#
+
 
* doInstantiate(org.osgi.framework.BundleContext, int, java.io.FileOutputStream)
+
    /**
*/
+
    * @inheritDoc
@Override
+
    *
public Mediator doInstantiate(BundleContext context)  
+
    * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#
throws InvalidSyntaxException
+
    * doStop()
{
+
    */
return new Mediator(context);
+
    @Override
}
+
    public void doStop() throws Exception {
 +
        connector.stop();
 +
    }
 +
 
 +
    /**
 +
    * @inheritDoc
 +
    *
 +
    * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#
 +
    * doInstantiate(org.osgi.framework.BundleContext, int, java.io.FileOutputStream)
 +
    */
 +
    @Override
 +
    public Mediator doInstantiate(BundleContext context) throws InvalidSyntaxException {
 +
        return new Mediator(context);
 +
    }
 
}
 
}
 
</source>
 
</source>
Line 879: Line 838:
 
----
 
----
  
<source lang="java">
+
<source lang="java" line start="1">
package fr.cea.sna.gateway.simulated.weather;
+
package org.eclipse.sensinact.gateway.simulated.weather;
  
import fr.cea.sna.gateway.common.bundle.Mediator;
+
import org.eclipse.sensinact.gateway.common.bundle.Mediator;
import fr.cea.sna.gateway.generic.core.InvalidPacketException;
+
import org.eclipse.sensinact.gateway.generic.core.InvalidPacketException;
import fr.cea.sna.gateway.generic.core.XmlModelConfiguration;
+
import org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration;
import fr.cea.sna.gateway.generic.core.impl.PacketReaderFactory;
+
import org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory;
import fr.cea.sna.gateway.generic.core.packet.Packet;
+
import org.eclipse.sensinact.gateway.generic.core.packet.Packet;
import fr.cea.sna.gateway.generic.core.packet.PacketReader;
+
import org.eclipse.sensinact.gateway.generic.core.packet.PacketReader;
import fr.cea.sna.gateway.sthbnd.http.HttpPacket;
+
import org.eclipse.sensinact.gateway.sthbnd.http.HttpPacket;
  
public class WeatherPacketReaderFactory implements PacketReaderFactory<HttpPacket>
+
public class WeatherPacketReaderFactory implements PacketReaderFactory<HttpPacket> {
{
+
    /**
/**
+
    * @inheritDoc
* @inheritDoc
+
    *
*
+
    * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
* @see fr.cea.sna.gateway.generic.core.impl.PacketReaderFactory#
+
    * handle(java.lang.Class)
* handle(java.lang.Class)
+
    */
*/
+
    public boolean handle(Class<? extends Packet> packetType) {
public boolean handle(Class<? extends Packet> packetType)
+
        return HttpPacket.class.isAssignableFrom(packetType);
{
+
    }
    return HttpPacket.class.isAssignableFrom(packetType);
+
}
+
  
/**
+
    /**
* @inheritDoc
+
    * @inheritDoc
*
+
    *
* @see fr.cea.sna.gateway.generic.core.impl.PacketReaderFactory#
+
    * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
* newInstance(fr.cea.sna.gateway.common.bundle.Mediator,  
+
    * newInstance(org.eclipse.sensinact.gateway.common.bundle.Mediator,  
* fr.cea.sna.gateway.generic.core.XmlModelConfiguration,  
+
    * org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration,  
* fr.cea.sna.gateway.generic.core.packet.Packet)
+
    * org.eclipse.sensinact.gateway.generic.core.packet.Packet)
*/
+
    */
public PacketReader<HttpPacket> newInstance(Mediator mediator,
+
    public PacketReader<HttpPacket> newInstance(Mediator mediator, XmlModelConfiguration<HttpPacket> manager,
        XmlModelConfiguration<HttpPacket> manager,  
+
                                                HttpPacket packet) throws InvalidPacketException {
        HttpPacket packet)
+
        WeatherPacketReader packetReader = new WeatherPacketReader(mediator);
        throws InvalidPacketException
+
        packetReader.parse(packet);
{
+
        return packetReader;
  WeatherPacketReader packetReader = new WeatherPacketReader(mediator);
+
    }
  packetReader.parse(packet);
+
  return packetReader;
+
}
+
 
}
 
}
 
</source>
 
</source>

Revision as of 09:11, 12 September 2018

A sensiNact bridge tutorial - the weather station example

Required knowledge

This tutorial requires knowledge about:

  • Java
  • Architecture and data model of sensiNact

The project skeleton

Let start by defining a simple weather station providing four kind of information:

  • temperature
  • humidity
  • atmospheric pressure
  • wind speed

We will first create the weather station module skeleton by the way of the ``sensinact-archetype``. You just have to invoke the ``mvn archetype:generate`` command in your favorite terminal emulator like specified below

> mvn archetype:generate -DarchetypeGroupId=org.eclipse.sensinact.archetypes -DarchetypeArtifactId=sensinact-archetype -DarchetypeVersion=1.0 -DgroupId=org.eclipse.sensinact.gateway.simulated -DartifactId=weather-station

Weather station skeleton

A new maven project has been created containing :

  • weather-station/pom.xml: this is the maven project description file in which dependencies are referenced, as well as the configuration of the maven-bundle-plugin that will be used to create our executable bundle.
  • weather-station/schema/sensinact-resource.xsd: this is the schema on which the XML description of the resources of our weather station is based on. It will not be added to the compiled module, but is provided by the sensinact-archetype while not accessible remotely.
  • weather-station/src/main/java/fr/cea/sna/gateway/simulated/osgi/Activator.java: this java class is the Bundle's Activator. We will have to complete it later.
  • weather-station/src/main/resources/sensiNact-conf.xml: this java xml properties file will be loaded at initialization time and allows to configure our module by adding properties.
  • weather-station/src/main/resources/resource.xml: we will later amend this xml file in which are described the resources provided by the weather station.

The simplest possible implementation

The first sample implementation does not operate the schema and thus the fine configuration possibilities offered by the XML resources description file;

The generic implementation we are using to create our weather station implies to provide some 'objects' that are required to synchronize the new sensiNact's resource model instance to its connected counterpart:

  • an XmlModelConfiguration, its initialization requires the name of the XML resources description (the resource.xml in our case);
  • a ProtocolStackEndpoint which is the connecting point to the remote counterpart of the resource model instance.

sensiNact resource model generic

To feed our weather station we also have to define the kind of data that will be used and how to consume them. To manage processing data, the sensiNact resource model implementation expects a data structure implementing the Packet interface, representing a communication object wrapping the raw transmitted data. Moreover we have to provide an appropriate PacketReader able to identify in an handled Packet type the relevant information for the sensiNact system. In our bridge example we will cope with JSON formated data structures, in which the properties' keys and values will refer respectively to our resources' identifiers and values. We will so define our own Packet type that we will call WeatherPacket, and the PacketReader in charge of parsing it, the WeatherPacketReader.

First of all, as mentioned above we instantiate the components that are required to create the bridge: the model instances configuration and the protocol stack connector endpoint. The configuration object (XmlModelConfiguration) allows to specify whether the elements of the resource model can be created dynamically when identified by the PacketReader; in this case it allows to define the extended DataResource type to use, as well as its embedded data type. Both XmlModelConfiguration and ProtocolStackEndpoint have next to be connected by the way of the connect method of the protocol stack connector endpoint.


  1. package org.eclipse.sensinact.gateway.simulated.osgi;
  2.  
  3. import org.osgi.framework.BundleContext;
  4. import org.osgi.framework.InvalidSyntaxException;
  5. import org.eclipse.sensinact.gateway.generic.core.impl.ExtProtocolStackEndpoint;
  6. import org.eclipse.sensinact.gateway.generic.core.impl.ExtXmlModelConfiguration;
  7. import org.eclipse.sensinact.gateway.common.bundle.AbstractActivator;
  8. import org.eclipse.sensinact.gateway.common.bundle.Mediator;
  9. import org.eclipse.sensinact.gateway.simulated.weather.WeatherPacket;
  10.  
  11. /**
  12.  * The weather station bundle activator
  13.  */
  14. public class Activator extends AbstractActivator<Mediator> {
  15.     private ExtProtocolStackEndpoint<WeatherPacket> connector;
  16.     private ExtXmlModelConfiguration<WeatherPacket> manager;
  17.  
  18.     /**
  19.      * @inheritDoc
  20.      *
  21.      * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStart()
  22.      */
  23.     @Override
  24.     public void doStart() throws Exception {
  25.         manager = new XmlModelInstanceBuilder<ExtXmlModelConfiguration,
  26.                 XmlModelInstance>(super.mediator,XmlModelInstance.class,
  27.                 ExtXmlModelConfiguration.class
  28.         ).withPacketType(WeatherPacket.class
  29.         ).withBuildDynamically(true
  30.         ).withDataResourceType(SensorDataResource.class
  31.         ).withDataType(float.class
  32.         ).withStartAtInitializationTime(true
  33.         ).buildConfiguration("resource.xml", Collections.<String,String>emptyMap());
  34.         connector = new ExtProtocolStackEndpoint<WeatherPacket>(super.mediator);
  35.         connector.connect(manager);
  36.     }
  37.  
  38.     /**
  39.      * @inheritDoc
  40.      *
  41.      * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStop()
  42.      */
  43.     @Override
  44.     public void doStop() throws Exception {
  45.         connector.stop();
  46.     }
  47.  
  48.     /**
  49.      * @inheritDoc
  50.      *
  51.      * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#
  52.      * doInstantiate(org.osgi.framework.BundleContext)
  53.      */
  54.     @Override
  55.     public Mediator doInstantiate(BundleContext context) throws InvalidSyntaxException {
  56.         return new Mediator(context);
  57.     }
  58. }

The mediator argument, needed by both XmlModelConfiguration and ProtocolStackEndpoint constructors allows to interact with the OSGi host environment by the way of the BundleContext from one hand, and on the other hand provides logging feature.


  1. package org.eclipse.sensinact.gateway.simulated.weather;
  2.  
  3. import org.eclipse.sensinact.gateway.generic.core.packet.Packet;
  4.  
  5. /**
  6.  * Extended {@link Packet} implementation wrapping
  7.  * a communication object targeting the weather station
  8.  */
  9. public class WeatherPacket implements Packet {
  10.  
  11.     /**
  12.      * the bytes array content of this WeatherPacket
  13.      */
  14.     private byte[] content;
  15.  
  16.     /**
  17.      * Constructor
  18.      *
  19.      * @param content
  20.      *      the bytes array content of the WeatherPacket to
  21.      *      instantiate
  22.      */
  23.     public WeatherPacket(byte[] content) {
  24.         int length = content==null?0:content.length;
  25.         this.content = new byte[length];
  26.  
  27.         if(length > 0) {
  28.             System.arraycopy(content, 0, this.content, 0, length);
  29.         }
  30.     }
  31.  
  32.     /**
  33.      * @inheritDoc
  34.      *
  35.      * @see org.eclipse.sensinact.gateway.generic.core.packet.Packet#getBytes()
  36.      */
  37.     @Override
  38.     public byte[] getBytes() {
  39.         byte[] content = new byte[this.content.length];
  40.  
  41.         if(this.content.length> 0) {
  42.             System.arraycopy(this.content, 0, content, 0, this.content.length);
  43.         }
  44.  
  45.         return content;
  46.     }
  47. }

the getBytes method is the only one specified in the Packet interface. We could have to define other methods to extract protocol specific information from the wrapped communication object, but in our case it is not needed.

The WeatherPacketReader will extend the SimplePacketReader abstract class which requires to only implement the parse method. The SimplePacketReader parsing mechanism consists in building a hierarchical data structure of contained information to map to the hierarchical sensiNact's resource model :

Packet reader mechanism

When a complete branch of the tree is built we just have to call the configure method before to complete a new one


  1. package org.eclipse.sensinact.gateway.simulated.weather;
  2.  
  3. import org.json.JSONObject;
  4. import org.eclipse.sensinact.gateway.generic.core.InvalidPacketException;
  5. import org.eclipse.sensinact.gateway.generic.core.packet.SimplePacketReader;
  6. import org.eclipse.sensinact.gateway.common.bundle.Mediator;
  7.  
  8. /**
  9.  * {@link PacketReader} dedicated to {@link WeatherPacket} parsing
  10.  */
  11. public class WeatherPacketReader extends SimplePacketReader<WeatherPacket> {
  12.     /**
  13.      * Constructor
  14.      *
  15.      * @param mediator
  16.      */
  17.     protected WeatherPacketReader(Mediator mediator) {
  18.         super(mediator);
  19.     }
  20.  
  21.     /**
  22.      * @inheritDoc
  23.      *
  24.      * @see org.eclipse.sensinact.gateway.generic.core.packet.PacketReader#
  25.      * parse(org.eclipse.sensinact.gateway.generic.core.packet.Packet)
  26.      */
  27.     @Override
  28.     public void parse(WeatherPacket packet) throws InvalidPacketException {
  29.         JSONObject jsonObject = new JSONObject(new String(packet.getBytes()));
  30.  
  31.         super.setServiceProviderId("station");
  32.         super.setServiceId("weather");
  33.         super.setResourceId("temperature");
  34.         super.setData(jsonObject.opt("temperature"));
  35.         super.configure();
  36.  
  37.         super.setServiceProviderId("station");
  38.         super.setServiceId("weather");
  39.         super.setResourceId("humidity");
  40.         super.setData(jsonObject.opt("humidity"));
  41.         super.configure();
  42.  
  43.         super.setServiceProviderId("station");
  44.         super.setServiceId("weather");
  45.         super.setResourceId("pressure");
  46.         super.setData(jsonObject.opt("pressure"));
  47.         super.configure();
  48.  
  49.         super.setServiceProviderId("station");
  50.         super.setServiceId("weather");
  51.         super.setResourceId("wind");
  52.         super.setData(jsonObject.opt("wind"));
  53.         super.configure();
  54.     }
  55. }

Our sensiNact's resource model instance can be updated by parsing a JSONObject of the form :

{
  "temperature" : 5.2,
  "humidity" : 52,
  "pressure" : 1027.5,
  "wind" : 6.4
}

To simulate a communication with a distant weather station we just have to wrap a JSONObject whose format is as described above into a WeatherPacket and ask the ProtocolStackEndpoint to process it.

We could have adopted a more efficient data structure, but this one has the advantage to be easily understandable. To allow our sensiNact's resource model instance to use our WeatherPacketReader we still have to provide an appropriate factory and to reference it for the java's ServiceLoader. The searched factory will be one implementing the PacketReaderFactory interface which defines two methods :

  • the handle method taking a Packet type as parameter and returning a boolean defining whether the specified Packet type is handled by the factory ;
  • the newInstance method instantiating a new PacketReader used to parse the Packet parameter.

  1. package org.eclipse.sensinact.gateway.simulated.weather;
  2.  
  3. import org.eclipse.sensinactgateway.generic.core.InvalidPacketException;
  4. import org.eclipse.sensinact.gateway.generic.core.impl.ExtXmlModelConfiguration;
  5. import org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration;
  6. import org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory;
  7. import org.eclipse.sensinact.gateway.generic.core.packet.Packet;
  8. import org.eclipse.sensinact.gateway.generic.core.packet.PacketReader;
  9. import org.eclipse.sensinact.gateway.common.bundle.Mediator;
  10.  
  11. public class WeatherPacketReaderFactory implements PacketReaderFactory<WeatherPacket> {
  12.     /**
  13.      * @inheritDoc
  14.      *
  15.      * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
  16.      * handle(java.lang.Class)
  17.      */
  18.     @Override
  19.     public boolean handle(Class<? extends Packet> packetType) {
  20.         return WeatherPacket.class.isAssignableFrom(packetType);
  21.     }
  22.  
  23.     /**
  24.      * @inheritDoc
  25.      *
  26.      * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
  27.      * newInstance(org.eclipse.sensinact.gateway.common.bundle.Mediator, 
  28.      * org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration, 
  29.      * org.eclipse.sensinact.gateway.generic.core.packet.Packet)
  30.      */
  31.     @Override
  32.     public PacketReader<WeatherPacket> newInstance(Mediator mediator, XmlModelConfiguration<WeatherPacket> manager,
  33.             WeatherPacket packet) throws InvalidPacketException {
  34.         WeatherPacketReader packetReader = new WeatherPacketReader(mediator);
  35.         packetReader.parse(packet);
  36.         return packetReader;
  37.     }
  38. }

Finally, in the META-INF/services/org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory text file we specify the canonical name of our factory class to make it accessible by the java's ServiceLoader: org.eclipse.sensinact.gateway.simulated.weather.WeatherPacketReaderFactory.

Let see below what our project looks like now:

Sna weather-station-skeleton2.png

The connected counterpart - Let's start with a simulation

For this first example we will use a simulated counterpart; a simple java thread sanding random values in a loop:


  1. package org.eclipse.sensinact.gateway.simulated.weather;
  2.  
  3. import java.util.Random;
  4. import java.util.logging.Level;
  5. import java.util.logging.Logger;
  6. import org.json.JSONObject;
  7. import org.eclipse.sensinact.gateway.generic.core.InvalidPacketException;
  8. import org.eclipse.sensinact.gateway.generic.core.ProtocolStackEndpoint;
  9.  
  10. public class WeatherStation implements Runnable {
  11.     private static final Logger LOGGER = Logger.getLogger(WeatherStation.class.getName());
  12.  
  13.     private static final Random RANDOM = new Random();
  14.  
  15.     private double temperature = 20.0d;
  16.     private double humidity = 53.0d;
  17.     private double pressure = 1024.0d;
  18.     private double wind = 22.0d;
  19.  
  20.     private ProtocolStackEndpoint<WeatherPacket> endpoint;
  21.     private boolean running;
  22.  
  23.     /**
  24.      * Constructor
  25.      */
  26.     public WeatherStation(ProtocolStackEndpoint<WeatherPacket> endpoint) {
  27.         this.endpoint = endpoint;
  28.         this.running = false;
  29.     }
  30.  
  31.     /**
  32.      * @inheritDoc
  33.      *
  34.      * @see java.lang.Runnable#run()
  35.      */
  36.     public void run() {
  37.         this.running = true;
  38.         while(running) {
  39.             temperature = RANDOM.nextBoolean()
  40.                     ?addition(temperature):subtraction(temperature);
  41.             humidity = RANDOM.nextBoolean()
  42.                     ?addition(humidity):subtraction(humidity);
  43.             pressure = RANDOM.nextBoolean()
  44.                     ?addition(pressure):subtraction(pressure);
  45.             wind = RANDOM.nextBoolean()
  46.                     ?addition(wind):subtraction(wind);
  47.  
  48.             JSONObject content = new JSONObject();
  49.             content.put("temperature", temperature);
  50.             content.put("humidity", humidity);
  51.             content.put("pressure", pressure);
  52.             content.put("wind", wind);
  53.  
  54.             WeatherPacket packet= new WeatherPacket(
  55.                     content.toString().getBytes());
  56.             try {
  57.                 this.endpoint.process(packet);
  58.             } catch (InvalidPacketException e) {
  59.                 LOGGER.log(Level.SEVERE, e.getMessage(), e);
  60.             } finally {
  61.                 try {
  62.                     Thread.sleep(60000);
  63.                 } catch(InterruptedException e) {
  64.                     Thread.interrupted();
  65.                     this.stop();
  66.                 }
  67.             }
  68.         }
  69.     }
  70.  
  71.     public void stop() {
  72.         this.running = false;
  73.     }
  74.  
  75.     private double addition(double value) {
  76.         value +=((value/10d)*RANDOM.nextDouble());
  77.         return value;
  78.     }
  79.  
  80.     private double subtraction(double value) {
  81.         value -=((value/10d)*RANDOM.nextDouble());
  82.         return value;
  83.     }
  84. }

Operate finer configuration features

The XML resources definition ( the resource.xml file ) is made of a set of <resourceInfo> XML tags describing resources held by the service(s) of the connected device(s) (called ServiceProvider in sensiNact). We can distinguish the resources providing data (called DataResource in sensiNact) from those referring to actuators (called ActionResource in sensiNact). For the first ones a default attribute holding the value of the provided data is created ; It constraints us to define the type of this data, and allows to specify an initial value. Apart from the properties of the 'value' attribute of a DataResource, it is possible to specify as many attributes as we want :


  1. <complexType name="metadata">
  2.     <complexContent>
  3.         <extension base="sensinact:nameTypeValue">
  4.             <sequence>
  5.                 <element name="type" type="sensinact:restrictedTypeType" minOccurs="1" maxOccurs="1"/>
  6.                 <element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded"/>
  7.             </sequence>
  8.             <attribute name="name" use="required" type="sensinact:modifiableHiddenKeyWordsExcludedString"/>
  9.         </extension>
  10.     </complexContent>
  11. </complexType>
  12.  
  13. <complexType name="attribute">
  14.     <complexContent>
  15.         <extension base="sensinact:nameTypeValue">
  16.             <sequence>
  17.                 <element name="type" type="sensinact:typeType" minOccurs="1" maxOccurs="unbounded"/>
  18.                 <element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded"/>
  19.                 <element name="metadata"  minOccurs="0" maxOccurs="unbounded" type="sensinact:metadata">
  20.                     <unique name="uniqueMetadataValueTarget">
  21.                         <selector xpath="./sensinact:value"/>
  22.                         <field xpath="@target"/>
  23.                     </unique>
  24.                 </element>
  25.                 <element name="constraints" type="sensinact:constraints" minOccurs="0" maxOccurs="1"/>
  26.             </sequence>
  27.             <attribute name="name" use="required" type="sensinact:nameTypeValueKeyWordsExcludedString"/>
  28.             <attribute name="modifiable" type="boolean" default="true" use="optional"/>
  29.             <attribute name="hidden" type="boolean" default="false" use="optional"/>
  30.         </extension>
  31.     </complexContent>
  32. </complexType>

The restriction applying on the name prevents using 'name', 'type' and 'value'. The modifiable and hidden attributes allow to define respectively whether the resulting attribute can be changed and is visible to the user. As an attribute holds a data, its type must be specified ; Allowed types are: boolean, byte, short, int, long, float, double, string, object array, or any java canonical class name. We can also append metadata to our attribute description, to precise any helpful information to understand the meaning of the provided data. As it is the case for an attribute's name, the one of a metadata is constrained, preventing to use 'modifiable' and 'hidden' values.

A <resourceInfo> XML tag must contain an <identifier> whose content defines how to identify specifically the resource while communicating with the connected counterpart ServiceProvider.


  1. <complexType name="resourceInfo" abstract="true">
  2.     <sequence maxOccurs="1" minOccurs="1">
  3.         <element name="policy" type="sensinact:policy" minOccurs="0" maxOccurs="1"/>
  4.         <element name="subscriptionModes" type="sensinact:subscriptionModes" minOccurs="0" maxOccurs="1"/>
  5.         <element name="identifier" type="sensinact:simpleContent" minOccurs="1" maxOccurs="1"/>
  6.         <element name="attribute" type="sensinact:attribute" minOccurs="0" maxOccurs="unbounded">
  7.             <unique name="uniqueAttributeTypeTarget">
  8.                 <selector xpath="./sensinact:type"/>
  9.                 <field xpath="@target"/>
  10.             </unique>
  11.             <unique name="uniqueAttributeValueTarget">
  12.                 <selector xpath="./sensinact:value"/>
  13.                 <field xpath="@target"/>
  14.             </unique>
  15.         </element>
  16.     </sequence>
  17.     <attribute name="name" type="string" use="required"/>
  18.     <attribute name="target" type="sensinact:targets" use="optional"/>
  19.     <attribute name="profile" type="sensinact:targets" use="optional"/>
  20. </complexType>

More precisely a DataResource definition can refer to a SensorDataResource, commonly mapped to a sensor whose value cannot be changed by the user; a StateVariableResource, commonly mapped to the state of the service to which it belongs and whose value can be linked to the invocation of an ActionResource; a PropertyResource commonly mapped to a configuration property of the service to which it belongs


  1. <complexType name="resourceInfoData" abstract="true">
  2.     <complexContent>
  3.         <extension base="sensinact:resourceInfo">
  4.             <sequence>
  5.                 <element name="type" type="sensinact:typeType" minOccurs="1" maxOccurs="unbounded">
  6.                 </element>
  7.                 <element name="value" type="sensinact:valueType" minOccurs="0" maxOccurs="unbounded">
  8.                 </element>
  9.                 <element name="metadata" type="sensinact:metadata" minOccurs="0" maxOccurs="unbounded">
  10.                     <unique name="uniqueResourceInfoMetadataValueTarget">
  11.                         <selector xpath="./sensinact:value"/>
  12.                         <field xpath="@target"/>
  13.                     </unique>
  14.                 </element>
  15.                 <element name="constraints" type="sensinact:constraints" minOccurs="0" maxOccurs="1"/>
  16.                 <element name="parameters" type="sensinact:dataResourceParameters" minOccurs="0" maxOccurs="unbounded">
  17.                     <key name="uniqueDataParameterName">
  18.                         <selector xpath="./sensinact:parameter"/>
  19.                         <field xpath="@name"/>
  20.                     </key>
  21.                 </element>
  22.             </sequence>
  23.             <attribute name="modifiable" type="sensinact:modifiable_enum" use="optional" />
  24.             <attribute name="hidden" type="boolean" use="optional" default="false" />
  25.         </extension>
  26.     </complexContent>
  27. </complexType>
  28.  
  29. <complexType name="resourceInfoVariable" >
  30.     <complexContent>
  31.         <extension base="sensinact:resourceInfoData">
  32.             <attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="STATE_VARIABLE"/>
  33.         </extension>
  34.     </complexContent>
  35. </complexType>
  36.  
  37. <complexType name="resourceInfoProperty" >
  38.     <complexContent>
  39.         <extension base="sensinact:resourceInfoData">
  40.             <attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="PROPERTY"/>
  41.         </extension>
  42.     </complexContent>
  43. </complexType>
  44.  
  45. <complexType name="resourceInfoSensor" >
  46.     <complexContent>
  47.         <extension base="sensinact:resourceInfoData">
  48.             <attribute name="policy" type="sensinact:policy_enum" use="optional" fixed="SENSOR"/>
  49.         </extension>
  50.     </complexContent>
  51. </complexType>

The type value, metadata and constraints elements, as well as the modifiable and hidden attributes refer to the value attribute that will be automatically created and attached to the DataResource.

You can have a look to the entire schema if you want to know more about the sensiNact resource XML definition, but for now you know enough to continue this tutorial.

As specified above, our weather station will provide four kind of information : temperature, humidity, atmospheric pressure, and wind speed. Each of them will be a readable measure of the physical environment, but not supposed to be changed by the user. We will so use SensorDataResources to define them, to which we will add two metadata, precising the unit and a short description of the resource:


  1. <resourceInfos xmlns="http://org.eclipse.sensinact/resource"
  2.                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.                xsi:schemaLocation="http://org.eclipse.sensinact/resource ../../../schema/sensinact-resource.xsd">
  4.     <resourceInfo xsi:type="resourceInfoSensor" name="TEMPERATURE" modifiable="UPDATABLE">
  5.         <identifier xsi:type="stringContent">temp</identifier>
  6.         <type>float</type>
  7.         <metadata name="unit">
  8.             <type>string</type>
  9.             <value>celcius (C°)</value>
  10.         </metadata>
  11.         <metadata name="description">
  12.             <type>string</type>
  13.             <value>temperature</value>
  14.         </metadata>
  15.     </resourceInfo>
  16.     <resourceInfo xsi:type="resourceInfoSensor" name="HUMIDITY" modifiable="UPDATABLE">
  17.         <identifier xsi:type="stringContent">humidity</identifier>
  18.         <type>int</type>
  19.         <metadata name="unit">
  20.             <type>string</type>
  21.             <value>percent (%)</value>
  22.         </metadata>
  23.         <metadata name="description">
  24.             <type>string</type>
  25.             <value>relative humidity</value>
  26.         </metadata>
  27.     </resourceInfo>
  28.     <resourceInfo xsi:type="resourceInfoSensor" name="ATMOSPHERIC_PRESSURE" modifiable="UPDATABLE">
  29.         <identifier xsi:type="stringContent">pressure</identifier>
  30.         <type>float</type>
  31.         <metadata name="unit">
  32.             <type>string</type>
  33.             <value>hecto pascal (hpa)</value>
  34.         </metadata>
  35.         <metadata name="description">
  36.             <type>string</type>
  37.             <value>atmospheric pressure</value>
  38.         </metadata>
  39.     </resourceInfo>
  40.     <resourceInfo xsi:type="resourceInfoSensor" name="WIND_SPEED" modifiable="UPDATABLE">
  41.         <identifier xsi:type="stringContent">wind</identifier>
  42.         <type>float</type>
  43.         <metadata name="unit">
  44.             <type>string</type>
  45.             <value>meters by second (m/s)</value>
  46.         </metadata>
  47.         <metadata name="description">
  48.             <type>string</type>
  49.             <value>wind speed</value>
  50.         </metadata>
  51.     </resourceInfo>
  52. </resourceInfos>

By default a ServiceProvider is created with one Service called admin and providing a location Resource. To link a new Resource to this admin Service, it is enough to define the target attribute of the <resourceInfo> XML tag to 'admin'. Otherwise, the new created Resource will be attached to all discovered Services or to those whose names are specified (by the way of a comma separated list of names) in the optional target attribute of the <resourceInfo> XML tag.

After having specified our set of <resourceInfo>, it is also possible to statically define a set of devices with their services, which will give birth to ServiceProviders and their Services in the system at launch time.


  1. <devices>
  2.      <device identifier="station">
  3.           <service name="weather"/>
  4.      </device>      
  5. </devices>

It is enough to create a new ServiceProvider in the system, holding two Services including the weather one, and providing the four Resources described in the resource.xml file.

The connected counterpart - OpenWeather service

We are going now to connect our weather station to a real remote service providing the appropriate data. For this example we will use the OpenWeatherMap service, available for free, and invokable using the GPS location we are interested in.

First, we can adapt our WeatherPacketReader to be able to read the content of the Http response of an Http request send to the OpenWeatherMap service (weather map API) :


  1. package org.eclipse.sensinact.gateway.simulated.weather;
  2.  
  3. import org.json.JSONArray;
  4. import org.json.JSONObject;
  5. import org.eclipse.sensinact.gateway.common.bundle.Mediator;
  6. import org.eclipse.sensinact.gateway.core.LocationResource;
  7. import org.eclipse.sensinact.gateway.core.ServiceProvider;
  8. import org.eclipse.sensinact.gateway.generic.core.InvalidPacketException;
  9. import org.eclipse.sensinact.gateway.generic.core.Task.CommandType;
  10. import org.eclipse.sensinact.gateway.generic.core.packet.SimplePacketReader;
  11. import org.eclipse.sensinact.gateway.sthbnd.http.HttpPacket;
  12. import org.eclipse.sensinact.gateway.util.JSONUtils;
  13.  
  14. /**
  15.  * {@link PacketReader} dedicated to {@link HttpPacket} response to 
  16.  * Http request send to the OpenWeatherMap service
  17.  */
  18. public class WeatherPacketReader extends SimplePacketReader<HttpPacket> {
  19.     /**
  20.      * @param mediator
  21.      */
  22.     protected WeatherPacketReader(Mediator mediator) {
  23.         super(mediator);
  24.     }
  25.  
  26.     /**
  27.      * @inheritDoc
  28.      *
  29.      * @see org.eclipse.sensinact.gateway.generic.core.packet.PacketReader#parse(org.eclipse.sensinact.gateway.generic.core.packet.Packet)
  30.      */
  31.     public void parse(HttpPacket packet) throws InvalidPacketException {
  32.         JSONObject object = new JSONObject(new String(packet.getBytes()));
  33.  
  34.         long timestamp = object.optLong("dt")*1000L;
  35.         JSONObject coord = object.optJSONObject("coord");
  36.         if(coord != null)
  37.         {
  38.             super.setServiceProviderId("station");
  39.             super.setServiceId(ServiceProvider.ADMINISTRATION_SERVICE_NAME);
  40.             super.setResourceId(LocationResource.LOCATION);
  41.             super.setData(new StringBuilder().append(coord.optDouble("lat")).append(JSONUtils.COLON
  42.             ).append(coord.optDouble("lon")).toString());
  43.             super.setCommand(CommandType.GET);
  44.             super.configure();
  45.         }
  46.         JSONArray weather = object.optJSONArray("weather");
  47.         JSONObject content = null;
  48.         if(weather != null && (content = weather.optJSONObject(0))!=null)
  49.         {
  50.             super.setServiceProviderId("station");
  51.             super.setServiceId("weather");
  52.             super.setResourceId("state");
  53.             super.setData(content.opt("main"));
  54.             super.setTimestamp(timestamp);
  55.             super.setCommand(CommandType.GET);
  56.             super.configure();
  57.  
  58.             super.setServiceProviderId("station");
  59.             super.setServiceId("weather");
  60.             super.setResourceId("description");
  61.             super.setData(content.opt("description"));
  62.             super.setTimestamp(timestamp);
  63.             super.setCommand(CommandType.GET);
  64.             super.configure();
  65.         }
  66.         JSONObject wind = object.optJSONObject("wind");
  67.         if(wind != null)
  68.         {
  69.             super.setServiceProviderId("station");
  70.             super.setServiceId("weather");
  71.             super.setResourceId("wind");
  72.             super.setData(wind.opt("speed"));
  73.             super.setTimestamp(timestamp);
  74.             super.setCommand(CommandType.GET);
  75.             super.configure();
  76.  
  77.             super.setServiceProviderId("station");
  78.             super.setServiceId("weather");
  79.             super.setResourceId("orientation");
  80.             super.setData(wind.opt("deg"));
  81.             super.setTimestamp(timestamp);
  82.             super.setCommand(CommandType.GET);
  83.             super.configure();
  84.         }
  85.         JSONObject main = object.optJSONObject("main");
  86.         if(main != null)
  87.         {
  88.             super.setServiceProviderId("station");
  89.             super.setServiceId("weather");
  90.             super.setResourceId("temperature");
  91.             super.setData(main.opt("temp"));
  92.             super.setTimestamp(timestamp);
  93.             super.setCommand(CommandType.GET);
  94.             super.configure();
  95.  
  96.             super.setServiceProviderId("station");
  97.             super.setServiceId("weather");
  98.             super.setResourceId("humidity");
  99.             super.setData(main.opt("humidity"));
  100.             super.setTimestamp(timestamp);
  101.             super.setCommand(CommandType.GET);
  102.             super.configure();
  103.  
  104.             super.setServiceProviderId("station");
  105.             super.setServiceId("weather");
  106.             super.setResourceId("pressure");
  107.             super.setData(main.opt("pressure"));
  108.             super.setTimestamp(timestamp);
  109.             super.setCommand(CommandType.GET);
  110.             super.configure();
  111.         }
  112.     }
  113. }

We are now able to understand the response coming from the OpenWeatherMap service, but we still have to send the appropriate requests; for this purpose we will use the sensiNact Http bridge, which provides a set of tools helping to create them.

Our Bundle Activator becomes an HttpActivator; allowing us to configure an annotated SimpleHttpTaskConfigurationAdapter, used to automatically generate the appropriate requests. The ExtProtocolStackEndpoint becomes a SimpleHttpProtocolStackEndpoint, and the ExtXmlModelConfiguration becomes an HttpModelConfiguration.

The SimpleHttpTask' annotation allows to configure every parameter of an Http request to create, including an SimpleHttpTaskContentConfiguration class in charge of building the Http requests content.


  1. package org.eclipse.sensinact.gateway.simulated.osgi;
  2.  
  3. import java.util.Collections;
  4. import org.osgi.framework.BundleContext;
  5. import org.osgi.framework.InvalidSyntaxException;
  6. import org.eclipse.sensinact.gateway.common.bundle.Mediator;
  7. import org.eclipse.sensinact.gateway.generic.core.Task.CommandType;
  8. import org.eclipse.sensinact.gateway.generic.core.XmlModelInstance;
  9. import org.eclipse.sensinact.gateway.generic.core.XmlModelInstanceBuilder;
  10. import org.eclipse.sensinact.gateway.sthbnd.http.HttpModelConfiguration;
  11. import org.eclipse.sensinact.gateway.sthbnd.http.HttpPacket;
  12. import org.eclipse.sensinact.gateway.sthbnd.http.impl.HttpActivator;
  13. import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpProtocolStackEndpoint;
  14. import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpTask;
  15. import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpTaskQuery;
  16. import org.eclipse.sensinact.gateway.sthbnd.http.impl.SimpleHttpTaskConfigurationAdapter;
  17.  
  18. /**
  19.  * Bundle Activator
  20.  */
  21. public class Activator extends HttpActivator<Mediator> {
  22.     private SimpleHttpProtocolStackEndpoint connector;
  23.     private HttpModelConfiguration manager;
  24.  
  25.     @SimpleHttpTask(command = CommandType.GET,
  26.             host = "api.openweathermap.org",
  27.             path = "data/2.5/weather",
  28.             query = {	@SimpleHttpTaskQuery(key = "lat", value = "<latitude>"),
  29.                             @SimpleHttpTaskQuery(key = "lon", value = "<longitude>"),
  30.                             @SimpleHttpTaskQuery(key = "APPID", value = "<your-key>")
  31.                     })
  32.     private SimpleHttpTaskConfigurationAdapter adapter;
  33.  
  34.  
  35.     /**
  36.      * @inheritDoc
  37.      *
  38.      * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#doStart()
  39.      */
  40.     @Override
  41.     public void doStart() throws Exception {
  42.         adapter = new SimpleHttpTaskConfigurationAdapter();
  43.         manager = new XmlModelInstanceBuilder<HttpModelConfiguration,
  44.                 XmlModelInstance>(super.mediator,XmlModelInstance.class,
  45.                 HttpModelConfiguration.class
  46.         ).withPacketType(HttpPacket.class
  47.         ).withStartAtInitializationTime(true
  48.         ).buildConfiguration("resource.xml", Collections.<String,String>emptyMap());
  49.  
  50.         super.configureAdapter();
  51.  
  52.         connector = new SimpleHttpProtocolStackEndpoint(super.mediator, adapter);
  53.         connector.connect(manager);
  54.     }
  55.  
  56.     /**
  57.      * @inheritDoc
  58.      *
  59.      * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#
  60.      * doStop()
  61.      */
  62.     @Override
  63.     public void doStop() throws Exception {
  64.         connector.stop();
  65.     }
  66.  
  67.     /**
  68.      * @inheritDoc
  69.      *
  70.      * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator#
  71.      * doInstantiate(org.osgi.framework.BundleContext, int, java.io.FileOutputStream)
  72.      */
  73.     @Override
  74.     public Mediator doInstantiate(BundleContext context) throws InvalidSyntaxException {
  75.         return new Mediator(context);
  76.     }
  77. }

After the adaptation of the WeatherPacketReaderFactory to be able to handle HttpPacket type, the WeatherPacket and WeatherStation classes can be deleted.


  1. package org.eclipse.sensinact.gateway.simulated.weather;
  2.  
  3. import org.eclipse.sensinact.gateway.common.bundle.Mediator;
  4. import org.eclipse.sensinact.gateway.generic.core.InvalidPacketException;
  5. import org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration;
  6. import org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory;
  7. import org.eclipse.sensinact.gateway.generic.core.packet.Packet;
  8. import org.eclipse.sensinact.gateway.generic.core.packet.PacketReader;
  9. import org.eclipse.sensinact.gateway.sthbnd.http.HttpPacket;
  10.  
  11. public class WeatherPacketReaderFactory implements PacketReaderFactory<HttpPacket> {
  12.     /**
  13.      * @inheritDoc
  14.      *
  15.      * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
  16.      * handle(java.lang.Class)
  17.      */
  18.     public boolean handle(Class<? extends Packet> packetType) {
  19.         return HttpPacket.class.isAssignableFrom(packetType);
  20.     }
  21.  
  22.     /**
  23.      * @inheritDoc
  24.      *
  25.      * @see org.eclipse.sensinact.gateway.generic.core.impl.PacketReaderFactory#
  26.      * newInstance(org.eclipse.sensinact.gateway.common.bundle.Mediator, 
  27.      * org.eclipse.sensinact.gateway.generic.core.XmlModelConfiguration, 
  28.      * org.eclipse.sensinact.gateway.generic.core.packet.Packet)
  29.      */
  30.     public PacketReader<HttpPacket> newInstance(Mediator mediator, XmlModelConfiguration<HttpPacket> manager,
  31.                                                 HttpPacket packet) throws InvalidPacketException {
  32.         WeatherPacketReader packetReader = new WeatherPacketReader(mediator);
  33.         packetReader.parse(packet);
  34.         return packetReader;
  35.     }
  36. }

Back to the top