From c78b5bad5cc7281185ffb1f860a5a4a9472adde6 Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Fri, 23 Mar 2012 15:14:24 +0100 Subject: [PATCH] some bugfixes regarding timeouts in test scripts, with simplified code --- tools/cooja/java/se/sics/cooja/GUI.java | 145 ++++----- .../sics/cooja/plugins/LogScriptEngine.java | 291 +++++++++--------- .../se/sics/cooja/plugins/ScriptRunner.java | 35 ++- 3 files changed, 246 insertions(+), 225 deletions(-) diff --git a/tools/cooja/java/se/sics/cooja/GUI.java b/tools/cooja/java/se/sics/cooja/GUI.java index 091592634..3d2227f8e 100644 --- a/tools/cooja/java/se/sics/cooja/GUI.java +++ b/tools/cooja/java/se/sics/cooja/GUI.java @@ -34,6 +34,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; +import java.awt.Dialog.ModalityType; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsDevice; @@ -41,7 +42,6 @@ import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; -import java.awt.Dialog.ModalityType; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; @@ -271,7 +271,7 @@ public class GUI extends Observable { "COMMAND_DATA_START", "COMMAND_DATA_END", "COMMAND_BSS_START", "COMMAND_BSS_END", "COMMAND_COMMON_START", "COMMAND_COMMON_END", - + "HIDE_WARNINGS" }; @@ -299,7 +299,7 @@ public class GUI extends Observable { private Vector startedPlugins = new Vector(); private ArrayList guiActions = new ArrayList(); - + // Platform configuration variables // Maintained via method reparseProjectConfig() private ProjectConfig projectConfig; @@ -380,12 +380,12 @@ public class GUI extends Observable { quickHelpScroll = new JScrollPane(quickHelpTextPane, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); quickHelpScroll.setPreferredSize(new Dimension(200, 0)); quickHelpScroll.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(Color.GRAY), + BorderFactory.createLineBorder(Color.GRAY), BorderFactory.createEmptyBorder(0, 3, 0, 0) )); quickHelpScroll.setVisible(false); loadQuickHelp("KEYBOARD_SHORTCUTS"); - + // Load default and overwrite with user settings (if any) loadExternalToolsDefaultSettings(); loadExternalToolsUserSettings(); @@ -597,7 +597,7 @@ public class GUI extends Observable { menu.add(lastItem); } } - + private void doLoadConfigAsync(final boolean ask, final boolean quick, final File file) { new Thread(new Runnable() { public void run() { @@ -646,7 +646,7 @@ public class GUI extends Observable { for (GUIAction a: guiActions) { a.setEnabled(a.shouldBeEnabled()); } - + /* Mote and mote type menues */ if (menuMoteTypeClasses != null) { menuMoteTypeClasses.setEnabled(getSimulation() != null); @@ -655,7 +655,7 @@ public class GUI extends Observable { menuMoteTypes.setEnabled(getSimulation() != null); } } - + private JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu; @@ -673,7 +673,7 @@ public class GUI extends Observable { guiActions.add(startStopSimulationAction); guiActions.add(removeAllMotesAction); guiActions.add(showBufferSettingsAction); - + /* File menu */ menu = new JMenu("File"); menu.addMenuListener(new MenuListener() { @@ -733,7 +733,7 @@ public class GUI extends Observable { }); menu.setMnemonic(KeyEvent.VK_S); menuBar.add(menu); - + menu.add(new JMenuItem(startStopSimulationAction)); GUIAction guiAction = new StartPluginGUIAction("Control panel"); @@ -929,7 +929,7 @@ public class GUI extends Observable { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } - + menuItem = new JMenuItem("Contiki mote configuration wizard"); menuItem.setActionCommand("configuration wizard"); menuItem.addActionListener(guiEventHandler); @@ -940,7 +940,7 @@ public class GUI extends Observable { } menu.add(new JMenuItem(showBufferSettingsAction)); - + /* Help */ menu = new JMenu("Help"); menu.setMnemonic(KeyEvent.VK_H); @@ -950,7 +950,7 @@ public class GUI extends Observable { menu.add(checkBox); menuBar.add(menu); - menu.addSeparator(); + menu.addSeparator(); menuItem = new JMenuItem("Java version: " + System.getProperty("java.version") + " (" @@ -965,7 +965,7 @@ public class GUI extends Observable { + System.getProperty("sun.arch.data.model")); menuItem.setEnabled(false); menu.add(menuItem); - + // Mote plugins popup menu (not available via menu bar) if (menuMotePluginClasses == null) { menuMotePluginClasses = new Vector>(); @@ -1356,7 +1356,7 @@ public class GUI extends Observable { unregisterPositioners(); unregisterRadioMediums(); projectDirClassLoader = null; - + /* Build cooja configuration */ try { projectConfig = new ProjectConfig(true); @@ -1381,7 +1381,7 @@ public class GUI extends Observable { "Error when reading project config: " + e.getMessage()).initCause(e); } } - + /* Create project class loader */ try { projectDirClassLoader = createClassLoader(currentProjects); @@ -1585,7 +1585,7 @@ public class GUI extends Observable { * Same as the {@link #startPlugin(Class, GUI, Simulation, Mote)} method, * but does not throw exceptions. If COOJA is visualised, an error dialog * is shown if plugin could not be started. - * + * * @see #startPlugin(Class, GUI, Simulation, Mote) * @param pluginClass Plugin class * @param argGUI Plugin GUI argument @@ -1609,7 +1609,7 @@ public class GUI extends Observable { return null; } } while (cause != null && (cause=cause.getCause()) != null); - + logger.fatal("Error when starting plugin", ex); } } @@ -1622,15 +1622,15 @@ public class GUI extends Observable { } public Plugin startPlugin(final Class pluginClass, - final GUI argGUI, final Simulation argSimulation, final Mote argMote) - throws PluginConstructionException + final GUI argGUI, final Simulation argSimulation, final Mote argMote) + throws PluginConstructionException { return startPlugin(pluginClass, argGUI, argSimulation, argMote, true); } /** * Starts given plugin. If visualized, the plugin is also shown. - * + * * @see PluginType * @param pluginClass Plugin class * @param argGUI Plugin GUI argument @@ -1665,7 +1665,7 @@ public class GUI extends Observable { throw new PluginConstructionException("No mote argument for mote plugin"); } - plugin = + plugin = pluginClass.getConstructor(new Class[] { Mote.class, Simulation.class, GUI.class }) .newInstance(argMote, argSimulation, argGUI); @@ -1678,7 +1678,7 @@ public class GUI extends Observable { throw new PluginConstructionException("No simulation argument for simulation plugin"); } - plugin = + plugin = pluginClass.getConstructor(new Class[] { Simulation.class, GUI.class }) .newInstance(argSimulation, argGUI); @@ -1717,7 +1717,7 @@ public class GUI extends Observable { if (activate && plugin.getGUI() != null) { myGUI.showPlugin(plugin); } - + return plugin; } @@ -1807,7 +1807,7 @@ public class GUI extends Observable { // Create 'start plugin'-menu item JMenuItem menuItem; String tooltip = ""; - + /* Sort menu according to plugin type */ int itemIndex=0; if (pluginType == PluginType.COOJA_PLUGIN || pluginType == PluginType.COOJA_STANDARD_PLUGIN) { @@ -1892,7 +1892,7 @@ public class GUI extends Observable { /** * Returns started plugin that ends with given class name, if any. - * + * * @param classname Class name * @return Plugin instance */ @@ -1904,10 +1904,10 @@ public class GUI extends Observable { } return null; } - + /** * Returns started plugin with given class name, if any. - * + * * @param classname Class name * @return Plugin instance * @deprecated @@ -1977,21 +1977,21 @@ public class GUI extends Observable { /** * Creates a new mote type of the given mote type class. * This may include displaying a dialog for user configurations. - * + * * If mote type is created successfully, the add motes dialog will appear. - * + * * @param moteTypeClass Mote type class */ public void doCreateMoteType(Class moteTypeClass) { doCreateMoteType(moteTypeClass, true); } - + /** * Creates a new mote type of the given mote type class. * This may include displaying a dialog for user configurations. - * + * * @param moteTypeClass Mote type class - * @param addMotes Show add motes dialog after successfully adding mote type + * @param addMotes Show add motes dialog after successfully adding mote type */ public void doCreateMoteType(Class moteTypeClass, boolean addMotes) { if (mySimulation == null) { @@ -2236,7 +2236,7 @@ public class GUI extends Observable { PROGRESS_WARNINGS.clear(); newSim = loadSimulationConfig(fileToLoad, quick); myGUI.setSimulation(newSim, false); - + /* Optionally show compilation warnings */ boolean hideWarn = Boolean.parseBoolean( GUI.getExternalToolsSetting("HIDE_WARNINGS", "false") @@ -2245,7 +2245,7 @@ public class GUI extends Observable { showWarningsDialog(frame, PROGRESS_WARNINGS.toArray(new String[0])); } PROGRESS_WARNINGS.clear(); - + } catch (UnsatisfiedLinkError e) { shouldRetry = showErrorDialog(GUI.getTopParentContainer(), "Simulation load error", e, true); } catch (SimulationCreationException e) { @@ -2276,7 +2276,7 @@ public class GUI extends Observable { if (warnMemory()) { return; } - + final JDialog progressDialog = new JDialog(frame, "Reloading", true); final Thread loadThread = new Thread(new Runnable() { public void run() { @@ -2305,7 +2305,7 @@ public class GUI extends Observable { if (autoStart) { newSim.startSimulation(); } - + /* Optionally show compilation warnings */ boolean hideWarn = Boolean.parseBoolean( GUI.getExternalToolsSetting("HIDE_WARNINGS", "false") @@ -2314,7 +2314,7 @@ public class GUI extends Observable { showWarningsDialog(frame, PROGRESS_WARNINGS.toArray(new String[0])); } PROGRESS_WARNINGS.clear(); - + } catch (UnsatisfiedLinkError e) { shouldRetry = showErrorDialog(frame, "Simulation reload error", e, true); @@ -2459,7 +2459,7 @@ public class GUI extends Observable { return saveFile; } else { JOptionPane.showMessageDialog( - getTopParentContainer(), "No write access to " + saveFile, "Save failed", + getTopParentContainer(), "No write access to " + saveFile, "Save failed", JOptionPane.ERROR_MESSAGE); logger.fatal("No write access to file: " + saveFile.getAbsolutePath()); } @@ -2757,8 +2757,8 @@ public class GUI extends Observable { ExternalToolsDialog.showDialog(GUI.getTopParentContainer()); } else if (e.getActionCommand().equals("manage projects")) { COOJAProject[] newProjects = ProjectDirectoriesDialog.showDialog( - GUI.getTopParentContainer(), - GUI.this, + GUI.getTopParentContainer(), + GUI.this, getProjects() ); if (newProjects != null) { @@ -3029,7 +3029,7 @@ public class GUI extends Observable { System.exit(1); } GUI gui = sim.getGUI(); - + /* Make sure at least one test editor is controlling the simulation */ boolean hasEditor = false; for (Plugin startedPlugin : gui.startedPlugins) { @@ -3051,7 +3051,12 @@ public class GUI extends Observable { System.exit(1); } plugin.updateScript(scriptFile); - plugin.setScriptActive(true); + try { + plugin.setScriptActive(true); + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); + System.exit(1); + } sim.setDelayTime(0); sim.startSimulation(); } else { @@ -3059,7 +3064,7 @@ public class GUI extends Observable { System.exit(1); } } - + } else if (args.length > 0 && args[0].startsWith("-applet")) { String tmpWebPath=null, tmpBuildPath=null, tmpEsbFirmware=null, tmpSkyFirmware=null; @@ -3258,11 +3263,11 @@ public class GUI extends Observable { // Create and write to document Document doc = new Document(extractSimulationConfig()); OutputStream out = new FileOutputStream(file); - + if (file.getName().endsWith(".gz")) { out = new GZIPOutputStream(out); } - + XMLOutputter outputter = new XMLOutputter(); outputter.setFormat(Format.getPrettyFormat()); outputter.output(doc, out); @@ -3274,7 +3279,7 @@ public class GUI extends Observable { e.printStackTrace(); } } - + public Element extractSimulationConfig() { // Create simulation config Element root = new Element("simconf"); @@ -3391,7 +3396,7 @@ public class GUI extends Observable { projectFile = projectFile.getCanonicalFile(); } catch (IOException e) { } - + boolean found = false; for (COOJAProject currentProject: currentProjects) { if (projectFile.getPath().replaceAll("\\\\", "/"). @@ -3473,7 +3478,7 @@ public class GUI extends Observable { /* Activate plugin */ startedPlugin.startPlugin(); - + /* If Cooja not visualized, ignore window configuration */ if (startedPlugin.getGUI() == null) { continue; @@ -3511,7 +3516,7 @@ public class GUI extends Observable { pluginGUI.setIcon(true); } catch (PropertyVetoException e) { } - }; + }; }); } } @@ -3618,7 +3623,7 @@ public class GUI extends Observable { list.addPopupMenuItem(null, true); tabbedPane.addTab("Contiki error", new JScrollPane(list)); } - + /* Compilation output */ MessageList compilationOutput = null; if (exception instanceof MoteTypeCreationException @@ -3704,7 +3709,7 @@ public class GUI extends Observable { private static void showWarningsDialog(final Frame parent, final String[] warnings) { new RunnableInEDT() { public Boolean work() { - final JDialog dialog = new JDialog((Frame)parent, "Compilation warnings", false); + final JDialog dialog = new JDialog(parent, "Compilation warnings", false); Box buttonBox = Box.createHorizontalBox(); /* Warnings message list */ @@ -3748,7 +3753,7 @@ public class GUI extends Observable { } }.invokeAndWait(); } - + /** * Runs work method in event dispatcher thread. * Worker method returns a value. @@ -3885,9 +3890,9 @@ public class GUI extends Observable { /** * Tries to convert given file to be "portable". * The portable path is either relative to Contiki, or to the configuration (.csc) file. - * + * * If this method fails, it returns the original file. - * + * * @param file Original file * @return Portable file, or original file is conversion failed */ @@ -3897,7 +3902,7 @@ public class GUI extends Observable { public File createPortablePath(File file, boolean allowConfigRelativePaths) { File portable = null; - + portable = createContikiRelativePath(file); if (portable != null) { /*logger.info("Generated Contiki relative path '" + file.getPath() + "' to '" + portable.getPath() + "'");*/ @@ -3911,7 +3916,7 @@ public class GUI extends Observable { return portable; } } - + logger.warn("Path is not portable: '" + file.getPath()); return file; } @@ -3919,7 +3924,7 @@ public class GUI extends Observable { /** * Tries to restore a previously "portable" file to be "absolute". * If the given file already exists, no conversion is performed. - * + * * @see #createPortablePath(File) * @param file Portable file * @return Absolute file @@ -3942,7 +3947,7 @@ public class GUI extends Observable { /*logger.info("Restored config relative path '" + file.getPath() + "' to '" + absolute.getPath() + "'");*/ return absolute; } - + /*logger.info("Portable path was not restored: '" + file.getPath());*/ return file; } @@ -3962,10 +3967,10 @@ public class GUI extends Observable { /* Replace Contiki's canonical path with Contiki identifier */ String portablePath = fileCanonical.replaceFirst( - java.util.regex.Matcher.quoteReplacement(contikiCanonical), + java.util.regex.Matcher.quoteReplacement(contikiCanonical), java.util.regex.Matcher.quoteReplacement(PATH_CONTIKI_IDENTIFIER)); File portable = new File(portablePath); - + /* Verify conversion */ File verify = restoreContikiRelativePath(portable); if (verify == null || !verify.exists()) { @@ -4028,10 +4033,10 @@ public class GUI extends Observable { /* Replace config's canonical path with config identifier */ String portablePath = fileCanonical.replaceFirst( - java.util.regex.Matcher.quoteReplacement(configCanonical), + java.util.regex.Matcher.quoteReplacement(configCanonical), java.util.regex.Matcher.quoteReplacement(id)); File portable = new File(portablePath); - + /* Verify conversion */ File verify = restoreConfigRelativePath(portable); if (verify == null || !verify.exists()) { @@ -4087,11 +4092,11 @@ public class GUI extends Observable { */ public String getQuickHelp(); } - + /** * Load quick help for given object or identifier. Note that this method does not * show the quick help pane. - * + * * @param obj If string: help identifier. Else, the class name of the argument * is used as help identifier. */ @@ -4106,7 +4111,7 @@ public class GUI extends Observable { } else { key = obj.getClass().getName(); } - + String help = null; if (obj instanceof HasQuickHelp) { help = ((HasQuickHelp) obj).getQuickHelp(); @@ -4292,13 +4297,13 @@ public class GUI extends Observable { } outputFile.delete(); } - + final File finalOutputFile = outputFile; setExternalToolsSetting("EXECUTE_JAR_LAST", outputFile.getPath()); new Thread() { public void run() { try { - ExecuteJAR.buildExecutableJAR(GUI.this, finalOutputFile); + ExecuteJAR.buildExecutableJAR(GUI.this, finalOutputFile); } catch (RuntimeException ex) { JOptionPane.showMessageDialog(GUI.getTopParentContainer(), ex.getMessage(), @@ -4359,7 +4364,7 @@ public class GUI extends Observable { public void actionPerformed(final ActionEvent e) { new Thread(new Runnable() { public void run() { - Class pluginClass = + Class pluginClass = (Class) ((JMenuItem) e.getSource()).getClientProperty("class"); Mote mote = (Mote) ((JMenuItem) e.getSource()).getClientProperty("mote"); tryStartPlugin(pluginClass, myGUI, mySimulation, mote); @@ -4434,5 +4439,5 @@ public class GUI extends Observable { return mySimulation != null; } }; - + } diff --git a/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java b/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java index 5a6fd82fd..2e867da80 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java +++ b/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java @@ -64,9 +64,9 @@ public class LogScriptEngine { private static Logger logger = Logger.getLogger(LogScriptEngine.class); private static final long DEFAULT_TIMEOUT = 20*60*1000*Simulation.MILLISECOND; /* 1200s = 20 minutes */ - private ScriptEngine engine = + private ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); - + /* Log output listener */ private LogOutputListener logOutputListener = new LogOutputListener() { public void moteWasAdded(Mote mote) { @@ -97,6 +97,11 @@ public class LogScriptEngine { private boolean scriptActive = false; + private long timeout; + private long startTime; + private long startRealTime; + private long nextProgress; + private interface ScriptLog { public void log(String log); public void testOK(); @@ -104,6 +109,10 @@ public class LogScriptEngine { public void generateMessage(long delay, String msg); } + public LogScriptEngine(Simulation simulation) { + this.simulation = simulation; + } + /* Only called from the simulation loop */ private void stepScript() { /* Release script - halt simulation */ @@ -130,10 +139,6 @@ public class LogScriptEngine { } } - public LogScriptEngine(Simulation simulation) { - this.simulation = simulation; - } - /* Only called from the simulation loop */ private void handleNewMoteOutput(Mote mote, int id, long time, String msg) { try { @@ -195,37 +200,31 @@ public class LogScriptEngine { } scriptActive = false; - if (semaphoreScript == null) { - /*logger.warn("semaphoreScript is not initialized");*/ - } - if (semaphoreSim == null) { - /*logger.warn("semaphoreSim is not initialized");*/ - } - if (scriptThread == null) { - /*logger.warn("scriptThread is not initialized");*/ - } - timeoutEvent.remove(); timeoutProgressEvent.remove(); - + simulation.getEventCentral().removeLogOutputListener(logOutputListener); engine.put("SHUTDOWN", true); try { - semaphoreScript.release(100); + if (semaphoreScript != null) { + semaphoreScript.release(100); + } } catch (Exception e) { } finally { semaphoreScript = null; } try { - semaphoreSim.release(100); + if (semaphoreSim != null) { + semaphoreSim.release(100); + } } catch (Exception e) { } finally { semaphoreSim = null; } - if (scriptThread != null && + if (scriptThread != null && scriptThread != Thread.currentThread() /* XXX May deadlock */ ) { try { scriptThread.join(); @@ -235,7 +234,6 @@ public class LogScriptEngine { } } scriptThread = null; - } public void activateScript(String scriptCode) throws ScriptException { @@ -245,43 +243,29 @@ public class LogScriptEngine { scriptActive = true; if (semaphoreScript != null) { - logger.warn("semaphoreScript is already initialized"); + logger.warn("Semaphores were not reset correctly"); + semaphoreScript.release(100); + semaphoreScript = null; } if (semaphoreSim != null) { - logger.warn("semaphoreSim is already initialized"); - } - if (scriptThread != null) { - logger.warn("scriptThread is already initialized"); + logger.warn("Semaphores were not reset correctly"); + semaphoreSim.release(100); + semaphoreSim = null; } + scriptThread = null; /* Parse current script */ ScriptParser parser = new ScriptParser(scriptCode); String jsCode = parser.getJSCode(); - long timeoutTime = parser.getTimeoutTime(); - if (timeoutTime < 0) { - logger.info("No timeout defined, using default (us): " + DEFAULT_TIMEOUT); - timeoutTime = DEFAULT_TIMEOUT; + timeout = parser.getTimeoutTime(); + if (timeout < 0) { + timeout = DEFAULT_TIMEOUT; + logger.info("Default script timeout in " + (timeout/Simulation.MILLISECOND) + " ms"); + } else { + logger.info("Script timeout in " + (timeout/Simulation.MILLISECOND) + " ms"); } - final long duration = timeoutTime; - simulation.invokeSimulationThread(new Runnable() { - public void run() { - final long startTime = simulation.getSimulationTime(); - final long interval = (long) (0.01*5*duration); - - simulation.scheduleEvent(timeoutProgressEvent = new TimeEvent(0) { - public void execute(long t) { - int percent = (int) (5*(t-startTime)/interval); - logger.info("Test script at " + percent + "%"); - simulation.scheduleEvent(this, t+interval); - } - }, startTime+interval); - - simulation.scheduleEvent(timeoutEvent, startTime + duration); - } - }); - engine.eval(jsCode); /* Setup script control */ @@ -352,95 +336,7 @@ public class LogScriptEngine { simulation.getEventCentral().addLogOutputListener(logOutputListener); /* Create script output logger */ - engine.put("log", new ScriptLog() { - public void log(String msg) { - if (scriptLogObserver != null) { - scriptLogObserver.update(null, msg); - } - } - public void append(String filename, String msg) { - try{ - FileWriter fstream = new FileWriter(filename, true); - BufferedWriter out = new BufferedWriter(fstream); - out.write(msg); - out.close(); - } catch (Exception e) { - logger.warn("Test append failed: " + filename + ": " + e.getMessage()); - } - } - public void testOK() { - log("TEST OK\n"); - - if (GUI.isVisualized()) { - log("[if test was run without visualization, COOJA would now have been terminated]\n"); - stopSimulation = true; - simulation.invokeSimulationThread(stopSimulationRunnable); - } else { - quitCooja = true; - simulation.invokeSimulationThread(quitRunnable); - } - - timeoutEvent.remove(); - timeoutProgressEvent.remove(); - - semaphoreSim.release(100); - throw new RuntimeException("test script killed"); - } - public void writeFile(String filename, String msg) { - try{ - FileWriter fstream = new FileWriter(filename, false); - BufferedWriter out = new BufferedWriter(fstream); - out.write(msg); - out.close(); - } catch (Exception e) { - logger.warn("Write file failed: " + filename + ": " + e.getMessage()); - } - } - public void testFailed() { - log("TEST FAILED\n"); - - if (GUI.isVisualized()) { - log("[if test was run without visualization, COOJA would now have been terminated]\n"); - stopSimulation = true; - simulation.invokeSimulationThread(stopSimulationRunnable); - } else { - quitCooja = true; - simulation.invokeSimulationThread(quitRunnable); - } - - semaphoreSim.release(100); - throw new RuntimeException("test script killed"); - } - - public void generateMessage(final long delay, final String msg) { - final Mote currentMote = (Mote) engine.get("mote"); - final TimeEvent generateEvent = new TimeEvent(0) { - public void execute(long t) { - if (scriptThread == null || - !scriptThread.isAlive()) { - logger.info("script thread not alive. try deactivating script."); - /*scriptThread.isInterrupted()*/ - return; - } - - /* Update script variables */ - engine.put("mote", currentMote); - engine.put("id", currentMote.getID()); - engine.put("time", currentMote.getSimulation().getSimulationTime()); - engine.put("msg", msg); - - stepScript(); - } - }; - simulation.invokeSimulationThread(new Runnable() { - public void run() { - simulation.scheduleEvent( - generateEvent, - simulation.getSimulationTime() + delay*Simulation.MILLISECOND); - } - }); - } - }); + engine.put("log", scriptLog); Hashtable hash = new Hashtable(); engine.put("global", hash); @@ -449,27 +345,53 @@ public class LogScriptEngine { scriptMote = new ScriptMote(); engine.put("node", scriptMote); + + Runnable activate = new Runnable() { + public void run() { + startRealTime = System.currentTimeMillis(); + startTime = simulation.getSimulationTime(); + long endTime = startTime + timeout; + nextProgress = startTime + (endTime - startTime)/20; + + timeoutProgressEvent.remove(); + simulation.scheduleEvent(timeoutProgressEvent, nextProgress); + timeoutEvent.remove(); + simulation.scheduleEvent(timeoutEvent, endTime); + } + }; + if (simulation.isRunning()) { + simulation.invokeSimulationThread(activate); + } else { + activate.run(); + } } private TimeEvent timeoutEvent = new TimeEvent(0) { public void execute(long t) { - if (!scriptActive) { - return; - } + if (!scriptActive) { + return; + } logger.info("Timeout event @ " + t); engine.put("TIMEOUT", true); stepScript(); } }; private TimeEvent timeoutProgressEvent = new TimeEvent(0) { - public void execute(long t) { } - }; - + public void execute(long t) { + nextProgress = t + timeout/20; + simulation.scheduleEvent(this, nextProgress); + + double progress = 1.0*(t - startTime)/timeout; + long realDuration = System.currentTimeMillis()-startRealTime; + double estimatedLeft = 1.0*realDuration/progress - realDuration; + if (estimatedLeft == 0) estimatedLeft = 1; + logger.info(String.format("Test script at %2.2f%%, done in %2.1f sec", 100*progress, estimatedLeft/1000)); + } + }; + private Runnable stopSimulationRunnable = new Runnable() { public void run() { simulation.stopSimulation(); - timeoutEvent.remove(); - timeoutProgressEvent.remove(); } }; private Runnable quitRunnable = new Runnable() { @@ -491,4 +413,83 @@ public class LogScriptEngine { } }; + private ScriptLog scriptLog = new ScriptLog() { + public void log(String msg) { + if (scriptLogObserver != null) { + scriptLogObserver.update(null, msg); + } + } + public void append(String filename, String msg) { + try{ + FileWriter fstream = new FileWriter(filename, true); + BufferedWriter out = new BufferedWriter(fstream); + out.write(msg); + out.close(); + } catch (Exception e) { + logger.warn("Test append failed: " + filename + ": " + e.getMessage()); + } + } + public void writeFile(String filename, String msg) { + try{ + FileWriter fstream = new FileWriter(filename, false); + BufferedWriter out = new BufferedWriter(fstream); + out.write(msg); + out.close(); + } catch (Exception e) { + logger.warn("Write file failed: " + filename + ": " + e.getMessage()); + } + } + + public void testOK() { + log("TEST OK\n"); + deactive(); + } + public void testFailed() { + log("TEST FAILED\n"); + deactive(); + } + private void deactive() { + deactivateScript(); + + if (GUI.isVisualized()) { + log("[if test was run without visualization, COOJA would now have been terminated]\n"); + stopSimulation = true; + simulation.invokeSimulationThread(stopSimulationRunnable); + } else { + quitCooja = true; + simulation.invokeSimulationThread(quitRunnable); + } + + throw new RuntimeException("test script killed"); + } + + public void generateMessage(final long delay, final String msg) { + final Mote currentMote = (Mote) engine.get("mote"); + final TimeEvent generateEvent = new TimeEvent(0) { + public void execute(long t) { + if (scriptThread == null || + !scriptThread.isAlive()) { + logger.info("script thread not alive. try deactivating script."); + /*scriptThread.isInterrupted()*/ + return; + } + + /* Update script variables */ + engine.put("mote", currentMote); + engine.put("id", currentMote.getID()); + engine.put("time", currentMote.getSimulation().getSimulationTime()); + engine.put("msg", msg); + + stepScript(); + } + }; + simulation.invokeSimulationThread(new Runnable() { + public void run() { + simulation.scheduleEvent( + generateEvent, + simulation.getSimulationTime() + delay*Simulation.MILLISECOND); + } + }); + } + }; } diff --git a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java index 4acb1b165..d67d86384 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java +++ b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java @@ -92,8 +92,7 @@ public class ScriptRunner extends VisPlugin { static boolean headless; { - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - headless = ge.isHeadless(); + headless = GraphicsEnvironment.isHeadless(); if (!headless) { DefaultSyntaxKit.initKit(); } @@ -183,10 +182,14 @@ public class ScriptRunner extends VisPlugin { toggleButton = new JButton("Activate"); toggleButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { - if (toggleButton.getText().equals("Activate")) { - setScriptActive(true); - } else { - setScriptActive(false); + try { + if (!isActive()) { + setScriptActive(true); + } else { + setScriptActive(false); + } + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); } } }); @@ -276,7 +279,8 @@ public class ScriptRunner extends VisPlugin { updateTitle(); } - public void setScriptActive(boolean active) { + public void setScriptActive(boolean active) + throws Exception { if (active) { /* setScriptActive(true) */ @@ -612,7 +616,10 @@ public class ScriptRunner extends VisPlugin { } public void closePlugin() { - setScriptActive(false); + try { + setScriptActive(false); + } catch (Exception e) { + } } public boolean setConfigXML(Collection configXML, boolean visAvailable) { @@ -628,14 +635,22 @@ public class ScriptRunner extends VisPlugin { } else if ("active".equals(name)) { boolean active = Boolean.parseBoolean(element.getText()); if (GUI.isVisualized()) { - setScriptActive(active); + try { + setScriptActive(active); + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); + } } } } if (!GUI.isVisualized()) { /* Automatically activate script */ - setScriptActive(true); + try { + setScriptActive(true); + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); + } simulation.setDelayTime(0); simulation.startSimulation(); }