Follow me

Exercise 4: A better energy management

In this exercise, you will try to manage the energy consumption of your system.

Question 1 – Extending the configuration service: Extend the configuration service of your Follow Me application so that it is possible to configure a maximum power per room :
FollowMeConfigurationService

package org.example.follow.me.configuration;
 
/**
 * The FollowMeConfiguration service allows to configure the Follow Me
 * application.
 */
public interface FollowMeConfiguration {
    public int getMaximumNumberOfLightsToTurnOn();
    public void setMaximumNumberOfLightsToTurnOn(int maximumNumberOfLightsToTurnOn);
 
    /**
     * Gets the maximum allowed energy consumption in Watts in each room
     * 
     * @return the maximum allowed energy consumption in Watts/hours
     */
    public double getMaximumAllowedEnergyInRoom();
 
    /**
     * Sets the maximum allowed energy consumption in Watts in each room
     * 
     * @param maximumEnergy
     *            the maximum allowed energy consumption in Watts/hours in each room
     */
    public void setMaximumAllowedEnergyInRoom(double maximumEnergy);
}

Implement this service so that the energy consumption of a room does not exceed the given maximum. To this end, you should create a new member variable:

/**
* The maximum energy consumption allowed in a room in Watt:
**/
private double maximumEnergyConsumptionAllowedInARoom = 100.0d;

Please note that the maximumNumberOfLights (we have introduced earlier) takes precedence over the given maximum power consumption.

To simplify the implementation you can assume that each light as a default 100Watt consumption and that the lights are binary lights only. In such case the number representing light is given by a simple Euclidean division (N = Target/100)

Question 2 – Test: Create a script to test your application and checks it is working according to the specification (the environment should be composed by binary lights only).

Question 3 – Manager: Extend the FollowMeAdministration interface and your manager implementation to add an energy saving goal :

FollowMeAdministration

public interface FollowMeAdministration {
 
    public void setIlluminancePreference(IlluminanceGoal illuminanceGoal);
    public illuminanceGoal getIlluminancePreference();
 
    /**
     * Configure the energy saving goal.
     * @param energyGoal : the targeted energy goal.
     */
    public void setEnergySavingGoal(EnergyGoal energyGoal);
 
    /**
     * Gets the current energy goal.
     * 
     * @return the current energy goal.
     */
    public EnergyGoal getEnergyGoal();
 
}

The different levels of energy could be :

package org.example.follow.me.manager;
 
/**
 * This enum describes the different energy goals associated with the
 * manager.
 */
public enum EnergyGoal {
    LOW(100d), MEDIUM(200d), HIGH(1000d);
 
    /**
     * The corresponding maximum energy in watt
     */
    private double maximumEnergyInRoom;
 
    /**
     * get the maximum energy consumption in each room
     * 
     * @return the energy in watt
     */
    public double getMaximumEnergyInRoom() {
        return maximumEnergyInRoom;
    }
 
    private EnergyGoal(double powerInWatt) {
        maximumEnergyInRoom = powerInWatt;
    }
}

Question 4 – Command: Write a command to be able to configure the energy saving goal and test your work.

FollowMeCommand

g! setEnergyPreference MEDIUM
g! getEnergyPreference
EnergyMode = MEDIUM

Question 5 – Using DimmerLights: Change your implementation to take dimmer lights into account. One way to achieve this is to try to turn on as many binary lights as possible and then turn the DimmerLights to reach the targeted power by adjusting their powers.

Question 6 (optional) – Using heterogeneous lights: In the previous questions, we assumed that all the lights had the same nominal power consumption.

Now, you can try to write a more generic algorithm to manage heterogeneous lights.

First consider only the problem with BinaryLights. To do that, you might have to test all the combinations of BinaryLights. This can be achieved by solving the Subset sum problem.

The consumption of a light is given by the getMaxPowerLevel() method of the BinaryLight interface.

