package ui.view; import classes.*; import ui.controller.Control; import ui.model.Model; import javax.swing.*; import java.awt.*; import java.awt.geom.GeneralPath; import java.util.ArrayList; public class StatisticGraph extends JPanel { /** * */ private static final long serialVersionUID = 1L; // Maximum y Value private float maximum = 0; // is the Simulation running? // private boolean isSimRunning; private GeneralPath path = new GeneralPath(); // model and controller private Model model; private Control controller; // Graphics2D private Graphics2D g2; // Data private ArrayList dataSets = new ArrayList<>(); private boolean showGrid = true; private float DISTANCE_IN_GRAPH = 1.0f; // private JTable table = new JTable(); // private ArrayList gridLines = new ArrayList<>(); /** * Constructor. * * @param model the Model * @param control the Controller */ StatisticGraph(final Model model, Control control) { this.controller = control; this.model = model; this.setBackground(Color.WHITE); } /** * Paints all Components on the Canvas. * * @param g Graphics */ public void paintComponent(Graphics g) { super.paintComponent(g); // Graphics2D init g2 = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHints(rh); // Paint the Grid if it should currently be shown if (showGrid) { g2.setStroke(new BasicStroke(0)); g2.setColor(Color.BLACK); for (int i = 0; i <= this.getWidth(); i += 10) { g2.drawLine(i, 0, i, this.getHeight()); } for (int i = 0; i <= this.getHeight(); i += 5) { g2.drawLine(0, i, this.getWidth(), i); } } g2.setStroke(new BasicStroke(3)); // Calculate the Maximum // calcMaximum(); // Calculate values for each set and add them // addValues(); // Create Paths and draw them for (TrackedDataSet set : dataSets) { path.reset(); switch (set.getProperty()) { case TrackedDataSet.CONSUMPTION: case TrackedDataSet.PRODUCTION: case TrackedDataSet.ACTIVATED_ELEMENTS: case TrackedDataSet.TOTAL_PRODUCTION: case TrackedDataSet.TOTAL_CONSUMPTION: case TrackedDataSet.WASTED_ENERGY: case TrackedDataSet.AMOUNT_HOLONS: case TrackedDataSet.GROUP_CONSUMPTION: case TrackedDataSet.GROUP_PRODUCTION: case TrackedDataSet.AMOUNT_CLOSED_SWITCHES: case TrackedDataSet.AVG_AMOUNT_OBJECTS_IN_HOLONS: case TrackedDataSet.AVG_AMOUNT_ELEMENTS_IN_HOLONS: case TrackedDataSet.AVG_AMOUNT_PRODUCERS_IN_HOLONS: case TrackedDataSet.AVG_CONSUMED_ENERGY_IN_HOLONS: case TrackedDataSet.AVG_WASTED_ENERGY_IN_HOLONS: case TrackedDataSet.AMOUNT_BROKEN_EDGES: case TrackedDataSet.AVG_AMOUNT_CLOSED_SWITCHES_IN_HOLONS: case TrackedDataSet.AVG_AMOUNT_ACTIVE_ELEMENTS_IN_HOLONS: case TrackedDataSet.AVG_AMOUNT_INACTIVE_ELEMENTS_IN_HOLONS: case TrackedDataSet.AVG_PRODUCED_ENERGY_IN_HOLONS: createPathFloats(set); break; case TrackedDataSet.ON_OFF: createPathBooleans(set); break; case TrackedDataSet.PERCENT_SUPPLIED: case TrackedDataSet.PERCENT_NOT_SUPPLIED: case TrackedDataSet.PERCENT_PARTIAL_SUPPLIED: case TrackedDataSet.RATIO_PRODUCERS_CONSUMERS: createPathPercent(set); break; default: break; } g2.setColor(set.getColor()); g2.draw(path); } } /** * Add an Object to the Graph if the maximum has not reached yet. * * @param set the Object to add */ public void addObject(TrackedDataSet set) { dataSets.add(set); } /** * Removes an Object from the Graph. * * @param id the id of the Object to remove */ public void removeObject(int id) { dataSets.remove(id); } /** * converts the number to fit the canvas. * * @param d the number to convert * @return the converted number */ private double convertToCanvasY(float d) { return Math.abs((this.getHeight() - (d * (this.getHeight() / maximum)))); } /** * Does take into account which timestep is watched, calculates the max * values. * * @return the currentEnergy */ public float getEnergyAtCurrentTimeStep(HolonObject obj) { float temp = 0; for (HolonElement e : obj.getElements()) { if (e.isActive()) { temp = temp + e.getEnergyAtTimeStep(model.getCurIteration()); } } return temp; } /** * Calculate the Max Value of the Graph */ void calcMaximum() { maximum = 0; for (TrackedDataSet set : dataSets) { float val = 0; switch (set.getProperty()) { case TrackedDataSet.CONSUMPTION: for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) { if (h.getEnergyPerElement() < 0) { val += (h.getEnergyPerElement() ) * h.getAmount(); } } val *= -1; break; case TrackedDataSet.PRODUCTION: for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) { if (h.getEnergyPerElement() > 0) { val += (h.getEnergyPerElement() ) * h.getAmount(); } } break; case TrackedDataSet.ACTIVATED_ELEMENTS: for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) { val += h.getAmount(); } break; case TrackedDataSet.ON_OFF: val = 1; break; case TrackedDataSet.TOTAL_PRODUCTION: val = getMaxTotalProduction(model.getObjectsOnCanvas()); break; case TrackedDataSet.TOTAL_CONSUMPTION: val = getMaxTotalConsumption(model.getObjectsOnCanvas()); val *= -1; break; case TrackedDataSet.WASTED_ENERGY: val = getMaxWastedEnergy(model.getObjectsOnCanvas()); break; case TrackedDataSet.PERCENT_SUPPLIED: case TrackedDataSet.PERCENT_NOT_SUPPLIED: case TrackedDataSet.PERCENT_PARTIAL_SUPPLIED: val = 1; break; case TrackedDataSet.GROUP_PRODUCTION: val = getMaxTotalProduction(((GroupNode) set.getCpsObject()).getNodes()); break; case TrackedDataSet.GROUP_CONSUMPTION: val = getMaxTotalConsumption(((GroupNode) set.getCpsObject()).getNodes()); val *= -1; break; case TrackedDataSet.AMOUNT_HOLONS: val = controller.getSimManager().getSubNets().size(); for (int i = 0; i < model.getCurIteration(); i++) { if (val < set.getValues()[i]) { val = set.getValues()[i]; } } break; case TrackedDataSet.AMOUNT_CLOSED_SWITCHES: val = model.getSwitches().size(); break; case TrackedDataSet.AVG_AMOUNT_OBJECTS_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { if (val < sub.getObjects().size()) { val = sub.getObjects().size(); } } break; case TrackedDataSet.AVG_AMOUNT_ELEMENTS_IN_HOLONS: case TrackedDataSet.AVG_AMOUNT_ACTIVE_ELEMENTS_IN_HOLONS: case TrackedDataSet.AVG_AMOUNT_INACTIVE_ELEMENTS_IN_HOLONS: float eCount = 0; for (SubNet sub : controller.getSimManager().getSubNets()) { for (HolonObject obj : sub.getObjects()) { eCount += obj.getElements().size(); } if (val < eCount) { val = eCount; } eCount = 0; } break; case TrackedDataSet.AVG_AMOUNT_PRODUCERS_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { if (val < sub.getObjects().size()) { val = sub.getObjects().size(); } } break; case TrackedDataSet.AVG_CONSUMED_ENERGY_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { float tempVal = -getMaxTotalConsumption(new ArrayList<>(sub.getObjects())); if (val < tempVal) { val = tempVal; } } break; case TrackedDataSet.AVG_WASTED_ENERGY_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { float tempVal = getMaxTotalProduction(new ArrayList<>(sub.getObjects())); if (val < tempVal) { val = tempVal; } } break; case TrackedDataSet.AMOUNT_BROKEN_EDGES: for (SubNet sub : controller.getSimManager().getSubNets()) { val += sub.getSwitches().size(); } break; case TrackedDataSet.RATIO_PRODUCERS_CONSUMERS: val = 1; break; case TrackedDataSet.AVG_AMOUNT_CLOSED_SWITCHES_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { float tempVal = sub.getSwitches().size(); if (val < tempVal) { val = tempVal; } } break; case TrackedDataSet.AVG_PRODUCED_ENERGY_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { float tempVal = getMaxTotalProduction(new ArrayList<>(sub.getObjects())); if (val < tempVal) { val = tempVal; } } break; default: maximum = 0; break; } if (val > maximum) { maximum = val; } } ((StatisticGraphPanel) this.getParent().getParent()).setMaximumLabel(maximum); } /** * Add the Current Values to each set */ void addValues() { for (TrackedDataSet set : dataSets) { float val = 0; switch (set.getProperty()) { case TrackedDataSet.CONSUMPTION: for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) { if (h.isActive() && h.getOverallEnergyAtTimeStep(model.getCurIteration()) < 0 ) { val += Math.abs(h.getOverallEnergyAtTimeStep(model.getCurIteration())); } set.setValAt(val, model.getCurIteration()); } break; case TrackedDataSet.PRODUCTION: for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) { if (h.isActive()&& h.getOverallEnergyAtTimeStep(model.getCurIteration()) > 0) { val += Math.abs(h.getOverallEnergyAtTimeStep(model.getCurIteration())); } set.setValAt(val, model.getCurIteration()); } break; case TrackedDataSet.ACTIVATED_ELEMENTS: for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) { if (h.isActive()) { val += h.getAmount(); } set.setValAt(val, model.getCurIteration()); } break; case TrackedDataSet.ON_OFF: if (((HolonSwitch) set.getCpsObject()).getManualMode()) { if (((HolonSwitch) set.getCpsObject()).getManualState()) { set.setValAt(1, model.getCurIteration()); } else { set.setValAt(0, model.getCurIteration()); } } else { if (((HolonSwitch) set.getCpsObject()).getState(model.getCurIteration())) { set.setValAt(1, model.getCurIteration()); } else { set.setValAt(0, model.getCurIteration()); } } break; case TrackedDataSet.TOTAL_PRODUCTION: set.setValAt(getTotalProductionAt(model.getObjectsOnCanvas(), model.getCurIteration()), model.getCurIteration()); break; case TrackedDataSet.TOTAL_CONSUMPTION: set.setValAt(-getTotalConsumptionAt(model.getObjectsOnCanvas(), model.getCurIteration()), model.getCurIteration()); break; case TrackedDataSet.WASTED_ENERGY: float wasted = getTotalWastedEnergyAt(model.getObjectsOnCanvas(), model.getCurIteration()); set.setValAt(wasted, model.getCurIteration()); break; case TrackedDataSet.GROUP_PRODUCTION: set.setValAt( getTotalProductionAt(((GroupNode) set.getCpsObject()).getNodes(), model.getCurIteration()), model.getCurIteration()); break; case TrackedDataSet.GROUP_CONSUMPTION: set.setValAt( -getTotalConsumptionAt(((GroupNode) set.getCpsObject()).getNodes(), model.getCurIteration()), model.getCurIteration()); break; case TrackedDataSet.AMOUNT_HOLONS: set.setValAt(controller.getSimManager().getSubNets().size(), model.getCurIteration()); break; case TrackedDataSet.AMOUNT_CLOSED_SWITCHES: for (HolonSwitch s : model.getSwitches()) { if (s.getState(model.getCurIteration())) { val++; } } set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_AMOUNT_OBJECTS_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { val += sub.getObjects().size(); } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_AMOUNT_ELEMENTS_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { for (HolonObject obj : sub.getObjects()) { val += obj.getElements().size(); } } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_AMOUNT_PRODUCERS_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { for (HolonObject obj : sub.getObjects()) { if (obj.getEnergyAtTimeStep(model.getCurIteration()) > 0) { val++; } } } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_CONSUMED_ENERGY_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { val += -getTotalConsumptionAt(new ArrayList<>(sub.getObjects()), model.getCurIteration()); } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_WASTED_ENERGY_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { val += (getTotalProductionAt(new ArrayList<>(sub.getObjects()), model.getCurIteration()) + getTotalConsumptionAt(new ArrayList<>(sub.getObjects()), model.getCurIteration())); } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AMOUNT_BROKEN_EDGES: for (SubNet sub : controller.getSimManager().getSubNets()) { for (HolonSwitch s : sub.getSwitches()) { if (s.getManualMode()?s.getManualState():s.getAutoActive()) { val++; } } } set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.RATIO_PRODUCERS_CONSUMERS: float prod = 0; float cons = 0; for (SubNet sub : controller.getSimManager().getSubNets()) { for (HolonObject obj : sub.getObjects()) { if (obj.getEnergyAtTimeStep(model.getCurIteration()) > 0) { prod++; } else if (obj.getEnergyAtTimeStep(model.getCurIteration()) < 0) { cons++; } } } val = prod / (prod + cons); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_AMOUNT_CLOSED_SWITCHES_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { val += sub.getSwitches().size(); } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_AMOUNT_ACTIVE_ELEMENTS_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { for (HolonObject obj : sub.getObjects()) { for (HolonElement ele : obj.getElements()) { if (ele.isActive()) { val++; } } } } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_AMOUNT_INACTIVE_ELEMENTS_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { for (HolonObject obj : sub.getObjects()) { for (HolonElement ele : obj.getElements()) { if (!ele.isActive()) { val++; } } } } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; case TrackedDataSet.AVG_PRODUCED_ENERGY_IN_HOLONS: for (SubNet sub : controller.getSimManager().getSubNets()) { val += getTotalProductionAt(new ArrayList<>(sub.getObjects()), model.getCurIteration()); } val /= controller.getSimManager().getSubNets().size(); set.setValAt(val, model.getCurIteration()); break; default: break; } } } /** * create Path with floats * * @param set tracked data */ private void createPathFloats(TrackedDataSet set) { int range = model.getCurIteration(); //to which iteration if (!model.getIsSimRunning()) { range = model.getIterations() - 1; } if (set.getValues()[0] != -1) { path.moveTo(0, convertToCanvasY(set.getValues()[0])); } else { path.moveTo(DISTANCE_IN_GRAPH * this.getWidth() / model.getIterations(), convertToCanvasY(set.getValues()[1])); } for (int i = 0; i < range; i++) { if (set.getValues()[i + 1] != -1) { path.lineTo((i + 1) * this.getWidth() / model.getIterations(), convertToCanvasY(set.getValues()[i + 1])); } else { if (i + 2 < range) { path.moveTo((i + 2) * this.getWidth() / model.getIterations(), convertToCanvasY(set.getValues()[i + 2])); } } } } /** * create Path with booleans(0 and 1) * * @param set tracked data */ private void createPathBooleans(TrackedDataSet set) { if (set.getValues()[0] != -1) { path.moveTo(0, convertToCanvasY((set.getValues()[0] * (maximum / 3 * 2)) + (maximum / 6))); } else { path.moveTo(DISTANCE_IN_GRAPH * this.getWidth() / model.getIterations(), convertToCanvasY((set.getValues()[1] * (maximum / 3 * 2)) + (maximum / 6))); } for (int i = 0; i < model.getCurIteration(); i++) { if (set.getValues()[i + 1] != -1) { path.lineTo((i + 1) * this.getWidth() / model.getIterations(), convertToCanvasY((set.getValues()[i + 1] * (maximum / 3 * 2)) + (maximum / 6))); } else { if (i + 2 < model.getCurIteration()) { path.moveTo((i + 2) * this.getWidth() / model.getIterations(), convertToCanvasY((set.getValues()[i + 2] * (maximum / 3 * 2)) + (maximum / 6))); } } } } /** * create Path for percent values * * @param set tracked data */ private void createPathPercent(TrackedDataSet set) { if (set.getValues()[0] != -1) { path.moveTo(0, convertToCanvasY(set.getValues()[0] * maximum)); } else { path.moveTo(DISTANCE_IN_GRAPH * this.getWidth() / model.getIterations(), convertToCanvasY(set.getValues()[1] * maximum)); } for (int i = 0; i < model.getCurIteration(); i++) { if (set.getValues()[i + 1] != -1) { path.lineTo((i + 1) * this.getWidth() / model.getIterations(), convertToCanvasY(set.getValues()[i + 1] * maximum)); } else { if (i + 2 < model.getCurIteration()) { path.moveTo((i + 2) * this.getWidth() / model.getIterations(), convertToCanvasY(set.getValues()[i + 2] * maximum)); } } } } /** * get the max total production of the given Objects */ private float getMaxTotalProduction(ArrayList objects) { float val = 0; for (AbstractCanvasObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.getEnergyPerElement() > 0) { val += (ele.getEnergyPerElement() ) * ele.getAmount(); } } } else if (obj instanceof GroupNode) { val += getMaxTotalProduction(((GroupNode) obj).getNodes()); } } return val; } /** * get the max total consumption of the given Objects */ private float getMaxTotalConsumption(ArrayList objects) { float val = 0; for (AbstractCanvasObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.getEnergyPerElement() < 0) { val += (ele.getEnergyPerElement() ) * ele.getAmount(); } } } else if (obj instanceof GroupNode) { val += getMaxTotalConsumption(((GroupNode) obj).getNodes()); } } return val; } /** * get the max total wasted energy of the given Objects * if it is smaller than 0, return 0 */ private float getMaxWastedEnergy(ArrayList objects) { float val = 0; for (AbstractCanvasObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { val += (ele.getEnergyPerElement() ) * ele.getAmount(); } } else if (obj instanceof GroupNode) { val += getMaxWastedEnergy(((GroupNode) obj).getNodes()); } } return val < 0 ? 0 : val; } /** * get the max total production of the given objects at the given timestep */ private float getTotalProductionAt(ArrayList objects, int tStep) { float val = 0; for (AbstractCanvasObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.isActive() && ele.getOverallEnergyAtTimeStep(tStep) > 0 ) { val += ele.getOverallEnergyAtTimeStep(tStep); } } } else if (obj instanceof GroupNode) { val += getTotalProductionAt(((GroupNode) obj).getNodes(), tStep); } } return val; } /** * get the total consumption of the given objects at the given timestep */ private float getTotalConsumptionAt(ArrayList objects, int tStep) { float val = 0; for (AbstractCanvasObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.isActive() && ele.getEnergyAtTimeStep(tStep) < 0 ) { val += ele.getOverallEnergyAtTimeStep(tStep); } } } else if (obj instanceof GroupNode) { val += getTotalConsumptionAt(((GroupNode) obj).getNodes(), tStep); } } return val; } /** * get the total wasted energy of the given objects at the given timestep */ private float getTotalWastedEnergyAt(ArrayList objects, int tStep) { float val = 0; for (AbstractCanvasObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.isActive()) { val += ele.getOverallEnergyAtTimeStep(tStep); } } } else if (obj instanceof GroupNode) { val += getTotalWastedEnergyAt(((GroupNode) obj).getNodes(), tStep); } } return val; } /** * Return all TrackedDataSets * * @return ArrayList of TrackedDataSet */ public ArrayList getDataSets() { return dataSets; } /** * Reset the Graph. Delete all calculated values. */ void resetGraph() { for (TrackedDataSet s : dataSets) { s.resetValues(); } } void hideGrid() { Graphics g = this.getGraphics(); showGrid = false; // clear the area g.clearRect(0, 0, this.getWidth(), this.getHeight()); paintComponent(g); } void showGrid() { Graphics g = this.getGraphics(); showGrid = true; paintComponent(g); } void updateIterations() { for(TrackedDataSet dataSet:dataSets){ dataSet.updateIterations(); } } }