package api; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.image.BufferedImage; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import classes.AbstractCpsObject; import classes.CpsUpperNode; import classes.HolonElement; import classes.HolonObject; import classes.HolonSwitch; import ui.controller.Control; import ui.model.DecoratedGroupNode; import ui.model.DecoratedState; import ui.model.Model; import ui.view.Console; public abstract class AlgorithmFramework implements AddOn{ //Algo protected int rounds = 3; //Panel private JPanel content = new JPanel(); protected Console console = new Console(); //Settings groupNode private boolean useGroupNode = false; private DecoratedGroupNode dGroupNode = null; //access private ArrayList access; LinkedList> resetChain = new LinkedList>(); //time private long startTime; private RunProgressBar runProgressbar = new RunProgressBar(); //concurrency private Thread runThread = new Thread(); protected boolean cancel = false; //holeg interaction private Control control; //printing protected RunDataBase db; public AlgorithmFramework(){ content.setLayout(new BorderLayout()); JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, createOptionPanel() , console); splitPane.setResizeWeight(0.0); content.add(splitPane, BorderLayout.CENTER); content.setPreferredSize(new Dimension(800,800)); } public JPanel createOptionPanel() { JPanel optionPanel = new JPanel(new BorderLayout()); JScrollPane scrollPane = new JScrollPane(createParameterPanel()); scrollPane.setBorder(BorderFactory.createTitledBorder("Parameter")); optionPanel.add(scrollPane, BorderLayout.CENTER); optionPanel.add(createButtonPanel(), BorderLayout.PAGE_END); return optionPanel; } private Component createParameterPanel() { JPanel parameterPanel = new JPanel(null); parameterPanel.setPreferredSize(new Dimension(510,300)); JProgressBar progressBar = runProgressbar.getJProgressBar(); progressBar.setBounds(350, 155, 185, 20); progressBar.setStringPainted(true); parameterPanel.add(progressBar); JButton selectGroupNodeButton = new JButton("Select GroupNode"); selectGroupNodeButton.setBounds(350, 120, 185, 30); selectGroupNodeButton.addActionListener(actionEvent -> selectGroupNode()); parameterPanel.add(selectGroupNodeButton); return parameterPanel; } public JPanel createButtonPanel() { JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); JButton resetButton = new JButton("Reset"); resetButton.setToolTipText("Resets the State to before the Algorithm has runed."); resetButton.addActionListener(actionEvent -> reset()); buttonPanel.add(resetButton); JButton cancelButton = new JButton("Cancel Run"); cancelButton.addActionListener(actionEvent -> cancel()); buttonPanel.add(cancelButton); JButton fitnessButton = new JButton("Fitness"); fitnessButton.setToolTipText("Fitness for the current state."); fitnessButton.addActionListener(actionEvent -> fitness()); buttonPanel.add(fitnessButton); JButton runButton = new JButton("Run"); runButton.addActionListener(actionEvent -> { Runnable task = () -> run(); runThread = new Thread(task); runThread.start(); }); buttonPanel.add(runButton); return buttonPanel; } private void startTimer(){ startTime = System.currentTimeMillis(); } private void printElapsedTime(){ long elapsedMilliSeconds = System.currentTimeMillis() - startTime; console.println("Execution Time of Algo in Milliseconds:" + elapsedMilliSeconds); } private void cancel() { if(runThread.isAlive()) { console.println("Cancel run."); cancel = true; runProgressbar.stop(); } else { console.println("Nothing to cancel."); } } private void fitness() { if(runThread.isAlive()) { console.println("Run have to be cancelled First."); return; } double currentFitness = evaluatePosition(extractPositionAndAccess()); resetChain.removeLast(); console.println("Actual Fitnessvalue: " + currentFitness); } private void selectGroupNode() { Object[] possibilities = control.getSimManager().getActualVisualRepresentationalState().getCreatedGroupNodes().values().stream().map(aCps -> new Handle(aCps)).toArray(); @SuppressWarnings("unchecked") Handle selected = (Handle) JOptionPane.showInputDialog(content, "Select GroupNode:", "GroupNode?", JOptionPane.OK_OPTION,new ImageIcon(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)) , possibilities, ""); if(selected != null) { console.println("Selected: " + selected); dGroupNode = selected.object; } } protected double evaluatePosition(List positionToEvaluate) { runProgressbar.step(); setState(positionToEvaluate); control.calculateStateOnlyForCurrentTimeStep(); DecoratedState actualstate = control.getSimManager().getActualDecorState(); return evaluateState(actualstate); } protected abstract double evaluateState(DecoratedState actualstate); private void run() { cancel = false; control.guiDisable(true); executeAlgoWithParameter(); updateVisual(); control.guiDisable(false); } private void executeAlgoWithParameter(){ runProgressbar.start(); int actualIteration = control.getModel().getCurIteration(); console.println("TimeStep:" + actualIteration); startTimer(); Individual runBest = new Individual(); runBest.fitness = Double.MAX_VALUE; db = new RunDataBase(); for(int r = 0; r < rounds; r++) { Individual roundBest = executeAlgo(); if(cancel)return; resetState(); if(roundBest.fitness < runBest.fitness) runBest = roundBest; } printElapsedTime(); setState(runBest.position); updateVisual(); console.println("AlgoResult:" + runBest.fitness); runProgressbar.stop(); } protected abstract Individual executeAlgo(); private void reset() { if(runThread.isAlive()) { console.println("Run have to be cancelled First."); return; } if(!resetChain.isEmpty()) { console.println("Resetting.."); setState(resetChain.getFirst()); resetChain.clear(); control.resetSimulation(); control.setCurIteration(0); updateVisual(); }else { console.println("No run inistialized."); } } /** * To let the User See the current state without touching the Canvas. */ private void updateVisual() { control.calculateStateAndVisualForCurrentTimeStep(); } /** * Sets the Model back to its original State before the LAST run. */ private void resetState() { setState(resetChain.getLast()); } /** * Sets the State out of the given position for calculation or to show the user. * @param position */ private void setState(List position) { int i = 0; for(Boolean bool: position) { access.get(i++).setState(bool); } } /** * Method to get the current Position alias a ListOf Booleans for aktive settings on the Objects on the Canvas. * Also initialize the Access Hashmap to swap faster positions. * @param model * @return */ protected List extractPositionAndAccess() { Model model = control.getModel(); access= new ArrayList(); List initialState = new ArrayList(); rollOutNodes((useGroupNode && (dGroupNode != null))? dGroupNode.getModel().getNodes() :model.getObjectsOnCanvas(), initialState, model.getCurIteration()); resetChain.add(initialState); return initialState; } /** * Method to extract the Informations recursively out of the Model. * @param nodes * @param positionToInit * @param timeStep */ private void rollOutNodes(List nodes, List positionToInit, int timeStep) { for(AbstractCpsObject aCps : nodes) { if (aCps instanceof HolonObject) { for (HolonElement hE : ((HolonObject) aCps).getElements()) { positionToInit.add(hE.isActive()); access.add(new AccessWrapper(hE)); } } else if (aCps instanceof HolonSwitch) { HolonSwitch sw = (HolonSwitch) aCps; positionToInit.add(sw.getState(timeStep)); access.add(new AccessWrapper(sw)); } else if(aCps instanceof CpsUpperNode) { rollOutNodes(((CpsUpperNode)aCps).getNodes(), positionToInit ,timeStep ); } } } @Override public JPanel getPanel() { return content; } @Override public void setController(Control control) { this.control = control; } private class RunProgressBar{ //progressbar private JProgressBar progressBar = new JProgressBar(); private int count = 0; private boolean isActive = false; public void step() { if(isActive) progressBar.setValue(count++); progressBar.setMaximum(getProgressBarMaxCount()); } public void start() { isActive = true; progressBar.setValue(0); } public void stop() { isActive = false; } public JProgressBar getJProgressBar(){ return progressBar; } } protected abstract int getProgressBarMaxCount(); public class RunDataBase{ List> allRuns = new ArrayList>(); public void insertNewRun(List newRun){ allRuns.add(newRun); } } public class RunPrinter{ //Fields private JFileChooser fileChooser = new JFileChooser(); private boolean append = false; //Constructor public RunPrinter(String filename) { this(filename, false); } public RunPrinter(String filename, boolean append){ this.append = append; fileChooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); fileChooser.setSelectedFile(new File(filename)); } //public methods public void enableAppend(boolean enable) { append = enable; } public void setFilename(String filename) { fileChooser.setSelectedFile(new File(filename)); } public void print(String info) { File file = fileChooser.getSelectedFile(); try { file.createNewFile(); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file, append), "UTF-8")); printToStream(out, info); out.close(); } catch (IOException e) { System.out.println(e.getMessage()); } } //private methods private void printToStream(BufferedWriter out, String info) throws IOException { out.write(info); out.newLine(); if(db != null) out.write(db.allRuns.stream().map(list -> list.stream().map(Object::toString).collect(Collectors.joining(","))).collect(Collectors.joining(System.lineSeparator()))); } } /** * A Wrapper Class for Access HolonElement and HolonSwitch in one Element and not have to split the List. */ private class AccessWrapper { public static final int HOLONELEMENT = 0; public static final int SWITCH = 1; private int type; private HolonSwitch hSwitch; private HolonElement hElement; public AccessWrapper(HolonSwitch hSwitch){ type = SWITCH; this.hSwitch = hSwitch; } public AccessWrapper(HolonElement hElement){ type = HOLONELEMENT; this.hElement = hElement; } public void setState(boolean state) { if(type == HOLONELEMENT) { hElement.setActive(state); }else{//is switch hSwitch.setManualMode(true); hSwitch.setManualState(state); } } public boolean getState(int timeStep) { return (type == HOLONELEMENT)?hElement.isActive():hSwitch.getState(timeStep); } public int getType() { return type; } } /** * To create Random and maybe switch the random generation in the future. */ protected static class Random{ private static java.util.Random random = new java.util.Random(); /** * True or false * @return the random boolean. */ public static boolean nextBoolean(){ return random.nextBoolean(); } /** * Between 0.0(inclusive) and 1.0 (exclusive) * @return the random double. */ public static double nextDouble() { return random.nextDouble(); } /** * Random Int in Range [min;max[ with UniformDistirbution * @param min * @param max * @return */ public static int nextIntegerInRange(int min, int max) { return min + random.nextInt(max - min); } } private class Handle{ public T object; Handle(T object){ this.object = object; } public String toString() { return object.toString(); } } public class Individual { public double fitness; public List position; public Individual(){}; /** * Copy Constructor */ public Individual(Individual c){ position = c.position.stream().collect(Collectors.toList()); fitness = c.fitness; } } }