|
@@ -1,11 +1,395 @@
|
|
|
package classes.holonControlUnit;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import com.google.gson.Gson;
|
|
|
+
|
|
|
+import classes.holonControlUnit.OptimizationManager.OptimizationScheme;
|
|
|
+import classes.holonControlUnit.messages.MergeMsg;
|
|
|
+import classes.holonControlUnit.messages.Message;
|
|
|
+import classes.holonControlUnit.messages.OrderMsg;
|
|
|
+import classes.holonControlUnit.messages.StateMsg;
|
|
|
+
|
|
|
public class TargetStateAssembler {
|
|
|
|
|
|
+ private HolonControlUnit hcu;
|
|
|
private float desiredPowerUsage = 100f;
|
|
|
+ private OrderMsg order;
|
|
|
+ private HashMap<String, OrderMsg> ordersForSubholon;
|
|
|
+
|
|
|
+ public TargetStateAssembler(HolonControlUnit hcu) {
|
|
|
+ this.order = null;
|
|
|
+ this.hcu = hcu;
|
|
|
+ this.ordersForSubholon = new HashMap<String, OrderMsg>();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void assembleTargetState(int timeStep) {
|
|
|
+ if(this.order == null || this.order.getTimeStep() != timeStep-1) {
|
|
|
+ this.desiredPowerUsage = 0f;
|
|
|
+ } else {
|
|
|
+ this.desiredPowerUsage = this.order.getDesiredPowerUsage();
|
|
|
+ }
|
|
|
+ this.ordersForSubholon.clear();
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" opt scheme: "+this.hcu.getOptimizer().getOptimizationScheme());
|
|
|
+
|
|
|
+ //balance the sub holons
|
|
|
+ switch(this.hcu.getOptimizer().getOptimizationScheme()) {
|
|
|
+ case COMFORT:
|
|
|
+ assembleTargetStateComfort(timeStep);
|
|
|
+ break;
|
|
|
+ case MIN_COST:
|
|
|
+ break;
|
|
|
+ case ENERGY_CONSUMPTION:
|
|
|
+ break;
|
|
|
+ case STABILITY:
|
|
|
+ assembleTargetStateStability(timeStep);
|
|
|
+ break;
|
|
|
+ case RECOVERY:
|
|
|
+ assembleTargetStateRecovery(timeStep);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ System.err.println("Unknown optimization scheme in "+this.hcu.getHolon().getUniqueID());
|
|
|
+ }
|
|
|
+ this.hcu.getHierarchyController().propagateMergeReqToParent(timeStep-1);
|
|
|
+ //send order to subholons
|
|
|
+ Gson gson = this.hcu.getCommunicator().getGson();
|
|
|
+ for(String s : this.ordersForSubholon.keySet()) {
|
|
|
+ if(this.hcu.getHierarchyController().getSubHolons().contains(s))
|
|
|
+ this.hcu.getCommunicator().sendMsg(s, Message.Type.ORDER, gson.toJson(this.ordersForSubholon.get(s)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void assembleTargetStateComfort(int timeStep) {
|
|
|
+ //overall power usage inside this holarchy
|
|
|
+ float powerUsage = this.hcu.getStateEstimator().getPowerUsage();
|
|
|
+ ArrayList<Float> predictedPowerUsage = this.hcu.getStateEstimator().getPredictedPowerUsage();
|
|
|
+ if(predictedPowerUsage.size() < 1) {
|
|
|
+ //no more iterations inside simulator
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //check if this holon can run independent from parent without risking stability
|
|
|
+ if(this.hcu.matchPowerRange(powerUsage, 0f, predictedPowerUsage, this.hcu.getOptimizer().getCurrentPowerThreshold())
|
|
|
+ && this.hcu.getHolon().getParent().canRunIndependent(this.hcu.getHolon()) && this.hcu.getHolon().getLayer() > 1) {
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" can run independent");
|
|
|
+ this.hcu.getHierarchyController().splitSuperHolon(timeStep);
|
|
|
+ this.desiredPowerUsage = 0f;
|
|
|
+ }
|
|
|
+
|
|
|
+ //powerUsage already matches desired value, everything as before
|
|
|
+ if(this.hcu.matchPowerRange(powerUsage, this.desiredPowerUsage, predictedPowerUsage, this.hcu.getOptimizer().getCurrentPowerThreshold())) {
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" power usage as desired "+powerUsage);
|
|
|
+ HashMap<String, StateMsg> childStates = this.hcu.getStateEstimator().getChildStates();
|
|
|
+ for(String s : childStates.keySet()) {
|
|
|
+ OrderMsg o = new OrderMsg(childStates.get(s).getPredictedPowerUsage().get(0), null, OptimizationScheme.STABILITY, timeStep);
|
|
|
+ this.ordersForSubholon.put(s, o);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" power usage "+powerUsage+" not as desired "+this.desiredPowerUsage);
|
|
|
+ float requiredPower = this.desiredPowerUsage - powerUsage;
|
|
|
+
|
|
|
+ //find flexibility which matches required power
|
|
|
+ float savedWithFlexibilities = gothroughFlexibilities(timeStep, requiredPower, predictedPowerUsage);
|
|
|
+ if(savedWithFlexibilities == requiredPower) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ requiredPower -= savedWithFlexibilities;
|
|
|
+
|
|
|
+ //go through merge requests from last iteration
|
|
|
+ float savedByMerging = gothroughMergeReq(timeStep, requiredPower, predictedPowerUsage, OptimizationScheme.COMFORT);
|
|
|
+ if(savedByMerging == requiredPower) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ requiredPower -= savedByMerging;
|
|
|
+
|
|
|
+ //look out for another superholon
|
|
|
+ findNewSuperHolon(timeStep, powerUsage, predictedPowerUsage);
|
|
|
+
|
|
|
+ //tell all holons to use a little more/less energy
|
|
|
+ orderSubholonsRequiredPower(timeStep, powerUsage, requiredPower, OptimizationScheme.COMFORT);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void assembleTargetStateStability(int timeStep) {
|
|
|
+ //overall power usage inside this holarchy
|
|
|
+ float powerUsage = this.hcu.getStateEstimator().getPowerUsage();
|
|
|
+ ArrayList<Float> predictedPowerUsage = this.hcu.getStateEstimator().getPredictedPowerUsage();
|
|
|
+ if(predictedPowerUsage.size() < 1) {
|
|
|
+ //no more iterations inside simulator
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //powerUsage already matches desired value, everything as before
|
|
|
+ if(this.hcu.matchPowerRange(powerUsage, this.desiredPowerUsage, predictedPowerUsage, this.hcu.getOptimizer().getCurrentPowerThreshold())) {
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" power usage as desired "+powerUsage);
|
|
|
+ HashMap<String, StateMsg> childStates = this.hcu.getStateEstimator().getChildStates();
|
|
|
+ for(String s : childStates.keySet()) {
|
|
|
+ OrderMsg o = new OrderMsg(childStates.get(s).getPredictedPowerUsage().get(0), null, OptimizationScheme.STABILITY, timeStep);
|
|
|
+ this.ordersForSubholon.put(s, o);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" power usage "+powerUsage+" not as desired "+this.desiredPowerUsage);
|
|
|
+ float requiredPower = this.desiredPowerUsage - powerUsage;
|
|
|
+
|
|
|
+ //find flexibility which matches required power
|
|
|
+ float savedWithFlexibilities = gothroughFlexibilities(timeStep, requiredPower, predictedPowerUsage);
|
|
|
+ if(savedWithFlexibilities == requiredPower) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ requiredPower -= savedWithFlexibilities;
|
|
|
+
|
|
|
+ //go through merge requests from last iteration
|
|
|
+ float savedByMerging = gothroughMergeReq(timeStep, requiredPower, predictedPowerUsage, OptimizationScheme.STABILITY);
|
|
|
+ if(savedByMerging == requiredPower) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ requiredPower -= savedByMerging;
|
|
|
+
|
|
|
+ //look out for another superholon
|
|
|
+ findNewSuperHolon(timeStep, powerUsage, predictedPowerUsage);
|
|
|
+
|
|
|
+ //tell all holons to use a little more/less energy
|
|
|
+ orderSubholonsRequiredPower(timeStep, powerUsage, requiredPower, OptimizationScheme.STABILITY);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void assembleTargetStateRecovery(int timeStep) {
|
|
|
+ //overall power usage inside this holarchy
|
|
|
+ float powerUsage = this.hcu.getStateEstimator().getPowerUsage();
|
|
|
+ ArrayList<Float> predictedPowerUsage = this.hcu.getStateEstimator().getPredictedPowerUsage();
|
|
|
+ if(predictedPowerUsage.size() < 1) {
|
|
|
+ //no more iterations inside simulator
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //powerUsage already matches desired value, everything as before
|
|
|
+ if(this.hcu.matchPowerRange(powerUsage, this.desiredPowerUsage, predictedPowerUsage, this.hcu.getOptimizer().getCurrentPowerThreshold())) {
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" power usage as desired "+powerUsage);
|
|
|
+ HashMap<String, StateMsg> childStates = this.hcu.getStateEstimator().getChildStates();
|
|
|
+ for(String s : childStates.keySet()) {
|
|
|
+ OrderMsg o = new OrderMsg(childStates.get(s).getPredictedPowerUsage().get(0), null, OptimizationScheme.STABILITY, timeStep);
|
|
|
+ this.ordersForSubholon.put(s, o);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ System.out.println(this.hcu.getHolon().getUniqueID()+" power usage "+powerUsage+" not as desired "+this.desiredPowerUsage);
|
|
|
+ float requiredPower = this.desiredPowerUsage - powerUsage;
|
|
|
+
|
|
|
+ //find flexibility which matches required power
|
|
|
+ float savedWithFlexibilities = gothroughFlexibilities(timeStep, requiredPower, predictedPowerUsage);
|
|
|
+ if(savedWithFlexibilities == requiredPower) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ requiredPower -= savedWithFlexibilities;
|
|
|
|
|
|
+ //go through merge requests from last iteration
|
|
|
+ float savedByMerging = gothroughMergeReq(timeStep, requiredPower, predictedPowerUsage, OptimizationScheme.RECOVERY);
|
|
|
+ if(savedByMerging == requiredPower) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ requiredPower -= savedByMerging;
|
|
|
+
|
|
|
+ float p = this.hcu.getHolon().isPhysical ? this.hcu.getHolon().getHolonObject().getEnergyAtTimeStepFlex(timeStep) : 0f;
|
|
|
+ requiredPower = this.desiredPowerUsage - p;
|
|
|
+
|
|
|
+ //split dysfunctional parts
|
|
|
+ HashMap<String, StateMsg> childStates = this.hcu.getStateEstimator().getChildStates();
|
|
|
+ List<List<String>> childPerm = getAllPermutations(List.copyOf(childStates.keySet()));
|
|
|
+ HashMap<Float, List<String>> potChild = new HashMap<Float, List<String>>();
|
|
|
+ for(List<String> l : childPerm) {
|
|
|
+ //check how much power this subset of child would consume
|
|
|
+ float pu = p;
|
|
|
+ for(String s : l) {
|
|
|
+ if(Math.abs(pu) > Math.abs(requiredPower))
|
|
|
+ break;
|
|
|
+ pu += childStates.get(s).getPowerUsage();
|
|
|
+ }
|
|
|
+ if(Math.abs(pu) <= Math.abs(requiredPower)) {
|
|
|
+ if(potChild.containsKey(pu) && potChild.get(pu).size() >= l.size())
|
|
|
+ continue;
|
|
|
+ potChild.put(pu, l);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //find subset of children with most fitting powerUsage
|
|
|
+ float max = 0f;
|
|
|
+ for(float f : potChild.keySet()) {
|
|
|
+ if(Math.abs(f) > Math.abs(max)) {
|
|
|
+ max = f;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //split all other subholons
|
|
|
+ List<String> list = new ArrayList<String>();
|
|
|
+ if(potChild.containsKey(max)) {
|
|
|
+ list = potChild.get(max);
|
|
|
+ }
|
|
|
+ List<String> subs = List.copyOf(this.hcu.getHierarchyController().getSubHolons());
|
|
|
+ for(String s : subs) {
|
|
|
+ if(!list.contains(s)) {
|
|
|
+ this.hcu.getHierarchyController().splitSubHolon(s, timeStep);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ requiredPower -= max;
|
|
|
+ if(requiredPower == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //Split from superholon?
|
|
|
+ if(Math.abs(requiredPower) > Math.abs(p) && this.hcu.getHolon().getLayer() > 1) {
|
|
|
+ this.hcu.getHierarchyController().splitSuperHolon(timeStep);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //look out for another superholon
|
|
|
+ findNewSuperHolon(timeStep, powerUsage, predictedPowerUsage);
|
|
|
+
|
|
|
+ //tell all holons to use a little more/less energy
|
|
|
+ orderSubholonsRequiredPower(timeStep, powerUsage, requiredPower, OptimizationScheme.RECOVERY);
|
|
|
+ }
|
|
|
+
|
|
|
+ private float gothroughFlexibilities(int timeStep, float requiredPower, ArrayList<Float> predictedPowerUsage) {
|
|
|
+ return this.hcu.getFlexMan().applyFlexibilities(requiredPower, predictedPowerUsage, timeStep);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * go through all previously received merge requests from other holons
|
|
|
+ * if there is a fitting one merge
|
|
|
+ * @param timeStep
|
|
|
+ * @param requiredPower
|
|
|
+ * @return saved power
|
|
|
+ */
|
|
|
+ private float gothroughMergeReq(int timeStep, float requiredPower, ArrayList<Float> predictedPowerUsage,
|
|
|
+ OptimizationScheme optScheme) {
|
|
|
+ List<MergeMsg> requests = this.hcu.getHierarchyController().getMergeRequestsForTimeStep(timeStep-1);
|
|
|
+ if(requests == null)
|
|
|
+ return 0f;
|
|
|
+ //store holon which would help decrease |requiredPower|
|
|
|
+ ArrayList<MergeMsg> poten = new ArrayList<MergeMsg>();
|
|
|
+ //find a perfect match or holon which would help decrease |requiredPower|
|
|
|
+ for(MergeMsg req : requests) {
|
|
|
+ float r = req.getPredictedPowerUsage().get(0); //since merge req is from timeStep-1
|
|
|
+ String requester = req.getRequester();
|
|
|
+ float newThroughput = req.getNetThroughput() + this.hcu.getStateEstimator().getNetThroughput();
|
|
|
+
|
|
|
+ if(!this.hcu.getHierarchyController().canMerge(requester, newThroughput)) {
|
|
|
+ //cannot merge with the requester
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(this.hcu.matchPowerRange(r, requiredPower, predictedPowerUsage, this.hcu.getOptimizer().getCurrentPowerThreshold())) {
|
|
|
+ //perfect match
|
|
|
+ this.hcu.getHierarchyController().sendMergeAck(timeStep, req);
|
|
|
+ OrderMsg o = new OrderMsg(r, null, optScheme, timeStep);
|
|
|
+ this.ordersForSubholon.put(requester, o);
|
|
|
+ requests.remove(req);
|
|
|
+ return r;
|
|
|
+ } else if( ((r < 0 && requiredPower < 0) || (r > 0 && requiredPower > 0))
|
|
|
+ && this.hcu.decreasesPowerUsage(requiredPower, r, req.getPredictedPowerUsage())) {
|
|
|
+ //accepting this request would help this holon
|
|
|
+ poten.add(req);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //go through all poten candidates and find most suiting subset
|
|
|
+ //all candidates have either power < 0 or > 0
|
|
|
+ if(poten.size() < 1)
|
|
|
+ return 0f;
|
|
|
+ List<List<MergeMsg>> potPermut = getAllPermutations(poten);
|
|
|
+ HashMap<Float, List<MergeMsg>> potSavings = new HashMap<Float, List<MergeMsg>>();
|
|
|
+ for(List<MergeMsg> l : potPermut) {
|
|
|
+ //check how much power this subset of merges would save
|
|
|
+ float saving = 0f;
|
|
|
+ Float[] savings = new Float[predictedPowerUsage.size()];
|
|
|
+ for(int i=0; i<savings.length; i++)
|
|
|
+ savings[i] = 0f;
|
|
|
+ for(MergeMsg m : l) {
|
|
|
+ if(!this.hcu.decreasesPowerUsage(requiredPower, saving, List.of(savings)))
|
|
|
+ break;
|
|
|
+ saving += m.getPredictedPowerUsage().get(0); //since merge req is from timeStep-1
|
|
|
+ for(int i=0; i<savings.length; i++) {
|
|
|
+ savings[i] = savings[i] + m.getPredictedPowerUsage().get(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(this.hcu.decreasesPowerUsage(requiredPower, saving, List.of(savings))) {
|
|
|
+ if(potSavings.containsKey(saving) && potSavings.get(saving).size() >= l.size())
|
|
|
+ continue;
|
|
|
+ potSavings.put(saving, l);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(potSavings.size() < 1) {
|
|
|
+ return 0f;
|
|
|
+ }
|
|
|
+ float max = 0f;
|
|
|
+ for(float f : potSavings.keySet()) {
|
|
|
+ if(Math.abs(f) > Math.abs(max)) {
|
|
|
+ max = f;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //merge with all holons that are part of the subset with the max saving
|
|
|
+ if(potSavings.containsKey(max)) {
|
|
|
+ for(MergeMsg req : potSavings.get(max)) {
|
|
|
+ float r = req.getPower();
|
|
|
+ String requester = req.getRequester();
|
|
|
+ this.hcu.getHierarchyController().sendMergeAck(timeStep, req);
|
|
|
+ OrderMsg o = new OrderMsg(r, null, optScheme, timeStep);
|
|
|
+ this.ordersForSubholon.put(requester, o);
|
|
|
+ }
|
|
|
+ requests.removeAll(potSavings.get(max));
|
|
|
+ }
|
|
|
+ return max;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void findNewSuperHolon(int timeStep, float powerUsage, ArrayList<Float> predictedPowerUsage) {
|
|
|
+ for(String physicalNeighbor : this.hcu.getHierarchyController().getPhysicalNeighborsFromOtherHolarchy()) {
|
|
|
+ this.hcu.getHierarchyController().sendMergeReq(powerUsage, predictedPowerUsage, timeStep, physicalNeighbor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * compute a new target value for all subholons
|
|
|
+ * "fairness": all subholons get same target value
|
|
|
+ * @param timeStep
|
|
|
+ * @param powerUsage
|
|
|
+ * @param requiredPower
|
|
|
+ * @param optScheme
|
|
|
+ */
|
|
|
+ private void orderSubholonsRequiredPower(int timeStep, float powerUsage, float requiredPower, OptimizationScheme optScheme) {
|
|
|
+ float p = this.hcu.getHolon().isPhysical ? this.hcu.getHolon().getHolonObject().getEnergyAtTimeStep(timeStep) : 0f;
|
|
|
+ requiredPower = this.desiredPowerUsage - p;
|
|
|
+ HashMap<String, StateMsg> childStates = this.hcu.getStateEstimator().getChildStates();
|
|
|
+ float opt = requiredPower/(childStates.size() != 0 ? childStates.size() : 1);
|
|
|
+ for(String s : this.hcu.getHierarchyController().getSubHolons()) {
|
|
|
+ //order for subholon
|
|
|
+ if(this.ordersForSubholon.containsKey(s))
|
|
|
+ continue;
|
|
|
+ OrderMsg o = new OrderMsg(opt, null, optScheme, timeStep);
|
|
|
+ this.ordersForSubholon.put(s, o);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private <T> List<List<T>> getAllPermutations(List<T> set){
|
|
|
+ List<List<T>> out = new ArrayList<List<T>>();
|
|
|
+
|
|
|
+ for(int i=0; i<set.size(); i++) {
|
|
|
+ for(int j=i; j<set.size(); j++) {
|
|
|
+ List<T> l = new ArrayList<T>();
|
|
|
+ l.addAll(set.subList(i, j+1));
|
|
|
+ out.add(l);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
public float getDesiredPowerUsage() {
|
|
|
- return desiredPowerUsage;
|
|
|
+ return this.desiredPowerUsage;
|
|
|
}
|
|
|
|
|
|
+ public void setOrder(OrderMsg order, String sender) {
|
|
|
+ if(sender.equals(this.hcu.getHierarchyController().getSuperHolon())) {
|
|
|
+ this.order = order;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|