Edit me

The Detecting a sensor value out of range recipe introduced the basics of filtering as well as the use of a Range.

Oftentimes, a user wants to initialize a range specification from an external configuration file so the application code is more easily configured and reusable.

The string form of a Range is natural, consise, and easy to use. As such it's a convenient form to use in configuration files or for users to enter. The range string can easily be converted back into a Range.

We're going to assume familiarity with that earlier recipe and those concepts and focus on just the "external range specification" aspect of this recipe.

Create a configuration file

The file's syntax is that for a java.util.Properties object. See the Range documentation for its string syntax.

Put this into a file:

# the Range string for the temperature sensor optimal range
optimalTempRange=[77.0..91.0]

Supply the pathname to this file as an argument to the application when you run it.

Loading the configuration file

A java.util.Properties object is often used for configuration parameters and it is easy to load the properties from a file.

// Load the configuration file with the path string in configFilePath
Properties props = new Properties();
props.load(Files.newBufferedReader(new File(configFilePath).toPath()));

Initializing the Range

// initialize the range from a Range string in the properties.
// Use a default value if a range isn't present.
static String DEFAULT_TEMP_RANGE_STR = "[60.0..100.0]";
static Range<Double> optimalTempRange = Ranges.valueOfDouble(
        props.getProperty("optimalTempRange", defaultRange));

The final application

import java.io.File;
import java.nio.file.Files;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import org.apache.edgent.analytics.sensors.Range;
import org.apache.edgent.analytics.sensors.Ranges;
import org.apache.edgent.providers.direct.DirectProvider;
import org.apache.edgent.samples.utils.sensor.SimulatedTemperatureSensor;
import org.apache.edgent.topology.TStream;
import org.apache.edgent.topology.Topology;

/**
 * Detect a sensor value out of expected range.
 * Get the range specification from a configuration file.
 */
public class ExternalFilterRange {
    /**
     * Optimal temperatures (in Fahrenheit)
     */
    static String DEFAULT_TEMP_RANGE_STR = "[60.0..100.0]";
    static Range<Double> optimalTempRange;

    /** Initialize the application's configuration */
    static void initializeConfiguration(String configFilePath) throws Exception {
        // Load the configuration file
        Properties props = new Properties();
        props.load(Files.newBufferedReader(new File(configFilePath).toPath()));

        // initialize the range from a Range string in the properties.
        // Use a default value if a range isn't present in the properties.
        optimalTempRange = Ranges.valueOfDouble(
                props.getProperty("optimalTempRange", DEFAULT_TEMP_RANGE_STR));
        System.out.println("Using optimal temperature range: " + optimalTempRange);
    }

    /**
     * Polls a simulated temperature sensor to periodically obtain
     * temperature readings (in Fahrenheit). Use a simple filter
     * to determine when the temperature is out of the optimal range.
     */
    public static void main(String[] args) throws Exception {
        if (args.length != 1)
            throw new Exception("missing pathname to configuration file");
        String configFilePath = args[0];

        DirectProvider dp = new DirectProvider();

        Topology top = dp.newTopology("TemperatureSensor");

        // Initialize the configuration
        initializeConfiguration(configFilePath);

        // Generate a stream of temperature sensor readings
        SimulatedTemperatureSensor tempSensor = new SimulatedTemperatureSensor();
        TStream<Double> temp = top.poll(tempSensor, 1, TimeUnit.SECONDS);

        // Simple filter: Perform analytics on sensor readings to detect when
        // the temperature is out of the optimal range and generate warnings
        TStream<Double> simpleFiltered = temp.filter(tuple ->
                !optimalTempRange.contains(tuple));
        simpleFiltered.sink(tuple -> System.out.println("Temperature is out of range! "
                + "It is " + tuple + "\u00b0F!"));

        // See what the temperatures look like
        temp.print();

        dp.submit(top);
    }
}