Skip to content

Commit

Permalink
GUI: use a ProgressMonitor when loading
Browse files Browse the repository at this point in the history
  • Loading branch information
pjonsson committed Jan 23, 2023
1 parent 8a90ba5 commit c79f71e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 49 deletions.
17 changes: 14 additions & 3 deletions java/org/contikios/cooja/Cooja.java
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,7 @@ public static void go(Config config, List<Simulation.SimConfig> simConfigs) {
} catch (Exception e) {
logger.error("Exception when loading simulation: ", e);
}
gui.setSimulation(sim);
if (sim == null) {
autoQuit = true;
logger.error("TEST {} FAILED\n", simConfig.file());
Expand Down Expand Up @@ -1270,9 +1271,7 @@ Simulation createSimulation(Simulation.SimConfig cfg, Element root, boolean quic
? Integer.parseInt(simCfg.getChild("motedelay_us").getText())
: Integer.parseInt(cfgDelay.getText()) * Simulation.MILLISECOND;
doRemoveSimulation();
var sim = new Simulation(cfg, this, title, generatedSeed, seed, medium, delay, quick, root);
setSimulation(sim);
return sim;
return new Simulation(cfg, this, title, generatedSeed, seed, medium, delay, quick, root);
}

/**
Expand Down Expand Up @@ -1716,6 +1715,18 @@ public static void setProgressMessage(String msg, int type) {
}
}

public static void tickProgress() {
if (gui != null) {
gui.tickProgress();
}
}

public static void setMaxProgress(int max) {
if (gui != null) {
gui.setMaxProgress(max);
}
}

/**
* Load quick help for given object or identifier. Note that this method does not
* show the quick help pane.
Expand Down
116 changes: 70 additions & 46 deletions java/org/contikios/cooja/GUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
Expand All @@ -47,6 +47,7 @@
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
Expand Down Expand Up @@ -81,7 +82,6 @@
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
Expand All @@ -90,6 +90,7 @@
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.ProgressMonitor;
import javax.swing.RepaintManager;
import javax.swing.SwingWorker;
import javax.swing.Timer;
Expand Down Expand Up @@ -121,7 +122,8 @@ public class GUI {

static JFrame frame;
final JDesktopPane myDesktopPane;
private static JProgressBar PROGRESS_BAR;
private SwingWorker<Simulation, Object> loadWorker;
private int loadProgress;
private final ArrayList<String> PROGRESS_WARNINGS = new ArrayList<>();

final ArrayList<Class<? extends Plugin>> menuMotePluginClasses = new ArrayList<>();
Expand Down Expand Up @@ -1356,42 +1358,43 @@ private SwingWorker<Simulation, Object> createLoadSimWorker(Simulation.SimConfig
addToFileHistory(configFile);
}

final JPanel progressPanel;
final JDialog progressDialog;
ProgressMonitor progressMonitor;
if (quick) {
final String progressTitle = "Loading " + (configFile == null ? "" : configFile.getAbsolutePath());
progressDialog = new JDialog(frame, progressTitle, Dialog.ModalityType.APPLICATION_MODAL);

progressPanel = new JPanel(new BorderLayout());
var progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setIndeterminate(true);

PROGRESS_BAR = progressBar; // Allow various parts of Cooja to show messages.

var button = new JButton("Abort");

progressPanel.add(BorderLayout.CENTER, progressBar);
progressPanel.add(BorderLayout.SOUTH, button);
progressPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

progressDialog.getContentPane().add(progressPanel);
progressDialog.setSize(400, 200);

progressDialog.getRootPane().setDefaultButton(button);
progressDialog.setLocationRelativeTo(frame);
progressDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
final String progressTitle = "Loading " + (configFile == null ? cooja.getSimulation().getCfg().file() : configFile.getAbsolutePath());
progressMonitor = new ProgressMonitor(frame, progressTitle, "", 0, 6);
progressMonitor.setMillisToDecideToPopup(0);
progressMonitor.setMillisToPopup(0);
progressMonitor.setProgress(0);
// Emulate a modal dialog by disabling the Cooja frame. This is to avoid strange
// behaviors with Ctrl-R, possibly conflicting with the reload simulation action.
frame.setEnabled(false);
} else {
progressPanel = null;
progressDialog = null;
progressMonitor = null;
}
loadProgress = 0;

// SwingWorker can pass information from worker to process() through publish().
// Communicate information the other way through this shared queue.
final var channel = new SynchronousQueue<>(true);
var worker = new SwingWorker<Simulation, Object>() {
loadWorker = new SwingWorker<>() {
private volatile boolean cancelled;
private final PropertyChangeListener progressListener = evt -> {
if (cancelled || progressMonitor == null) return;
if (progressMonitor.isCanceled()) {
cancelled = true;
cancel(true);
return;
}
switch (evt.getPropertyName()) {
case "progress" -> progressMonitor.setProgress((Integer) evt.getNewValue());
case "maxProgress" -> progressMonitor.setMaximum((Integer) evt.getNewValue());
case "progressMessage" -> progressMonitor.setNote(evt.getNewValue().toString());
}
};

@Override
public Simulation doInBackground() {
addPropertyChangeListener(progressListener);
Element root;
try {
root = configFile == null ? cooja.extractSimulationConfig() : cooja.readSimulationConfig(cfg);
Expand Down Expand Up @@ -1433,6 +1436,19 @@ public Simulation doInBackground() {
shouldRetry = false;
PROGRESS_WARNINGS.clear();
newSim = cooja.createSimulation(config, root, quick, manualRandomSeed);
if (isCancelled() || cancelled) {
// Simulation.startPlugin can be waiting for the AWT thread at this point. Schedule
// the removal of plugins in the AWT thread too.
final var finalNewSim = newSim;
EventQueue.invokeLater(() -> {
frame.setEnabled(true);
finalNewSim.removed();
});
return null;
}
// Simulation is loaded, close progress dialog so user cannot cancel.
if (progressMonitor != null) progressMonitor.close();
cooja.setSimulation(newSim);
if (newSim != null && autoStart) {
newSim.startSimulation();
}
Expand All @@ -1443,6 +1459,7 @@ public Simulation doInBackground() {
var rv = channel.take();
if (!(rv instanceof Integer i)) return null;
shouldRetry = i == 1;
if (!shouldRetry) cancelled = true;
} catch (InterruptedException ex) {
cooja.doRemoveSimulation();
return null;
Expand Down Expand Up @@ -1498,12 +1515,16 @@ protected void process(List<Object> exs) {
}
}
} else if (ex instanceof Exception e) { // Display failure + reload button.
var retry = showErrorDialog("Simulation load error", e, true);
var retry = !isCancelled() && showErrorDialog("Simulation load error", e, true);
rv = retry ? 1 : 0;
loadProgress = 0;
if (progressMonitor != null) progressMonitor.setProgress(0);
setProgress(retry ? 0 : 100);
}
try {
channel.put(rv);
} catch (InterruptedException e) {
cancelled = true;
cancel(true);
return;
}
Expand All @@ -1512,6 +1533,9 @@ protected void process(List<Object> exs) {

@Override
protected void done() {
frame.setEnabled(true);
if (progressMonitor != null) progressMonitor.close();
if (cancelled) return;
// Simulation loaded, plugins started, now Z-order visualized plugins.
for (int z = 0; z < myDesktopPane.getAllFrames().length; z++) {
for (var plugin : myDesktopPane.getAllFrames()) {
Expand Down Expand Up @@ -1572,20 +1596,9 @@ public void actionPerformed(ActionEvent e) {
dialog.setVisible(true);
}
PROGRESS_WARNINGS.clear();
if (progressDialog != null && progressDialog.isDisplayable()) {
progressDialog.dispose();
}
}
};

if (progressDialog != null) {
java.awt.EventQueue.invokeLater(() -> {
progressPanel.setVisible(true);
progressDialog.getRootPane().getDefaultButton().addActionListener(e -> worker.cancel(true));
progressDialog.setVisible(true);
});
}
return worker;
return loadWorker;
}

public void updateProgress(boolean stoppedSimulation) {
Expand Down Expand Up @@ -1623,10 +1636,21 @@ private void updateDesktopSize() {
myDesktopPane.revalidate();
}

public void tickProgress() {
if (loadWorker != null) {
loadWorker.firePropertyChange("progress", loadProgress, ++loadProgress);
}
}

public void setMaxProgress(int max) {
if (loadWorker != null) {
loadWorker.firePropertyChange("maxProgress", 0, max);
}
}

public void setProgressMessage(String msg, int type) {
if (PROGRESS_BAR != null && PROGRESS_BAR.isShowing()) {
PROGRESS_BAR.setString(msg);
PROGRESS_BAR.setStringPainted(true);
if (loadWorker != null) {
loadWorker.firePropertyChange("progressMessage", null, msg);
}
if (type != MessageListUI.NORMAL) {
PROGRESS_WARNINGS.add(msg);
Expand Down
5 changes: 5 additions & 0 deletions java/org/contikios/cooja/Simulation.java
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ public Simulation(SimConfig cfg, Cooja cooja, String title, boolean generateSeed
}, "sim");
simulationThread.start();
if (root != null) {
var numMoteTypes = root.getChild("simulation").getChildren("motetype").size();
var numPlugins = root.getChildren("plugin").size();
Cooja.setMaxProgress(numMoteTypes + numPlugins);
// Track identifier of mote types to deal with the legacy-XML format that used <motetype_identifier>.
var moteTypesMap = new HashMap<String, MoteType>();
// Parse elements
Expand All @@ -295,6 +298,7 @@ public Simulation(SimConfig cfg, Cooja cooja, String title, boolean generateSeed
throw new MoteType.MoteTypeCreationException("Mote type could not be configured: " + element.getText().trim());
}
addMoteType(moteType);
Cooja.tickProgress();
for (var mote : element.getChildren("mote")) {
createMote(moteType, mote);
}
Expand Down Expand Up @@ -426,6 +430,7 @@ private SimulationCreationException startPlugin(Class<? extends Plugin> pluginCl
} catch (PluginConstructionException ex) {
return new SimulationCreationException("Failed to start plugin: " + ex.getMessage(), ex);
}
Cooja.tickProgress();
return null;
}

Expand Down

0 comments on commit c79f71e

Please sign in to comment.