Here is a very naive implementation of this algorithm (feel free to implement your own) :

package org.example.algorithm;
 
import java.util.Arrays;
import java.util.BitSet;
 
/**
 * This class implements an algorithm that use a very naive approach of solving
 * the closest sum subset problem on an array of doubles.
 */
public final class ClosestSumAlgorithm {
 
    /**
     * Find the subset of the items whose sum is closest to, without exceeding
     * maximalSum.
     * 
     * Performance are low with doubles. Could largely be improved by using
     * integers instead. The algorithm is sufficiently effective for 15 lights
     * or less.
     * 
     * @param maximalSum
     *            the maximal sum of the subset;
     * @param items
     *            an array containing the weight of each items. The order of
     *            element will be preserved to produce the best
     *            combination.
     * @return the subset of items whose sum is closest to maximalSum
     *         without exceeding it. The combination is given in the same order
     *         as the input array. array[i] contains the value of item[i] if it
     *         is involved in the computing of the closest-sum, or 0 if not.
     */
    public static double[] greadySubSetClosestSum(final double maximalSum, final double[] items) {
 
        // the current best results :
        double bestSum = 0.0d;
        double[] bestCombination = new double[0];
 
        /*
         * Generate all the possible combinations. There are 2^N possibilities
         * that can therefore be represented by a bitset.
         * The use of bitset is done to reduce the number of line of codes.
         * The solution is thus far from being optimized.
         */
        for (int i = 0; i < Math.pow(2, items.length); i++) {
            // Get the current combination 
            double[] currentCombination = multiplyByBitset(convertToBitSet(i), items);
            double currentSum = sum(currentCombination);
 
            // if we have the best result possible
            if (currentSum == maximalSum) {
                // return it
                return currentCombination;
            }
 
            // if the current result is better than the previous best result
            if ((currentSum <= maximalSum) && (currentSum > bestSum)) {
                // store it
                bestSum = currentSum;
                bestCombination = currentCombination;
            }
        }
 
        return bestCombination;
    }
 
    /**
     * Sum of the given variables or array.
     * 
     * @param variables
     *            the variables to be summed.
     * @return the sum of the variables.
     */
    private static double sum(double... variables) {
        double sum = 0;
        for (double var : variables) {
            sum += var;
        }
        return sum;
    }
 
    /**
     * Convert a number into BitSet.
     * This could be obtained directly in JAVA7 (iCASA is not compatible)
     * 
     * @param number
     *            the number to convert
     * @return the resulting bit set
     */
    private static BitSet convertToBitSet(long number) {
        BitSet bits = new BitSet();
        int index = 0;
        while (number != 0L) {
            if ((number % 2L) != 0) {
                bits.set(index);
            }
            ++index;
            number = number >>> 1;
        }
        return bits;
    }
 
    /**
     * Multiply an array by a bitset
     * 
     * @param bitset
     *            the BitSet
     * @param array
     *            the array
     * @return the resulting array
     */
    private static double[] multiplyByBitset(BitSet bitset, double[] array) {
        assert (bitset.length() == array.length) : "array and bitset must have the same size";
 
        double[] result = new double[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = bitset.get(i) ? array[i] : 0;
        }
        return result;
    }
}

Example of use :

public static void main(String[] args) {
    double[] items = new double[] { 1.5d, 7.4d, 3.4d, 8.3d, 15.233d, 99d, 22d, 76d, 38d, 22d, 7d, 0.10d, 54.9d, 45.9d, 90d, 48.6d, 6.1d, 4.2d, 89.3d };
 
    // Targeted sum :
    double maxSum = 99.97484;
    // Compute the best combination :
    double[] result = ClosestSumAlgorithm.greadySubSetClosestSum(maxSum, items);
    System.out.println(Arrays.toString(result));
    System.out.println(sum(result));
 
}

Then you may try to find a general solution for environment including some dimmer lights.