package kernel.ui; import static rescuecore2.misc.java.JavaTools.instantiate; import java.awt.GridLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JPanel; import javax.swing.JButton; import javax.swing.SwingUtilities; import javax.swing.JOptionPane; import java.util.Collection; import java.util.List; import java.util.ArrayList; import kernel.Kernel; import kernel.KernelException; import kernel.ComponentManager; import kernel.InlineComponentLauncher; import rescuecore2.misc.WorkerThread; import rescuecore2.config.Config; import rescuecore2.config.NoSuchConfigOptionException; import rescuecore2.connection.ConnectionException; import rescuecore2.components.Component; import rescuecore2.components.ComponentLauncher; import rescuecore2.components.ComponentInitialisationException; import rescuecore2.components.ComponentConnectionException; import rescuecore2.log.LogException; import rescuecore2.log.Logger; import rescuecore2.registry.Registry; /** A JComponent containing various controls for the kernel GUI. */ public class KernelControlPanel extends JPanel { private Kernel kernel; private Config config; private Registry registry; private ComponentManager componentManager; private ComponentLauncher launcher; private Collection<JButton> controlButtons; private JButton stepButton; private JButton runButton; private volatile boolean running; private volatile boolean step; private RunThread runThread; private final Object runLock = new Object(); /** Create a KernelControlPanel component. @param kernel The kernel to control. @param config The kernel configuration. @param componentManager The kernel component manager. @param registry The registry to use for new connections. */ public KernelControlPanel(Kernel kernel, Config config, ComponentManager componentManager, Registry registry) { super(new GridLayout(0, 1)); this.kernel = kernel; this.config = config; this.componentManager = componentManager; this.registry = registry; controlButtons = new ArrayList<JButton>(); JButton addAgent = new JButton("Add agent"); JButton removeAgent = new JButton("Remove agent"); JButton addSim = new JButton("Add simulator"); JButton removeSim = new JButton("Remove simulator"); JButton addViewer = new JButton("Add viewer"); JButton removeViewer = new JButton("Remove viewer"); stepButton = new JButton("Step"); runButton = new JButton("Run"); add(addAgent); add(removeAgent); add(addSim); add(removeSim); add(addViewer); add(removeViewer); add(stepButton); add(runButton); controlButtons.add(addAgent); controlButtons.add(removeAgent); controlButtons.add(addSim); controlButtons.add(removeSim); controlButtons.add(addViewer); controlButtons.add(removeViewer); addAgent.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { addAgent(); } }); removeAgent.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeAgent(); } }); addSim.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { addSim(); } }); removeSim.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeSim(); } }); addViewer.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { addViewer(); } }); removeViewer.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeViewer(); } }); stepButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { stepButtonPressed(); } }); runButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { runButtonPressed(); } }); runThread = new RunThread(); running = false; } /** Activate this control panel. */ public void activate() { runThread.start(); launcher = new InlineComponentLauncher(componentManager, config); launcher.setDefaultRegistry(Registry.getCurrentRegistry()); } private void addAgent() { Component[] as = createComponents("agents"); addComponent(as, "agent"); } private void removeAgent() { } private void addSim() { Component[] ss = createComponents("simulators"); addComponent(ss, "simulator"); } private void removeSim() { } private void addViewer() { Component[] vs = createComponents("viewers"); addComponent(vs, "viewer"); } private void removeViewer() { } private void addComponent(Component[] options, String type) { if (options.length == 0) { return; } Component c = (Component)JOptionPane.showInputDialog(this, "Choose a " + type, "Choose a " + type, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (c == null) { return; } try { c.initialise(); launcher.connect(c); } catch (NoSuchConfigOptionException e) { JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); } catch (ComponentInitialisationException e) { JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); } catch (ComponentConnectionException e) { JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e.getMessage()); } catch (ConnectionException e) { JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); } catch (InterruptedException e) { JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); } } private void stepButtonPressed() { // This method should only be called on the event dispatch thread so it's OK to update the GUI. // Do a sanity check just in case. if (!SwingUtilities.isEventDispatchThread()) { throw new RuntimeException("stepButtonPressed called by thread " + Thread.currentThread() + ", not the event dispatch thread."); } synchronized (runLock) { if (!running) { step = true; stepButton.setText("Working"); stepButton.setEnabled(false); runButton.setEnabled(false); setControlButtonsEnabled(false); runLock.notifyAll(); } } } private void endStep() { step = false; stepButton.setText("Step"); stepButton.setEnabled(true); runButton.setEnabled(true); setControlButtonsEnabled(true); } private void runButtonPressed() { // This method should only be called on the event dispatch thread so it's OK to update the GUI. // Do a sanity check just in case. if (!SwingUtilities.isEventDispatchThread()) { throw new RuntimeException("runButtonPressed called by thread " + Thread.currentThread() + ", not the event dispatch thread."); } synchronized (runLock) { if (running) { setControlButtonsEnabled(true); stepButton.setEnabled(true); runButton.setText("Run"); } else { setControlButtonsEnabled(false); stepButton.setEnabled(false); runButton.setText("Stop"); } running = !running; runLock.notifyAll(); } } private void setControlButtonsEnabled(boolean b) { for (JButton next : controlButtons) { next.setEnabled(b); } } private void disableAllButtons() throws InterruptedException { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { setControlButtonsEnabled(false); stepButton.setEnabled(false); runButton.setEnabled(false); } }); } catch (java.lang.reflect.InvocationTargetException e) { // Should never happen Logger.error("KernelControlPanel.disableAllButtons", e); } } private boolean shouldStep() { synchronized (runLock) { return running || step; } } private Component[] createComponents(String type) { List<String> classNames = config.getArrayValue("kernel." + type, null); List<Component> instances = new ArrayList<Component>(); for (String next : classNames) { Component c = instantiate(next, Component.class); if (c != null) { instances.add(c); } } return instances.toArray(new Component[0]); } private class RunThread extends WorkerThread { @Override public boolean work() throws InterruptedException { if (shouldStep()) { if (!kernel.hasTerminated()) { try { kernel.timestep(); } catch (KernelException e) { Logger.error("Kernel error", e); kernel.shutdown(); disableAllButtons(); return false; } catch (LogException e) { Logger.error("Log error", e); kernel.shutdown(); disableAllButtons(); return false; } synchronized (runLock) { if (step) { endStep(); } } return true; } else { kernel.shutdown(); disableAllButtons(); return false; } } else { synchronized (runLock) { runLock.wait(1000); } return true; } } } }