From 9ea19b4e81f055315e57067936e4cd7658c1789b Mon Sep 17 00:00:00 2001 From: joxe Date: Thu, 19 Mar 2009 18:55:44 +0000 Subject: [PATCH] added generic serial GUI component --- .../java/se/sics/cooja/dialogs/SerialUI.java | 339 ++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 tools/cooja/java/se/sics/cooja/dialogs/SerialUI.java diff --git a/tools/cooja/java/se/sics/cooja/dialogs/SerialUI.java b/tools/cooja/java/se/sics/cooja/dialogs/SerialUI.java new file mode 100644 index 000000000..468a5557d --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/dialogs/SerialUI.java @@ -0,0 +1,339 @@ + /* + * Copyright (c) 2007, 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: SerialUI.java,v 1.1 2009/03/19 18:55:44 joxe Exp $ + */ + +package se.sics.cooja.dialogs; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Collection; +import java.util.Observable; +import java.util.Observer; + +import org.jdom.Element; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import org.apache.log4j.Logger; + +import se.sics.cooja.Mote; +import se.sics.cooja.interfaces.Log; +import se.sics.cooja.interfaces.SerialPort; +import se.sics.cooja.plugins.SLIP; + +public abstract class SerialUI extends Log implements SerialPort { + private static Logger logger = Logger.getLogger(SerialUI.class); + + private String lastLogMessage = ""; + private StringBuilder newMessage = new StringBuilder(); + + private JTextArea logTextPane = null; + private JTextField commandField; + private JCheckBox slipCheckbox; + private String[] history = new String[50]; + private int historyPos = 0; + private int historyCount = 0; + + private class SerialDataObservable extends Observable { + private void notifyNewData() { + if (this.countObservers() == 0) { + return; + } + setChanged(); + notifyObservers(SerialUI.this); + } + } + + private SerialDataObservable serialDataObservable = new SerialDataObservable(); + private byte lastSerialData = 0; + + public String getLastLogMessage() { + return lastLogMessage; + } + + public abstract Mote getMote(); + + public void addSerialDataObserver(Observer o) { + serialDataObservable.addObserver(o); + } + + public void deleteSerialDataObserver(Observer o) { + serialDataObservable.deleteObserver(o); + } + + public byte getLastSerialData() { + return lastSerialData; + } + + public JPanel getInterfaceVisualizer() { + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + + if (logTextPane == null) { + logTextPane = new JTextArea(); + } + + // Send RS232 data visualizer + JPanel sendPane = new JPanel(new BorderLayout()); + commandField = new JTextField(15); + JButton sendButton = new JButton("Send data"); + ActionListener action = new ActionListener() { + + public void actionPerformed(ActionEvent e) { + String command = trim(commandField.getText()); + if (command != null) { + try { + int previous = historyCount - 1; + if (previous < 0) { + previous += history.length; + } + if (!command.equals(history[previous])) { + history[historyCount] = command; + historyCount = (historyCount + 1) % history.length; + } + historyPos = historyCount; + + if (slipCheckbox.isSelected()) { + addToLog("SLIP> " + command); + command += "\n"; + writeArray(SLIP.asSlip(command.getBytes())); + } else { + addToLog("> " + command); + writeString(command); + } + commandField.setText(""); + } catch (Exception ex) { + System.err.println("could not send '" + command + "':"); + ex.printStackTrace(); + JOptionPane.showMessageDialog(logTextPane, + "could not send '" + command + "':\n" + + ex, "ERROR", + JOptionPane.ERROR_MESSAGE); + } + } else { + commandField.getToolkit().beep(); + } + } + + }; + commandField.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + switch (e.getKeyCode()) { + case KeyEvent.VK_UP: { + int nextPos = (historyPos + history.length - 1) % history.length; + if (nextPos == historyCount || history[nextPos] == null) { + commandField.getToolkit().beep(); + } else { + String cmd = trim(commandField.getText()); + if (cmd != null) { + history[historyPos] = cmd; + } + historyPos = nextPos; + commandField.setText(history[historyPos]); + } + break; + } + case KeyEvent.VK_DOWN: { + int nextPos = (historyPos + 1) % history.length; + if (nextPos == historyCount) { + historyPos = nextPos; + commandField.setText(""); + } else if (historyPos == historyCount || history[nextPos] == null) { + commandField.getToolkit().beep(); + } else { + String cmd = trim(commandField.getText()); + if (cmd != null) { + history[historyPos] = cmd; + } + historyPos = nextPos; + commandField.setText(history[historyPos]); + } + break; + } + } + } + + }); + + slipCheckbox = new JCheckBox("", false); + slipCheckbox.setToolTipText("Wrap data as SLIP"); + + commandField.addActionListener(action); + sendButton.addActionListener(action); + sendPane.add(BorderLayout.WEST, slipCheckbox); + sendPane.add(BorderLayout.CENTER, commandField); + sendPane.add(BorderLayout.EAST, sendButton); + + // Receive RS232 data visualizer + logTextPane.setOpaque(false); + logTextPane.setEditable(false); + + if (getLastLogMessage() == null) { + logTextPane.setText(""); + } else { + logTextPane.append(getLastLogMessage()); + } + + Observer observer; + this.addObserver(observer = new Observer() { + public void update(Observable obs, Object obj) { + final String logMessage = getLastLogMessage(); + EventQueue.invokeLater(new Runnable() { + public void run() { + addToLog(logMessage); + } + }); + } + }); + + // Saving observer reference for releaseInterfaceVisualizer + panel.putClientProperty("intf_obs", observer); + + JScrollPane scrollPane = new JScrollPane(logTextPane); + scrollPane.setPreferredSize(new Dimension(100, 100)); + panel.add(BorderLayout.NORTH, new JLabel("Last serial data:")); + panel.add(BorderLayout.CENTER, scrollPane); + panel.add(BorderLayout.SOUTH, sendPane); + return panel; + } + + protected void addToLog(String text) { + String current = logTextPane.getText(); + int len = current.length(); + if (len > 8192) { + current = current.substring(len - 8192); + } + current = len > 0 ? (current + '\n' + text) : text; + logTextPane.setText(current); + logTextPane.setCaretPosition(current.length()); + } + + public void releaseInterfaceVisualizer(JPanel panel) { + Observer observer = (Observer) panel.getClientProperty("intf_obs"); + if (observer == null) { + logger.fatal("Error when releasing panel, observer is null"); + return; + } + + this.deleteObserver(observer); + } + + public double energyConsumption() { + return 0; + } + + public Collection getConfigXML() { + return null; + } + + public void setConfigXML(Collection configXML, boolean visAvailable) { + } + + private int tosChars = 0; + boolean tosMode = false; + private int[] tosData = new int[255]; + private int tosPos = 0; + private int tosLen = 0; + + public void dataReceived(int data) { + if (tosMode) { + /* needs to add checks to CRC */ + // System.out.println("Received: " + Integer.toString(data, 16) + " = " + (char) data + " tosPos: " + tosPos); + if (data == 0x7e) { + if (tosPos > 6) { + lastLogMessage = "TinyOS: " + newMessage.toString(); + newMessage.setLength(0); + this.setChanged(); + this.notifyObservers(getMote()); + // System.out.println("*** Printing TOS String: " + lastLogMessage); + tosPos = 0; + tosLen = 0; + } else { + /* start of new message! */ + tosPos = 0; + tosLen = 0; + } + } + if (tosPos == 7) { + tosLen = data; + // System.out.println("TOS Payload len: " + tosLen); + } + if (tosPos > 9 && tosPos < 10 + tosLen) { + if (data < 32) { + data = 32; + } + newMessage.append((char) data); + } + tosData[tosPos++] = data; + } else { + if (data == 0x7e) { + tosChars++; + if (tosChars == 2) { + tosMode = true; + /* already read one char here */ + tosPos = 1; + } + } else { + tosChars = 0; + } + if (data == '\n') { + lastLogMessage = newMessage.toString(); + newMessage.setLength(0); + this.setChanged(); + this.notifyObservers(getMote()); + } else { + newMessage.append((char) data); + } + lastSerialData = (byte) data; + serialDataObservable.notifyNewData(); + } + } + + public void close() { + } + + public void flushInput() { + } + + private String trim(String text) { + return (text != null) && ((text = text.trim()).length() > 0) ? text : null; + } +}