abstract contiki compile dialog using compile library.

to be implemented by msp mote type
This commit is contained in:
fros4943 2009-03-09 13:58:12 +00:00
parent 37586984f6
commit 217d8cf0b3

View file

@ -0,0 +1,666 @@
/*
* Copyright (c) 2009, 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: AbstractCompileDialog.java,v 1.1 2009/03/09 13:58:12 fros4943 Exp $
*/
package se.sics.cooja.dialogs;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter;
import org.apache.log4j.Logger;
import se.sics.cooja.*;
import se.sics.cooja.dialogs.MessageList;
/**
* Abstract configure mote type dialog used by Contiki-based mote type implementations.
*
* The dialog uses tabs for configuring the mote type.
* Three tabs are provided: compile commands, mote interfaces, and compilation output.
*
* In addition, the dialog implementation can provide additional tabs.
*
* @see #tabbedPane
* @see ContikiMoteCompileDialog
*
* @author Fredrik Osterlind
*/
public abstract class AbstractCompileDialog extends JDialog {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AbstractCompileDialog.class);
protected final static Dimension LABEL_DIMENSION = new Dimension(170, 15);
private static File lastFile = null;
public static enum DialogState {
NO_SELECTION,
SELECTED_SOURCE, AWAITING_COMPILATION, IS_COMPILING, COMPILED_FIRMWARE,
SELECTED_FIRMWARE,
}
protected Simulation simulation;
protected GUI gui;
protected JTabbedPane tabbedPane;
protected Box moteIntfBox;
private JTextField contikiField;
private JTextField descriptionField;
private JTextArea commandsArea;
private JButton nextButton;
private Component currentCompilationOutput = null;
private Process currentCompilationProcess = null;
/* Accessible at Contiki compilation success */
public File contikiSource = null;
public File contikiFirmware = null;
public AbstractCompileDialog(Container parent, Simulation simulation, final MoteType moteType) {
super(
parent instanceof Dialog?(Dialog)parent:
parent instanceof Window?(Window)parent:
(Frame)parent, "Create Mote Type: Compile Contiki", ModalityType.APPLICATION_MODAL);
this.simulation = simulation;
this.gui = simulation.getGUI();
JPanel mainPanel = new JPanel(new BorderLayout());
JLabel label;
/* Top: Contiki source */
JPanel topPanel = new JPanel();
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
Box descriptionBox = Box.createHorizontalBox();
label = new JLabel("Description:");
label.setPreferredSize(LABEL_DIMENSION);
descriptionBox.add(label);
descriptionField = new JTextField(40);
descriptionField.setText("[enter mote type description]");
descriptionBox.add(descriptionField);
topPanel.add(descriptionBox);
JPanel sourcePanel = new JPanel();
sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.X_AXIS));
label = new JLabel("Contiki process / Firmware:");
label.setPreferredSize(LABEL_DIMENSION);
sourcePanel.add(label);
contikiField = new JTextField(40);
final Runnable selectedContikiFile = new Runnable() {
public void run() {
setContikiSelection(new File(contikiField.getText()));
}
};
DocumentListener contikiFieldListener = new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
SwingUtilities.invokeLater(selectedContikiFile);
}
public void insertUpdate(DocumentEvent e) {
SwingUtilities.invokeLater(selectedContikiFile);
}
public void removeUpdate(DocumentEvent e) {
SwingUtilities.invokeLater(selectedContikiFile);
}
};
sourcePanel.add(contikiField);
JButton browseButton = new JButton("Browse");
browseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
/* Last file/directory */
if (lastFile != null) {
if (lastFile.isDirectory()) {
fc.setCurrentDirectory(lastFile);
} else if (lastFile.isFile() && lastFile.exists()) {
fc.setCurrentDirectory(lastFile.getParentFile());
fc.setSelectedFile(lastFile);
} else if (lastFile.isFile() && !lastFile.exists()) {
fc.setCurrentDirectory(lastFile.getParentFile());
}
} else {
File helloworldSourceFile =
new java.io.File(
GUI.getExternalToolsSetting("PATH_CONTIKI"), "examples/hello-world/hello-world.c");
try {
helloworldSourceFile = helloworldSourceFile.getCanonicalFile();
fc.setCurrentDirectory(helloworldSourceFile.getParentFile());
fc.setSelectedFile(helloworldSourceFile);
} catch (IOException e1) {
}
}
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.addChoosableFileFilter(new FileFilter() {
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String filename = f.getName();
if (filename == null) {
return false;
}
if (filename.endsWith(".c")) {
return true;
}
if (canLoadFirmware(f)) {
return true;
}
return false;
}
public String getDescription() {
return "Contiki process source or Contiki firmware";
}
});
fc.setDialogTitle("Select Contiki process source");
if (fc.showOpenDialog(AbstractCompileDialog.this) == JFileChooser.APPROVE_OPTION) {
contikiField.setText(fc.getSelectedFile().getAbsolutePath());
}
}
});
sourcePanel.add(browseButton);
topPanel.add(sourcePanel);
nextButton = new JButton("NEXT");
nextButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (nextButton.getText().equals("Compile")) {
compileContiki();
} else if (nextButton.getText().equals("Create")) {
/* Write mote type settings (generic) */
moteType.setDescription(descriptionField.getText());
moteType.setContikiSourceFile(contikiSource);
moteType.setContikiFirmwareFile(contikiFirmware);
moteType.setMoteInterfaceClasses(getSelectedMoteInterfaceClasses());
moteType.setCompileCommands(getCompileCommands());
/* Write mote type settings (mote type specific) */
writeSettingsToMoteType();
AbstractCompileDialog.this.dispose();
}
}
});
getRootPane().setDefaultButton(nextButton);
Box buttonPanel = Box.createHorizontalBox();
buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
buttonPanel.add(Box.createHorizontalGlue());
buttonPanel.add(new JLabel("Next:"));
buttonPanel.add(Box.createHorizontalStrut(5));
buttonPanel.add(nextButton);
buttonPanel.setAlignmentX(LEFT_ALIGNMENT);
topPanel.add(buttonPanel);
/* Center: Tabs showing configuration, compilation output, ... */
tabbedPane = new JTabbedPane();
addCompileCommandTab(tabbedPane);
addMoteInterfacesTab(tabbedPane);
/* Build panel */
mainPanel.add(BorderLayout.NORTH, topPanel);
mainPanel.add(BorderLayout.CENTER, tabbedPane);
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setContentPane(mainPanel);
setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
addWindowListener(new 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) {
abortAnyCompilation();
contikiSource = null;
contikiFirmware = null;
AbstractCompileDialog.this.dispose();
}
});
setDialogState(DialogState.NO_SELECTION);
descriptionField.requestFocus();
descriptionField.select(0, descriptionField.getText().length());
/* Restore old configuration if mote type is already configured */
if (moteType != null) {
/* Restore description */
if (moteType.getDescription() != null) {
descriptionField.setText(moteType.getDescription());
}
/* Restore Contiki source or firmware */
if (moteType.getContikiSourceFile() != null) {
contikiField.setText(moteType.getContikiSourceFile().getAbsolutePath());
setDialogState(DialogState.SELECTED_SOURCE);
} else if (moteType.getContikiFirmwareFile() != null) {
contikiField.setText(moteType.getContikiFirmwareFile().getAbsolutePath());
setDialogState(DialogState.SELECTED_FIRMWARE);
}
/* Restore mote interface classes */
if (moteType.getMoteInterfaceClasses() != null) {
for (Class<? extends MoteInterface> intf: moteType.getMoteInterfaceClasses()) {
addMoteInterface(intf, true);
}
}
/* Restore compile commands */
if (moteType.getCompileCommands() != null) {
setCompileCommands(moteType.getCompileCommands());
setDialogState(DialogState.AWAITING_COMPILATION);
}
}
/* Add listener only after restoring old config */
contikiField.getDocument().addDocumentListener(contikiFieldListener);
/* Final touches: respect window size, focus on description etc */
Rectangle maxSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
if (maxSize != null &&
(getSize().getWidth() > maxSize.getWidth() || getSize().getHeight() > maxSize.getHeight())) {
Dimension newSize = new Dimension();
newSize.height = Math.min((int) maxSize.getHeight(), (int) getSize().getHeight());
newSize.width = Math.min((int) maxSize.getWidth(), (int) getSize().getWidth());
/*logger.info("Resizing dialog: " + myDialog.getSize() + " -> " + newSize);*/
setSize(newSize);
}
pack();
setLocationRelativeTo(parent);
}
/**
* @return Mote type was created without errors
*/
public boolean createdOK() {
if (contikiFirmware == null) {
return false;
}
if (!contikiFirmware.exists()) {
return false;
}
return true;
}
/**
* Called when user is closing the dialog.
* Should immediately write mote type specific settings
* to the current mote type.
*
* Note that generic mote type settings are already written,
* such as description, Contiki source, compile commands, and mote interfaces.
*/
public abstract void writeSettingsToMoteType();
public abstract boolean canLoadFirmware(File file);
private void compileContiki() {
final MessageList taskOutput = new MessageList();
/* Handle multiple compilation commands one by one */
final ArrayList<String> commands = new ArrayList<String>();
String[] arr = getCompileCommands().split("\n");
for (String cmd: arr) {
commands.add(cmd);
}
setDialogState(DialogState.IS_COMPILING);
createNewCompilationTab(taskOutput);
/* Add abort compilation menu item */
final JMenuItem abortMenuItem = new JMenuItem("Abort compilation");
abortMenuItem.setEnabled(true);
abortMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
abortAnyCompilation();
}
});
taskOutput.addPopupMenuItem(abortMenuItem, true);
/* Called when last command has finished (success only) */
final Action compilationSuccessAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
abortMenuItem.setEnabled(false);
setDialogState(DialogState.COMPILED_FIRMWARE);
}
};
/* Called immediately if any command fails */
final Action compilationFailureAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
abortMenuItem.setEnabled(false);
setDialogState(DialogState.AWAITING_COMPILATION);
}
};
/* Called once per command */
final Action nextCommandAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
Action nextSuccessAction;
if (commands.size() == 1) {
nextSuccessAction = compilationSuccessAction;
} else {
nextSuccessAction = this;
}
String command = commands.remove(0);
if (command.trim().isEmpty()) {
nextSuccessAction.actionPerformed(null);
} else {
try {
currentCompilationProcess = CompileContiki.compile(
command,
null,
new File(contikiField.getText()).getParentFile(),
nextSuccessAction,
compilationFailureAction,
taskOutput,
false
);
} catch (Exception ex) {
logger.fatal("Exception when compiling: " + ex.getMessage());
ex.printStackTrace();
compilationFailureAction.actionPerformed(null);
}
}
}
};
nextCommandAction.actionPerformed(null); /* Recursive calls for all commands */
}
private void setContikiSelection(File file) {
if (!file.exists()) {
setDialogState(DialogState.NO_SELECTION);
return;
}
lastFile = file;
if (file.getName().endsWith(".c")) {
setDialogState(DialogState.SELECTED_SOURCE);
return;
}
if (canLoadFirmware(file)) {
setDialogState(DialogState.SELECTED_FIRMWARE);
return;
}
setDialogState(DialogState.SELECTED_SOURCE);
}
/**
* @see DialogState
* @param dialogState New dialog state
*/
public void setDialogState(DialogState dialogState) {
switch (dialogState) {
case NO_SELECTION:
nextButton.setText("Compile");
nextButton.setEnabled(false);
commandsArea.setEnabled(false);
commandsArea.setText("");
break;
case SELECTED_SOURCE:
File sourceFile = new File(contikiField.getText());
if (!sourceFile.exists()) {
setDialogState(DialogState.NO_SELECTION);
return;
}
if (!sourceFile.getName().endsWith(".c")) {
setDialogState(DialogState.NO_SELECTION);
return;
}
nextButton.setText("Compile");
nextButton.setEnabled(true);
commandsArea.setEnabled(true);
setCompileCommands(getDefaultCompileCommands(sourceFile));
contikiFirmware = getExpectedFirmwareFile(sourceFile);
contikiSource = sourceFile;
setDialogState(DialogState.AWAITING_COMPILATION);
break;
case AWAITING_COMPILATION:
nextButton.setText("Compile");
nextButton.setEnabled(true);
commandsArea.setEnabled(true);
break;
case IS_COMPILING:
nextButton.setText("Compiling");
nextButton.setEnabled(false);
commandsArea.setEnabled(false);
break;
case COMPILED_FIRMWARE:
nextButton.setText("Create");
nextButton.setEnabled(true);
commandsArea.setEnabled(true);
break;
case SELECTED_FIRMWARE:
contikiFirmware = new File(contikiField.getText());
if (!contikiFirmware.exists()) {
setDialogState(DialogState.NO_SELECTION);
return;
}
if (!canLoadFirmware(contikiFirmware)) {
setDialogState(DialogState.NO_SELECTION);
return;
}
nextButton.setText("Create");
nextButton.setEnabled(true);
commandsArea.setEnabled(false);
setCompileCommands("");
break;
default:
break;
}
}
private void addCompileCommandTab(JTabbedPane parent) {
commandsArea = new JTextArea(10, 1);
commandsArea.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
setDialogState(DialogState.AWAITING_COMPILATION);
}
public void insertUpdate(DocumentEvent e) {
setDialogState(DialogState.AWAITING_COMPILATION);
}
public void removeUpdate(DocumentEvent e) {
setDialogState(DialogState.AWAITING_COMPILATION);
}
});
parent.addTab("Compile commands", null, commandsArea, "Manually alter Contiki compilation commands");
}
private void addMoteInterfacesTab(JTabbedPane parent) {
moteIntfBox = Box.createVerticalBox();
parent.addTab("Mote interfaces", null, new JScrollPane(moteIntfBox), "Mote interfaces");
}
/**
* @return Currently selected mote interface classes
*/
public Class<? extends MoteInterface>[] getSelectedMoteInterfaceClasses() {
ArrayList<Class<? extends MoteInterface>> selected = new ArrayList();
for (Component c : moteIntfBox.getComponents()) {
if (!(c instanceof JCheckBox)) {
continue;
}
if (!((JCheckBox) c).isSelected()) {
continue;
}
Class<? extends MoteInterface> intfClass =
(Class<? extends MoteInterface>)
((JCheckBox) c).getClientProperty("class");
if (intfClass == null) {
continue;
}
selected.add(intfClass);
}
Class<? extends MoteInterface>[] arr = new Class[selected.size()];
selected.toArray(arr);
return arr;
}
/**
* Adds given mote interface to mote interface list, represented by a checkbox.
* If mote interface already exists, this method call is ignored.
*
* @param intfClass Mote interface class
* @param selected If true, interface will initially be selected
*/
public void addMoteInterface(Class<? extends MoteInterface> intfClass, boolean selected) {
/* If mote interface was already added, do nothing */
for (Component c : moteIntfBox.getComponents()) {
if (!(c instanceof JCheckBox)) {
continue;
}
Class<? extends MoteInterface> existingClass =
(Class<? extends MoteInterface>)
((JCheckBox) c).getClientProperty("class");
if (existingClass == null) {
continue;
}
if (existingClass == intfClass) {
return;
}
}
/* Create new mote interface checkbox */
JCheckBox intfCheckBox = new JCheckBox(GUI.getDescriptionOf(intfClass));
intfCheckBox.setSelected(selected);
intfCheckBox.putClientProperty("class", intfClass);
intfCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
intfCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setDialogState(DialogState.AWAITING_COMPILATION);
}
});
moteIntfBox.add(intfCheckBox);
}
/**
* @param commands User configured compile commands
*/
public void setCompileCommands(String commands) {
/* TODO Merge from String[] */
commandsArea.setText(commands);
}
/**
* @return User configured compile commands
*/
public String getCompileCommands() {
/* TODO Split into String[] */
return commandsArea.getText();
}
/**
* @param source Contiki source
* @return Suggested compile commands for compiling source
*/
public abstract String getDefaultCompileCommands(File source);
/**
* @param source Contiki source
* @return Expected Contiki firmware compiled from source
*/
public abstract File getExpectedFirmwareFile(File source);
private void abortAnyCompilation() {
if (currentCompilationProcess == null) {
return;
}
currentCompilationProcess.destroy();
currentCompilationProcess = null;
}
private boolean createNewCompilationTab(MessageList output) {
abortAnyCompilation();
tabbedPane.remove(currentCompilationOutput);
JScrollPane scrollOutput = new JScrollPane(output);
tabbedPane.addTab("Compilation output", null, scrollOutput, "Shows Contiki compilation output");
tabbedPane.setSelectedComponent(scrollOutput);
currentCompilationOutput = scrollOutput;
return true;
}
}