org.energy_home.jemma.ah.ebrain.algo.OverloadDetectorTask.java Source code

Java tutorial

Introduction

Here is the source code for org.energy_home.jemma.ah.ebrain.algo.OverloadDetectorTask.java

Source

/**
 * This file is part of JEMMA - http://jemma.energy-home.org
 * (C) Copyright 2013 Telecom Italia (http://www.telecomitalia.it)
 *
 * JEMMA is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (LGPL) version 3
 * or later as published by the Free Software Foundation, which accompanies
 * this distribution and is available at http://www.gnu.org/licenses/lgpl.html
 *
 * JEMMA is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License (LGPL) for more details.
 *
 */
package org.energy_home.jemma.ah.ebrain.algo;

import java.util.*;
import java.util.concurrent.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.energy_home.jemma.ah.ebrain.old.SmartAppliance;

public class OverloadDetectorTask extends TimerTask {
    public static final int DETECTION_INTERVAL = 5 * 1000; // 10 seconds
    public static final int SAME_EVENT_NOTIFICATION_DELAY = 20 * 1000; // 60 seconds

    public static final byte OVERLOAD_OVER_EVENT = 1;
    public static final byte OVERLOAD_UNDER_EVENT = 2;
    public static final byte SAFE_THRESHOLD_EVENT = 3;

    public static final float POWER_USAGE_WEIGHT = 1;
    public static final float ELAPSED_TIME_SWITCH_WEIGHT = .1f;
    public static final float SUSPENSION_PRIORITY_WEIGHT = 10000;
    public static final float REACTIVATION_PRIORITY_WEIGHT = SUSPENSION_PRIORITY_WEIGHT / 100;
    public static final int ELAPSED_TIME_WINDOW = 60 * 1000;

    private static final Log log = LogFactory.getLog(OverloadDetectorTask.class);

    private Timer timer = new Timer(true);
    private OverloadDetectorListener listener;
    private List<SmartAppliance> appliances = new CopyOnWriteArrayList<SmartAppliance>();
    private long lastNotifiedTimeTime;
    private long currentTime;
    private float totalPowerUsage = 0;
    private byte lastNotifiedEvent = 0;
    private float upperPowerThreshold, lowerPowerThreshold;

    public OverloadDetectorTask(OverloadDetectorListener l) {
        listener = l;
        setUpperPowerThreshold(3300.0f);
        timer.scheduleAtFixedRate(this, DETECTION_INTERVAL / 10, DETECTION_INTERVAL);
    }

    public void addAppliance(SmartAppliance a) {
        appliances.add(a);
    }

    void removeAppliance(SmartAppliance a) {
        appliances.remove(a);
    }

    void setTotalPowerUsage(float p) {
        totalPowerUsage = p;
    }

    void setUpperPowerThreshold(float up) {
        upperPowerThreshold = up;
        lowerPowerThreshold = up * .75f;
    }

    public void close() {
        timer.cancel();
    }

