-
Notifications
You must be signed in to change notification settings - Fork 12
Extending the Framework
In general, the framework consists of the following components:
- Sensors - provide data to the pipeline (only output)
- Transformers - modify data within the pipeline (input + output)
- Consumers - take data from the pipeline and perform an external action (only input)
To create your own components you can follow the steps below.
The first step is to integrate a new sensor is to create a new Java class that extends the abstract Sensor
class.
import hcm.ssj.core.Sensor;
public class MySensor extends Sensor
{
}
Afterwards, implement the abstract connect()
, disconnect()
and getOptions()
methods. The basic structure should look like this (you can also look at existing sensors such as Empatica
, Bitalino
, EstimoteBeacon
, etc... for inspiration):
import hcm.ssj.core.Log;
import hcm.ssj.core.SSJFatalException;
import hcm.ssj.core.Sensor;
import hcm.ssj.core.option.Option;
import hcm.ssj.core.option.OptionList;
public class MySensor extends Sensor
{
public class Options extends OptionList
{
// Specify your options here
public final Option<String> myStringOption = new Option<>("myStringOption", "default value", String.class, "option description");
public final Option<Boolean> myBooleanOption = new Option<>("myBooleanOption", true, Boolean.class, "option description");
public final Option<Float> myFloatOption = new Option<>("myFloatOption", 1.0f, Float.class, "option description");
public final Option<Integer> myIntOption = new Option<>("myIntOption", 1, Integer.class, "option description");
private Options()
{
addOptions();
}
}
public final Options options = new Options();
public MySensor()
{
// Set custom name for your sensor component (shows up in SSJ Creator)
_name = "MySensor";
}
@Override
public OptionList getOptions()
{
return options;
}
@Override
protected boolean connect() throws SSJFatalException
{
boolean connected = false;
// Example for accessing your options
if (options.myBooleanOption.get())
{
// Example for using the integrated logger
Log.i("String option value: " + options.myStringOption.get());
}
// Establish connection to your sensor here
// Return true if connected successfully
connected = true;
return connected;
}
@Override
protected void disconnect() throws SSJFatalException
{
// Disconnect from your sensor here and perform cleanup
}
}
In addition to the Sensor
component which handles the connection to the sensor, at least one SensorChannel
component needs to be created that is responsible for providing the sensor data to the pipeline. It is also common that a sensor can have multiple channels (e.g., if you want to integrate a wearable device that provides the heart rate and accelerometer data via bluetooth, then you would create a sensor component that handles the bluetooth connection and two sensor channels - one that provides the heart rate and another one for the accelerometer data).
To create a sensor channel component simply create a new Java class and extend the abstract SensorChannel
class.
import hcm.ssj.core.SensorChannel;
public class MySensorChannel extends SensorChannel
{
}
Afterwards, overwrite the abstract methods. A basic structure should look like this:
import hcm.ssj.core.Cons;
import hcm.ssj.core.SSJFatalException;
import hcm.ssj.core.SensorChannel;
import hcm.ssj.core.option.Option;
import hcm.ssj.core.option.OptionList;
import hcm.ssj.core.stream.Stream;
public class MySensorChannel extends SensorChannel
{
public class Options extends OptionList
{
// Specify your options here
public final Option<String> myStringOption = new Option<>("myStringOption", "default value", String.class, "option description");
public final Option<Boolean> myBooleanOption = new Option<>("myBooleanOption", true, Boolean.class, "option description");
public final Option<Float> myFloatOption = new Option<>("myFloatOption", 1.0f, Float.class, "option description");
public final Option<Integer> sampleRate = new Option<>("sampleRate", 5, Integer.class, "sample rate");
private Options()
{
addOptions();
}
}
public final Options options = new Options();
public MySensorChannel()
{
_name = "MySensorChannel";
}
@Override
public OptionList getOptions()
{
return options;
}
@Override
public void enter(Stream stream_out) throws SSJFatalException
{
// Called once at the pipeline start (before processing loop)
// Used for setup
// Example how to access sensor class, e.g. to read data from the connection established there
MySensor mySensor = ((MySensor) _sensor);
}
@Override
protected boolean process(Stream stream_out) throws SSJFatalException
{
// Example for providing data
float[] out = stream_out.ptrF();
// Provide values for each sample dimension
out[0] = (float) Math.random();
out[1] = (float) Math.random();
out[2] = (float) Math.random();
// Return true if data was provided successfully
return true;
}
@Override
public void flush(Stream stream_out) throws SSJFatalException
{
// Called once at the pipeline stop (after processing loop)
// Used for cleanup
}
@Override
protected double getSampleRate()
{
// Number of samples per second. process() method will be called this amount of times per second
return 5; // or use options.sampleRate.get() to make it adjustable
}
@Override
protected int getSampleDimension()
{
// Each sample can have multiple dimensions e.g., accelerometer data has 3 dimensions for x, y and z
return 3;
}
@Override
protected Cons.Type getSampleType()
{
// Datatype of the provided samples
return Cons.Type.FLOAT;
}
@Override
protected void describeOutput(Stream stream_out)
{
// Describe the dimensions of the sample
stream_out.desc = new String[stream_out.dim];
stream_out.desc[0] = "Acc X";
stream_out.desc[1] = "Acc Y";
stream_out.desc[2] = "Acc Z";
}
}
You can now use your sensor and channel(s) like this:
// Get pipeline
Pipeline pipeline = Pipeline.getInstance();
// Create sensor
MySensor sensor = new MySensor();
sensor.options.myStringOption.set("custom string");
// Create channel
MySensorChannel channel = new MySensorChannel();
channel.options.myBoolOption.set(true);
// Create consumer
Logger logger = new Logger();
// Add components to pipeline
pipeline.addSensor(sensor, channel);
pipeline.addConsumer(logger, channel);
pipeline.start();