/* * Copyright (c) 2006, Swedish Institute of Computer Science. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. 2. Redistributions in * binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. 3. Neither the name of the * Institute nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: GUI.java,v 1.77 2008/04/22 13:04:43 fros4943 Exp $ */ package se.sics.cooja; import java.awt.*; import java.awt.Dialog.ModalityType; import java.awt.event.*; import java.beans.PropertyVetoException; import java.io.*; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessControlException; import java.util.*; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.*; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.filechooser.FileFilter; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Logger; import org.apache.log4j.xml.DOMConfigurator; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import se.sics.cooja.MoteType.MoteTypeCreationException; import se.sics.cooja.contikimote.*; import se.sics.cooja.dialogs.*; import se.sics.cooja.plugins.*; /** * Main file of COOJA Simulator. Typically contains a visualizer for the * simulator, but can also be started without visualizer. * * This class loads external Java classes (in project directories), and handles the * COOJA plugins as well as the configuration system. If provides a number of * help methods for the rest of the COOJA system, and is the starting point for * loading and saving simulation configs. * * @author Fredrik Osterlind */ public class GUI extends Observable { /** * External tools default Win32 settings filename. */ public static final String EXTERNAL_TOOLS_WIN32_SETTINGS_FILENAME = "/external_tools_win32.config"; /** * External tools default Mac OS X settings filename. */ public static final String EXTERNAL_TOOLS_MACOSX_SETTINGS_FILENAME = "/external_tools_macosx.config"; /** * External tools default Linux/Unix settings filename. */ public static final String EXTERNAL_TOOLS_LINUX_SETTINGS_FILENAME = "/external_tools_linux.config"; /** * External tools user settings filename. */ public static final String EXTERNAL_TOOLS_USER_SETTINGS_FILENAME = ".cooja.user.properties"; public static File externalToolsUserSettingsFile; private static boolean externalToolsUserSettingsFileReadOnly = false; private static String specifiedContikiPath = null; /** * Logger settings filename. */ public static final String LOG_CONFIG_FILE = "log4j_config.xml"; /** * Default project configuration filename. */ public static String PROJECT_DEFAULT_CONFIG_FILENAME = null; /** * User project configuration filename. */ public static final String PROJECT_CONFIG_FILENAME = "cooja.config"; /** * File filter only showing saved simulations files (*.csc). */ public static final FileFilter SAVED_SIMULATIONS_FILES = new FileFilter() { public boolean accept(File file) { if (file.isDirectory()) { return true; } if (file.getName().endsWith(".csc")) { return true; } return false; } public String getDescription() { return "COOJA Configuration files"; } public String toString() { return ".csc"; } }; private static JFrame frame = null; private static JApplet applet = null; private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(GUI.class); // External tools setting names private static Properties defaultExternalToolsSettings; private static Properties currentExternalToolsSettings; private static final String externalToolsSettingNames[] = new String[] { "PATH_CONTIKI", "PATH_COOJA_CORE_RELATIVE", "PATH_MAKE", "PATH_SHELL", "PATH_C_COMPILER", "COMPILER_ARGS", "PATH_LINKER", "LINK_COMMAND_1", "LINK_COMMAND_2", "PATH_AR", "AR_COMMAND_1", "AR_COMMAND_2", "PATH_OBJDUMP", "OBJDUMP_ARGS", "PATH_JAVAC", "CONTIKI_STANDARD_PROCESSES", "CONTIKI_MAIN_TEMPLATE_FILENAME", "CMD_GREP_PROCESSES", "REGEXP_PARSE_PROCESSES", "CMD_GREP_INTERFACES", "REGEXP_PARSE_INTERFACES", "CMD_GREP_SENSORS", "REGEXP_PARSE_SENSORS", "DEFAULT_PROJECTDIRS", "CORECOMM_TEMPLATE_FILENAME", "MAPFILE_DATA_START", "MAPFILE_DATA_SIZE", "MAPFILE_BSS_START", "MAPFILE_BSS_SIZE", "MAPFILE_VAR_NAME", "MAPFILE_VAR_ADDRESS_1", "MAPFILE_VAR_ADDRESS_2", "MAPFILE_VAR_SIZE_1", "MAPFILE_VAR_SIZE_2", "PARSE_WITH_COMMAND", "PARSE_COMMAND", "COMMAND_VAR_NAME_ADDRESS", "COMMAND_DATA_START", "COMMAND_DATA_END", "COMMAND_BSS_START", "COMMAND_BSS_END", }; private static final int FRAME_NEW_OFFSET = 30; private static final int FRAME_STANDARD_WIDTH = 150; private static final int FRAME_STANDARD_HEIGHT = 300; private GUI myGUI; private Simulation mySimulation; protected GUIEventHandler guiEventHandler = new GUIEventHandler(); private JMenu menuPlugins, menuMoteTypeClasses, menuMoteTypes; private JMenu menuOpenSimulation, menuConfOpenSimulation; private Vector> menuMotePluginClasses; private JDesktopPane myDesktopPane; private Vector startedPlugins = new Vector(); // Platform configuration variables // Maintained via method reparseProjectConfig() private ProjectConfig projectConfig; private Vector currentProjectDirs = new Vector(); private ClassLoader projectDirClassLoader; private Vector> moteTypeClasses = new Vector>(); private Vector> pluginClasses = new Vector>(); private Vector> pluginClassesTemporary = new Vector>(); private Vector> radioMediumClasses = new Vector>(); private Vector> ipDistributorClasses = new Vector>(); private Vector> positionerClasses = new Vector>(); // Mote highlight observable private class HighlightObservable extends Observable { private void highlightMote(Mote mote) { setChanged(); notifyObservers(mote); } } private HighlightObservable moteHighlightObservable = new HighlightObservable(); /** * Creates a new COOJA Simulator GUI. * * @param desktop Desktop pane */ public GUI(JDesktopPane desktop) { myGUI = this; mySimulation = null; myDesktopPane = desktop; if (menuPlugins == null) { menuPlugins = new JMenu("Plugins"); } if (menuMotePluginClasses == null) { menuMotePluginClasses = new Vector>(); } // Load default and overwrite with user settings (if any) loadExternalToolsDefaultSettings(); loadExternalToolsUserSettings(); if (specifiedContikiPath != null) { setExternalToolsSetting("PATH_CONTIKI", specifiedContikiPath); } // Register default project directories String defaultProjectDirs = getExternalToolsSetting( "DEFAULT_PROJECTDIRS", null); if (defaultProjectDirs != null) { if (!isVisualizedInApplet()) { String[] defaultProjectDirsArr = defaultProjectDirs.split(";"); if (defaultProjectDirsArr.length > 0) { for (String defaultProjectDir : defaultProjectDirsArr) { File projectDir = new File(defaultProjectDir); if (projectDir.exists() && projectDir.isDirectory()) { currentProjectDirs.add(projectDir); } } } } // Load extendable parts (using current project config) try { reparseProjectConfig(); } catch (ParseProjectsException e) { logger.fatal("Error when loading project directories: " + e.getMessage()); e.printStackTrace(); if (myDesktopPane != null) { JOptionPane.showMessageDialog(GUI.getTopParentContainer(), "Loading project directories failed.\nStack trace printed to console.", "Error", JOptionPane.ERROR_MESSAGE); } } } // Start all standard GUI plugins for (Class visPluginClass : pluginClasses) { int pluginType = visPluginClass.getAnnotation(PluginType.class).value(); if (pluginType == PluginType.COOJA_STANDARD_PLUGIN) { startPlugin(visPluginClass, this, null, null); } } } /** * Add mote highlight observer. * * @see #deleteMoteHighlightObserver(Observer) * @param newObserver * New observer */ public void addMoteHighlightObserver(Observer newObserver) { moteHighlightObservable.addObserver(newObserver); } /** * Delete an mote highlight observer. * * @see #addMoteHighlightObserver(Observer) * @param observer * Observer to delete */ public void deleteMoteHighlightObserver(Observer observer) { moteHighlightObservable.deleteObserver(observer); } /** * @return True if simulator is visualized */ public static boolean isVisualized() { return isVisualizedInFrame() || isVisualizedInApplet(); } public static Container getTopParentContainer() { if (isVisualizedInFrame()) { return frame; } if (isVisualizedInApplet()) { /* Find parent frame for applet */ Container container = applet; while((container = container.getParent()) != null){ if (container instanceof Frame) { return container; } if (container instanceof Dialog) { return container; } if (container instanceof Window) { return container; } } logger.fatal("Returning null top owner container"); } return null; } public static boolean isVisualizedInFrame() { return frame != null; } public static URL getAppletCodeBase() { return applet.getCodeBase(); } public static boolean isVisualizedInApplet() { return applet != null; } /** * Tries to create/remove simulator visualizer. * * @param visualized Visualized */ public void setVisualizedInFrame(boolean visualized) { if (visualized) { if (!isVisualizedInFrame()) { configureFrame(myGUI, false); } } else { if (frame != null) { frame.setVisible(false); frame.dispose(); frame = null; } } } private Vector getFileHistory() { Vector history = new Vector(); // Fetch current history String[] historyArray = getExternalToolsSetting("SIMCFG_HISTORY", "").split(";"); for (String file: historyArray) { history.add(new File(file)); } return history; } private void addToFileHistory(File file) { // Fetch current history String[] history = getExternalToolsSetting("SIMCFG_HISTORY", "").split(";"); // Create new history String[] newHistory = null; if (history == null || history.length <= 1 && history[0].equals("")) { newHistory = new String[1]; } else { newHistory = new String[Math.min(5, history.length+1)]; System.arraycopy(history, 0, newHistory, 1, newHistory.length-1); } newHistory[0] = file.getAbsolutePath(); // Abort if file added is equal to last file if (history.length >= 1 && file.getAbsolutePath().equals(new File(history[0]).getAbsolutePath())) { return; } String newHistoryConfig = null; for (String path: newHistory) { if (newHistoryConfig == null) { newHistoryConfig = path; } else { newHistoryConfig += ";" + path; } } setExternalToolsSetting("SIMCFG_HISTORY", newHistoryConfig); saveExternalToolsUserSettings(); } private void updateOpenHistoryMenuItems() { menuConfOpenSimulation.removeAll(); if (isVisualizedInApplet()) { return; } JMenuItem browseItem = new JMenuItem("Browse..."); browseItem.setActionCommand("confopen sim"); browseItem.addActionListener(guiEventHandler); menuConfOpenSimulation.add(browseItem); menuConfOpenSimulation.add(new JSeparator()); Vector openFilesHistory = getFileHistory(); for (File file: openFilesHistory) { JMenuItem lastItem = new JMenuItem(file.getName()); lastItem.setActionCommand("confopen last sim"); lastItem.putClientProperty("file", file); lastItem.setToolTipText(file.getAbsolutePath()); lastItem.addActionListener(guiEventHandler); menuConfOpenSimulation.add(lastItem); } menuOpenSimulation.removeAll(); browseItem = new JMenuItem("Browse..."); browseItem.setActionCommand("open sim"); browseItem.addActionListener(guiEventHandler); menuOpenSimulation.add(browseItem); menuOpenSimulation.add(new JSeparator()); for (File file: openFilesHistory) { JMenuItem lastItem = new JMenuItem(file.getName()); lastItem.setActionCommand("open last sim"); lastItem.putClientProperty("file", file); lastItem.setToolTipText(file.getAbsolutePath()); lastItem.addActionListener(guiEventHandler); menuOpenSimulation.add(lastItem); } } private JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu; JMenuItem menuItem; // File menu menu = new JMenu("File"); menu.addMenuListener(new MenuListener() { public void menuSelected(MenuEvent e) { updateOpenHistoryMenuItems(); } public void menuDeselected(MenuEvent e) { } public void menuCanceled(MenuEvent e) { } }); menu.setMnemonic(KeyEvent.VK_F); menuBar.add(menu); menuItem = new JMenuItem("New simulation"); menuItem.setMnemonic(KeyEvent.VK_N); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK)); menuItem.setActionCommand("new sim"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); menuItem = new JMenuItem("Reload simulation"); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { reloadCurrentSimulation(false); } }); menu.add(menuItem); menuItem = new JMenuItem("Close simulation"); menuItem.setMnemonic(KeyEvent.VK_C); menuItem.setActionCommand("close sim"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); menuOpenSimulation = new JMenu("Open simulation"); menuOpenSimulation.setMnemonic(KeyEvent.VK_O); menu.add(menuOpenSimulation); if (isVisualizedInApplet()) { menuOpenSimulation.setEnabled(false); menuOpenSimulation.setToolTipText("Not available in applet version"); } menuConfOpenSimulation = new JMenu("Open & Reconfigure simulation"); menuConfOpenSimulation.setMnemonic(KeyEvent.VK_R); menu.add(menuConfOpenSimulation); if (isVisualizedInApplet()) { menuConfOpenSimulation.setEnabled(false); menuConfOpenSimulation.setToolTipText("Not available in applet version"); } menuItem = new JMenuItem("Save simulation"); menuItem.setMnemonic(KeyEvent.VK_S); menuItem.setActionCommand("save sim"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); if (isVisualizedInApplet()) { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } menu.addSeparator(); menuItem = new JMenuItem("Close all plugins"); menuItem.setActionCommand("close plugins"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); menu.addSeparator(); menuItem = new JMenuItem("Exit"); menuItem.setMnemonic(KeyEvent.VK_X); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK)); menuItem.setActionCommand("quit"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); if (isVisualizedInApplet()) { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } // Simulation menu menu = new JMenu("Simulation"); menu.setMnemonic(KeyEvent.VK_S); menuBar.add(menu); menuItem = new JMenuItem("Open Control"); menuItem.setMnemonic(KeyEvent.VK_C); menuItem.setActionCommand("start plugin"); menuItem.putClientProperty("class", SimControl.class); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); menuItem = new JMenuItem("Information"); menuItem.setMnemonic(KeyEvent.VK_I); menuItem.setActionCommand("start plugin"); menuItem.putClientProperty("class", SimInformation.class); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); // Mote type menu menu = new JMenu("Mote Types"); menu.setMnemonic(KeyEvent.VK_T); menuBar.add(menu); // Mote type classes sub menu menuMoteTypeClasses = new JMenu("Create mote type"); menuMoteTypeClasses.setMnemonic(KeyEvent.VK_C); menuMoteTypeClasses.addMenuListener(new MenuListener() { public void menuSelected(MenuEvent e) { // Clear menu menuMoteTypeClasses.removeAll(); // Recreate menu items JMenuItem menuItem; for (Class moteTypeClass : moteTypeClasses) { /* Sort mote types according to abstraction level */ String abstractionLevelDescription = GUI.getAbstractionLevelDescriptionOf(moteTypeClass); if(abstractionLevelDescription == null) { abstractionLevelDescription = "[unknown cross-level]"; } /* Check if abstraction description already exists */ JSeparator abstractionLevelSeparator = null; for (Component component: menuMoteTypeClasses.getMenuComponents()) { if (component == null || !(component instanceof JSeparator)) { continue; } JSeparator existing = (JSeparator) component; if (abstractionLevelDescription.equals(existing.getToolTipText())) { abstractionLevelSeparator = existing; break; } } if (abstractionLevelSeparator == null) { abstractionLevelSeparator = new JSeparator(); abstractionLevelSeparator.setToolTipText(abstractionLevelDescription); menuMoteTypeClasses.add(abstractionLevelSeparator); } String description = GUI.getDescriptionOf(moteTypeClass); menuItem = new JMenuItem(description); menuItem.setActionCommand("create mote type"); menuItem.putClientProperty("class", moteTypeClass); menuItem.setToolTipText(abstractionLevelDescription); menuItem.addActionListener(guiEventHandler); if (isVisualizedInApplet() && moteTypeClass.equals(ContikiMoteType.class)) { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } /* Add new item directly after cross level separator */ for (int i=0; i < menuMoteTypeClasses.getMenuComponentCount(); i++) { if (menuMoteTypeClasses.getMenuComponent(i) == abstractionLevelSeparator) { menuMoteTypeClasses.add(menuItem, i+1); break; } } } } public void menuDeselected(MenuEvent e) { } public void menuCanceled(MenuEvent e) { } }); menu.add(menuMoteTypeClasses); menuItem = new JMenuItem("Information"); menuItem.setActionCommand("start plugin"); menuItem.putClientProperty("class", MoteTypeInformation.class); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); // Mote menu menu = new JMenu("Motes"); menu.setMnemonic(KeyEvent.VK_M); menuBar.add(menu); // Mote types sub menu menuMoteTypes = new JMenu("Add motes of type"); menuMoteTypes.setMnemonic(KeyEvent.VK_A); menuMoteTypes.addMenuListener(new MenuListener() { public void menuSelected(MenuEvent e) { // Clear menu menuMoteTypes.removeAll(); if (mySimulation == null) { return; } // Recreate menu items JMenuItem menuItem; for (MoteType moteType : mySimulation.getMoteTypes()) { menuItem = new JMenuItem(moteType.getDescription()); menuItem.setActionCommand("add motes"); menuItem.setToolTipText(getDescriptionOf(moteType.getClass())); menuItem.putClientProperty("motetype", moteType); menuItem.addActionListener(guiEventHandler); menuMoteTypes.add(menuItem); } } public void menuDeselected(MenuEvent e) { } public void menuCanceled(MenuEvent e) { } }); menu.add(menuMoteTypes); menuItem = new JMenuItem("Remove all motes"); menuItem.setActionCommand("remove all motes"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); // Plugins menu if (menuPlugins == null) { menuPlugins = new JMenu("Plugins"); } else { menuPlugins.setText("Plugins"); } menuPlugins.setMnemonic(KeyEvent.VK_P); menuBar.add(menuPlugins); // Settings menu menu = new JMenu("Settings"); menuBar.add(menu); menuItem = new JMenuItem("External tools paths"); menuItem.setActionCommand("edit paths"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); if (isVisualizedInApplet()) { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } menuItem = new JMenuItem("Manage project directories"); menuItem.setActionCommand("manage projects"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); if (isVisualizedInApplet()) { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } menu.addSeparator(); menuItem = new JMenuItem("Java version: " + System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + ")"); menuItem.setEnabled(false); menu.add(menuItem); // Mote plugins popup menu (not available via menu bar) if (menuMotePluginClasses == null) { menuMotePluginClasses = new Vector>(); } return menuBar; } private static void configureFrame(final GUI gui, boolean createSimDialog) { // Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true); JDialog.setDefaultLookAndFeelDecorated(true); Rectangle maxSize = GraphicsEnvironment.getLocalGraphicsEnvironment() .getMaximumWindowBounds(); // Create and set up the window. frame = new JFrame("COOJA Simulator"); if (maxSize != null) { frame.setMaximizedBounds(maxSize); } frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // Add menu bar frame.setJMenuBar(gui.createMenuBar()); JComponent newContentPane = gui.getDesktopPane(); newContentPane.setOpaque(true); frame.setContentPane(newContentPane); frame.setSize(700, 700); frame.setLocationRelativeTo(null); frame.addWindowListener(gui.guiEventHandler); // Restore last frame size and position int framePosX = Integer.parseInt(getExternalToolsSetting("FRAME_POS_X", "-1")); int framePosY = Integer.parseInt(getExternalToolsSetting("FRAME_POS_Y", "-1")); int frameWidth = Integer.parseInt(getExternalToolsSetting("FRAME_WIDTH", "-1")); int frameHeight = Integer.parseInt(getExternalToolsSetting("FRAME_HEIGHT", "-1")); if (framePosX >= 0 && framePosY >= 0 && frameWidth > 0 && frameHeight > 0) { frame.setLocation(framePosX, framePosY); frame.setSize(frameWidth, frameHeight); // Assume window was maximized if loaded size matches maximum bounds if (maxSize != null && framePosX == 0 && framePosY == 0 && frameWidth == maxSize.width && frameHeight == maxSize.height) { frame.setExtendedState(JFrame.MAXIMIZED_BOTH); } } // Display the window. frame.setVisible(true); if (createSimDialog) { SwingUtilities.invokeLater(new Runnable() { public void run() { gui.doCreateSimulation(true); } }); } } private static void configureApplet(final GUI gui, boolean createSimDialog) { applet = CoojaApplet.applet; // Make sure we have nice window decorations. try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { } // Add menu bar JMenuBar menuBar = gui.createMenuBar(); applet.setJMenuBar(menuBar); JComponent newContentPane = gui.getDesktopPane(); newContentPane.setOpaque(true); applet.setContentPane(newContentPane); applet.setSize(700, 700); if (createSimDialog) { SwingUtilities.invokeLater(new Runnable() { public void run() { gui.doCreateSimulation(true); } }); } } /** * @return Current desktop pane (simulator visualizer) */ public JDesktopPane getDesktopPane() { return myDesktopPane; } /** * Quick-starts a simulation using given parameters. TODO Experimental code * * @param moteTypeID * Mote type ID (if null "mtype1" will be used) * @param projectDirs * GUI project directories * @param sensors * Contiki sensors (if null sensors will be scanned for) * @param coreInterfaces * COOJA core interfaces (if null interfaces will be scanned for) * @param userProcesses * Contiki user processes (if null processes all in given main file * will be added) * @param addAutostartProcesses * Should autostart processes automatically be added? * @param numberOfNodes * Number of nodes to add * @param areaSideLength * Side of node positioning square * @param delayTime * Initial delay time * @param simulationStartinge * Simulation automatically started? * @param filename * Main Contiki process file * @param contikiPath * Contiki path * @return True if simulation was quickstarted correctly */ private static boolean quickStartSimulation(String moteTypeID, Vector projectDirs, Vector sensors, Vector coreInterfaces, Vector userProcesses, boolean addAutostartProcesses, int numberOfNodes, double areaSideLength, int delayTime, boolean simulationStarting, String filename, String contikiPath) { // Create GUI and GUI frame (not visible yet) JFrame.setDefaultLookAndFeelDecorated(true); JDialog.setDefaultLookAndFeelDecorated(true); logger.info("> Creating GUI and main frame (invisible)"); frame = new JFrame("COOJA Simulator"); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // Create and set up the content pane. JDesktopPane desktop = new JDesktopPane(); desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); GUI gui = new GUI(desktop); // loads external settings and creates initial project config // Add menu bar frame.setSize(700, 700); frame.addWindowListener(gui.guiEventHandler); JComponent newContentPane = gui.getDesktopPane(); newContentPane.setOpaque(true); frame.setContentPane(newContentPane); frame.setLocationRelativeTo(null); // Set manual Contiki path if specified if (contikiPath != null) { setExternalToolsSetting("PATH_CONTIKI", contikiPath); } // Parse project directories and create config if (projectDirs == null) { projectDirs = new Vector(); projectDirs.add("."); } // TODO Should add user prop projects as well here... logger.info("> Reparsing project directories and creating config"); for (String projectDir : projectDirs) { logger.info(">> Adding: " + projectDir); // Check if config file exists File configFile = new File(projectDir + File.separatorChar + PROJECT_CONFIG_FILENAME); if (!configFile.exists()) { logger.debug(">>> Creating empty cooja.config file"); try { configFile.createNewFile(); } catch (IOException e) { logger.fatal(">> Error when creating cooja.config file, aborting"); return false; } } gui.currentProjectDirs.add(new File(projectDir)); } try { gui.reparseProjectConfig(); } catch (ParseProjectsException e) { logger.fatal(">> Error when parsing project directories: " + e.getMessage()); return false; } // Check file permissions and paths logger.info("> Checking paths and file permissions"); if (moteTypeID == null) { moteTypeID = "mtype1"; } File contikiBaseDir = new File(getExternalToolsSetting("PATH_CONTIKI")); File contikiCoreDir = new File(contikiBaseDir, getExternalToolsSetting("PATH_COOJA_CORE_RELATIVE")); File libFile = new File(ContikiMoteType.tempOutputDirectory, moteTypeID + ContikiMoteType.librarySuffix); File mapFile = new File(ContikiMoteType.tempOutputDirectory, moteTypeID + ContikiMoteType.mapSuffix); File depFile = new File(ContikiMoteType.tempOutputDirectory, moteTypeID + ContikiMoteType.dependSuffix); if (libFile.exists()) { libFile.delete(); } if (depFile.exists()) { depFile.delete(); } if (mapFile.exists()) { mapFile.delete(); } if (libFile.exists()) { logger.fatal(">> Can't delete output file, aborting: " + libFile); return false; } if (depFile.exists()) { logger.fatal(">> Can't delete output file, aborting: " + depFile); return false; } if (mapFile.exists()) { logger.fatal(">> Can't delete output file, aborting: " + mapFile); return false; } // Search for main file in current directory (or arg) File mainProcessFile = new File(filename); logger.info(">> Searching main process file: " + mainProcessFile.getAbsolutePath()); if (!mainProcessFile.exists()) { logger.info(">> Searching main process file: " + mainProcessFile.getAbsolutePath()); boolean foundFile = false; for (String projectDir : projectDirs) { mainProcessFile = new File(projectDir, filename); logger.info(">> Searching main process file: " + mainProcessFile.getAbsolutePath()); if (mainProcessFile.exists()) { foundFile = true; break; } } if (!foundFile) { logger.fatal(">> Could not locate main process file, aborting"); return false; } } // Setup compilation arguments logger.info("> Setting up compilation arguments"); Vector filesToCompile = new Vector(); filesToCompile.add(mainProcessFile); // main process file for (String projectDir : projectDirs) { // project directories filesToCompile.add(new File(projectDir)); } String[] projectSources = // project config sources gui.getProjectConfig().getStringArrayValue(ContikiMoteType.class, "C_SOURCES"); for (String projectSource : projectSources) { if (!projectSource.equals("")) { File file = new File(projectSource); if (file.getParent() != null) { // Find which project directory added this file File projectDir = gui.getProjectConfig().getUserProjectDefining( ContikiMoteType.class, "C_SOURCES", projectSource); if (projectDir != null) { // We found a project directory - Add it filesToCompile.add(new File(projectDir.getPath(), file .getParent())); } } filesToCompile.add(new File(file.getName())); } } // Scan for sensors if (sensors == null) { logger.info("> Scanning for sensors"); sensors = new Vector(); Vector scannedSensorInfo = ContikiMoteTypeDialog .scanForSensors(contikiCoreDir); for (String projectDir : projectDirs) { // project directories scannedSensorInfo.addAll(ContikiMoteTypeDialog.scanForSensors(new File( projectDir))); } for (String[] sensorInfo : scannedSensorInfo) { // logger.info(">> Found and added: " + sensorInfo[1] + " (" + // sensorInfo[0] + ")"); sensors.add(sensorInfo[1]); } } // Scan for core interfaces if (coreInterfaces == null) { logger.info("> Scanning for core interfaces"); coreInterfaces = new Vector(); Vector scannedCoreInterfaceInfo = ContikiMoteTypeDialog .scanForInterfaces(contikiCoreDir); for (String projectDir : projectDirs) { // project directories scannedCoreInterfaceInfo.addAll(ContikiMoteTypeDialog .scanForInterfaces(new File(projectDir))); } for (String[] coreInterfaceInfo : scannedCoreInterfaceInfo) { // logger.info(">> Found and added: " + coreInterfaceInfo[1] + " (" + // coreInterfaceInfo[0] + ")"); coreInterfaces.add(coreInterfaceInfo[1]); } } // Scan for mote interfaces logger.info("> Loading mote interfaces"); String[] moteInterfaces = gui.getProjectConfig().getStringArrayValue( ContikiMoteType.class, "MOTE_INTERFACES"); Vector> moteIntfClasses = new Vector>(); for (String moteInterface : moteInterfaces) { try { Class newMoteInterfaceClass = gui .tryLoadClass(gui, MoteInterface.class, moteInterface); moteIntfClasses.add(newMoteInterfaceClass); // logger.info(">> Loaded mote interface: " + newMoteInterfaceClass); } catch (Exception e) { logger.fatal(">> Failed to load mote interface, aborting: " + moteInterface + ", " + e.getMessage()); return false; } } // Scan for processes if (userProcesses == null) { logger.info("> Scanning for user processes"); userProcesses = new Vector(); Vector autostartProcesses = new Vector(); Vector scannedProcessInfo = ContikiMoteTypeDialog .scanForProcesses(contikiCoreDir); for (String projectDir : projectDirs) { // project directories scannedProcessInfo.addAll(ContikiMoteTypeDialog .scanForProcesses(new File(projectDir))); } for (String[] processInfo : scannedProcessInfo) { if (processInfo[0].equals(mainProcessFile.getName())) { logger.info(">> Found and added: " + processInfo[1] + " (" + processInfo[0] + ")"); userProcesses.add(processInfo[1]); if (addAutostartProcesses) { // Parse any autostart processes try { // logger.info(">>> Parsing " + processInfo[0] + " for autostart // processes"); Vector autostarters = ContikiMoteTypeDialog .parseAutostartProcesses(mainProcessFile); if (autostarters != null) { autostartProcesses.addAll(autostarters); } } catch (Exception e) { logger .fatal(">>> Error when parsing autostart processes, aborting: " + e); return false; } } } else { // logger.info(">> Found and ignored: " + processInfo[1] + " (" + // processInfo[0] + ")"); } } if (addAutostartProcesses) { // Add autostart process sources if found logger.info("> Adding autostart processes"); for (String autostartProcess : autostartProcesses) { boolean alreadyExists = false; for (String existingProcess : userProcesses) { if (existingProcess.equals(autostartProcess)) { alreadyExists = true; break; } } if (!alreadyExists) { userProcesses.add(autostartProcess); logger.info(">> Added autostart process: " + autostartProcess); } } } } // Generate Contiki main source file logger.info("> Generating Contiki main source file"); if (!ContikiMoteType.tempOutputDirectory.exists()) { ContikiMoteType.tempOutputDirectory.mkdir(); } if (!ContikiMoteType.tempOutputDirectory.exists()) { logger.fatal(">> Could not create output directory: " + ContikiMoteType.tempOutputDirectory); return false; } try { String generatedFilename = ContikiMoteTypeDialog.generateSourceFile( moteTypeID, sensors, coreInterfaces, userProcesses); // logger.info(">> Generated source file: " + generatedFilename); } catch (Exception e) { logger.fatal(">> Error during file generation, aborting: " + e.getMessage()); return false; } // Compile library logger.info("> Compiling library (Rime comm stack)"); // TODO Warning, assuming Rime communication stack boolean compilationSucceded = ContikiMoteTypeDialog.compileLibrary( moteTypeID, contikiBaseDir, filesToCompile, false, ContikiMoteType.CommunicationStack.RIME, null, System.err); if (!libFile.exists() || !depFile.exists() || !mapFile.exists()) { compilationSucceded = false; } if (compilationSucceded) { // logger.info(">> Compilation complete"); } else { logger.fatal(">> Error during compilation, aborting"); return false; } // Create mote type logger.info("> Creating mote type"); ContikiMoteType moteType; try { moteType = new ContikiMoteType(moteTypeID); } catch (MoteTypeCreationException e) { logger.fatal("Exception when creating mote type: " + e); return false; } moteType.setDescription("Mote type: " + filename); moteType.setContikiBaseDir(contikiBaseDir.getPath()); moteType.setContikiCoreDir(contikiCoreDir.getPath()); moteType.setProjectDirs(new Vector()); moteType.setCompilationFiles(filesToCompile); moteType.setConfig(gui.getProjectConfig()); moteType.setProcesses(userProcesses); moteType.setSensors(sensors); moteType.setCoreInterfaces(coreInterfaces); moteType.setMoteInterfaces(moteIntfClasses); // Create simulation logger.info("> Creating simulation"); Simulation simulation = new Simulation(gui); simulation.setTitle("Quickstarted: " + filename); simulation.setDelayTime(delayTime); simulation.setSimulationTime(0); simulation.setTickTime(1); String radioMediumClassName = null; try { radioMediumClassName = gui.getProjectConfig().getStringArrayValue( GUI.class, "RADIOMEDIUMS")[0]; Class radioMediumClass = gui.tryLoadClass(gui, RadioMedium.class, radioMediumClassName); RadioMedium radioMedium = RadioMedium.generateRadioMedium( radioMediumClass, simulation); simulation.setRadioMedium(radioMedium); } catch (Exception e) { logger.fatal(">> Failed to load radio medium, aborting: " + radioMediumClassName + ", " + e); return false; } // Create nodes logger.info("> Creating motes"); Vector motes = new Vector(); Random random = new Random(); int nextMoteID = 1; int nextIP = 0; for (int i = 0; i < numberOfNodes; i++) { ContikiMote mote = (ContikiMote) moteType.generateMote(simulation); // Set random position if (mote.getInterfaces().getPosition() != null) { mote.getInterfaces().getPosition().setCoordinates( random.nextDouble() * areaSideLength, random.nextDouble() * areaSideLength, 0); } // Set unique mote ID's if (mote.getInterfaces().getMoteID() != null) { mote.getInterfaces().getMoteID().setMoteID(nextMoteID++); } // Set unique IP address if (mote.getInterfaces().getIPAddress() != null) { mote.getInterfaces().getIPAddress().setIPNumber((char) 10, (char) ((nextIP / (254 * 255)) % 255), (char) ((nextIP / 254) % 255), (char) (nextIP % 254 + 1)); nextIP++; } motes.add(mote); } // Add mote type and motes to simulation logger.info("> Adding motes and mote type to simulation"); simulation.addMoteType(moteType); for (Mote mote : motes) { simulation.addMote(mote); } // Add simulation to GUI logger.info("> Adding simulation to GUI"); gui.setSimulation(simulation); // Start plugins and try to place them wisely logger.info("> Starting plugin and showing GUI"); VisPlugin plugin = (VisPlugin) gui.startPlugin(VisState.class, gui, simulation, null); plugin.setLocation(350, 20); plugin = (VisPlugin) gui.startPlugin(VisTraffic.class, gui, simulation, null); plugin.setLocation(350, 340); plugin = (VisPlugin) gui.startPlugin(LogListener.class, gui, simulation, null); plugin.setLocation(20, 420); frame.setJMenuBar(gui.createMenuBar()); // Finally show GUI frame.setVisible(true); if (simulationStarting) { simulation.startSimulation(); } return true; } //// PROJECT CONFIG AND EXTENDABLE PARTS METHODS //// /** * Register new mote type class. * * @param moteTypeClass * Class to register */ public void registerMoteType(Class moteTypeClass) { moteTypeClasses.add(moteTypeClass); } /** * Unregister all mote type classes. */ public void unregisterMoteTypes() { moteTypeClasses.clear(); } /** * @return All registered mote type classes */ public Vector> getRegisteredMoteTypes() { return moteTypeClasses; } /** * Register new IP distributor class * * @param ipDistributorClass * Class to register * @return True if class was registered */ public boolean registerIPDistributor( Class ipDistributorClass) { // Check that vector constructor exists try { ipDistributorClass.getConstructor(new Class[] { Vector.class }); } catch (Exception e) { logger.fatal("No vector constructor found of IP distributor: " + ipDistributorClass); return false; } ipDistributorClasses.add(ipDistributorClass); return true; } /** * Unregister all IP distributors. */ public void unregisterIPDistributors() { ipDistributorClasses.clear(); } /** * @return All registered IP distributors */ public Vector> getRegisteredIPDistributors() { return ipDistributorClasses; } /** * Register new positioner class. * * @param positionerClass * Class to register * @return True if class was registered */ public boolean registerPositioner(Class positionerClass) { // Check that interval constructor exists try { positionerClass .getConstructor(new Class[] { int.class, double.class, double.class, double.class, double.class, double.class, double.class }); } catch (Exception e) { logger.fatal("No interval constructor found of positioner: " + positionerClass); return false; } positionerClasses.add(positionerClass); return true; } /** * Unregister all positioner classes. */ public void unregisterPositioners() { positionerClasses.clear(); } /** * @return All registered positioner classes */ public Vector> getRegisteredPositioners() { return positionerClasses; } /** * Register new radio medium class. * * @param radioMediumClass * Class to register * @return True if class was registered */ public boolean registerRadioMedium( Class radioMediumClass) { // Check that simulation constructor exists try { radioMediumClass.getConstructor(new Class[] { Simulation.class }); } catch (Exception e) { logger.fatal("No simulation constructor found of radio medium: " + radioMediumClass); return false; } radioMediumClasses.add(radioMediumClass); return true; } /** * Unregister all radio medium classes. */ public void unregisterRadioMediums() { radioMediumClasses.clear(); } /** * @return All registered radio medium classes */ public Vector> getRegisteredRadioMediums() { return radioMediumClasses; } /** * Builds new project configuration using current project directories settings. * Reregisters mote types, plugins, IP distributors, positioners and radio * mediums. This method may still return true even if all classes could not be * registered, but always returns false if all project directory configuration * files were not parsed correctly. * * Any registered temporary plugins will be saved and reregistered. */ public void reparseProjectConfig() throws ParseProjectsException { if (PROJECT_DEFAULT_CONFIG_FILENAME == null) { if (isVisualizedInApplet()) { PROJECT_DEFAULT_CONFIG_FILENAME = "/cooja_applet.config"; } else { PROJECT_DEFAULT_CONFIG_FILENAME = "/cooja_default.config"; } } // Backup temporary plugins Vector> oldTempPlugins = (Vector>) pluginClassesTemporary .clone(); // Reset current configuration unregisterMoteTypes(); unregisterPlugins(); unregisterIPDistributors(); unregisterPositioners(); unregisterRadioMediums(); try { // Read default configuration projectConfig = new ProjectConfig(true); } catch (FileNotFoundException e) { logger.fatal("Could not find default project config file: " + PROJECT_DEFAULT_CONFIG_FILENAME); throw (ParseProjectsException) new ParseProjectsException( "Could not find default project config file: " + PROJECT_DEFAULT_CONFIG_FILENAME).initCause(e); } catch (IOException e) { logger.fatal("Error when reading default project config file: " + PROJECT_DEFAULT_CONFIG_FILENAME); throw (ParseProjectsException) new ParseProjectsException( "Error when reading default project config file: " + PROJECT_DEFAULT_CONFIG_FILENAME).initCause(e); } if (!isVisualizedInApplet()) { // Append project directory configurations for (File projectDir : currentProjectDirs) { try { // Append config to general config projectConfig.appendProjectDir(projectDir); } catch (FileNotFoundException e) { logger.fatal("Could not find project config file: " + projectDir); throw (ParseProjectsException) new ParseProjectsException( "Could not find project config file: " + projectDir).initCause(e); } catch (IOException e) { logger.fatal("Error when reading project config file: " + projectDir); throw (ParseProjectsException) new ParseProjectsException( "Error when reading project config file: " + projectDir).initCause(e); } } // Create class loader try { projectDirClassLoader = createClassLoader(currentProjectDirs); } catch (ClassLoaderCreationException e) { throw (ParseProjectsException) new ParseProjectsException( "Error when creating class loader").initCause(e); } } else { projectDirClassLoader = null; } // Register mote types String[] moteTypeClassNames = projectConfig.getStringArrayValue(GUI.class, "MOTETYPES"); if (moteTypeClassNames != null) { for (String moteTypeClassName : moteTypeClassNames) { Class moteTypeClass = tryLoadClass(this, MoteType.class, moteTypeClassName); if (moteTypeClass != null) { registerMoteType(moteTypeClass); // logger.info("Loaded mote type class: " + moteTypeClassName); } else { logger.warn("Could not load mote type class: " + moteTypeClassName); } } } // Register plugins registerPlugin(SimControl.class, false); // Not in menu registerPlugin(SimInformation.class, false); // Not in menu registerPlugin(MoteTypeInformation.class, false); // Not in menu String[] pluginClassNames = projectConfig.getStringArrayValue(GUI.class, "PLUGINS"); if (pluginClassNames != null) { for (String pluginClassName : pluginClassNames) { Class pluginClass = tryLoadClass(this, Plugin.class, pluginClassName); if (pluginClass != null) { registerPlugin(pluginClass); // logger.info("Loaded plugin class: " + pluginClassName); } else { logger.warn("Could not load plugin class: " + pluginClassName); } } } // Reregister temporary plugins again if (oldTempPlugins != null) { for (Class pluginClass : oldTempPlugins) { if (registerTemporaryPlugin(pluginClass)) { // logger.info("Reregistered temporary plugin class: " + // getDescriptionOf(pluginClass)); } else { logger.warn("Could not reregister temporary plugin class: " + getDescriptionOf(pluginClass)); } } } // Register IP distributors String[] ipDistClassNames = projectConfig.getStringArrayValue(GUI.class, "IP_DISTRIBUTORS"); if (ipDistClassNames != null) { for (String ipDistClassName : ipDistClassNames) { Class ipDistClass = tryLoadClass(this, IPDistributor.class, ipDistClassName); if (ipDistClass != null) { registerIPDistributor(ipDistClass); // logger.info("Loaded IP distributor class: " + ipDistClassName); } else { logger .warn("Could not load IP distributor class: " + ipDistClassName); } } } // Register positioners String[] positionerClassNames = projectConfig.getStringArrayValue( GUI.class, "POSITIONERS"); if (positionerClassNames != null) { for (String positionerClassName : positionerClassNames) { Class positionerClass = tryLoadClass(this, Positioner.class, positionerClassName); if (positionerClass != null) { registerPositioner(positionerClass); // logger.info("Loaded positioner class: " + positionerClassName); } else { logger .warn("Could not load positioner class: " + positionerClassName); } } } // Register radio mediums String[] radioMediumsClassNames = projectConfig.getStringArrayValue( GUI.class, "RADIOMEDIUMS"); if (radioMediumsClassNames != null) { for (String radioMediumClassName : radioMediumsClassNames) { Class radioMediumClass = tryLoadClass(this, RadioMedium.class, radioMediumClassName); if (radioMediumClass != null) { registerRadioMedium(radioMediumClass); // logger.info("Loaded radio medium class: " + radioMediumClassName); } else { logger.warn("Could not load radio medium class: " + radioMediumClassName); } } } } /** * Returns the current project configuration common to the entire simulator. * * @return Current project configuration */ public ProjectConfig getProjectConfig() { return projectConfig; } /** * Returns the current project directories common to the entire simulator. * * @return Current project directories. */ public Vector getProjectDirs() { return currentProjectDirs; } // // PLUGIN METHODS //// /** * Show a started plugin in working area. * * @param plugin * Internal frame to add */ public void showPlugin(VisPlugin plugin) { int nrFrames = myDesktopPane.getAllFrames().length; myDesktopPane.add(plugin); // Set standard size if not specified by plugin itself if (plugin.getWidth() <= 0 || plugin.getHeight() <= 0) { plugin.setSize(FRAME_STANDARD_WIDTH, FRAME_STANDARD_HEIGHT); } // Set location if not already visible if (!plugin.isVisible()) { plugin.setLocation((nrFrames + 1) * FRAME_NEW_OFFSET, (nrFrames + 1) * FRAME_NEW_OFFSET); plugin.setVisible(true); } // Deselect all other plugins before selecting the new one try { for (JInternalFrame existingPlugin : myDesktopPane.getAllFrames()) { existingPlugin.setSelected(false); } plugin.setSelected(true); } catch (Exception e) { // Ignore } // Mote plugin to front myDesktopPane.moveToFront(plugin); } /** * Close all mote plugins for given mote. * * @param mote Mote */ public void closeMotePlugins(Mote mote) { Vector pluginsToRemove = new Vector(); for (Plugin startedPlugin : startedPlugins) { int pluginType = startedPlugin.getClass().getAnnotation(PluginType.class).value(); if (pluginType != PluginType.MOTE_PLUGIN) { continue; } Mote pluginMote = (Mote) startedPlugin.getTag(); if (pluginMote == mote) { pluginsToRemove.add(startedPlugin); } } for (Plugin pluginToRemove: pluginsToRemove) { removePlugin(pluginToRemove, false); } } /** * Remove a plugin from working area. * * @param plugin * Plugin to remove * @param askUser * If plugin is the last one, ask user if we should remove current * simulation also? */ public void removePlugin(Plugin plugin, boolean askUser) { // Clear any allocated resources and remove plugin plugin.closePlugin(); startedPlugins.remove(plugin); // Dispose plugin if it has visualizer if (plugin instanceof VisPlugin) { ((VisPlugin) plugin).dispose(); } if (getSimulation() != null && askUser && startedPlugins.isEmpty()) { String s1 = "Remove"; String s2 = "Cancel"; Object[] options = { s1, s2 }; int n = JOptionPane.showOptionDialog(frame, "You have an active simulation.\nDo you want to remove it?", "Remove current simulation?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, s1); if (n != JOptionPane.YES_OPTION) { return; } doRemoveSimulation(false); } } /** * Starts a plugin of given plugin class with given arguments. * * @param pluginClass * Plugin class * @param gui * GUI passed as argument to all plugins * @param simulation * Simulation passed as argument to mote and simulation plugins * @param mote * Mote passed as argument to mote plugins * @return Start plugin if any */ public Plugin startPlugin(Class pluginClass, GUI gui, Simulation simulation, Mote mote) { // Check that plugin class is registered if (!pluginClasses.contains(pluginClass)) { logger.fatal("Plugin class not registered: " + pluginClass); return null; } // Check that visualizer plugin is not started without GUI if (!isVisualized()) { try { pluginClass.asSubclass(VisPlugin.class); // Cast succeded, plugin is visualizer plugin! logger.fatal("Can't start visualizer plugin (no GUI): " + pluginClass); return null; } catch (ClassCastException e) { } } // Construct plugin depending on plugin type Plugin newPlugin = null; int pluginType = pluginClass.getAnnotation(PluginType.class).value(); try { if (pluginType == PluginType.MOTE_PLUGIN) { if (mote == null) { logger.fatal("Can't start mote plugin (no mote selected)"); return null; } newPlugin = pluginClass.getConstructor( new Class[] { Mote.class, Simulation.class, GUI.class }) .newInstance(mote, simulation, gui); // Tag plugin with mote newPlugin.tagWithObject(mote); } else if (pluginType == PluginType.SIM_PLUGIN || pluginType == PluginType.SIM_STANDARD_PLUGIN) { if (simulation == null) { logger.fatal("Can't start simulation plugin (no simulation)"); return null; } newPlugin = pluginClass.getConstructor( new Class[] { Simulation.class, GUI.class }).newInstance( simulation, gui); } else if (pluginType == PluginType.COOJA_PLUGIN || pluginType == PluginType.COOJA_STANDARD_PLUGIN) { if (gui == null) { logger.fatal("Can't start COOJA plugin (no GUI)"); return null; } newPlugin = pluginClass.getConstructor(new Class[] { GUI.class }) .newInstance(gui); } } catch (Exception e) { logger.fatal("Exception thrown when starting plugin: " + e); e.printStackTrace(); return null; } if (newPlugin == null) { return null; } // Add to active plugins list startedPlugins.add(newPlugin); // Show plugin if visualizer type if (newPlugin instanceof VisPlugin) { myGUI.showPlugin((VisPlugin) newPlugin); } return newPlugin; } /** * Register a plugin to be included in the GUI. The plugin will be visible in * the menubar. * * @param newPluginClass * New plugin to register * @return True if this plugin was registered ok, false otherwise */ public boolean registerPlugin(Class newPluginClass) { return registerPlugin(newPluginClass, true); } /** * Register a temporary plugin to be included in the GUI. The plugin will be * visible in the menubar. This plugin will automatically be unregistered if * the current simulation is removed. * * @param newPluginClass * New plugin to register * @return True if this plugin was registered ok, false otherwise */ public boolean registerTemporaryPlugin(Class newPluginClass) { if (pluginClasses.contains(newPluginClass)) { return false; } boolean returnVal = registerPlugin(newPluginClass, true); if (!returnVal) { return false; } pluginClassesTemporary.add(newPluginClass); return true; } /** * Unregister a plugin class. Removes any plugin menu items links as well. * * @param pluginClass * Plugin class to unregister */ public void unregisterPlugin(Class pluginClass) { // Remove (if existing) plugin class menu items for (Component menuComponent : menuPlugins.getMenuComponents()) { if (menuComponent.getClass().isAssignableFrom(JMenuItem.class)) { JMenuItem menuItem = (JMenuItem) menuComponent; if (menuItem.getClientProperty("class").equals(pluginClass)) { menuPlugins.remove(menuItem); } } } if (menuMotePluginClasses.contains(pluginClass)) { menuMotePluginClasses.remove(pluginClass); } // Remove from plugin vectors (including temporary) if (pluginClasses.contains(pluginClass)) { pluginClasses.remove(pluginClass); } if (pluginClassesTemporary.contains(pluginClass)) { pluginClassesTemporary.remove(pluginClass); } } /** * Register a plugin to be included in the GUI. * * @param newPluginClass * New plugin to register * @param addToMenu * Should this plugin be added to the dedicated plugins menubar? * @return True if this plugin was registered ok, false otherwise */ private boolean registerPlugin(Class newPluginClass, boolean addToMenu) { // Get description annotation (if any) String description = getDescriptionOf(newPluginClass); // Get plugin type annotation (required) int pluginType; if (newPluginClass.isAnnotationPresent(PluginType.class)) { pluginType = newPluginClass.getAnnotation(PluginType.class).value(); } else { pluginType = PluginType.UNDEFINED_PLUGIN; } // Check that plugin type is valid and constructor exists try { if (pluginType == PluginType.MOTE_PLUGIN) { newPluginClass.getConstructor(new Class[] { Mote.class, Simulation.class, GUI.class }); } else if (pluginType == PluginType.SIM_PLUGIN || pluginType == PluginType.SIM_STANDARD_PLUGIN) { newPluginClass .getConstructor(new Class[] { Simulation.class, GUI.class }); } else if (pluginType == PluginType.COOJA_PLUGIN || pluginType == PluginType.COOJA_STANDARD_PLUGIN) { newPluginClass.getConstructor(new Class[] { GUI.class }); } else { logger.fatal("Could not find valid plugin type annotation in class " + newPluginClass); return false; } } catch (NoSuchMethodException e) { logger.fatal("Could not find valid constructor in class " + newPluginClass + ": " + e); return false; } if (addToMenu && menuPlugins != null) { // Create 'start plugin'-menu item JMenuItem menuItem = new JMenuItem(description); menuItem.setActionCommand("start plugin"); menuItem.putClientProperty("class", newPluginClass); menuItem.addActionListener(guiEventHandler); menuPlugins.add(menuItem); if (pluginType == PluginType.MOTE_PLUGIN) { // Disable previous menu item and add new item to mote plugins menu menuItem.setEnabled(false); menuItem.setToolTipText("Mote plugin"); menuMotePluginClasses.add(newPluginClass); } } pluginClasses.add(newPluginClass); return true; } /** * Unregister all plugin classes, including temporary plugins. */ public void unregisterPlugins() { if (menuPlugins != null) { menuPlugins.removeAll(); } if (menuMotePluginClasses != null) { menuMotePluginClasses.clear(); } pluginClasses.clear(); pluginClassesTemporary.clear(); } /** * Return a mote plugins submenu for given mote. * * @param mote Mote * @return Mote plugins menu */ public JMenu createMotePluginsSubmenu(Mote mote) { JMenu menuMotePlugins = new JMenu("Open mote plugin for " + mote); for (Class motePluginClass: menuMotePluginClasses) { JMenuItem menuItem = new JMenuItem(getDescriptionOf(motePluginClass)); menuItem.setActionCommand("start plugin"); menuItem.putClientProperty("class", motePluginClass); menuItem.putClientProperty("mote", mote); menuItem.addActionListener(guiEventHandler); menuMotePlugins.add(menuItem); } return menuMotePlugins; } // // GUI CONTROL METHODS //// /** * @return Current simulation */ public Simulation getSimulation() { return mySimulation; } public void setSimulation(Simulation sim) { if (sim != null) { doRemoveSimulation(false); } mySimulation = sim; // Set frame title if (frame != null) { frame.setTitle("COOJA Simulator" + " - " + sim.getTitle()); } // Open standard plugins (if none opened already) if (startedPlugins.size() == 0) { for (Class pluginClass : pluginClasses) { int pluginType = pluginClass.getAnnotation(PluginType.class).value(); if (pluginType == PluginType.SIM_STANDARD_PLUGIN) { startPlugin(pluginClass, this, sim, null); } } } setChanged(); notifyObservers(); } /** * Creates a new mote type of the given mote type class. * * @param moteTypeClass * Mote type class */ public void doCreateMoteType(Class moteTypeClass) { if (mySimulation == null) { logger.fatal("Can't create mote type (no simulation)"); return; } // Stop simulation (if running) mySimulation.stopSimulation(); // Create mote type MoteType newMoteType = null; boolean moteTypeOK = false; try { newMoteType = moteTypeClass.newInstance(); moteTypeOK = newMoteType.configureAndInit(GUI.getTopParentContainer(), mySimulation, isVisualized()); } catch (InstantiationException e) { logger.fatal("Exception when creating mote type: " + e); return; } catch (IllegalAccessException e) { logger.fatal("Exception when creating mote type: " + e); return; } catch (MoteTypeCreationException e) { logger.fatal("Exception when creating mote type: " + e); return; } // Add mote type to simulation if (newMoteType != null && moteTypeOK) { mySimulation.addMoteType(newMoteType); } } /** * Remove current simulation * * @param askForConfirmation * Should we ask for confirmation if a simulation is already active? */ public void doRemoveSimulation(boolean askForConfirmation) { if (mySimulation != null) { if (askForConfirmation) { String s1 = "Remove"; String s2 = "Cancel"; Object[] options = { s1, s2 }; int n = JOptionPane.showOptionDialog(GUI.getTopParentContainer(), "You have an active simulation.\nDo you want to remove it?", "Remove current simulation?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, s1); if (n != JOptionPane.YES_OPTION) { return; } } // Close all started non-GUI plugins for (Object startedPlugin : startedPlugins.toArray()) { int pluginType = startedPlugin.getClass().getAnnotation( PluginType.class).value(); if (pluginType != PluginType.COOJA_PLUGIN && pluginType != PluginType.COOJA_STANDARD_PLUGIN) { removePlugin((Plugin) startedPlugin, false); } } // Delete simulation mySimulation.deleteObservers(); mySimulation.stopSimulation(); mySimulation = null; // Unregister temporary plugin classes Enumeration> pluginClasses = pluginClassesTemporary .elements(); while (pluginClasses.hasMoreElements()) { unregisterPlugin(pluginClasses.nextElement()); } // Reset frame title if (isVisualizedInFrame()) { frame.setTitle("COOJA Simulator"); } setChanged(); notifyObservers(); } } /** * Load a simulation configuration file from disk * * @param askForConfirmation Ask for confirmation before removing any current simulation * @param quick Quick-load simulation * @param configFile Configuration file to load, if null a dialog will appear */ public void doLoadConfig(boolean askForConfirmation, final boolean quick, File configFile) { if (isVisualizedInApplet()) { return; } /*if (CoreComm.hasLibraryBeenLoaded()) { JOptionPane.showMessageDialog(GUI.getTopParentContainer(), "Shared libraries has already been loaded.\nYou need to restart the simulator!", "Can't load simulation", JOptionPane.ERROR_MESSAGE); return; }*/ if (askForConfirmation && mySimulation != null) { String s1 = "Remove"; String s2 = "Cancel"; Object[] options = { s1, s2 }; int n = JOptionPane.showOptionDialog(GUI.getTopParentContainer(), "You have an active simulation.\nDo you want to remove it?", "Remove current simulation?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, s1); if (n != JOptionPane.YES_OPTION) { return; } } doRemoveSimulation(false); // Check already selected file, or select file using filechooser if (configFile != null) { if (!configFile.exists() || !configFile.canRead()) { logger.fatal("No read access to file"); return; } } else { JFileChooser fc = new JFileChooser(); fc.setFileFilter(GUI.SAVED_SIMULATIONS_FILES); // Suggest file using history Vector history = getFileHistory(); if (history != null && history.size() > 0) { File suggestedFile = getFileHistory().firstElement(); fc.setSelectedFile(suggestedFile); } int returnVal = fc.showOpenDialog(GUI.getTopParentContainer()); if (returnVal == JFileChooser.APPROVE_OPTION) { configFile = fc.getSelectedFile(); // Try adding extension if not founds if (!configFile.exists()) { configFile = new File(configFile.getParent(), configFile.getName() + SAVED_SIMULATIONS_FILES); } if (!configFile.exists() || !configFile.canRead()) { logger.fatal("No read access to file"); return; } } else { logger.info("Load command cancelled by user..."); return; } } // Load simulation in separate thread, while showing progress monitor final JDialog progressDialog; if (GUI.getTopParentContainer() instanceof Window) { progressDialog = new JDialog((Window) GUI.getTopParentContainer(), "Loading", ModalityType.APPLICATION_MODAL); } else if (GUI.getTopParentContainer() instanceof Frame) { progressDialog = new JDialog((Frame) GUI.getTopParentContainer(), "Loading", ModalityType.APPLICATION_MODAL); } else if (GUI.getTopParentContainer() instanceof Dialog) { progressDialog = new JDialog((Dialog) GUI.getTopParentContainer(), "Loading", ModalityType.APPLICATION_MODAL); } else { logger.warn("No parent container"); progressDialog = new JDialog((Frame) null, "Loading", ModalityType.APPLICATION_MODAL); } final File fileToLoad = configFile; final Thread loadThread = new Thread(new Runnable() { public void run() { Simulation newSim = null; try { newSim = loadSimulationConfig(fileToLoad, quick); addToFileHistory(fileToLoad); if (progressDialog != null && progressDialog.isDisplayable()) { progressDialog.dispose(); } } catch (UnsatisfiedLinkError e) { showErrorDialog(GUI.getTopParentContainer(), "Simulation load error", e, false); if (progressDialog != null && progressDialog.isDisplayable()) { progressDialog.dispose(); } newSim = null; } catch (SimulationCreationException e) { showErrorDialog(GUI.getTopParentContainer(), "Simulation load error", e, false); if (progressDialog != null && progressDialog.isDisplayable()) { progressDialog.dispose(); } newSim = null; } if (newSim != null) { myGUI.setSimulation(newSim); } } }); JPanel progressPanel = new JPanel(new BorderLayout()); JProgressBar progressBar; JButton button; progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setIndeterminate(true); button = new JButton("Cancel"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (loadThread != null && loadThread.isAlive()) { loadThread.interrupt(); doRemoveSimulation(false); } if (progressDialog != null && progressDialog.isDisplayable()) { progressDialog.dispose(); } } }); progressPanel.add(BorderLayout.CENTER, progressBar); progressPanel.add(BorderLayout.SOUTH, button); progressPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); progressPanel.setVisible(true); progressDialog.getContentPane().add(progressPanel); progressDialog.pack(); progressDialog.getRootPane().setDefaultButton(button); progressDialog.setLocationRelativeTo(GUI.getTopParentContainer()); progressDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); loadThread.start(); if (quick) { SwingUtilities.invokeLater(new Runnable() { public void run() { progressDialog.setVisible(true); } }); } } /** * Reloads current simulation. * This may include recompiling libraries and renaming mote type identifiers. */ public void reloadCurrentSimulation(final boolean autoStart) { if (getSimulation() == null) { logger.fatal("No simulation to reload"); return; } final JDialog progressDialog = new JDialog(frame, "Reloading", true); final Thread loadThread = new Thread(new Runnable() { public void run() { /* Get current simulation configuration */ Element root = new Element("simconf"); Element simulationElement = new Element("simulation"); simulationElement.addContent(getSimulation().getConfigXML()); root.addContent(simulationElement); Collection pluginsConfig = getPluginsConfigXML(); if (pluginsConfig != null) { root.addContent(pluginsConfig); } /* Remove current simulation, and load config */ boolean shouldRetry = false; do { try { shouldRetry = false; myGUI.doRemoveSimulation(false); Simulation newSim = loadSimulationConfig(root, true); myGUI.setSimulation(newSim); if (autoStart) { newSim.startSimulation(); } } catch (UnsatisfiedLinkError e) { shouldRetry = showErrorDialog(frame, "Simulation reload error", e, true); myGUI.doRemoveSimulation(false); } catch (SimulationCreationException e) { shouldRetry = showErrorDialog(frame, "Simulation reload error", e, true); myGUI.doRemoveSimulation(false); } } while (shouldRetry); if (progressDialog != null && progressDialog.isDisplayable()) { progressDialog.dispose(); } } }); // Display progress dialog while reloading JProgressBar progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setIndeterminate(true); JButton button = new JButton("Cancel"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (loadThread != null && loadThread.isAlive()) { loadThread.interrupt(); doRemoveSimulation(false); } if (progressDialog != null && progressDialog.isDisplayable()) { progressDialog.dispose(); } } }); JPanel progressPanel = new JPanel(new BorderLayout()); progressPanel.add(BorderLayout.CENTER, progressBar); progressPanel.add(BorderLayout.SOUTH, button); progressPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); progressPanel.setVisible(true); progressDialog.getContentPane().add(progressPanel); progressDialog.pack(); progressDialog.getRootPane().setDefaultButton(button); progressDialog.setLocationRelativeTo(frame); progressDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); loadThread.start(); progressDialog.setVisible(true); } /** * Save current simulation configuration to disk * * @param askForConfirmation * Ask for confirmation before overwriting file */ public void doSaveConfig(boolean askForConfirmation) { if (isVisualizedInApplet()) { return; } if (mySimulation != null) { mySimulation.stopSimulation(); JFileChooser fc = new JFileChooser(); fc.setFileFilter(GUI.SAVED_SIMULATIONS_FILES); // Suggest file using history Vector history = getFileHistory(); if (history != null && history.size() > 0) { File suggestedFile = getFileHistory().firstElement(); fc.setSelectedFile(suggestedFile); } int returnVal = fc.showSaveDialog(myDesktopPane); if (returnVal == JFileChooser.APPROVE_OPTION) { File saveFile = fc.getSelectedFile(); if (!fc.accept(saveFile)) { saveFile = new File(saveFile.getParent(), saveFile.getName() + SAVED_SIMULATIONS_FILES); } if (saveFile.exists()) { if (askForConfirmation) { String s1 = "Overwrite"; String s2 = "Cancel"; Object[] options = { s1, s2 }; int n = JOptionPane .showOptionDialog( GUI.getTopParentContainer(), "A file with the same name already exists.\nDo you want to remove it?", "Overwrite existing file?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, s1); if (n != JOptionPane.YES_OPTION) { return; } } } if (!saveFile.exists() || saveFile.canWrite()) { saveSimulationConfig(saveFile); addToFileHistory(saveFile); } else { logger.fatal("No write access to file"); } } else { logger.info("Save command cancelled by user..."); } } } /** * Add new mote to current simulation */ public void doAddMotes(MoteType moteType) { if (mySimulation != null) { mySimulation.stopSimulation(); Vector newMotes = AddMoteDialog.showDialog(getTopParentContainer(), mySimulation, moteType); if (newMotes != null) { for (Mote newMote : newMotes) { mySimulation.addMote(newMote); } } } else { logger.warn("No simulation active"); } } /** * Create a new simulation * * @param askForConfirmation * Should we ask for confirmation if a simulation is already active? */ public void doCreateSimulation(boolean askForConfirmation) { if (askForConfirmation && mySimulation != null) { String s1 = "Remove"; String s2 = "Cancel"; Object[] options = { s1, s2 }; int n = JOptionPane.showOptionDialog(GUI.getTopParentContainer(), "You have an active simulation.\nDo you want to remove it?", "Remove current simulation?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, s1); if (n != JOptionPane.YES_OPTION) { return; } } // Create new simulation doRemoveSimulation(false); Simulation newSim = new Simulation(this); boolean createdOK = CreateSimDialog.showDialog(GUI.getTopParentContainer(), newSim); if (createdOK) { myGUI.setSimulation(newSim); } } /** * Quit program * * @param askForConfirmation * Should we ask for confirmation before quitting? */ public void doQuit(boolean askForConfirmation) { if (isVisualizedInApplet()) { return; } if (askForConfirmation) { String s1 = "Quit"; String s2 = "Cancel"; Object[] options = { s1, s2 }; int n = JOptionPane.showOptionDialog(GUI.getTopParentContainer(), "Sure you want to quit?", "Close COOJA Simulator", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, s1); if (n != JOptionPane.YES_OPTION) { return; } } // Clean up resources Object[] plugins = startedPlugins.toArray(); for (Object plugin : plugins) { removePlugin((Plugin) plugin, false); } // Restore last frame size and position if (isVisualizedInFrame()) { setExternalToolsSetting("FRAME_POS_X", "" + frame.getLocationOnScreen().x); setExternalToolsSetting("FRAME_POS_Y", "" + frame.getLocationOnScreen().y); setExternalToolsSetting("FRAME_WIDTH", "" + frame.getWidth()); setExternalToolsSetting("FRAME_HEIGHT", "" + frame.getHeight()); } saveExternalToolsUserSettings(); System.exit(0); } // // EXTERNAL TOOLS SETTINGS METHODS //// /** * @return Number of external tools settings */ public static int getExternalToolsSettingsCount() { return externalToolsSettingNames.length; } /** * Get name of external tools setting at given index. * * @param index * Setting index * @return Name */ public static String getExternalToolsSettingName(int index) { return externalToolsSettingNames[index]; } /** * @param name * Name of setting * @return Value */ public static String getExternalToolsSetting(String name) { return currentExternalToolsSettings.getProperty(name); } /** * @param name * Name of setting * @param defaultValue * Default value * @return Value */ public static String getExternalToolsSetting(String name, String defaultValue) { return currentExternalToolsSettings.getProperty(name, defaultValue); } /** * @param name * Name of setting * @param defaultValue * Default value * @return Value */ public static String getExternalToolsDefaultSetting(String name, String defaultValue) { return defaultExternalToolsSettings.getProperty(name, defaultValue); } /** * @param name * Name of setting * @param newVal * New value */ public static void setExternalToolsSetting(String name, String newVal) { currentExternalToolsSettings.setProperty(name, newVal); } /** * Load external tools settings from default file. */ public static void loadExternalToolsDefaultSettings() { String osName = System.getProperty("os.name").toLowerCase(); String filename = GUI.EXTERNAL_TOOLS_LINUX_SETTINGS_FILENAME; if (osName.startsWith("win")) { filename = GUI.EXTERNAL_TOOLS_WIN32_SETTINGS_FILENAME; } else if (osName.startsWith("mac os x")) { filename = GUI.EXTERNAL_TOOLS_MACOSX_SETTINGS_FILENAME; } logger.info("Loading external tools user settings from: " + filename); try { InputStream in = GUI.class.getResourceAsStream(filename); if (in == null) { throw new FileNotFoundException(filename + " not found"); } Properties settings = new Properties(); settings.load(in); in.close(); currentExternalToolsSettings = settings; defaultExternalToolsSettings = (Properties) currentExternalToolsSettings.clone(); } catch (IOException e) { // Error while importing default properties logger.warn( "Error when reading external tools settings from " + filename, e); } finally { if (currentExternalToolsSettings == null) { defaultExternalToolsSettings = new Properties(); currentExternalToolsSettings = new Properties(); } } } /** * Load user values from external properties file */ public static void loadExternalToolsUserSettings() { if (externalToolsUserSettingsFile == null) { return; } try { FileInputStream in = new FileInputStream(externalToolsUserSettingsFile); Properties settings = new Properties(); settings.load(in); in.close(); Enumeration en = settings.keys(); while (en.hasMoreElements()) { String key = (String) en.nextElement(); setExternalToolsSetting(key, settings.getProperty(key)); } } catch (FileNotFoundException e) { // No default configuration file found, using default } catch (IOException e) { // Error while importing saved properties, using default logger.warn("Error when reading default settings from " + externalToolsUserSettingsFile); } } /** * Save external tools user settings to file. */ public static void saveExternalToolsUserSettings() { if (isVisualizedInApplet()) { return; } if (externalToolsUserSettingsFileReadOnly) { return; } try { FileOutputStream out = new FileOutputStream(externalToolsUserSettingsFile); Properties differingSettings = new Properties(); Enumeration keyEnum = currentExternalToolsSettings.keys(); while (keyEnum.hasMoreElements()) { String key = (String) keyEnum.nextElement(); String defaultSetting = getExternalToolsDefaultSetting(key, ""); String currentSetting = getExternalToolsSetting(key, ""); if (!defaultSetting.equals(currentSetting)) { differingSettings.setProperty(key, currentSetting); } } differingSettings.store(out, "COOJA External Tools (User specific)"); out.close(); } catch (FileNotFoundException ex) { // Could not open settings file for writing, aborting logger.warn("Could not save external tools user settings to " + externalToolsUserSettingsFile + ", aborting"); } catch (IOException ex) { // Could not open settings file for writing, aborting logger.warn("Error while saving external tools user settings to " + externalToolsUserSettingsFile + ", aborting"); } } // // GUI EVENT HANDLER //// private class GUIEventHandler implements ActionListener, WindowListener { public void windowDeactivated(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowClosing(WindowEvent e) { myGUI.doQuit(true); } public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("new sim")) { myGUI.doCreateSimulation(true); } else if (e.getActionCommand().equals("close sim")) { myGUI.doRemoveSimulation(true); } else if (e.getActionCommand().equals("confopen sim")) { myGUI.doLoadConfig(true, false, null); } else if (e.getActionCommand().equals("confopen last sim")) { File file = (File) ((JMenuItem) e.getSource()).getClientProperty("file"); myGUI.doLoadConfig(true, false, file); } else if (e.getActionCommand().equals("open sim")) { myGUI.doLoadConfig(true, true, null); } else if (e.getActionCommand().equals("open last sim")) { File file = (File) ((JMenuItem) e.getSource()).getClientProperty("file"); myGUI.doLoadConfig(true, true, file); } else if (e.getActionCommand().equals("save sim")) { myGUI.doSaveConfig(true); } else if (e.getActionCommand().equals("quit")) { myGUI.doQuit(true); } else if (e.getActionCommand().equals("create mote type")) { myGUI.doCreateMoteType((Class) ((JMenuItem) e .getSource()).getClientProperty("class")); } else if (e.getActionCommand().equals("add motes")) { myGUI.doAddMotes((MoteType) ((JMenuItem) e.getSource()) .getClientProperty("motetype")); } else if (e.getActionCommand().equals("edit paths")) { ExternalToolsDialog.showDialog(GUI.getTopParentContainer()); } else if (e.getActionCommand().equals("close plugins")) { Object[] plugins = startedPlugins.toArray(); for (Object plugin : plugins) { removePlugin((Plugin) plugin, false); } } else if (e.getActionCommand().equals("remove all motes")) { if (getSimulation() != null) { if (getSimulation().isRunning()) { getSimulation().stopSimulation(); } while (getSimulation().getMotesCount() > 0) { getSimulation().removeMote(getSimulation().getMote(0)); } } } else if (e.getActionCommand().equals("manage projects")) { Vector newProjects = ProjectDirectoriesDialog.showDialog( GUI.getTopParentContainer(), currentProjectDirs, null); if (newProjects != null) { currentProjectDirs = newProjects; try { reparseProjectConfig(); } catch (ParseProjectsException e2) { logger.fatal("Error when loading projects: " + e2.getMessage()); e2.printStackTrace(); if (myGUI.isVisualized()) { JOptionPane.showMessageDialog(GUI.getTopParentContainer(), "Error when loading projects.\nStack trace printed to console.", "Error", JOptionPane.ERROR_MESSAGE); } return; } } } else if (e.getActionCommand().equals("start plugin")) { Class pluginClass = (Class) ((JMenuItem) e .getSource()).getClientProperty("class"); Mote mote = (Mote) ((JMenuItem) e.getSource()).getClientProperty("mote"); startPlugin(pluginClass, myGUI, mySimulation, mote); } else { logger.warn("Unhandled action: " + e.getActionCommand()); } } } // // VARIOUS HELP METHODS //// /** * Help method that tries to load and initialize a class with given name. * * @param * Class extending given class type * @param classType * Class type * @param className * Class name * @return Class extending given class type or null if not found */ public Class tryLoadClass( Object callingObject, Class classType, String className) { if (callingObject != null) { try { return callingObject.getClass().getClassLoader().loadClass(className) .asSubclass(classType); } catch (ClassNotFoundException e) { } catch (UnsupportedClassVersionError e) { } } try { return Class.forName(className).asSubclass(classType); } catch (ClassNotFoundException e) { } catch (UnsupportedClassVersionError e) { } if (!isVisualizedInApplet()) { try { if (projectDirClassLoader != null) { return projectDirClassLoader.loadClass(className).asSubclass( classType); } } catch (NoClassDefFoundError e) { } catch (ClassNotFoundException e) { } catch (UnsupportedClassVersionError e) { } } return null; } public ClassLoader createProjectDirClassLoader(Vector projectsDirs) throws ParseProjectsException, ClassLoaderCreationException { if (projectDirClassLoader == null) { reparseProjectConfig(); } return createClassLoader(projectDirClassLoader, projectsDirs); } private ClassLoader createClassLoader(Vector currentProjectDirs) throws ClassLoaderCreationException { return createClassLoader(ClassLoader.getSystemClassLoader(), currentProjectDirs); } private File findJarFile(File projectDir, String jarfile) { File fp = new File(jarfile); if (!fp.exists()) { fp = new File(projectDir, jarfile); } if (!fp.exists()) { fp = new File(projectDir, "java/" + jarfile); } if (!fp.exists()) { fp = new File(projectDir, "java/lib/" + jarfile); } if (!fp.exists()) { fp = new File(projectDir, "lib/" + jarfile); } return fp.exists() ? fp : null; } private ClassLoader createClassLoader(ClassLoader parent, Vector projectDirs) throws ClassLoaderCreationException { if (projectDirs == null || projectDirs.isEmpty()) { return parent; } // Combine class loader from all project directories (including any // specified JAR files) ArrayList urls = new ArrayList(); for (int j = projectDirs.size() - 1; j >= 0; j--) { File projectDir = projectDirs.get(j); try { urls.add((new File(projectDir, "java")).toURI().toURL()); // Read configuration to check if any JAR files should be loaded ProjectConfig projectConfig = new ProjectConfig(false); projectConfig.appendProjectDir(projectDir); String[] projectJarFiles = projectConfig.getStringArrayValue( GUI.class, "JARFILES"); if (projectJarFiles != null && projectJarFiles.length > 0) { for (String jarfile : projectJarFiles) { File jarpath = findJarFile(projectDir, jarfile); if (jarpath == null) { throw new FileNotFoundException(jarfile); } urls.add(jarpath.toURI().toURL()); } } } catch (Exception e) { logger.fatal("Error when trying to read JAR-file in " + projectDir + ": " + e); throw (ClassLoaderCreationException) new ClassLoaderCreationException( "Error when trying to read JAR-file in " + projectDir).initCause(e); } } URL[] urlsArray = urls.toArray(new URL[urls.size()]); /* TODO Load from webserver if applet */ return new URLClassLoader(urlsArray, parent); } /** * Help method that returns the description for given object. This method * reads from the object's class annotations if existing. Otherwise it returns * the simple class name of object's class. * * @param object * Object * @return Description */ public static String getDescriptionOf(Object object) { return getDescriptionOf(object.getClass()); } /** * Help method that returns the description for given class. This method reads * from class annotations if existing. Otherwise it returns the simple class * name. * * @param clazz * Class * @return Description */ public static String getDescriptionOf(Class clazz) { if (clazz.isAnnotationPresent(ClassDescription.class)) { return clazz.getAnnotation(ClassDescription.class).value(); } return clazz.getSimpleName(); } /** * Help method that returns the abstraction level description for given mote type class. * * @param clazz * Class * @return Description */ public static String getAbstractionLevelDescriptionOf(Class clazz) { if (clazz.isAnnotationPresent(AbstractionLevelDescription.class)) { return clazz.getAnnotation(AbstractionLevelDescription.class).value(); } return null; } /** * Load configurations and create a GUI. * * @param args * null */ public static void main(String[] args) { try { // Configure logger if ((new File(LOG_CONFIG_FILE)).exists()) { DOMConfigurator.configure(LOG_CONFIG_FILE); } else { // Used when starting from jar DOMConfigurator.configure(GUI.class.getResource("/" + LOG_CONFIG_FILE)); } externalToolsUserSettingsFile = new File(System.getProperty("user.home"), EXTERNAL_TOOLS_USER_SETTINGS_FILENAME); } catch (AccessControlException e) { BasicConfigurator.configure(); externalToolsUserSettingsFile = null; } // Parse general command arguments for (String element : args) { if (element.startsWith("-contiki=")) { String arg = element.substring("-contiki=".length()); GUI.specifiedContikiPath = arg; } if (element.startsWith("-external_tools_config=")) { String arg = element.substring("-external_tools_config=".length()); File specifiedExternalToolsConfigFile = new File(arg); if (!specifiedExternalToolsConfigFile.exists()) { logger.fatal("Specified external tools configuration not found: " + specifiedExternalToolsConfigFile); specifiedExternalToolsConfigFile = null; System.exit(1); } else { GUI.externalToolsUserSettingsFile = specifiedExternalToolsConfigFile; GUI.externalToolsUserSettingsFileReadOnly = true; } } } // Check if simulator should be quick-started if (args.length > 0 && args[0].startsWith("-quickstart=")) { String filename = args[0].substring("-quickstart=".length()); String moteTypeID = "mtype1"; Vector projectDirs = null; Vector sensors = null; Vector coreInterfaces = null; Vector userProcesses = null; boolean addAutostartProcesses = true; int numberOfNodes = 100; double areaSideLength = 100; int delayTime = 5; boolean startSimulation = true; String contikiPath = null; // Parse arguments for (int i = 1; i < args.length; i++) { if (args[i].startsWith("-id=")) { moteTypeID = args[i].substring("-id=".length()); } else if (args[i].startsWith("-projects=")) { String arg = args[i].substring("-projects=".length()); String[] argArray = arg.split(","); projectDirs = new Vector(); for (String argValue : argArray) { projectDirs.add(argValue); } } else if (args[i].startsWith("-sensors=")) { String arg = args[i].substring("-sensors=".length()); String[] argArray = arg.split(","); sensors = new Vector(); for (String argValue : argArray) { sensors.add(argValue); } } else if (args[i].startsWith("-interfaces=")) { String arg = args[i].substring("-interfaces=".length()); String[] argArray = arg.split(","); coreInterfaces = new Vector(); for (String argValue : argArray) { coreInterfaces.add(argValue); } } else if (args[i].startsWith("-processes=")) { String arg = args[i].substring("-processes=".length()); String[] argArray = arg.split(","); userProcesses = new Vector(); for (String argValue : argArray) { userProcesses.add(argValue); } } else if (args[i].equals("-noautostartscan")) { addAutostartProcesses = false; } else if (args[i].equals("-paused")) { startSimulation = false; } else if (args[i].startsWith("-nodes=")) { String arg = args[i].substring("-nodes=".length()); numberOfNodes = Integer.parseInt(arg); } else if (args[i].startsWith("-contiki=")) { String arg = args[i].substring("-contiki=".length()); contikiPath = arg; } else if (args[i].startsWith("-delay=")) { String arg = args[i].substring("-delay=".length()); delayTime = Integer.parseInt(arg); } else if (args[i].startsWith("-side=")) { String arg = args[i].substring("-side=".length()); areaSideLength = Double.parseDouble(arg); } else { logger.fatal("Unknown argument, aborting: " + args[i]); System.exit(1); } } boolean ok = quickStartSimulation(moteTypeID, projectDirs, sensors, coreInterfaces, userProcesses, addAutostartProcesses, numberOfNodes, areaSideLength, delayTime, startSimulation, filename, contikiPath); if (!ok) { System.exit(1); } } else if (args.length > 0 && args[0].startsWith("-nogui")) { /* Parse optional script argument */ String tmpTest=null; for (int i=1; i < args.length; i++) { if (args[i].startsWith("-test=")) { tmpTest = args[i].substring("-test=".length()); } else { logger.fatal("Unknown argument: " + args[i]); System.exit(1); } } final File scriptFile; final File configFile; final File logFile; if (tmpTest != null) { /* Locate script and simulation config files */ scriptFile = new File(tmpTest + ".js"); configFile = new File(tmpTest + ".csc"); logFile = new File(tmpTest + ".log"); if (!scriptFile.exists()) { logger.fatal("Can't locate script: " + scriptFile); System.exit(1); } if (!configFile.exists()) { logger.fatal("Can't locate simulation config: " + configFile); System.exit(1); } if (logFile.exists()) { logFile.delete(); } if (logFile.exists() && !logFile.canWrite()) { logger.fatal("Can't write to log file: " + logFile); System.exit(1); } } else { scriptFile = null; configFile = null; logFile = null; } /* No GUI start-up */ javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { JDesktopPane desktop = new JDesktopPane(); desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); GUI gui = new GUI(desktop); if (scriptFile != null && configFile != null) { /* Load and start script plugin (no-GUI version) */ gui.registerPlugin(ScriptRunnerNoGUI.class, false); ScriptRunnerNoGUI scriptPlugin = (ScriptRunnerNoGUI) gui.startPlugin(ScriptRunnerNoGUI.class, gui, null, null); /* Activate test */ scriptPlugin.activateTest(configFile, scriptFile, logFile); } } }); } else if (args.length > 0 && args[0].startsWith("-applet")) { String tmpWebPath=null, tmpBuildPath=null, tmpEsbFirmware=null, tmpSkyFirmware=null; for (int i = 1; i < args.length; i++) { if (args[i].startsWith("-web=")) { tmpWebPath = args[i].substring("-web=".length()); } else if (args[i].startsWith("-sky_firmware=")) { tmpSkyFirmware = args[i].substring("-sky_firmware=".length()); } else if (args[i].startsWith("-esb_firmware=")) { tmpEsbFirmware = args[i].substring("-esb_firmware=".length()); } else if (args[i].startsWith("-build=")) { tmpBuildPath = args[i].substring("-build=".length()); } } // Applet start-up final String webPath = tmpWebPath, buildPath = tmpBuildPath; final String skyFirmware = tmpSkyFirmware, esbFirmware = tmpEsbFirmware; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { JDesktopPane desktop = new JDesktopPane(); desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); applet = CoojaApplet.applet; GUI gui = new GUI(desktop); GUI.setExternalToolsSetting("PATH_CONTIKI_BUILD", buildPath); GUI.setExternalToolsSetting("PATH_CONTIKI_WEB", webPath); GUI.setExternalToolsSetting("SKY_FIRMWARE", skyFirmware); GUI.setExternalToolsSetting("ESB_FIRMWARE", esbFirmware); configureApplet(gui, false); } }); } else { // Frame start-up javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { JDesktopPane desktop = new JDesktopPane(); desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); GUI gui = new GUI(desktop); configureFrame(gui, false); } }); } } /** * Loads a simulation configuration from given file. * * When loading Contiki mote types, the libraries must be recompiled. User may * change mote type settings at this point. * * @see #saveSimulationConfig(File) * @param file * File to read * @return New simulation or null if recompiling failed or aborted * @throws UnsatisfiedLinkError * If associated libraries could not be loaded */ public Simulation loadSimulationConfig(File file, boolean quick) throws UnsatisfiedLinkError, SimulationCreationException { try { SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(file); Element root = doc.getRootElement(); return loadSimulationConfig(root, quick); } catch (JDOMException e) { logger.fatal("Config not wellformed: " + e.getMessage()); return null; } catch (IOException e) { logger.fatal("IOException: " + e.getMessage()); return null; } } private Simulation loadSimulationConfig(StringReader stringReader, boolean quick) throws SimulationCreationException { try { SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(stringReader); Element root = doc.getRootElement(); return loadSimulationConfig(root, quick); } catch (JDOMException e) { throw (SimulationCreationException) new SimulationCreationException( "Configuration file not wellformed: " + e.getMessage()).initCause(e); } catch (IOException e) { throw (SimulationCreationException) new SimulationCreationException( "IO Exception: " + e.getMessage()).initCause(e); } } private Simulation loadSimulationConfig(Element root, boolean quick) throws SimulationCreationException { Simulation newSim = null; try { // Check that config file version is correct if (!root.getName().equals("simconf")) { logger.fatal("Not a valid COOJA simulation config!"); return null; } /* GENERATE UNIQUE MOTE TYPE IDENTIFIERS */ root.detach(); String configString = new XMLOutputter().outputString(new Document(root)); /* Locate Contiki mote types in config */ Properties moteTypeIDMappings = new Properties(); String identifierExtraction = ContikiMoteType.class.getName() + "[\\s\\n]*([^<]*)"; Matcher matcher = Pattern.compile(identifierExtraction).matcher(configString); while (matcher.find()) { moteTypeIDMappings.setProperty(matcher.group(1), ""); } /* Create old to new identifier mappings */ Enumeration existingIdentifiers = moteTypeIDMappings.keys(); while (existingIdentifiers.hasMoreElements()) { String existingIdentifier = (String) existingIdentifiers.nextElement(); Collection existingMoteTypes = null; if (mySimulation != null) { existingMoteTypes = mySimulation.getMoteTypes(); } String newID = ContikiMoteType.generateUniqueMoteTypeID(existingMoteTypes, moteTypeIDMappings.values()); moteTypeIDMappings.setProperty(existingIdentifier, newID); } /* Create new config */ existingIdentifiers = moteTypeIDMappings.keys(); while (existingIdentifiers.hasMoreElements()) { String existingIdentifier = (String) existingIdentifiers.nextElement(); configString = configString.replaceAll( "" + existingIdentifier + "", "" + moteTypeIDMappings.get(existingIdentifier) + ""); configString = configString.replaceAll( "" + existingIdentifier + "", "" + moteTypeIDMappings.get(existingIdentifier) + ""); } /* Replace existing config */ root = new SAXBuilder().build(new StringReader(configString)).getRootElement(); // Create new simulation from config for (Object element : root.getChildren()) { if (((Element) element).getName().equals("simulation")) { Collection config = ((Element) element).getChildren(); newSim = new Simulation(this); System.gc(); boolean createdOK = newSim.setConfigXML(config, !quick); if (!createdOK) { logger.info("Simulation not loaded"); return null; } } } // Restart plugins from config setPluginsConfigXML(root.getChildren(), newSim, !quick); } catch (JDOMException e) { throw (SimulationCreationException) new SimulationCreationException( "Configuration file not wellformed: " + e.getMessage()).initCause(e); } catch (IOException e) { throw (SimulationCreationException) new SimulationCreationException( "No access to configuration file: " + e.getMessage()).initCause(e); } catch (MoteTypeCreationException e) { throw (SimulationCreationException) new SimulationCreationException( "Mote type creation error: " + e.getMessage()).initCause(e); } catch (Exception e) { throw (SimulationCreationException) new SimulationCreationException( "Unknown error: " + e.getMessage()).initCause(e); } return newSim; } /** * Saves current simulation configuration to given file and notifies * observers. * * @see #loadSimulationConfig(File, boolean) * @param file * File to write */ public void saveSimulationConfig(File file) { try { // Create simulation configL Element root = new Element("simconf"); Element simulationElement = new Element("simulation"); simulationElement.addContent(mySimulation.getConfigXML()); root.addContent(simulationElement); // Create started plugins config Collection pluginsConfig = getPluginsConfigXML(); if (pluginsConfig != null) { root.addContent(pluginsConfig); } // Create and write to document Document doc = new Document(root); FileOutputStream out = new FileOutputStream(file); XMLOutputter outputter = new XMLOutputter(); outputter.setFormat(Format.getPrettyFormat()); outputter.output(doc, out); out.close(); logger.info("Saved to file: " + file.getAbsolutePath()); } catch (Exception e) { logger.warn("Exception while saving simulation config: " + e); e.printStackTrace(); } } /** * Returns started plugins config. * * @return Config or null */ public Collection getPluginsConfigXML() { Vector config = new Vector(); // Loop through all started plugins // (Only return config of non-GUI plugins) Element pluginElement, pluginSubElement; for (Plugin startedPlugin : startedPlugins) { int pluginType = startedPlugin.getClass().getAnnotation(PluginType.class) .value(); // Ignore GUI plugins if (pluginType == PluginType.COOJA_PLUGIN || pluginType == PluginType.COOJA_STANDARD_PLUGIN) { continue; } pluginElement = new Element("plugin"); pluginElement.setText(startedPlugin.getClass().getName()); // Create mote argument config (if mote plugin) if (pluginType == PluginType.MOTE_PLUGIN && startedPlugin.getTag() != null) { pluginSubElement = new Element("mote_arg"); Mote taggedMote = (Mote) startedPlugin.getTag(); for (int moteNr = 0; moteNr < mySimulation.getMotesCount(); moteNr++) { if (mySimulation.getMote(moteNr) == taggedMote) { pluginSubElement.setText(Integer.toString(moteNr)); pluginElement.addContent(pluginSubElement); break; } } } // Create plugin specific configuration Collection pluginXML = startedPlugin.getConfigXML(); if (pluginXML != null) { pluginSubElement = new Element("plugin_config"); pluginSubElement.addContent(pluginXML); pluginElement.addContent(pluginSubElement); } // If plugin is visualizer plugin, create visualization arguments if (startedPlugin instanceof VisPlugin) { VisPlugin startedVisPlugin = (VisPlugin) startedPlugin; pluginSubElement = new Element("width"); pluginSubElement.setText("" + startedVisPlugin.getSize().width); pluginElement.addContent(pluginSubElement); pluginSubElement = new Element("z"); pluginSubElement.setText("" + getDesktopPane().getComponentZOrder(startedVisPlugin)); pluginElement.addContent(pluginSubElement); pluginSubElement = new Element("height"); pluginSubElement.setText("" + startedVisPlugin.getSize().height); pluginElement.addContent(pluginSubElement); pluginSubElement = new Element("location_x"); pluginSubElement.setText("" + startedVisPlugin.getLocation().x); pluginElement.addContent(pluginSubElement); pluginSubElement = new Element("location_y"); pluginSubElement.setText("" + startedVisPlugin.getLocation().y); pluginElement.addContent(pluginSubElement); pluginSubElement = new Element("minimized"); pluginSubElement.setText(new Boolean(startedVisPlugin.isIcon()) .toString()); pluginElement.addContent(pluginSubElement); } config.add(pluginElement); } return config; } /** * Starts plugins with arguments in given config. * * @param configXML * Config XML elements * @param simulation * Simulation on which to start plugins * @return True if all plugins started, false otherwise */ public boolean setPluginsConfigXML(Collection configXML, Simulation simulation, boolean visAvailable) { for (Element pluginElement : configXML.toArray(new Element[0])) { if (pluginElement.getName().equals("plugin")) { // Read plugin class String pluginClassName = pluginElement.getText().trim(); Class visPluginClass = tryLoadClass(this, Plugin.class, pluginClassName); if (visPluginClass == null) { logger.fatal("Could not load plugin class: " + pluginClassName); return false; } // Parse plugin mote argument (if any) Mote mote = null; for (Element pluginSubElement : (List) pluginElement .getChildren()) { if (pluginSubElement.getName().equals("mote_arg")) { int moteNr = Integer.parseInt(pluginSubElement.getText()); if (moteNr >= 0 && moteNr < simulation.getMotesCount()) { mote = simulation.getMote(moteNr); } } } // Start plugin (before applying rest of config) Plugin startedPlugin = startPlugin(visPluginClass, this, simulation, mote); // Apply plugin specific configuration for (Element pluginSubElement : (List) pluginElement .getChildren()) { if (pluginSubElement.getName().equals("plugin_config")) { startedPlugin.setConfigXML(pluginSubElement.getChildren(), visAvailable); } } // If plugin is visualizer plugin, parse visualization arguments if (startedPlugin instanceof VisPlugin) { Dimension size = new Dimension(100, 100); Point location = new Point(100, 100); VisPlugin startedVisPlugin = (VisPlugin) startedPlugin; for (Element pluginSubElement : (List) pluginElement .getChildren()) { if (pluginSubElement.getName().equals("width") && size != null) { size.width = Integer.parseInt(pluginSubElement.getText()); startedVisPlugin.setSize(size); } else if (pluginSubElement.getName().equals("height")) { size.height = Integer.parseInt(pluginSubElement.getText()); startedVisPlugin.setSize(size); } else if (pluginSubElement.getName().equals("z")) { int zOrder = Integer.parseInt(pluginSubElement.getText()); // Save z order as temporary client property startedVisPlugin.putClientProperty("zorder", zOrder); } else if (pluginSubElement.getName().equals("location_x")) { location.x = Integer.parseInt(pluginSubElement.getText()); startedVisPlugin.setLocation(location); } else if (pluginSubElement.getName().equals("location_y")) { location.y = Integer.parseInt(pluginSubElement.getText()); startedVisPlugin.setLocation(location); } else if (pluginSubElement.getName().equals("minimized")) { try { startedVisPlugin.setIcon(Boolean.parseBoolean(pluginSubElement .getText())); } catch (PropertyVetoException e) { // Ignoring } } } } } } // For all started visplugins, check if they have a zorder property try { for (JInternalFrame plugin : getDesktopPane().getAllFrames()) { if (plugin.getClientProperty("zorder") != null) { getDesktopPane().setComponentZOrder(plugin, ((Integer) plugin.getClientProperty("zorder")).intValue()); plugin.putClientProperty("zorder", null); } } } catch (Exception e) { // Ignore errors } return true; } public class ParseProjectsException extends Exception { public ParseProjectsException(String message) { super(message); } } public class ClassLoaderCreationException extends Exception { public ClassLoaderCreationException(String message) { super(message); } } public class SimulationCreationException extends Exception { public SimulationCreationException(String message) { super(message); } } /** * Shows a simple dialog with information about the thrown exception. A user * may watch the stack trace and, if the exception is a * MoteTypeCreationException, watch compilation output. * * @param parentComponent * Parent component * @param title * Title of error window * @param exception * Exception causing window to be shown * @param retryAvailable * If true, a retry option is available */ public static boolean showErrorDialog(Component parentComponent, final String title, Throwable exception, boolean retryAvailable) { MessageList compilationOutput = null; MessageList stackTrace = null; String message = title; // Create message if (exception != null) { message = exception.getMessage(); } // Create stack trace message list if (exception != null) { stackTrace = new MessageList(); PrintStream printStream = stackTrace.getInputStream(MessageList.NORMAL); exception.printStackTrace(printStream); } // Create compilation out message list (if available) if (exception != null && exception instanceof MoteTypeCreationException && ((MoteTypeCreationException) exception).hasCompilationOutput()) { compilationOutput = ((MoteTypeCreationException) exception) .getCompilationOutput(); } else if (exception != null && exception.getCause() != null && exception.getCause() instanceof MoteTypeCreationException && ((MoteTypeCreationException) exception.getCause()) .hasCompilationOutput()) { compilationOutput = ((MoteTypeCreationException) exception.getCause()) .getCompilationOutput(); } // Create error dialog final JDialog errorDialog; if (parentComponent instanceof Dialog) { errorDialog = new JDialog((Dialog) parentComponent, title, true); } else if (parentComponent instanceof Frame) { errorDialog = new JDialog((Frame) parentComponent, title, true); } else { logger.fatal("Bad parent for error dialog"); errorDialog = new JDialog((Frame) null, title + " (Java stack trace)"); } final JPanel errorPanel = new JPanel(); errorPanel.setLayout(new BoxLayout(errorPanel, BoxLayout.Y_AXIS)); errorPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); Box messageBox = Box.createHorizontalBox(); // Icon myIcon = (Icon)DefaultLookup.get(errorPanel, null, // "OptionPane.errorIcon"); // messageBox.add(new JLabel(myIcon)); messageBox.add(Box.createHorizontalGlue()); messageBox.add(new JLabel(message)); messageBox.add(Box.createHorizontalGlue()); Box buttonBox = Box.createHorizontalBox(); if (compilationOutput != null) { final MessageList listToDisplay = compilationOutput; final String titleToDisplay = title + " (Compilation output)"; JButton showCompilationButton = new JButton("Show compilation output"); showCompilationButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JDialog messageListDialog = new JDialog(errorDialog, titleToDisplay); JPanel messageListPanel = new JPanel(new BorderLayout()); messageListPanel.add(BorderLayout.CENTER, new JScrollPane( listToDisplay)); messageListPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); messageListPanel.setVisible(true); messageListDialog.getContentPane().add(messageListPanel); messageListDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); messageListDialog.pack(); messageListDialog.setLocationRelativeTo(errorDialog); Rectangle maxSize = GraphicsEnvironment.getLocalGraphicsEnvironment() .getMaximumWindowBounds(); if (maxSize != null && (messageListDialog.getSize().getWidth() > maxSize.getWidth() || messageListDialog .getSize().getHeight() > maxSize.getHeight())) { Dimension newSize = new Dimension(); newSize.height = Math.min((int) maxSize.getHeight(), (int) messageListDialog.getSize().getHeight()); newSize.width = Math.min((int) maxSize.getWidth(), (int) messageListDialog.getSize().getWidth()); messageListDialog.setSize(newSize); } messageListDialog.setVisible(true); } }); buttonBox.add(showCompilationButton); } if (stackTrace != null) { final MessageList listToDisplay = stackTrace; final String titleToDisplay = title + " (Java stack trace)"; JButton showTraceButton = new JButton("Show Java stack trace"); showTraceButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JDialog messageListDialog = new JDialog(errorDialog, titleToDisplay); JPanel messageListPanel = new JPanel(new BorderLayout()); messageListPanel.add(BorderLayout.CENTER, new JScrollPane( listToDisplay)); messageListPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); messageListPanel.setVisible(true); messageListDialog.getContentPane().add(messageListPanel); messageListDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); messageListDialog.pack(); messageListDialog.setLocationRelativeTo(errorDialog); Rectangle maxSize = GraphicsEnvironment.getLocalGraphicsEnvironment() .getMaximumWindowBounds(); if (maxSize != null && (messageListDialog.getSize().getWidth() > maxSize.getWidth() || messageListDialog .getSize().getHeight() > maxSize.getHeight())) { Dimension newSize = new Dimension(); newSize.height = Math.min((int) maxSize.getHeight(), (int) messageListDialog.getSize().getHeight()); newSize.width = Math.min((int) maxSize.getWidth(), (int) messageListDialog.getSize().getWidth()); messageListDialog.setSize(newSize); } messageListDialog.setVisible(true); } }); buttonBox.add(showTraceButton); } if (retryAvailable) { JButton retryButton = new JButton("Retry"); retryButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { errorDialog.setTitle("-RETRY-"); errorDialog.dispose(); } }); buttonBox.add(retryButton); } final JButton closeButton = new JButton("Close"); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { errorDialog.dispose(); } }); buttonBox.add(closeButton); // Dispose on escape key InputMap inputMap = errorDialog.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), "dispose"); AbstractAction cancelAction = new AbstractAction(){ public void actionPerformed(ActionEvent e) { closeButton.doClick(); } }; errorDialog.getRootPane().getActionMap().put("dispose", cancelAction); errorPanel.add(messageBox); errorPanel.add(Box.createVerticalStrut(20)); errorPanel.add(buttonBox); errorDialog.getContentPane().add(errorPanel); errorDialog.pack(); errorDialog.setLocationRelativeTo(parentComponent); errorDialog.setVisible(true); if (errorDialog.getTitle().equals("-RETRY-")) { return true; } return false; } /** * This method can be used by various different modules in the simulator to * indicate for example that a mote has been selected. All mote highlight * listeners will be notified. An example application of mote highlightinh is * a simulator visualizer that highlights the mote. * * @see #addMoteHighlightObserver(Observer) * @param m * Mote to highlight */ public void signalMoteHighlight(Mote m) { moteHighlightObservable.highlightMote(m); } }