    public void run() {
        try {
            float power = computeTotalPowerUsage();
            log.debug("TotalPowerUsage: " + power);
            currentTime = System.currentTimeMillis();

            // check suspendable appliance and report the 1st appliance to switch based on priority
            if (power > upperPowerThreshold) {
                log.debug("upperPowerThreshold exceeded by: " + (power - upperPowerThreshold));
                if (canNotifyEvent(OVERLOAD_OVER_EVENT)) {
                    checkOverload();
                }
                // check resumable appliance and report the 1st appliance to switch based on priority
            } else if (power < lowerPowerThreshold) {
                log.debug("lowerPowerThreshold exceeded by: " + (lowerPowerThreshold - power));
                if (canNotifyEvent(OVERLOAD_UNDER_EVENT)) {
                    checkUnderload();
                }
            } else { // here should be in the hysteresis window
                if (canNotifyEvent(SAFE_THRESHOLD_EVENT)) {
                    lastNotifiedEvent = SAFE_THRESHOLD_EVENT;
                    lastNotifiedTimeTime = currentTime;
                    log.debug("notify overload re-entered.");
                    listener.notifySafeLoad();
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            log.debug(e.getMessage(), e);
            e.printStackTrace();
        }

    }

    private boolean canNotifyEvent(byte event) {
        return lastNotifiedEvent != event || currentTime - lastNotifiedTimeTime > SAME_EVENT_NOTIFICATION_DELAY;
    }

    private void checkUnderload() {
        SmartAppliance candidate = null;
        float weighedCandidate = 0;
        for (SmartAppliance a : appliances) {
            if (a.getState() == SmartAppliance.STATE_SUSPENDED) {
                float weighedPriority = weighedActivationPriority(a);
                log.debug("weighed Activation Priority " + a.getApplianceId() + " = " + weighedPriority);

                if (weighedPriority > weighedCandidate) {
                    weighedCandidate = weighedPriority;
                    candidate = a;
                }
            }
        }
        if (candidate != null) {
            candidate.setState(SmartAppliance.STATE_PENDING_ACTIVATION);
            lastNotifiedEvent = OVERLOAD_UNDER_EVENT;
            lastNotifiedTimeTime = currentTime;
            log.debug("notify underload for appliance: " + candidate.getApplianceId());
            listener.notifyUnderload(candidate);
        }
    }

    private void checkOverload() {
        SmartAppliance candidate = null;
        float weighedCandidate = 0;
        for (SmartAppliance a : appliances) {
            if (a.getState() == SmartAppliance.STATE_ACTIVE) {
                float weighedPriority = weighedSustensionPriority(a);
                log.debug("weighed Suspension Priority " + a.getApplianceId() + " = " + weighedPriority);

                if (weighedPriority > weighedCandidate) {
                    weighedCandidate = weighedPriority;
                    candidate = a;
                }
            }
        }
        if (candidate != null) {
            candidate.setState(SmartAppliance.STATE_PENDING_SUSPENSION);
            lastNotifiedEvent = OVERLOAD_OVER_EVENT;
            lastNotifiedTimeTime = currentTime;
            log.debug("notify overload for appliance: " + candidate.getApplianceId());
            listener.notifyOverload(candidate);
        }
    }

    private float weighedSustensionPriority(SmartAppliance a) {
        // weighed function = w1*time + w2*power + w3*priority
        float weighedPower = POWER_USAGE_WEIGHT * a.getIstantaneousPower();
        float weighedTime = ELAPSED_TIME_SWITCH_WEIGHT
                * Math.min(currentTime - a.getLastStateChange(), ELAPSED_TIME_WINDOW);
        float weighedPriority = SUSPENSION_PRIORITY_WEIGHT / (1 + a.getPriority());
        log.debug(String.format("weighed suspension: Pow[%.2f] Time[%.2f] Pry[%.2f]", weighedPower, weighedTime,
                weighedPriority));
        return weighedPower + weighedTime + weighedPriority;
    }

    private float weighedActivationPriority(SmartAppliance a) {
        // weighed function = w1*time + w2*power + w3*priority
        float weighedPower = POWER_USAGE_WEIGHT * (upperPowerThreshold - a.getIstantaneousPower()); // a lower consumption is preferred
        float weighedTime = ELAPSED_TIME_SWITCH_WEIGHT
                * Math.min(currentTime - a.getLastStateChange(), ELAPSED_TIME_WINDOW);
        float weighedPriority = REACTIVATION_PRIORITY_WEIGHT * a.getPriority();
        log.debug(String.format("weighed reactivation: Pow[%.2f] Time[%.2f] Pry[%.2f]", weighedPower, weighedTime,
                weighedPriority));
        return weighedPower + weighedTime + weighedPriority;
    }

    public float computeTotalPowerUsage() {
        float p = totalPowerUsage;
        if (p == 0) {
            for (int i = appliances.size(); --i >= 0; p += appliances.get(i).getIstantaneousPower())
                ;
        }
        return p;
    }
}