package storm.applications.spout.generator;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import storm.applications.constants.SmartGridConstants.Conf;
import storm.applications.constants.SmartGridConstants.Measurement;
import storm.applications.model.smartgrid.House;
import storm.applications.model.smartgrid.Household;
import storm.applications.model.smartgrid.SmartPlug;
import storm.applications.spout.GeneratorSpout;
import storm.applications.util.config.Configuration;
import storm.applications.util.math.RandomUtil;
import storm.applications.util.stream.StreamValues;
/**
* This class generates smart plug readings at fixed time intervals, storing them
* into a queue that will be consumed by a {@link GeneratorSpout}.
*
* The readings are generated by a separated thread and the interval resolutions is
* of seconds. In order to increase the volume of readings you can decrease the
* interval down to 1 second. If you need more data volume you will have to tune
* the other configuration parameters.
*
* Configurations parameters:
*
* - {@link Conf#GENERATOR_INTERVAL_SECONDS}: interval of record generation in seconds.
* - {@link Conf#GENERATOR_NUM_HOUSES}: number of houses in the scenario.
* - {@link Conf#GENERATOR_HOUSEHOLDS_MIN} and {@link Conf#GENERATOR_HOUSEHOLDS_MAX}: the range of number of households within a house.
* - {@link Conf#GENERATOR_PLUGS_MIN} and {@link Conf#GENERATOR_PLUGS_MAX}: the range of smart plugs within a household.
* - {@link Conf#GENERATOR_LOADS}: a comma-separated list of peak loads that will be randomly assigned to smart plugs.
* - {@link Conf#GENERATOR_LOAD_OSCILLATION}: by how much the peak load of the smart plug will oscillate.
* - {@link Conf#GENERATOR_PROBABILITY_ON}: the probability of the smart plug being on.
* - {@link Conf#GENERATOR_ON_LENGTHS}: a comma-separated list of lengths of time to be selected from to set the amount of time that the smart plug will be on.
*
*
* From: http://corsi.dei.polimi.it/distsys/2013-2014/projects.html
*
* Generates a dataset of a random set of smart plugs, each being part of a household,
* which is, in turn, part of a house. Each smart plug records the actual load
* (in Watts) at each second. The generated dataset is inspired by the DEBS 2014
* challenge and follow a similar format, a sequence of 6 comma separated values
* for each line (i.e., for each reading):
*
* - a unique identifier of the measurement [64 bit unsigned integer value]
* - a timestamp of measurement (number of seconds since January 1, 1970, 00:00:00 GMT) [64 bit unsigned integer value]
* - a unique identifier (within a household) of the smart plug [32 bit unsigned integer value]
* - a unique identifier of a household (within a house) where the plug is located [32 bit unsigned integer value]
* - a unique identifier of a house where the household with the plug is located [32 bit unsigned integer value]
* - the measurement [32 bit unsigned integer]
*
* @author Alessandro Sivieri
* @author Maycon Viana Bordin <mayconbordin@gmail.com>
*/
public class SmartPlugGenerator extends Generator implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(SmartPlugGenerator.class);
// default config values
private static final int HOUSES = 10;
private static final int HOUSEHOLDS_MIN = 2;
private static final int HOUSEHOLD_MAX = 10;
private static final int PLUGS_MIN = 5;
private static final int PLUGS_MAX = 20;
private static final int LOADS[] = {25, 35, 50, 70, 150, 300, 600, 800, 900, 1200};
private static final int OSCILLATION = 10;
private static final double PROBABILITY_ON = 0.5D;
private static final int LENGTHS[] = {600, 900, 1800, 3600, 5400, 7200, 0x1a5e0};
private long currentId = 0l;
private int interval;
private int numHouses;
private int householdMin;
private int householdMax;
private int plugsMin;
private int plugsMax;
private int[] loads;
private int loadOscillation;
private double probabilityOn;
private int[] onLenghts;
private HashMap<Integer, House> houses;
private ScheduledExecutorService scheduler;
private BlockingQueue<StreamValues> queue;
@Override
public void initialize(Configuration config) {
super.initialize(config);
interval = config.getInt(Conf.GENERATOR_INTERVAL_SECONDS, 1);
numHouses = config.getInt(Conf.GENERATOR_NUM_HOUSES, HOUSES);
householdMin = config.getInt(Conf.GENERATOR_HOUSEHOLDS_MIN, HOUSEHOLDS_MIN);
householdMax = config.getInt(Conf.GENERATOR_HOUSEHOLDS_MAX, HOUSEHOLD_MAX);
plugsMin = config.getInt(Conf.GENERATOR_PLUGS_MIN, PLUGS_MIN);
plugsMax = config.getInt(Conf.GENERATOR_PLUGS_MAX, PLUGS_MAX);
loads = config.getIntArray(Conf.GENERATOR_LOADS, LOADS);
loadOscillation = config.getInt(Conf.GENERATOR_LOAD_OSCILLATION, OSCILLATION);
probabilityOn = config.getDouble(Conf.GENERATOR_PROBABILITY_ON, PROBABILITY_ON);
onLenghts = config.getIntArray(Conf.GENERATOR_ON_LENGTHS, LENGTHS);
houses = new HashMap<>(numHouses);
queue = new LinkedBlockingQueue<>();
scheduler = Executors.newScheduledThreadPool(1);
this.createScenario();
scheduler.scheduleAtFixedRate(this, 0, interval, TimeUnit.SECONDS);
}
/**
* Create {@link #numHouses} house instances, each with a random number of
* households between {@link #householdMin} and {@link #householdMax}. Each
* household in its turn will have a random number of smart plugs between
* {@link #plugsMin} and {@link #plugsMax}.
*/
private void createScenario() {
for (int i = 0; i < numHouses; ++i) {
House house = new House(i);
for (int j = 0; j < RandomUtil.randomMinMax(householdMin, householdMax); ++j) {
Household household = new Household(j);
for (int k = 0; k < RandomUtil.randomMinMax(plugsMin, plugsMax); ++k) {
SmartPlug sp = new SmartPlug(k, loads[RandomUtil.randomMinMax(0, loads.length - 1)],
loadOscillation, probabilityOn, onLenghts);
household.addSmartPlug(sp);
}
house.addHousehold(household);
}
houses.put(i, house);
}
}
@Override
public void run() {
long ts = (System.currentTimeMillis() / 1000) - interval;
for (House house : houses.values()) {
for (Household household : house.getHouseholds()) {
for (SmartPlug plug : household.getPlugs()) {
plug.tryToSetOn(ts);
try {
queue.put(new StreamValues(String.valueOf(nextId()), ts,
plug.getLoad(), Measurement.LOAD, plug.getId(),
household.getId(), house.getId()));
queue.put(new StreamValues(String.valueOf(nextId()), ts,
plug.getTotalLoadkWh(), Measurement.WORK, plug.getId(),
household.getId(), house.getId()));
} catch (InterruptedException ex) {
LOG.error("Something wrong happened while waiting to put record in the queue", ex);
}
}
}
}
}
@Override
public StreamValues generate() {
try {
return queue.take();
} catch (InterruptedException ex) {
LOG.error("Unable to get record from queue", ex);
}
return null;
}
private long nextId() {
return currentId++;
}
}