From e770e87995f0687b20aac7384a28211a42709ecd Mon Sep 17 00:00:00 2001 From: nifi Date: Thu, 7 Oct 2010 21:13:00 +0000 Subject: [PATCH] Added more options to send and receive data (via command, file, standard in) --- .../sics/contiki/collect/CollectServer.java | 300 ++++++++++++------ .../contiki/collect/CommandConnection.java | 165 ++++++++++ .../src/se/sics/contiki/collect/Node.java | 9 +- .../contiki/collect/SerialConnection.java | 155 +++------ .../collect/SerialConnectionListener.java | 50 +++ .../contiki/collect/SerialDumpConnection.java | 105 ++++++ .../sics/contiki/collect/StdinConnection.java | 121 +++++++ .../sics/contiki/collect/gui/NodeControl.java | 104 ++++-- .../contiki/collect/gui/SerialConsole.java | 13 +- 9 files changed, 777 insertions(+), 245 deletions(-) create mode 100644 examples/sky-shell/src/se/sics/contiki/collect/CommandConnection.java create mode 100644 examples/sky-shell/src/se/sics/contiki/collect/SerialConnectionListener.java create mode 100644 examples/sky-shell/src/se/sics/contiki/collect/SerialDumpConnection.java create mode 100644 examples/sky-shell/src/se/sics/contiki/collect/StdinConnection.java diff --git a/examples/sky-shell/src/se/sics/contiki/collect/CollectServer.java b/examples/sky-shell/src/se/sics/contiki/collect/CollectServer.java index f7b719327..e65b22b59 100644 --- a/examples/sky-shell/src/se/sics/contiki/collect/CollectServer.java +++ b/examples/sky-shell/src/se/sics/contiki/collect/CollectServer.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: CollectServer.java,v 1.23 2010/10/03 20:19:12 adamdunkels Exp $ + * $Id: CollectServer.java,v 1.24 2010/10/07 21:13:00 nifi Exp $ * * ----------------------------------------------------------------- * @@ -34,8 +34,8 @@ * * Authors : Joakim Eriksson, Niclas Finne * Created : 3 jul 2008 - * Updated : $Date: 2010/10/03 20:19:12 $ - * $Revision: 1.23 $ + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.24 $ */ package se.sics.contiki.collect; @@ -93,9 +93,10 @@ import se.sics.contiki.collect.gui.TimeChartPanel; /** * */ -public class CollectServer { +public class CollectServer implements SerialConnectionListener { public static final String WINDOW_TITLE = "Sensor Data Collect with Contiki"; + public static final String STDIN_COMMAND = ""; public static final String CONFIG_FILE = "collect.conf"; public static final String SENSORDATA_FILE = "sensordata.log"; @@ -116,6 +117,7 @@ public class CollectServer { private ArrayList sensorDataList = new ArrayList(); private PrintWriter sensorDataOutput; + private boolean isSensorLogUsed; private Hashtable nodeTable = new Hashtable(); private Node[] nodeCache; @@ -136,21 +138,20 @@ public class CollectServer { private Node[] selectedNodes; private SerialConnection serialConnection; + private boolean hasSerialOpened; + private boolean hasSentInit; private String initScript; private long nodeTimeDelta; @SuppressWarnings("serial") - public CollectServer(String comPort) { + public CollectServer() { loadConfig(config, CONFIG_FILE); this.configFile = config.getProperty("config.datafile", CONFIG_DATA_FILE); if (this.configFile != null) { loadConfig(configTable, this.configFile); } - if (comPort == null) { - comPort = configTable.getProperty("collect.serialport"); - } this.initScript = config.getProperty("init.script", INIT_SCRIPT); /* Make sure we have nice window decorations */ @@ -631,7 +632,9 @@ public class CollectServer { item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - int reply = JOptionPane.showConfirmDialog(window, "Also clear the sensor data log file?"); + int reply = isSensorLogUsed + ? JOptionPane.showConfirmDialog(window, "Also clear the sensor data log file?") + : JOptionPane.NO_OPTION; if (reply == JOptionPane.YES_OPTION) { // Clear data from both memory and sensor log file clearSensorDataLog(); @@ -713,96 +716,31 @@ public class CollectServer { getNode(property, true); } } - initSensorData(); + } + void start(SerialConnection connection) { + if (this.serialConnection != null) { + throw new IllegalStateException("already started"); + } + this.serialConnection = connection; + if (isSensorLogUsed) { + initSensorData(); + } SwingUtilities.invokeLater(new Runnable() { public void run() { window.setVisible(true); } }); - - serialConnection = new SerialConnection() { - - private boolean hasOpened; - private boolean hasSentInit; - - @Override - protected void serialOpened() { - serialConsole.addSerialData("*** Serial console listening on port: " + getComPort() + " ***"); - hasOpened = true; - // Remember the last selected serial port - configTable.put("collect.serialport", getComPort()); - setSystemMessage("connected to " + getComPort()); - - // Send any initial commands - if (!hasSentInit) { - hasSentInit = true; - - if (hasInitScript()) { - // Wait a short time before running the init script - sleep(3000); - - runInitScript(); - } - } - } - - @Override - protected void serialClosed() { - String comPort = getComPort(); - String prefix; - if (hasOpened) { - serialConsole.addSerialData("*** Serial connection terminated ***"); - prefix = "Serial connection terminated.\n"; - hasOpened = false; - setSystemMessage("not connected"); - } else { - prefix = "Failed to connect to " + getComPort() + '\n'; - } - if (!isClosed) { - String options[] = {"Retry", "Search for connected nodes", "Cancel"}; - int value = JOptionPane.showOptionDialog(window, - prefix + "Do you want to retry or search for connected nodes?", - "Reconnect to serial port?", - JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, - null, options, options[0]); - if (value == JOptionPane.CLOSED_OPTION || value == 2) { -// exit(); - } else { - if (value == 1) { - // Select new serial port - comPort = MoteFinder.selectComPort(window); - if (comPort == null) { -// exit(); - } - } - // Try to open com port again - if (comPort != null) { - open(comPort); - } - } - } - } - - @Override - protected void serialData(String line) { - parseIncomingLine(System.currentTimeMillis(), line); - } - - }; - if (comPort != null) { - serialConnection.setComPort(comPort); - } connectToSerial(); } - + protected void connectToSerial() { if (!serialConnection.isOpen()) { String comPort = serialConnection.getComPort(); - if (comPort == null) { + if (comPort == null && serialConnection.isMultiplePortsSupported()) { comPort = MoteFinder.selectComPort(window); } - if (comPort != null) { + if (comPort != null || !serialConnection.isMultiplePortsSupported()) { serialConnection.open(comPort); } } @@ -893,14 +831,15 @@ public class CollectServer { SwingUtilities.invokeLater(new Runnable() { public void run() { - boolean isOpen = serialConnection.isOpen(); + boolean isOpen = serialConnection != null && serialConnection.isOpen(); if (message == null) { window.setTitle(WINDOW_TITLE); } else { window.setTitle(WINDOW_TITLE + " (" + message + ')'); } serialItem.setText(isOpen ? "Disconnect from serial" : "Connect to serial"); - runInitScriptItem.setEnabled(isOpen && hasInitScript()); + runInitScriptItem.setEnabled(isOpen + && serialConnection.isSerialOutputSupported() && hasInitScript()); } }); @@ -1073,7 +1012,7 @@ public class CollectServer { // ------------------------------------------------------------------- public boolean sendToNode(String data) { - if (serialConnection != null && serialConnection.isOpen()) { + if (serialConnection != null && serialConnection.isOpen() && serialConnection.isSerialOutputSupported()) { serialConsole.addSerialData("SEND: " + data); serialConnection.writeSerialData(data); return true; @@ -1148,10 +1087,10 @@ public class CollectServer { } private void initSensorData() { - loadSensorData(SENSORDATA_FILE); + loadSensorData(SENSORDATA_FILE, true); } - private boolean loadSensorData(String filename) { + private boolean loadSensorData(String filename, boolean isStrict) { File fp = new File(filename); if (fp.exists() && fp.canRead()) { BufferedReader in = null; @@ -1171,7 +1110,7 @@ public class CollectServer { sensorDataList.add(data); handleLinks(data); } - } else { + } else if (isStrict) { // TODO exit here? System.err.println("Failed to parse sensor data from log line " + no + ": " + line); } @@ -1189,7 +1128,7 @@ public class CollectServer { private void saveSensorData(SensorData data) { PrintWriter output = this.sensorDataOutput; - if (output == null) { + if (output == null && isSensorLogUsed) { try { output = sensorDataOutput = new PrintWriter(new FileWriter(SENSORDATA_FILE, true)); } catch (IOException e) { @@ -1288,8 +1227,10 @@ public class CollectServer { int reply = JOptionPane.showConfirmDialog(window, "Found " + motes.length + " connected Sky nodes.\n" + "Do you want to upload the firmware " + FIRMWARE_FILE + '?'); if (reply == JFileChooser.APPROVE_OPTION) { - boolean wasOpen = serialConnection.isOpen(); - serialConnection.close(); + boolean wasOpen = serialConnection != null && serialConnection.isOpen(); + if (serialConnection != null) { + serialConnection.close(); + } if (wasOpen) { Thread.sleep(1000); } @@ -1309,20 +1250,177 @@ public class CollectServer { } + + // ------------------------------------------------------------------- + // SerialConnection Listener + // ------------------------------------------------------------------- + + @Override + public void serialData(SerialConnection connection, String line) { + parseIncomingLine(System.currentTimeMillis(), line); + } + + @Override + public void serialOpened(SerialConnection connection) { + String connectionName = connection.getConnectionName(); + serialConsole.addSerialData("*** Serial console listening on " + connectionName + " ***"); + hasSerialOpened = true; + if (connection.isMultiplePortsSupported()) { + String comPort = connection.getComPort(); + // Remember the last selected serial port + configTable.put("collect.serialport", comPort); + } + setSystemMessage("connected to " + connectionName); + + if (!connection.isSerialOutputSupported()) { + serialConsole.addSerialData("*** Serial output not supported ***"); + } else if (!hasSentInit) { + // Send any initial commands + hasSentInit = true; + + if (hasInitScript()) { + // Wait a short time before running the init script + sleep(3000); + + runInitScript(); + } + } + } + + @Override + public void serialClosed(SerialConnection connection) { + String prefix; + if (hasSerialOpened) { + serialConsole.addSerialData("*** Serial connection terminated ***"); + prefix = "Serial connection terminated.\n"; + hasSerialOpened = false; + setSystemMessage("not connected"); + } else { + prefix = "Failed to connect to " + connection.getConnectionName() + '\n'; + } + if (!connection.isClosed()) { + if (connection.isMultiplePortsSupported()) { + String options[] = {"Retry", "Search for connected nodes", "Cancel"}; + int value = JOptionPane.showOptionDialog(window, + prefix + "Do you want to retry or search for connected nodes?", + "Reconnect to serial port?", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + if (value == JOptionPane.CLOSED_OPTION || value == 2) { +// exit(); + } else { + String comPort = connection.getComPort(); + if (value == 1) { + // Select new serial port + comPort = MoteFinder.selectComPort(window); + if (comPort == null) { +// exit(); + } + } + // Try to open com port again + if (comPort != null) { + connection.open(comPort); + } + } + } else { +// JOptionPane.showMessageDialog(window, +// prefix, "Serial Connection Closed", JOptionPane.ERROR_MESSAGE); + } + } + } + + // ------------------------------------------------------------------- // Main // ------------------------------------------------------------------- public static void main(String[] args) { + boolean resetSensorLog = false; + boolean useSensorLog = true; + boolean useSerialOutput = true; + String command = null; + String logFileToLoad = null; String comPort = null; - if (args.length > 0) { - if (args.length > 1 || args[0].startsWith("-h")) { - System.err.println("Usage: java CollectServer COMPORT"); - System.exit(1); + for(int i = 0, n = args.length; i < n; i++) { + String arg = args[i]; + if (arg.length() == 2 && arg.charAt(0) == '-') { + switch (arg.charAt(1)) { + case 'c': + if (i + 1 < n) { + command = args[++i]; + } else { + usage(arg); + } + break; + case 'r': + resetSensorLog = true; + break; + case 'n': + useSensorLog = false; + break; + case 'i': + useSerialOutput = false; + break; + case 'f': + command = STDIN_COMMAND; + if (i + 1 < n && !args[i + 1].startsWith("-")) { + logFileToLoad = args[++i]; + } + break; + case 'h': + usage(null); + break; + default: + usage(arg); + break; + } + } else if (comPort == null) { + comPort = arg; + } else { + usage(arg); } - comPort = args[0]; } - new CollectServer(comPort); + + CollectServer server = new CollectServer(); + SerialConnection serialConnection; + if (command == null) { + serialConnection = new SerialDumpConnection(server); + } else if (command == STDIN_COMMAND) { + serialConnection = new StdinConnection(server); + } else { + serialConnection = new CommandConnection(server, command); + } + if (comPort == null) { + comPort = server.getConfig("collect.serialport"); + } + if (comPort != null) { + serialConnection.setComPort(comPort); + } + if (!useSerialOutput) { + serialConnection.setSerialOutputSupported(false); + } + + server.isSensorLogUsed = useSensorLog; + if (useSensorLog && resetSensorLog) { + server.clearSensorDataLog(); + } + if (logFileToLoad != null) { + server.loadSensorData(logFileToLoad, false); + } + server.start(serialConnection); } + private static void usage(String arg) { + if (arg != null) { + System.err.println("Unknown argument '" + arg + '\''); + } + System.err.println("Usage: java CollectServer [-n] [-i] [-r] [-f [file]] [-c command] [COMPORT]"); + System.err.println(" -n : Do not read or save sensor data log"); + System.err.println(" -r : Clear any existing sensor data log at startup"); + System.err.println(" -i : Do not allow serial output"); + System.err.println(" -f : Read serial data from standard in"); + System.err.println(" -c : Use specified command for serial data input/output"); + System.err.println(" COMPORT: The serial port to connect to"); + System.exit(arg != null ? 1 : 0); + } } diff --git a/examples/sky-shell/src/se/sics/contiki/collect/CommandConnection.java b/examples/sky-shell/src/se/sics/contiki/collect/CommandConnection.java new file mode 100644 index 000000000..c0e3f0377 --- /dev/null +++ b/examples/sky-shell/src/se/sics/contiki/collect/CommandConnection.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010, 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: CommandConnection.java,v 1.1 2010/10/07 21:13:00 nifi Exp $ + * + * ----------------------------------------------------------------- + * + * CommandConnection + * + * Authors : Joakim Eriksson, Niclas Finne + * Created : 5 oct 2010 + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.1 $ + */ + +package se.sics.contiki.collect; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +/** + * + */ +public class CommandConnection extends SerialConnection { + + protected Process commandProcess; + protected String command; + + public CommandConnection(SerialConnectionListener listener) { + super(listener); + } + + public CommandConnection(SerialConnectionListener listener, String command) { + super(listener); + this.command = command; + } + + @Override + public String getConnectionName() { + return command; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + @Override + public void open(String comPort) { + close(); + this.comPort = comPort == null ? "" : comPort; + + String fullCommand = getCommand(); + + isClosed = false; + try { + String[] cmd = fullCommand.split(" "); + System.err.println("Running '" + fullCommand + '\''); + + commandProcess = Runtime.getRuntime().exec(cmd); + final BufferedReader input = new BufferedReader(new InputStreamReader(commandProcess.getInputStream())); + final BufferedReader err = new BufferedReader(new InputStreamReader(commandProcess.getErrorStream())); + setSerialOutput(new PrintWriter(new OutputStreamWriter(commandProcess.getOutputStream()))); + + /* Start thread listening on standard out */ + Thread readInput = new Thread(new Runnable() { + public void run() { + String line; + try { + while ((line = input.readLine()) != null) { + standardData(line); + } + input.close(); + System.err.println("SerialConnection command terminated."); + closeConnection(); + } catch (IOException e) { + lastError = "Error when reading from SerialConnection command: " + e; + System.err.println(lastError); + if (!isClosed) { + e.printStackTrace(); + closeConnection(); + } + } + } + }, "read input stream thread"); + + /* Start thread listening on standard err */ + Thread readError = new Thread(new Runnable() { + public void run() { + String line; + try { + while ((line = err.readLine()) != null) { + errorData(line); + } + err.close(); + } catch (IOException e) { + if (!isClosed) { + System.err.println("Error when reading from SerialConnection command: " + e); + e.printStackTrace(); + } + } + } + }, "read error stream thread"); + + readInput.start(); + readError.start(); + } catch (Exception e) { + lastError = "Failed to execute '" + fullCommand + "': " + e; + System.err.println(lastError); + e.printStackTrace(); + closeConnection(); + } + } + + protected void standardData(String line) { + if (!isOpen) { + isOpen = true; + serialOpened(); + } + serialData(line); + } + + protected void errorData(String line) { + System.err.println("SerialConnection error stream> " + line); + } + + @Override + protected void doClose() { + if (commandProcess != null) { + commandProcess.destroy(); + commandProcess = null; + } + } + +} diff --git a/examples/sky-shell/src/se/sics/contiki/collect/Node.java b/examples/sky-shell/src/se/sics/contiki/collect/Node.java index 33d749bb5..cfb403378 100644 --- a/examples/sky-shell/src/se/sics/contiki/collect/Node.java +++ b/examples/sky-shell/src/se/sics/contiki/collect/Node.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: Node.java,v 1.6 2010/09/30 22:24:45 nifi Exp $ + * $Id: Node.java,v 1.7 2010/10/07 21:13:00 nifi Exp $ * * ----------------------------------------------------------------- * @@ -34,8 +34,8 @@ * * Authors : Joakim Eriksson, Niclas Finne * Created : 3 jul 2008 - * Updated : $Date: 2010/09/30 22:24:45 $ - * $Revision: 1.6 $ + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.7 $ */ package se.sics.contiki.collect; @@ -176,8 +176,7 @@ public class Node implements Comparable { public boolean addSensorData(SensorData data) { if (sensorDataList.size() > 0) { SensorData last = sensorDataList.get(sensorDataList.size() - 1); - // TODO should check seqno! - if (data.getNodeTime() <= last.getNodeTime()) { + if (data.getNodeTime() < last.getNodeTime()) { // Sensor data already added System.out.println("SensorData: ignoring (time " + (data.getNodeTime() - last.getNodeTime()) + "msec): " + data); diff --git a/examples/sky-shell/src/se/sics/contiki/collect/SerialConnection.java b/examples/sky-shell/src/se/sics/contiki/collect/SerialConnection.java index 824060a12..a1ff4b872 100644 --- a/examples/sky-shell/src/se/sics/contiki/collect/SerialConnection.java +++ b/examples/sky-shell/src/se/sics/contiki/collect/SerialConnection.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: SerialConnection.java,v 1.3 2008/09/03 13:35:21 nifi Exp $ + * $Id: SerialConnection.java,v 1.4 2010/10/07 21:13:00 nifi Exp $ * * ----------------------------------------------------------------- * @@ -34,15 +34,11 @@ * * Authors : Joakim Eriksson, Niclas Finne * Created : 5 jul 2008 - * Updated : $Date: 2008/09/03 13:35:21 $ - * $Revision: 1.3 $ + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.4 $ */ package se.sics.contiki.collect; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; import java.io.PrintWriter; /** @@ -50,20 +46,43 @@ import java.io.PrintWriter; */ public abstract class SerialConnection { - public static final String SERIALDUMP_WINDOWS = "./tools/serialdump-windows.exe"; - public static final String SERIALDUMP_LINUX = "./tools/serialdump-linux"; + protected final SerialConnectionListener listener; - private String comPort; - private Process serialDumpProcess; - private PrintWriter serialOutput; + protected boolean isSerialOutputSupported = true; + + protected String comPort; protected boolean isOpen; protected boolean isClosed = true; protected String lastError; + protected PrintWriter serialOutput; + + protected SerialConnection(SerialConnectionListener listener) { + this.listener = listener; + } + + public boolean isMultiplePortsSupported() { + return false; + } + + public void setSerialOutputSupported(boolean isSerialOutputSupported) { + this.isSerialOutputSupported = isSerialOutputSupported; + } + + public boolean isSerialOutputSupported() { + return isSerialOutputSupported; + } + public boolean isOpen() { return isOpen; } + public boolean isClosed() { + return isClosed; + } + + public abstract String getConnectionName(); + public String getComPort() { return comPort; } @@ -76,91 +95,12 @@ public abstract class SerialConnection { return lastError; } - public void open(String comPort) { - if (comPort == null) { - throw new IllegalStateException("no com port"); - } - close(); - this.comPort = comPort; - - /* Connect to COM using external serialdump application */ - String osName = System.getProperty("os.name").toLowerCase(); - String fullCommand; - if (osName.startsWith("win")) { - fullCommand = SERIALDUMP_WINDOWS + " " + "-b115200" + " " + getMappedComPortForWindows(comPort); - } else { - fullCommand = SERIALDUMP_LINUX + " " + "-b115200" + " " + comPort; - } - - isClosed = false; - try { - String[] cmd = fullCommand.split(" "); - - serialDumpProcess = Runtime.getRuntime().exec(cmd); - final BufferedReader input = new BufferedReader(new InputStreamReader(serialDumpProcess.getInputStream())); - final BufferedReader err = new BufferedReader(new InputStreamReader(serialDumpProcess.getErrorStream())); - serialOutput = new PrintWriter(new OutputStreamWriter(serialDumpProcess.getOutputStream())); - - /* Start thread listening on stdout */ - Thread readInput = new Thread(new Runnable() { - public void run() { - String line; - try { - while ((line = input.readLine()) != null) { - serialData(line); - } - input.close(); - System.out.println("Serialdump process terminated."); - closeConnection(); - } catch (IOException e) { - lastError = "Error when reading from serialdump process: " + e; - System.err.println(lastError); - if (!isClosed) { - e.printStackTrace(); - closeConnection(); - } - } - } - }, "read input stream thread"); - - /* Start thread listening on stderr */ - Thread readError = new Thread(new Runnable() { - public void run() { - String line; - try { - while ((line = err.readLine()) != null) { - if (!isOpen && line.startsWith("connecting") && line.endsWith("[OK]")) { - isOpen = true; - serialOpened(); - } else { - System.err.println("Serialdump error stream> " + line); - } - } - err.close(); - } catch (IOException e) { - if (!isClosed) { - System.err.println("Error when reading from serialdump process: " + e); - e.printStackTrace(); - } - } - } - }, "read error stream thread"); - - readInput.start(); - readError.start(); - } catch (Exception e) { - lastError = "Failed to execute '" + fullCommand + "': " + e; - System.err.println(lastError); - e.printStackTrace(); - closeConnection(); - } + protected PrintWriter getSerialOutput() { + return serialOutput; } - private String getMappedComPortForWindows(String comPort) { - if (comPort.startsWith("COM")) { - comPort = "/dev/com" + comPort.substring(3); - } - return comPort; + protected void setSerialOutput(PrintWriter serialOutput) { + this.serialOutput = serialOutput; } public void writeSerialData(String data) { @@ -171,29 +111,36 @@ public abstract class SerialConnection { } } - public void close() { + public abstract void open(String comPort); + + public final void close() { isClosed = true; lastError = null; closeConnection(); } - protected void closeConnection() { + protected final void closeConnection() { isOpen = false; if (serialOutput != null) { serialOutput.close(); serialOutput = null; } - if (serialDumpProcess != null) { - serialDumpProcess.destroy(); - serialDumpProcess = null; - } + doClose(); serialClosed(); } - protected abstract void serialData(String line); + protected abstract void doClose(); - protected abstract void serialOpened(); + protected final void serialData(String line) { + listener.serialData(this, line); + } - protected abstract void serialClosed(); + protected final void serialOpened() { + listener.serialOpened(this); + } + + protected final void serialClosed() { + listener.serialClosed(this); + } } diff --git a/examples/sky-shell/src/se/sics/contiki/collect/SerialConnectionListener.java b/examples/sky-shell/src/se/sics/contiki/collect/SerialConnectionListener.java new file mode 100644 index 000000000..81fe72956 --- /dev/null +++ b/examples/sky-shell/src/se/sics/contiki/collect/SerialConnectionListener.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, 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: SerialConnectionListener.java,v 1.1 2010/10/07 21:13:00 nifi Exp $ + * + * ----------------------------------------------------------------- + * + * SerialConnectionListener + * + * Authors : Joakim Eriksson, Niclas Finne + * Created : 5 oct 2010 + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.1 $ + */ +package se.sics.contiki.collect; + +public interface SerialConnectionListener { + + public void serialData(SerialConnection connection, String line); + + public void serialOpened(SerialConnection connection); + + public void serialClosed(SerialConnection connection); + +} diff --git a/examples/sky-shell/src/se/sics/contiki/collect/SerialDumpConnection.java b/examples/sky-shell/src/se/sics/contiki/collect/SerialDumpConnection.java new file mode 100644 index 000000000..2593593f2 --- /dev/null +++ b/examples/sky-shell/src/se/sics/contiki/collect/SerialDumpConnection.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010, 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: SerialDumpConnection.java,v 1.1 2010/10/07 21:13:00 nifi Exp $ + * + * ----------------------------------------------------------------- + * + * SerialDumpConnection + * + * Authors : Joakim Eriksson, Niclas Finne + * Created : 5 oct 2010 + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.1 $ + */ + +package se.sics.contiki.collect; + +/** + * + */ +public class SerialDumpConnection extends CommandConnection { + + public static final String SERIALDUMP_WINDOWS = "./tools/serialdump-windows.exe"; + public static final String SERIALDUMP_LINUX = "./tools/serialdump-linux"; + + public SerialDumpConnection(SerialConnectionListener listener) { + super(listener); + } + + @Override + public boolean isMultiplePortsSupported() { + return true; + } + + @Override + public String getConnectionName() { + return comPort; + } + + @Override + public void open(String comPort) { + if (comPort == null) { + throw new IllegalStateException("no com port"); + } + + /* Connect to COM using external serialdump application */ + String osName = System.getProperty("os.name").toLowerCase(); + String fullCommand; + if (osName.startsWith("win")) { + fullCommand = SERIALDUMP_WINDOWS + " " + "-b115200" + " " + getMappedComPortForWindows(comPort); + } else { + fullCommand = SERIALDUMP_LINUX + " " + "-b115200" + " " + comPort; + } + setCommand(fullCommand); + super.open(comPort); + } + + @Override + protected void standardData(String line) { + serialData(line); + } + + @Override + protected void errorData(String line) { + if (!isOpen && line.startsWith("connecting") && line.endsWith("[OK]")) { + isOpen = true; + serialOpened(); + } else { + super.errorData(line); + } + } + + private String getMappedComPortForWindows(String comPort) { + if (comPort.startsWith("COM")) { + comPort = "/dev/com" + comPort.substring(3); + } + return comPort; + } + +} diff --git a/examples/sky-shell/src/se/sics/contiki/collect/StdinConnection.java b/examples/sky-shell/src/se/sics/contiki/collect/StdinConnection.java new file mode 100644 index 000000000..2298e501b --- /dev/null +++ b/examples/sky-shell/src/se/sics/contiki/collect/StdinConnection.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2010, 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: StdinConnection.java,v 1.1 2010/10/07 21:13:00 nifi Exp $ + * + * ----------------------------------------------------------------- + * + * StdinConnection + * + * Authors : Niclas Finne + * Created : 5 oct 2010 + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.1 $ + */ + +package se.sics.contiki.collect; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +/** + * + */ +public class StdinConnection extends SerialConnection { + + private PrintWriter stdout; + + public StdinConnection(SerialConnectionListener listener) { + super(listener); + // Redirect standard out as standard err to use standard out for serial output + stdout = new PrintWriter(new OutputStreamWriter(System.out)); + System.setOut(System.err); + } + + @Override + public String getConnectionName() { + return ""; + } + + @Override + public void open(String comPort) { + close(); + this.comPort = comPort == null ? "" : comPort; + + isClosed = false; + try { + final BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); + setSerialOutput(stdout); + + /* Start thread listening on standard in */ + Thread readInput = new Thread(new Runnable() { + public void run() { + String line; + try { + while ((line = input.readLine()) != null) { + serialData(line); + // Do not send data too fast + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + input.close(); + System.out.println("SerialConnection stdin terminated."); + closeConnection(); + } catch (IOException e) { + lastError = "Error when reading from SerialConnection stdin: " + e; + System.err.println(lastError); + if (!isClosed) { + e.printStackTrace(); + closeConnection(); + } + } + } + }, "read input stream thread"); + + isOpen = true; + serialOpened(); + readInput.start(); + + } catch (Exception e) { + lastError = "Failed to open stdin for reading: " + e; + System.err.println(lastError); + e.printStackTrace(); + closeConnection(); + } + } + + @Override + protected void doClose() { + } + +} diff --git a/examples/sky-shell/src/se/sics/contiki/collect/gui/NodeControl.java b/examples/sky-shell/src/se/sics/contiki/collect/gui/NodeControl.java index 428eb4bdc..d54d0ec7c 100644 --- a/examples/sky-shell/src/se/sics/contiki/collect/gui/NodeControl.java +++ b/examples/sky-shell/src/se/sics/contiki/collect/gui/NodeControl.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: NodeControl.java,v 1.3 2010/10/03 20:19:37 adamdunkels Exp $ + * $Id: NodeControl.java,v 1.4 2010/10/07 21:13:00 nifi Exp $ * * ----------------------------------------------------------------- * @@ -34,14 +34,15 @@ * * Authors : Niclas Finne * Created : 27 sep 2010 - * Updated : $Date: 2010/10/03 20:19:37 $ - * $Revision: 1.3 $ + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.4 $ */ package se.sics.contiki.collect.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; +import java.awt.Cursor; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; @@ -63,6 +64,8 @@ import se.sics.contiki.collect.Visualizer; */ public class NodeControl implements Visualizer { + private final static String SET_TIME_COMMAND = "time %TIME% | null"; + private final CollectServer server; private final String category; private final JPanel panel; @@ -79,15 +82,11 @@ public class NodeControl implements Visualizer { final JFormattedTextField rexmitsField = new JFormattedTextField(new Integer(15)); statusLabel = new JLabel("", JLabel.CENTER); statusLabel.setOpaque(true); + statusLabel.setBackground(Color.white); + statusLabel.setBorder(LineBorder.createBlackLineBorder()); + statusLabel.setVisible(false); - JButton stopButton = new JButton("Send stop to nodes"); - stopButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - sendCommand("netcmd killall"); - } - - }); + JButton stopButton = createCommandButton("Send stop to nodes", "netcmd killall"); JButton sendButton = new JButton("Send command to nodes"); sendButton.addActionListener(new ActionListener() { @@ -104,6 +103,10 @@ public class NodeControl implements Visualizer { }); + JButton collectButton = createCommandButton("Start Collect", + "mac 0", SET_TIME_COMMAND, "collect | timestamp | blink | binprint &"); + JButton stopCollectButton = createCommandButton("Stop Collect", "~K", "killall"); + JPanel controlPanel = new JPanel(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); @@ -144,35 +147,76 @@ public class NodeControl implements Visualizer { controlPanel.add(new JLabel("(0 = report forever)"), c); c.gridy++; - c.gridx = 1; + c.gridwidth = 3; c.weightx = 0; c.fill = GridBagConstraints.NONE; - c.insets.bottom = 50; - controlPanel.add(sendButton, c); + c.insets.bottom = 20; + JPanel p = new JPanel(); + p.add(sendButton); + p.add(stopButton); + controlPanel.add(p, c); - c.gridx = 0; c.gridy++; - c.gridwidth = 3; - c.ipadx = c.ipady = 6; - controlPanel.add(statusLabel, c); + c.insets.bottom = 3; + controlPanel.add(new JLabel("Base Station Control", JLabel.CENTER), c); + + c.gridy++; + c.insets.bottom = 20; + p = new JPanel(); + p.add(collectButton); + p.add(stopCollectButton); + controlPanel.add(p, c); panel.add(controlPanel, BorderLayout.NORTH); - - controlPanel = new JPanel(); - controlPanel.add(stopButton); - panel.add(controlPanel, BorderLayout.SOUTH); + panel.add(statusLabel, BorderLayout.SOUTH); } - protected void sendCommand(String command) { - statusLabel.setBackground(Color.white); - statusLabel.setBorder(LineBorder.createBlackLineBorder()); + private JButton createCommandButton(String name, final String... command) { + JButton button = new JButton(name); + button.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + try { + // TODO Should use separate thread to send commands + panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + for(int i = 0, n = command.length; i < n; i++) { + if (i > 0) { + try { + // Do not send multiple commands too fast + Thread.sleep(1000); + } catch (InterruptedException e1) { + } + } + String cmd = command[i]; + if (cmd == SET_TIME_COMMAND) { + cmd = "time " + (System.currentTimeMillis() / 1000) + " | null"; + } + if (!sendCommand(cmd)) { + break; + } + } + } finally { + panel.setCursor(Cursor.getDefaultCursor()); + } + } + + }); + return button; + } + + protected boolean sendCommand(String command) { if (server.sendToNode(command)) { - statusLabel.setForeground(Color.black); - statusLabel.setText("Sent command '" + command + "'"); - } else { - statusLabel.setForeground(Color.red); - statusLabel.setText("Failed to send command. No serial connection."); + setStatus("Sent command '" + command + "'", false); + return true; } + setStatus("Failed to send command. No serial connection.", true); + return false; + } + + private void setStatus(String text, boolean isWarning) { + statusLabel.setForeground(isWarning ? Color.red : Color.black); + statusLabel.setText(text); + statusLabel.setVisible(true); } public String getCategory() { diff --git a/examples/sky-shell/src/se/sics/contiki/collect/gui/SerialConsole.java b/examples/sky-shell/src/se/sics/contiki/collect/gui/SerialConsole.java index 048e6aa79..ca09842c1 100644 --- a/examples/sky-shell/src/se/sics/contiki/collect/gui/SerialConsole.java +++ b/examples/sky-shell/src/se/sics/contiki/collect/gui/SerialConsole.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: SerialConsole.java,v 1.2 2010/09/15 16:15:10 nifi Exp $ + * $Id: SerialConsole.java,v 1.3 2010/10/07 21:13:00 nifi Exp $ * * ----------------------------------------------------------------- * @@ -34,8 +34,8 @@ * * Authors : Joakim Eriksson, Niclas Finne * Created : 4 jul 2008 - * Updated : $Date: 2010/09/15 16:15:10 $ - * $Revision: 1.2 $ + * Updated : $Date: 2010/10/07 21:13:00 $ + * $Revision: 1.3 $ */ package se.sics.contiki.collect.gui; @@ -106,8 +106,11 @@ public class SerialConsole implements Visualizer { historyCount = (historyCount + 1) % history.length; } historyPos = historyCount; - SerialConsole.this.server.sendToNode(command); - commandField.setText(""); + if (SerialConsole.this.server.sendToNode(command)) { + commandField.setText(""); + } else { + addSerialData("*** failed to send command ***"); + } } catch (Exception ex) { System.err.println("could not send '" + command + "':"); ex.printStackTrace();