package classes; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.UUID; import classes.holonControlUnit.HolonControlUnit; import classes.holonControlUnit.OptimizationManager; import classes.holonControlUnit.StateEstimator.StateIndicator; import ui.controller.Control; import ui.model.DecoratedNetwork; import ui.model.IntermediateCableWithState; import ui.model.MinimumModel; import ui.model.Model; public class Holon { public String name = new String(); private Holon parent = null; public ArrayList childHolons = new ArrayList(); private List elements = new ArrayList(); //HolonObject is the lowest representation of a holon. private final HolonObject holonObject; public final boolean isPhysical; public HolonControlUnit holonControlUnit; private String uniqueID; public Model model; private MinimumModel minModel; /** stores whether the part between this holon and the child is occupied by the superholon */ private HashMap> pathsToChildren; private int mergeCounter = 0, splitCounter = 0; private int[] stateCounter = {0, 0, 0}; public Holon(String name, Model model) { this.name = name; this.holonObject = null; isPhysical = false; this.uniqueID = UUID.randomUUID().toString(); this.model = model; this.holonControlUnit = new HolonControlUnit(this); this.minModel = new MinimumModel(new ArrayList(), new ArrayList()); this.pathsToChildren = new HashMap>(); } public Holon(HolonObject object, Model model) { holonObject = object; object.holon = this; name = object.getName(); elements.addAll(object.getElements()); for(HolonElement ele : elements) { ele.holon = this; } isPhysical = true; this.uniqueID = UUID.randomUUID().toString(); this.model = model; this.holonControlUnit = new HolonControlUnit(this); ArrayList list = new ArrayList(); list.add(object); this.minModel = new MinimumModel(list, new ArrayList()); this.pathsToChildren = new HashMap>(); } public void addElement(HolonElement element) { element.holon = this; elements.add(element); } public void removeElement(HolonElement element) { element.holon = null; elements.remove(element); } public int elementsCount() { return elements.size(); } public void addChild(Holon child) { if(child.equals(this)) { System.err.println(this.uniqueID+" wants to merge itself"); } if(!this.childHolons.contains(child)) { child.setParent(this); this.childHolons.add(child); } if(this.parent != null) { this.holonControlUnit.addSubHolon(child); } } public void addChildHolon(Holon child, int index) { if(!this.childHolons.contains(child)) { child.setParent(this); childHolons.add(index, child); } if(this.parent != null) { this.holonControlUnit.addSubHolon(child); } } public void removeChildHolon(Holon child) { child.setParent(null); childHolons.remove(child); if(this.parent == null) return; this.holonControlUnit.getHierarchyController().removeSubHolon(child.getUniqueID()); this.pathsToChildren.remove(child); revalidateMinModel(); } public void removeFromParent() { if(parent != null) { parent.removeChildHolon(this); } this.setParent(model.getStateHolon()); } public int getChildCount() { return childHolons.size(); } public Holon getParent() { return parent; } public void setParent(Holon parent) { this.parent = parent; if(parent != null) { this.holonControlUnit.getHierarchyController().setSuperHolon(parent.getUniqueID()); } } public HolonObject getHolonObject() { return holonObject; } public List getChildView(){ return Collections.unmodifiableList(childHolons); } public List getElementView(){ return Collections.unmodifiableList(elements); } @Override public String toString() { return name; } public void reassignAllChildren(Holon other) { for(Holon child: this.childHolons) { other.merge(child, true); } childHolons.clear(); } public void removeAllRefrences() { this.setParent(null); this.childHolons.clear(); this.elements.clear(); } public int getLayer() { if(this.parent == null) return 0; if(this.parent.equals(this)) { return 0; } return parent != null ? parent.getLayer() + 1 : 0; } public Holon cloneWithoutParent() { Holon cloned = new Holon(this.name, this.model); model.getHolonsByID().put(cloned.getUniqueID(), cloned); cloned.childHolons = this.childHolons; cloned.elements = this.elements; return cloned; } public boolean checkHolonArePhysicalConnected(Holon other, Control control) { HashMap table = control.getSimManager().getActualDecorState().getHolonObjectNetworkTable(); HolonObject a = tryGetAPhysicalHolon(); HolonObject b = other.tryGetAPhysicalHolon(); boolean aHolonIsPureAbstract = a == null || b == null; return aHolonIsPureAbstract || table.get(a) == table.get(b); } private HolonObject tryGetAPhysicalHolon() { if(holonObject != null) { return holonObject; } for(Holon holon : childHolons) { HolonObject object = holon.tryGetAPhysicalHolon(); if(object != null) { return object; } } return null; } @Deprecated public void checkRepairHolarchy(HashMap table, Holon stateHolon) { if(childHolons.isEmpty()) { return; } //To establish the invariant that all child holons are repaired for(Holon other : childHolons) { other.checkRepairHolarchy(table, stateHolon); } //Repair this Holon HolonObject first = tryGetAPhysicalHolon(); if(first == null) { return; } List removeList = new ArrayList(); for(Holon other : childHolons) { HolonObject otherHolonObject = other.tryGetAPhysicalHolon(); boolean isPureAbstract = otherHolonObject == null; if(isPureAbstract) { continue; } boolean isPhysicalConnected = model.checkHolonObjectsAreConnected(this, other, 0f); if(!isPhysicalConnected) { removeList.add(other); } } //Remove holons for(Holon holon : removeList) { holon.split(null, true); } } public void addNewVirtualNeighbor(String virtualNeighbor) { if(!this.uniqueID.equals(virtualNeighbor)) { this.holonControlUnit.addNewVirtualNeighbor(virtualNeighbor); } } public String getUniqueID() { return uniqueID; } public ArrayList getAllHolonObjects() { ArrayList list = new ArrayList(); for(Holon h : this.childHolons) { list.addAll(h.getAllHolonObjects()); } if(this.holonObject != null) { list.add(this.holonObject); } return list; } public ArrayList getAllObjects(){ ArrayList list = new ArrayList(); list.addAll(this.minModel.getHolonObjectList()); list.addAll(this.minModel.getNodeList()); list.addAll(this.minModel.getSwitchList()); list.addAll(this.minModel.getUppderNodeList()); return list; } public MinimumModel getMinimumModel() { return this.minModel; } public HashMap> getPathsToChildren() { return pathsToChildren; } /** * merge this holon as a child with the holon specified by the id * @param id */ public void merge(String id, boolean allowOccupiedPath) { if(id.equals("State Holon")) { this.split(this.model.getStateHolon(), allowOccupiedPath); return; } Holon h = this.model.getHolonsByID().get(id); if(h != null) { h.split(this, allowOccupiedPath); } } /** * merge this holon with the specified child holon * @param child */ public void merge(Holon child, boolean allowOccupiedPath) { if(!this.model.getHolonsByID().containsKey(child.getUniqueID())) { System.err.println("could not find: "+child.getHolonObject()); return; } if(this.childHolons.contains(child)) { return; } if(this.parent == null) { this.addChild(child); return; } this.mergeCounter++; // System.out.println(this.uniqueID+" merge with "+child.getUniqueID()); HashMap> paths = this.model.getShortestPathToHolarchy(this.minModel, child.getMinimumModel()); HashMap> free_paths = new HashMap>(); HashMap> occ_paths = new HashMap>(); HashMap, Holon> occ_by = new HashMap, Holon>(); for(Float f : paths.keySet()) { ArrayList path = paths.get(f); Holon holder = isPathOccupied(path); if(holder == null || holder.getParent() == null || holder.equals(this) || holder.equals(this.parent)) { //path between this holon and child holon is free free_paths.put(f, path); } else { //path between this holon and child holon is already occupied occ_paths.put(f, path); occ_by.put(path, holder); } } // System.out.println("free: "+free_paths+"\noccupied: "+occ_paths); if(free_paths.size() > 0) { //there is a free path, take it and add child directly to sub holons ArrayList path = free_paths.get(getShortestPath(free_paths)); Holon holder = this.containsPath(path, this); if(holder != null && !holder.equals(this)) { //the path already used by a child holon holder.merge(child, true); return; } this.addChild(child); this.addChildToMinModel(child, path); this.pathsToChildren.put(child, path); if(this.parent != null && this.parent.getParent() != null) { this.parent.recalculateMinModel(); } return; } if(occ_paths.size() < 1) { System.err.println("something went wrong when looking for shortest path while merging "+this.uniqueID+" and "+child.getUniqueID()); } ArrayList> shortestPaths = sortPaths(occ_paths); //take the shortest path occupied by another holon ArrayList path = shortestPaths.get(0); Holon holder = occ_by.get(path); if(holder != null && holder.getParent() != null && allowOccupiedPath) { // System.out.println("occupied by "+holder.getUniqueID()); this.split(holder, true); this.addChild(child); this.addChildToMinModel(child, path); this.pathsToChildren.put(child, path); if(this.parent != null && this.parent.getParent() != null) { this.parent.recalculateMinModel(); } } } /** * checks whether a path is occupied * if true returns the holon in which the path is located * @param path * @return */ private Holon isPathOccupied(ArrayList path) { Holon h = this.parent; Holon r = this; Holon holder = null; while(h != null) { holder = h.containsPath(path, r); if(holder != null) { //found a holon which contains the path return holder; } r = h; h = h.parent; } return null; } private ArrayList> sortPaths(HashMap> paths) { ArrayList> shortestPaths = new ArrayList>(); while(paths.size() > 0) { shortestPaths.add(paths.remove(getShortestPath(paths))); } return shortestPaths; } private Float getShortestPath(HashMap> paths){ float min = Float.MAX_VALUE; for(Float f : paths.keySet()) { if(f < min) { min = f; } } return min; } /** * removes this holon from current superholon and merge with new superholon */ public void split(Holon newParent, boolean allowOccupiedPath) { //ensure that this holarchy can run independent(path is not occupied by superholon) from the superholon if(this.parent == null || !this.parent.canRunIndependent(this)) { return; } if(this.parent.parent != null) { //we actually split from a super holon and not from the state holon this.splitCounter++; } if(newParent == null) { newParent = this.model.getStateHolon(); allowOccupiedPath = true; } else { //next step is merge with new parent this.mergeCounter++; } this.parent.incSplitCounter(); this.parent.removeChildHolon(this); this.holonControlUnit.getHierarchyController().resetVirtualNeighbors(); newParent.merge(this, allowOccupiedPath); } public void splitById(String id) { Holon child = this.model.getHolonsByID().get(id); if(child.parent == this) child.split(null, false); } /** * recalculates the min model after a holon was added to a subholon * does NOT recalculate the shortest paths between its subholons */ public void recalculateMinModel() { ArrayList objects = new ArrayList(); ArrayList edges = new ArrayList(); for(Holon child : this.childHolons) { MinimumModel cmm = child.getMinimumModel(); objects.addAll(cmm.getHolonObjectList()); objects.addAll(cmm.getNodeList()); objects.addAll(cmm.getSwitchList()); for(IntermediateCableWithState icws : cmm.getEdgeList()) { edges.add(icws.getModel()); } } //go through the edge list and add all edges connecting the subholons for(IntermediateCableWithState icws : this.minModel.getEdgeList()) { if(!edges.contains(icws.getModel())) { edges.add(icws.getModel()); } } this.minModel = new MinimumModel(objects, edges); //notify superholon to recalculate its minModel if(this.parent != null && this.parent.getParent() != null && this.parent != this) { this.parent.recalculateMinModel(); } } /** * adds a child and its min model (incl. all its subholons) to this min model * @param child */ private void addChildToMinModel(Holon child, ArrayList path) { if(this.minModel.getHolonObjectList().contains(child.getHolonObject()) || this.parent == null) return; ArrayList edgeList = new ArrayList(); //add all holon objects that are part of this holarchy ArrayList holarchy = getAllHolonObjects(); holarchy.addAll(child.getAllHolonObjects()); edgeList.addAll(path); for(IntermediateCableWithState icws : this.minModel.getEdgeList()) { edgeList.add(icws.getModel()); } for(IntermediateCableWithState icws : child.minModel.getEdgeList()) { edgeList.add(icws.getModel()); } //aggregate all objects ArrayList objects = new ArrayList(); for(Edge e : edgeList) { AbstractCanvasObject a = e.getA(); AbstractCanvasObject b = e.getB(); if(!objects.contains(a)) { objects.add(a); } if(!objects.contains(b)) { objects.add(b); } } this.minModel = new MinimumModel(objects, edgeList); } /** * revalidate this min model after a subholon was removed */ public void revalidateMinModel() { if(this.parent == null) return; ArrayList list = new ArrayList(); list.add(this.holonObject); this.minModel = new MinimumModel(list, new ArrayList()); for(int i=0; i< this.childHolons.size(); i++) { Holon child = this.childHolons.remove(0); if(this.model.checkHolonObjectsAreConnected(this, child, this.holonControlUnit.getStateEstimator().getNetThroughput())) { this.merge(child, true); } } if(this.parent != null) { this.parent.revalidateMinModel(); } } public void revalidateMinModelAfterLoad() { if(this.childHolons.size() == 0) { this.revalidateMinModel(); } else { List list = List.copyOf(this.childHolons); for(Holon child : list) { child.revalidateMinModelAfterLoad(); } } } /** * returns the holon which uses the specified path if it is included in min model, otherwise null */ public Holon containsPath(ArrayList path, Holon requester) { if(this.minModel.containsPath(path) || this.parent == null) { for(Holon child : this.childHolons) { if(child.equals(requester)) continue; Holon h = child.containsPath(path, this); if(h != null) { return h; } } return this; } for(Edge e : path) { AbstractCanvasObject a = e.getA(); AbstractCanvasObject b = e.getB(); if(a instanceof HolonObject && this.childHolons.contains(((HolonObject) a).holon)) return ((HolonObject) a).holon; if(b instanceof HolonObject && this.childHolons.contains(((HolonObject) b).holon)) return ((HolonObject) b).holon; if(this.getAllObjects().contains(a) || this.getAllObjects().contains(b)) return this; } return null; } /** * checks whether the requester can run without this holon (none of the paths inside the requesters model is occupied by this model) * @param requester * @return */ public boolean canRunIndependent(Holon requester) { if(this.parent == null || this.parent.getParent() == null) return true; ArrayList objects = new ArrayList(); objects.addAll(requester.getMinimumModel().getHolonObjectList()); objects.addAll(requester.getMinimumModel().getNodeList()); objects.addAll(requester.getMinimumModel().getSwitchList()); for(Holon child : this.childHolons) { if(child.equals(requester)) continue; ArrayList path = this.pathsToChildren.get(child); ArrayList pathObjects = new ArrayList(); for(Edge e : path) { AbstractCanvasObject a = e.getA(); AbstractCanvasObject b = e.getB(); if(!pathObjects.contains(a)) pathObjects.add(a); if(!pathObjects.contains(b)) pathObjects.add(b); } for(AbstractCanvasObject aco : pathObjects) { if(objects.contains(aco)) return false; } } return true; } public boolean isASuperiorHolon(String holon) { return this.parent != null ? (this.parent.getUniqueID().equals(holon) || this.parent.isASuperiorHolon(holon)) : false; } public int getMergeCounter() { return mergeCounter; } public int getSplitCounter() { return splitCounter; } public void incSplitCounter() { this.splitCounter++; } public int[] getStateCounter() { return this.stateCounter; } public void countState(float dev) { if(Math.abs(dev) <= 1-OptimizationManager.POWER_THRESHOLD_COMFORT) { this.stateCounter[0]++; } else if(Math.abs(dev) <= 1-OptimizationManager.POWER_THRESHOLD_STABILITY) { this.stateCounter[1]++; } else { this.stateCounter[2]++; } // switch (this.holonControlUnit.getStateEstimator().getStateIndicator()) { // case DESIRED: // this.stateCounter[0]++; // break; // case ENDANGERED: // this.stateCounter[1]++; // break; // case DYSFUNCTIONAL: // this.stateCounter[2]++; // break; // default: // throw new IllegalArgumentException("Unexpected value: " + this.holonControlUnit.getStateEstimator().getStateIndicator()); // } } }