From af7f9ced811df02a91762e052d92fb880ff55505 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sat, 12 Apr 2014 00:50:56 +0200 Subject: [PATCH 01/14] [cooja/serialsocket] Moved to package org.contikios.cooja.serialsocket --- tools/cooja/apps/serial_socket/cooja.config | 2 +- .../contikios/cooja/serialsocket}/SerialSocketClient.java | 2 ++ .../contikios/cooja/serialsocket}/SerialSocketServer.java | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) rename tools/cooja/apps/serial_socket/java/{ => org/contikios/cooja/serialsocket}/SerialSocketClient.java (99%) rename tools/cooja/apps/serial_socket/java/{ => org/contikios/cooja/serialsocket}/SerialSocketServer.java (99%) diff --git a/tools/cooja/apps/serial_socket/cooja.config b/tools/cooja/apps/serial_socket/cooja.config index 10b86f27f..8c492dc83 100644 --- a/tools/cooja/apps/serial_socket/cooja.config +++ b/tools/cooja/apps/serial_socket/cooja.config @@ -1,2 +1,2 @@ -org.contikios.cooja.Cooja.PLUGINS = + SerialSocketClient SerialSocketServer +org.contikios.cooja.Cooja.PLUGINS = + org.contikios.cooja.serialsocket.SerialSocketClient org.contikios.cooja.serialsocket.SerialSocketServer org.contikios.cooja.Cooja.JARFILES = + serial_socket.jar diff --git a/tools/cooja/apps/serial_socket/java/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java similarity index 99% rename from tools/cooja/apps/serial_socket/java/SerialSocketClient.java rename to tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index c3333c0b0..d29d8a2a6 100644 --- a/tools/cooja/apps/serial_socket/java/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -1,3 +1,5 @@ +package org.contikios.cooja.serialsocket; + /* * Copyright (c) 2010, Swedish Institute of Computer Science. * All rights reserved. diff --git a/tools/cooja/apps/serial_socket/java/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java similarity index 99% rename from tools/cooja/apps/serial_socket/java/SerialSocketServer.java rename to tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index b12197ee0..2c99dd7a9 100644 --- a/tools/cooja/apps/serial_socket/java/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -1,3 +1,5 @@ +package org.contikios.cooja.serialsocket; + /* * Copyright (c) 2010, Swedish Institute of Computer Science. * All rights reserved. From 404fd8239998d046e44dcb4c5a66850a1f59728b Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sat, 12 Apr 2014 01:04:18 +0200 Subject: [PATCH 02/14] [cooja/serialsocket] Added @Override annotations --- .../contikios/cooja/serialsocket/SerialSocketClient.java | 7 +++++++ .../contikios/cooja/serialsocket/SerialSocketServer.java | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index d29d8a2a6..133af33d0 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -128,6 +128,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { /* Observe serial port for outgoing data */ serialPort.addSerialDataObserver(serialDataObserver = new Observer() { + @Override public void update(Observable obs, Object obj) { try { if (out == null) { @@ -149,6 +150,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { private void startSocketReadThread(final DataInputStream in) { /* Forward data: virtual port -> mote */ Thread incomingDataThread = new Thread(new Runnable() { + @Override public void run() { int numRead = 0; byte[] data = new byte[1024]; @@ -193,10 +195,12 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { return label; } + @Override public boolean setConfigXML(Collection configXML, boolean visAvailable) { return true; } + @Override public Collection getConfigXML() { return null; } @@ -227,6 +231,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { } SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { SerialSocketClient.this.setTitle(SerialSocketClient.this.getTitle() + " *DISCONNECTED*"); statusLabel.setText("Disconnected from server"); @@ -234,10 +239,12 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { }); } + @Override public void closePlugin() { cleanup(); } + @Override public Mote getMote() { return mote; } diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index 2c99dd7a9..f79d8b360 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -129,6 +129,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } server = new ServerSocket(LISTEN_PORT); new Thread() { + @Override public void run() { while (server != null) { try { @@ -157,6 +158,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { /* Observe serial port for outgoing data */ serialPort.addSerialDataObserver(serialDataObserver = new Observer() { + @Override public void update(Observable obs, Object obj) { try { if (out == null) { @@ -178,6 +180,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private void startSocketReadThread(final DataInputStream in) { /* Forward data: virtual port -> mote */ Thread incomingDataThread = new Thread(new Runnable() { + @Override public void run() { int numRead = 0; byte[] data = new byte[1024]; @@ -218,10 +221,12 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { return label; } + @Override public boolean setConfigXML(Collection configXML, boolean visAvailable) { return true; } + @Override public Collection getConfigXML() { return null; } @@ -251,6 +256,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { if (Cooja.isVisualized()) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { statusLabel.setText("Listening on port: " + LISTEN_PORT); } @@ -259,6 +265,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } private boolean closed = false; + @Override public void closePlugin() { closed = true; cleanupClient(); @@ -269,12 +276,14 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } } + @Override public Mote getMote() { return mote; } private static final int UPDATE_INTERVAL = 150; private Timer updateTimer = new Timer(UPDATE_INTERVAL, new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { if (closed) { updateTimer.stop(); From d90aec2376e9a6a75f643def89b9b0ba59ef0459 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sat, 12 Apr 2014 03:07:21 +0200 Subject: [PATCH 03/14] [cooja/serialsocket] SerialSocketClient: New visual appearance with input for server hostname and address including connect button --- .../serialsocket/SerialSocketClient.java | 179 +++++++++++++----- 1 file changed, 136 insertions(+), 43 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index 133af33d0..647739556 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -31,21 +31,34 @@ package org.contikios.cooja.serialsocket; */ import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.ComponentOrientation; import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; +import java.text.NumberFormat; import java.util.Collection; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JComponent; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JTextField; import javax.swing.SwingUtilities; +import javax.swing.text.NumberFormatter; import org.apache.log4j.Logger; import org.jdom.Element; @@ -94,57 +107,137 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { /* GUI components */ if (Cooja.isVisualized()) { - Box northBox = Box.createHorizontalBox(); - northBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - statusLabel = configureLabel(northBox, "", ""); - Box mainBox = Box.createHorizontalBox(); - mainBox.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5)); - inLabel = configureLabel(mainBox, "socket -> mote:", "0 bytes"); - outLabel = configureLabel(mainBox, "mote -> socket", "0 bytes"); - - getContentPane().add(BorderLayout.NORTH, northBox); - getContentPane().add(BorderLayout.CENTER, mainBox); + setResizable(false); + setLayout(new BorderLayout()); + + GridBagConstraints c = new GridBagConstraints(); + JPanel serverSelectPanel = new JPanel(new GridBagLayout()); pack(); - } + c.gridx = 0; + c.gridy = 0; + serverSelectPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + + label = new JLabel("Host:"); + c.gridx++; + serverSelectPanel.add(label, c); + + final JTextField serverHostField = new JTextField("localhost"); + serverHostField.setColumns(10); + c.gridx++; + c.weightx = 1.0; + serverSelectPanel.add(serverHostField, c); + + label = new JLabel("Port:"); + c.gridx++; + c.weightx = 0.0; + serverSelectPanel.add(label, c); + + final JFormattedTextField serverPort = new JFormattedTextField( + new NumberFormatter(NumberFormat.getIntegerInstance())); + serverPort.setColumns(5); + serverPort.setText("1234"); + c.gridx++; + serverSelectPanel.add(serverPort, c); + + final JButton serverSelectButton = new JButton("Connect"); + c.gridx++; + serverSelectPanel.add(serverSelectButton, c); - /* Mote serial port */ - serialPort = (SerialPort) mote.getInterfaces().getLog(); - if (serialPort == null) { - throw new RuntimeException("No mote serial port"); - } + c.gridx = 0; + c.gridy++; + c.gridwidth = GridBagConstraints.REMAINDER; + c.fill = GridBagConstraints.HORIZONTAL; + serverSelectPanel.add(new JSeparator(JSeparator.HORIZONTAL), c); - try { - logger.info("Connecting: " + SERVER_HOST + ":" + SERVER_PORT); - socket = new Socket(SERVER_HOST, SERVER_PORT); - in = new DataInputStream(socket.getInputStream()); - out = new DataOutputStream(socket.getOutputStream()); - out.flush(); - startSocketReadThread(in); - } catch (Exception e) { - throw (RuntimeException) new RuntimeException( - "Connection error: " + e.getMessage()).initCause(e); - } + add(BorderLayout.NORTH, serverSelectPanel); + + serverSelectButton.addActionListener(new ActionListener() { - /* Observe serial port for outgoing data */ - serialPort.addSerialDataObserver(serialDataObserver = new Observer() { - @Override - public void update(Observable obs, Object obj) { - try { - if (out == null) { - return; + @Override + public void actionPerformed(ActionEvent e) { + try { + logger.info("Connecting: " + SERVER_HOST + ":" + SERVER_PORT); + socket = new Socket(SERVER_HOST, SERVER_PORT); + in = new DataInputStream(socket.getInputStream()); + out = new DataOutputStream(socket.getOutputStream()); + out.flush(); + startSocketReadThread(in); + } catch (Exception ex) { + throw (RuntimeException) new RuntimeException( + "Connection error: " + ex.getMessage()).initCause(ex); } - out.write(serialPort.getLastSerialData()); - out.flush(); - outBytes++; - if (Cooja.isVisualized()) { - outLabel.setText(outBytes + " bytes"); - } - } catch (IOException e) { - e.printStackTrace(); } + }); + + + JPanel connectionInfoPanel = new JPanel(new GridBagLayout()); + connectionInfoPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + c = new GridBagConstraints(); + + label = new JLabel("Status:"); + c.gridx = 0; + c.gridy = 0; + c.anchor = GridBagConstraints.EAST; + c.ipadx = 5; + connectionInfoPanel.add(label, c); + + final JLabel socketStatusLabel = new JLabel("disconnected"); + c.gridx++; + c.anchor = GridBagConstraints.WEST; + connectionInfoPanel.add(socketStatusLabel, c); + + label = new JLabel("socket -> mote:"); + c.gridx = 0; + c.gridy++; + c.anchor = GridBagConstraints.EAST; + connectionInfoPanel.add(label, c); + + final JLabel socketToMoteLabel = new JLabel("0 bytes"); + c.gridx++; + c.anchor = GridBagConstraints.WEST; + connectionInfoPanel.add(socketToMoteLabel, c); + + label = new JLabel("mote -> socket:"); + c.gridx = 0; + c.gridy++; + c.anchor = GridBagConstraints.EAST; + connectionInfoPanel.add(label, c); + + final JLabel moteToSocketLabel = new JLabel("0 bytes"); + c.gridx++; + c.anchor = GridBagConstraints.WEST; + connectionInfoPanel.add(moteToSocketLabel, c); + + add(BorderLayout.CENTER, connectionInfoPanel); + + /* Mote serial port */ + serialPort = (SerialPort) mote.getInterfaces().getLog(); + if (serialPort == null) { + throw new RuntimeException("No mote serial port"); } - }); + + + /* Observe serial port for outgoing data */ + serialPort.addSerialDataObserver(serialDataObserver = new Observer() { + @Override + public void update(Observable obs, Object obj) { + try { + if (out == null) { + return; + } + out.write(serialPort.getLastSerialData()); + out.flush(); + outBytes++; + if (Cooja.isVisualized()) { + outLabel.setText(outBytes + " bytes"); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } } private void startSocketReadThread(final DataInputStream in) { From 153457a151ea7bcdade8ffd7372993573d972d79 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sat, 12 Apr 2014 15:14:11 +0200 Subject: [PATCH 04/14] [cooja/serialsocket] SerialSocketClient: Status bar and improved action/event handling and visualization --- .../serialsocket/SerialSocketClient.java | 215 ++++++++++-------- 1 file changed, 123 insertions(+), 92 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index 647739556..83a6f1e0e 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -1,6 +1,7 @@ package org.contikios.cooja.serialsocket; /* + * Copyright (c) 2014, TU Braunschweig. * Copyright (c) 2010, Swedish Institute of Computer Science. * All rights reserved. * @@ -31,11 +32,10 @@ package org.contikios.cooja.serialsocket; */ import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.ComponentOrientation; -import java.awt.Dimension; +import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.DataInputStream; @@ -43,21 +43,22 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.text.NumberFormat; +import java.text.ParseException; import java.util.Collection; import java.util.Observable; import java.util.Observer; +import java.util.logging.Level; import javax.swing.BorderFactory; -import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; -import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JTextField; import javax.swing.SwingUtilities; +import javax.swing.border.EtchedBorder; import javax.swing.text.NumberFormatter; import org.apache.log4j.Logger; @@ -76,30 +77,36 @@ import org.contikios.cooja.interfaces.SerialPort; * Socket to simulated serial port forwarder. Client version. * * @author Fredrik Osterlind + * @author Enrico Jorns */ @ClassDescription("Serial Socket (CLIENT)") @PluginType(PluginType.MOTE_PLUGIN) public class SerialSocketClient extends VisPlugin implements MotePlugin { private static final long serialVersionUID = 1L; - private static Logger logger = Logger.getLogger(SerialSocketClient.class); - - private final static int LABEL_WIDTH = 100; - private final static int LABEL_HEIGHT = 15; - - public final static String SERVER_HOST = "localhost"; - public final static int SERVER_PORT = 1234; + private static final Logger logger = Logger.getLogger(SerialSocketClient.class); + private static final String SERVER_DEFAULT_HOST = "localhost"; + private static final int SERVER_DEFAULT_PORT = 1234; + + private static final Color ST_COLOR_UNCONNECTED = Color.DARK_GRAY; + private static final Color ST_COLOR_CONNECTED = new Color(0, 161, 83); + private static final Color ST_COLOR_FAILED = Color.RED; + private SerialPort serialPort; private Observer serialDataObserver; - private JLabel statusLabel, inLabel, outLabel; + private JLabel socketToMoteLabel; + private JLabel moteToSocketLabel; + private JLabel socketStatusLabel; + private JButton serverSelectButton; + private int inBytes = 0, outBytes = 0; private Socket socket; private DataInputStream in; private DataOutputStream out; - private Mote mote; + private final Mote mote; public SerialSocketClient(Mote mote, Simulation simulation, final Cooja gui) { super("Serial Socket (CLIENT) (" + mote + ")", gui, false); @@ -111,6 +118,8 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { setResizable(false); setLayout(new BorderLayout()); + // --- Server setup + GridBagConstraints c = new GridBagConstraints(); JPanel serverSelectPanel = new JPanel(new GridBagLayout()); pack(); @@ -122,7 +131,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { c.gridx++; serverSelectPanel.add(label, c); - final JTextField serverHostField = new JTextField("localhost"); + final JTextField serverHostField = new JTextField(SERVER_DEFAULT_HOST); serverHostField.setColumns(10); c.gridx++; c.weightx = 1.0; @@ -132,15 +141,16 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { c.gridx++; c.weightx = 0.0; serverSelectPanel.add(label, c); - - final JFormattedTextField serverPort = new JFormattedTextField( - new NumberFormatter(NumberFormat.getIntegerInstance())); - serverPort.setColumns(5); - serverPort.setText("1234"); + + NumberFormat nf = NumberFormat.getIntegerInstance(); + nf.setGroupingUsed(false); + final JFormattedTextField serverPortField = new JFormattedTextField(new NumberFormatter(nf)); + serverPortField.setColumns(5); + serverPortField.setText(String.valueOf(SERVER_DEFAULT_PORT)); c.gridx++; - serverSelectPanel.add(serverPort, c); - - final JButton serverSelectButton = new JButton("Connect"); + serverSelectPanel.add(serverPortField, c); + + serverSelectButton = new JButton("Connect"); c.gridx++; serverSelectPanel.add(serverSelectButton, c); @@ -149,67 +159,54 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { c.gridwidth = GridBagConstraints.REMAINDER; c.fill = GridBagConstraints.HORIZONTAL; serverSelectPanel.add(new JSeparator(JSeparator.HORIZONTAL), c); - + add(BorderLayout.NORTH, serverSelectPanel); - serverSelectButton.addActionListener(new ActionListener() { + // --- Incoming / outgoing info - @Override - public void actionPerformed(ActionEvent e) { - try { - logger.info("Connecting: " + SERVER_HOST + ":" + SERVER_PORT); - socket = new Socket(SERVER_HOST, SERVER_PORT); - in = new DataInputStream(socket.getInputStream()); - out = new DataOutputStream(socket.getOutputStream()); - out.flush(); - startSocketReadThread(in); - } catch (Exception ex) { - throw (RuntimeException) new RuntimeException( - "Connection error: " + ex.getMessage()).initCause(ex); - } - } - }); - - - JPanel connectionInfoPanel = new JPanel(new GridBagLayout()); - connectionInfoPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + JPanel connectionInfoPanel = new JPanel(new GridLayout(0, 2)); + connectionInfoPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); c = new GridBagConstraints(); - label = new JLabel("Status:"); + label = new JLabel("socket -> mote: "); + label.setHorizontalAlignment(JLabel.RIGHT); c.gridx = 0; c.gridy = 0; c.anchor = GridBagConstraints.EAST; - c.ipadx = 5; - connectionInfoPanel.add(label, c); + connectionInfoPanel.add(label); - final JLabel socketStatusLabel = new JLabel("disconnected"); + socketToMoteLabel = new JLabel("0 bytes"); c.gridx++; c.anchor = GridBagConstraints.WEST; - connectionInfoPanel.add(socketStatusLabel, c); + connectionInfoPanel.add(socketToMoteLabel); - label = new JLabel("socket -> mote:"); + label = new JLabel("mote -> socket: "); + label.setHorizontalAlignment(JLabel.RIGHT); c.gridx = 0; c.gridy++; c.anchor = GridBagConstraints.EAST; - connectionInfoPanel.add(label, c); + connectionInfoPanel.add(label); - final JLabel socketToMoteLabel = new JLabel("0 bytes"); + moteToSocketLabel = new JLabel("0 bytes"); c.gridx++; c.anchor = GridBagConstraints.WEST; - connectionInfoPanel.add(socketToMoteLabel, c); - - label = new JLabel("mote -> socket:"); - c.gridx = 0; - c.gridy++; - c.anchor = GridBagConstraints.EAST; - connectionInfoPanel.add(label, c); - - final JLabel moteToSocketLabel = new JLabel("0 bytes"); - c.gridx++; - c.anchor = GridBagConstraints.WEST; - connectionInfoPanel.add(moteToSocketLabel, c); + connectionInfoPanel.add(moteToSocketLabel); add(BorderLayout.CENTER, connectionInfoPanel); + + // --- Status bar + + JPanel statusBarPanel = new JPanel(new BorderLayout()); + statusBarPanel.setLayout(new BoxLayout(statusBarPanel, BoxLayout.LINE_AXIS)); + statusBarPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED)); + label = new JLabel("Status: "); + statusBarPanel.add(label); + + socketStatusLabel = new JLabel("disconnected"); + socketStatusLabel.setForeground(Color.DARK_GRAY); + statusBarPanel.add(socketStatusLabel); + + add(BorderLayout.SOUTH, statusBarPanel); /* Mote serial port */ serialPort = (SerialPort) mote.getInterfaces().getLog(); @@ -217,8 +214,50 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { throw new RuntimeException("No mote serial port"); } + serverSelectButton.addActionListener(new ActionListener() { - /* Observe serial port for outgoing data */ + @Override + public void actionPerformed(ActionEvent e) { + try { + serverPortField.commitEdit(); + } catch (ParseException ex) { + java.util.logging.Logger.getLogger(SerialSocketClient.class.getName()).log(Level.SEVERE, null, ex); + } + if (socket == null) { + // connect to serer + try { + logger.info("Connecting: " + serverHostField.getText() + ":" + serverPortField.getValue()); + socket = new Socket(serverHostField.getText(), ((Long) serverPortField.getValue()).intValue()); + in = new DataInputStream(socket.getInputStream()); + out = new DataOutputStream(socket.getOutputStream()); + out.flush(); + startSocketReadThread(in); + socketStatusLabel.setText("connected"); + socketStatusLabel.setForeground(ST_COLOR_CONNECTED); + serverSelectButton.setEnabled(false); + } catch (IOException ex) { + logger.error(ex.getMessage()); + socketStatusLabel.setText("failed"); + socketStatusLabel.setForeground(ST_COLOR_FAILED); + } + } else { + // disconnect from server + try { + logger.info("Closing connection to serer..."); + socket.close(); + socketStatusLabel.setText("disconnected"); + socketStatusLabel.setForeground(ST_COLOR_UNCONNECTED); + } catch (IOException ex) { + logger.error(ex); + socketStatusLabel.setText("failed"); + socketStatusLabel.setForeground(ST_COLOR_FAILED); + } + } + } + }); + + + /* Observe serial port for outgoing data and write to socket */ serialPort.addSerialDataObserver(serialDataObserver = new Observer() { @Override public void update(Observable obs, Object obj) { @@ -230,10 +269,12 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { out.flush(); outBytes++; if (Cooja.isVisualized()) { - outLabel.setText(outBytes + " bytes"); + moteToSocketLabel.setText(outBytes + " bytes"); } - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ex) { + logger.error(ex.getMessage()); + socketStatusLabel.setText("failed"); + socketStatusLabel.setForeground(ST_COLOR_FAILED); } } }); @@ -247,13 +288,13 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { public void run() { int numRead = 0; byte[] data = new byte[1024]; - logger.info("Forwarder: socket -> serial port"); + logger.info("Start forwarding: socket -> serial port"); while (true) { numRead = -1; try { numRead = in.read(data); } catch (IOException e) { - e.printStackTrace(); + logger.error(e.getMessage()); return; } @@ -263,11 +304,18 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { } inBytes += numRead; if (Cooja.isVisualized()) { - inLabel.setText(inBytes + " bytes"); + socketToMoteLabel.setText(inBytes + " bytes"); } } else { logger.warn("Incoming data thread shut down"); - cleanup(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + socketStatusLabel.setForeground(ST_COLOR_FAILED); + socketStatusLabel.setText("Disconnected from server"); + serverSelectButton.setEnabled(true); + } + }); break; } } @@ -276,18 +324,6 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { incomingDataThread.start(); } - private JLabel configureLabel(JComponent pane, String desc, String value) { - JPanel smallPane = new JPanel(new BorderLayout()); - JLabel label = new JLabel(desc); - label.setPreferredSize(new Dimension(LABEL_WIDTH,LABEL_HEIGHT)); - smallPane.add(BorderLayout.WEST, label); - label = new JLabel(value); - label.setPreferredSize(new Dimension(LABEL_WIDTH,LABEL_HEIGHT)); - smallPane.add(BorderLayout.CENTER, label); - pane.add(smallPane); - return label; - } - @Override public boolean setConfigXML(Collection configXML, boolean visAvailable) { return true; @@ -307,6 +343,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { socket = null; } } catch (IOException e1) { + logger.warn(e1.getMessage()); } try { if (in != null) { @@ -314,6 +351,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { in = null; } } catch (IOException e) { + logger.warn(e.getMessage()); } try { if (out != null) { @@ -321,15 +359,8 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { out = null; } } catch (IOException e) { + logger.warn(e.getMessage()); } - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - SerialSocketClient.this.setTitle(SerialSocketClient.this.getTitle() + " *DISCONNECTED*"); - statusLabel.setText("Disconnected from server"); - } - }); } @Override From 0d9698b908dd14aa6f3d87bd7cab0f530487a39b Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sun, 13 Apr 2014 18:28:36 +0200 Subject: [PATCH 05/14] [cooja/serialsocket] SerialSocketServer: New visual appearance with status bar, port setting, and start button --- .../serialsocket/SerialSocketServer.java | 133 ++++++++++++++---- 1 file changed, 108 insertions(+), 25 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index f79d8b360..2ebfdf241 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -31,7 +31,11 @@ package org.contikios.cooja.serialsocket; */ import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.DataInputStream; @@ -39,17 +43,22 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; +import java.text.NumberFormat; import java.util.Collection; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.JComponent; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JSeparator; import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.border.EtchedBorder; +import javax.swing.text.NumberFormatter; import org.apache.log4j.Logger; import org.jdom.Element; @@ -74,8 +83,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(SerialSocketServer.class); - private final static int LABEL_WIDTH = 100; - private final static int LABEL_HEIGHT = 15; + private final static int STATUSBAR_WIDTH = 350; public final int LISTEN_PORT; @@ -83,6 +91,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private Observer serialDataObserver; private JLabel statusLabel, inLabel, outLabel; + private JButton serverStartButton; private int inBytes = 0, outBytes = 0; private ServerSocket server; @@ -102,17 +111,103 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { /* GUI components */ if (Cooja.isVisualized()) { - Box northBox = Box.createHorizontalBox(); - northBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - statusLabel = configureLabel(northBox, "", ""); - Box mainBox = Box.createHorizontalBox(); - mainBox.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5)); - inLabel = configureLabel(mainBox, "socket -> mote:", "0 bytes"); - outLabel = configureLabel(mainBox, "mote -> socket", "0 bytes"); + setResizable(false); + setLayout(new BorderLayout()); - getContentPane().add(BorderLayout.NORTH, northBox); - getContentPane().add(BorderLayout.CENTER, mainBox); + // --- Server Port setup + + GridBagConstraints c = new GridBagConstraints(); + JPanel socketPanel = new JPanel(new GridBagLayout()); + socketPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + + JLabel label = new JLabel("Listen port: "); + c.gridx = 0; + c.gridy = 0; + socketPanel.add(label, c); + + NumberFormat nf = NumberFormat.getIntegerInstance(); + nf.setGroupingUsed(false); + final JFormattedTextField serverPortField = new JFormattedTextField(new NumberFormatter(nf)); + serverPortField.setColumns(5); + serverPortField.setText(String.valueOf(LISTEN_PORT)); + c.gridx++; + socketPanel.add(serverPortField, c); + + serverStartButton = new JButton("Start"); + c.gridx++; + c.weightx = 0.1; + c.anchor = GridBagConstraints.EAST; + socketPanel.add(serverStartButton, c); + + c.gridx = 0; + c.gridy++; + c.gridwidth = GridBagConstraints.REMAINDER; + c.fill = GridBagConstraints.HORIZONTAL; + socketPanel.add(new JSeparator(JSeparator.HORIZONTAL), c); + + add(BorderLayout.NORTH, socketPanel); + + // --- Incoming / outgoing info + + JPanel connectionInfoPanel = new JPanel(new GridLayout(0, 2)); + connectionInfoPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + c = new GridBagConstraints(); + + label = new JLabel("socket -> mote: "); + label.setHorizontalAlignment(JLabel.RIGHT); + c.gridx = 0; + c.gridy = 0; + c.anchor = GridBagConstraints.EAST; + connectionInfoPanel.add(label); + + inLabel = new JLabel("0 bytes"); + c.gridx++; + c.anchor = GridBagConstraints.WEST; + connectionInfoPanel.add(inLabel); + + label = new JLabel("mote -> socket: "); + label.setHorizontalAlignment(JLabel.RIGHT); + c.gridx = 0; + c.gridy++; + c.anchor = GridBagConstraints.EAST; + connectionInfoPanel.add(label); + + outLabel = new JLabel("0 bytes"); + c.gridx++; + c.anchor = GridBagConstraints.WEST; + connectionInfoPanel.add(outLabel); + + add(BorderLayout.CENTER, connectionInfoPanel); + + // --- Status bar + + JPanel statusBarPanel = new JPanel(new BorderLayout()) { + @Override + public Dimension getPreferredSize() { + Dimension d = super.getPreferredSize(); + return new Dimension(STATUSBAR_WIDTH, d.height); + } + }; + statusBarPanel.setLayout(new BoxLayout(statusBarPanel, BoxLayout.LINE_AXIS)); + statusBarPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED)); + label = new JLabel("Status: "); + statusBarPanel.add(label); + + statusLabel = new JLabel("Not started"); + statusLabel.setForeground(Color.DARK_GRAY); + statusBarPanel.add(statusLabel); + + add(BorderLayout.SOUTH, statusBarPanel); + + serverStartButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + // XXX + } + }); + pack(); } @@ -209,18 +304,6 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { incomingDataThread.start(); } - private JLabel configureLabel(JComponent pane, String desc, String value) { - JPanel smallPane = new JPanel(new BorderLayout()); - JLabel label = new JLabel(desc); - label.setPreferredSize(new Dimension(LABEL_WIDTH,LABEL_HEIGHT)); - smallPane.add(BorderLayout.WEST, label); - label = new JLabel(value); - label.setPreferredSize(new Dimension(LABEL_WIDTH,LABEL_HEIGHT)); - smallPane.add(BorderLayout.CENTER, label); - pane.add(smallPane); - return label; - } - @Override public boolean setConfigXML(Collection configXML, boolean visAvailable) { return true; From 585db7df9610d7f9e537bbf80c18a8c83cf9ca72 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sun, 13 Apr 2014 19:11:40 +0200 Subject: [PATCH 06/14] [cooja/serialsocket] SerialSocketServer: Applied some renames --- .../serialsocket/SerialSocketServer.java | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index 2ebfdf241..6ced26962 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -81,21 +81,22 @@ import org.contikios.cooja.interfaces.SerialPort; @PluginType(PluginType.MOTE_PLUGIN) public class SerialSocketServer extends VisPlugin implements MotePlugin { private static final long serialVersionUID = 1L; - private static Logger logger = Logger.getLogger(SerialSocketServer.class); + private static final Logger logger = Logger.getLogger(SerialSocketServer.class); private final static int STATUSBAR_WIDTH = 350; - public final int LISTEN_PORT; + private final int SERVER_DEFAULT_PORT; - private SerialPort serialPort; + private final SerialPort serialPort; private Observer serialDataObserver; - private JLabel statusLabel, inLabel, outLabel; + private JLabel socketStatusLabel, socketToMoteLabel, moteToSocketLabel; private JButton serverStartButton; + private int inBytes = 0, outBytes = 0; private ServerSocket server; - private Socket client; + private Socket clientSocket; private DataInputStream in; private DataOutputStream out; @@ -107,7 +108,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { updateTimer.start(); - LISTEN_PORT = 60000 + mote.getID(); + SERVER_DEFAULT_PORT = 60000 + mote.getID(); /* GUI components */ if (Cooja.isVisualized()) { @@ -130,7 +131,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { nf.setGroupingUsed(false); final JFormattedTextField serverPortField = new JFormattedTextField(new NumberFormatter(nf)); serverPortField.setColumns(5); - serverPortField.setText(String.valueOf(LISTEN_PORT)); + serverPortField.setText(String.valueOf(SERVER_DEFAULT_PORT)); c.gridx++; socketPanel.add(serverPortField, c); @@ -161,10 +162,10 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { c.anchor = GridBagConstraints.EAST; connectionInfoPanel.add(label); - inLabel = new JLabel("0 bytes"); + socketToMoteLabel = new JLabel("0 bytes"); c.gridx++; c.anchor = GridBagConstraints.WEST; - connectionInfoPanel.add(inLabel); + connectionInfoPanel.add(socketToMoteLabel); label = new JLabel("mote -> socket: "); label.setHorizontalAlignment(JLabel.RIGHT); @@ -173,10 +174,10 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { c.anchor = GridBagConstraints.EAST; connectionInfoPanel.add(label); - outLabel = new JLabel("0 bytes"); + moteToSocketLabel = new JLabel("0 bytes"); c.gridx++; c.anchor = GridBagConstraints.WEST; - connectionInfoPanel.add(outLabel); + connectionInfoPanel.add(moteToSocketLabel); add(BorderLayout.CENTER, connectionInfoPanel); @@ -194,9 +195,9 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { label = new JLabel("Status: "); statusBarPanel.add(label); - statusLabel = new JLabel("Not started"); - statusLabel.setForeground(Color.DARK_GRAY); - statusBarPanel.add(statusLabel); + socketStatusLabel = new JLabel("Not started"); + socketStatusLabel.setForeground(Color.DARK_GRAY); + statusBarPanel.add(socketStatusLabel); add(BorderLayout.SOUTH, statusBarPanel); @@ -218,24 +219,24 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } try { - logger.info("Listening on port: " + LISTEN_PORT); + logger.info("Listening on port: " + SERVER_DEFAULT_PORT); if (Cooja.isVisualized()) { - statusLabel.setText("Listening on port: " + LISTEN_PORT); + socketStatusLabel.setText("Listening on port: " + SERVER_DEFAULT_PORT); } - server = new ServerSocket(LISTEN_PORT); + server = new ServerSocket(SERVER_DEFAULT_PORT); new Thread() { @Override public void run() { while (server != null) { try { - client = server.accept(); - in = new DataInputStream(client.getInputStream()); - out = new DataOutputStream(client.getOutputStream()); + clientSocket = server.accept(); + in = new DataInputStream(clientSocket.getInputStream()); + out = new DataOutputStream(clientSocket.getOutputStream()); out.flush(); startSocketReadThread(in); if (Cooja.isVisualized()) { - statusLabel.setText("Client connected: " + client.getInetAddress()); + socketStatusLabel.setText("Client connected: " + clientSocket.getInetAddress()); } } catch (IOException e) { logger.fatal("Listening thread shut down: " + e.getMessage()); @@ -316,9 +317,9 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private void cleanupClient() { try { - if (client != null) { - client.close(); - client = null; + if (clientSocket != null) { + clientSocket.close(); + clientSocket = null; } } catch (IOException e1) { } @@ -341,7 +342,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - statusLabel.setText("Listening on port: " + LISTEN_PORT); + socketStatusLabel.setText("Listening on port: " + SERVER_DEFAULT_PORT); } }); } @@ -373,8 +374,8 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { return; } - inLabel.setText(inBytes + " bytes"); - outLabel.setText(outBytes + " bytes"); + socketToMoteLabel.setText(inBytes + " bytes"); + moteToSocketLabel.setText(outBytes + " bytes"); } }); } From a18a7fa717af97645161bfbfefb9ebd49a735a8d Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sun, 13 Apr 2014 19:13:24 +0200 Subject: [PATCH 07/14] [cooja/serialsocket] SerialSocketServer: Fixed and improved server implementation. Added visual feedback for new user interface. --- .../serialsocket/SerialSocketServer.java | 278 +++++++++++++----- 1 file changed, 207 insertions(+), 71 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index 6ced26962..2491cf248 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -44,9 +44,13 @@ import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.text.NumberFormat; +import java.text.ParseException; import java.util.Collection; +import java.util.LinkedList; +import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.logging.Level; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -85,12 +89,18 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private final static int STATUSBAR_WIDTH = 350; + private static final Color COLOR_NEUTRAL = Color.DARK_GRAY; + private static final Color COLOR_POSITIVE = new Color(0, 161, 83); + private static final Color COLOR_NEGATIVE = Color.RED; + private final int SERVER_DEFAULT_PORT; private final SerialPort serialPort; private Observer serialDataObserver; - private JLabel socketStatusLabel, socketToMoteLabel, moteToSocketLabel; + private JLabel socketToMoteLabel; + private JLabel moteToSocketLabel; + private JLabel socketStatusLabel; private JButton serverStartButton; private int inBytes = 0, outBytes = 0; @@ -205,7 +215,12 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void actionPerformed(ActionEvent e) { - // XXX + try { + serverPortField.commitEdit(); + } catch (ParseException ex) { + java.util.logging.Logger.getLogger(SerialSocketClient.class.getName()).log(Level.SEVERE, null, ex); + } + startServer(((Long) serverPortField.getValue()).intValue()); } }); @@ -218,59 +233,188 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { throw new RuntimeException("No mote serial port"); } - try { - logger.info("Listening on port: " + SERVER_DEFAULT_PORT); - if (Cooja.isVisualized()) { - socketStatusLabel.setText("Listening on port: " + SERVER_DEFAULT_PORT); - } - server = new ServerSocket(SERVER_DEFAULT_PORT); - new Thread() { - @Override - public void run() { - while (server != null) { - try { - clientSocket = server.accept(); - in = new DataInputStream(clientSocket.getInputStream()); - out = new DataOutputStream(clientSocket.getOutputStream()); - out.flush(); + if (Cooja.isVisualized()) { + // gui updates for server status updates + addServerListener(new ServerListener() { - startSocketReadThread(in); - if (Cooja.isVisualized()) { - socketStatusLabel.setText("Client connected: " + clientSocket.getInetAddress()); + @Override + public void onServerStarted(final int port) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + socketStatusLabel.setForeground(COLOR_NEUTRAL); + socketStatusLabel.setText("Listening on port " + String.valueOf(port)); + serverStartButton.setEnabled(false); + } + }); + } + + @Override + public void onClientConnected(final Socket client) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + socketStatusLabel.setForeground(COLOR_POSITIVE); + socketStatusLabel.setText(String.format("Client " + client.getInetAddress() + " connected.")); + } + }); + } + + @Override + public void onClientDisconnected(final Socket client) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + socketStatusLabel.setForeground(COLOR_NEUTRAL); + socketStatusLabel.setText("Listening on port " + String.valueOf(server.getLocalPort())); + } + }); + } + + @Override + public void onServerStopped() { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + serverStartButton.setEnabled(true); + socketStatusLabel.setForeground(COLOR_NEUTRAL); + socketStatusLabel.setText("Idle"); + } + }); + } + + @Override + public void onServerError(final String msg) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + socketStatusLabel.setForeground(COLOR_NEGATIVE); + socketStatusLabel.setText(msg); + } + }); + } + + }); + + } + + } + + private List listeners = new LinkedList<>(); + + public interface ServerListener { + void onServerStarted(int port); + void onClientConnected(Socket client); + void onClientDisconnected(Socket client); + void onServerStopped(); + void onServerError(String msg); + } + + private void addServerListener(ServerListener listener) { + listeners.add(listener); + } + + public void notifyServerStarted(int port) { + for (ServerListener listener : listeners) { + listener.onServerStarted(port); + } + } + + public void notifyClientConnected(Socket client) { + for (ServerListener listener : listeners) { + listener.onClientConnected(client); + } + } + + public void notifyClientDisconnected(Socket client) { + for (ServerListener listener : listeners) { + listener.onClientDisconnected(client); + } + } + + public void notifyServerStopped() { + for (ServerListener listener : listeners) { + listener.onServerStopped(); + } + } + + public void notifyServerError(String msg) { + for (ServerListener listener : listeners) { + listener.onServerError(msg); + } + } + + /** + * Start server .. + * @param port + */ + public void startServer(int port) { + try { + server = new ServerSocket(port); + logger.info("Listening on port: " + port); + notifyServerStarted(port); + } catch (IOException ex) { + logger.error(ex.getMessage()); + notifyServerError(ex.getMessage()); + return; + } + + new Thread() { + @Override + public void run() { + while (!server.isClosed()) { + try { + // wait for next client + clientSocket = server.accept(); + in = new DataInputStream(clientSocket.getInputStream()); + out = new DataOutputStream(clientSocket.getOutputStream()); + out.flush(); + startSocketReadThread(in); + + /* Observe serial port for outgoing data */ + serialPort.addSerialDataObserver(serialDataObserver = new Observer() { + @Override + public void update(Observable obs, Object obj) { + try { + if (out == null) { + /*logger.debug("out is null");*/ + return; + } + + out.write(serialPort.getLastSerialData()); + out.flush(); + + outBytes++; + } catch (IOException ex) { + logger.error(ex); + cleanupClient(); + } } - } catch (IOException e) { - logger.fatal("Listening thread shut down: " + e.getMessage()); - server = null; - cleanupClient(); - break; + }); + + inBytes = outBytes = 0; + + logger.info("Client connected: " + clientSocket.getInetAddress()); + notifyClientConnected(clientSocket); + + } catch (IOException e) { + logger.fatal("Listening thread shut down: " + e.getMessage()); + try { + server.close(); + } catch (IOException ex) { + logger.error(ex); } } } - }.start(); - } catch (Exception e) { - throw (RuntimeException) new RuntimeException( - "Connection error: " + e.getMessage()).initCause(e); - } - - /* Observe serial port for outgoing data */ - serialPort.addSerialDataObserver(serialDataObserver = new Observer() { - @Override - public void update(Observable obs, Object obj) { - try { - if (out == null) { - /*logger.debug("out is null");*/ - return; - } - - out.write(serialPort.getLastSerialData()); - out.flush(); - - outBytes++; - } catch (IOException e) { - cleanupClient(); - } + cleanupClient(); + notifyServerStopped(); } - }); + }.start(); } private void startSocketReadThread(final DataInputStream in) { @@ -281,25 +425,21 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { int numRead = 0; byte[] data = new byte[1024]; logger.info("Forwarder: socket -> serial port"); - while (true) { - numRead = -1; + while (numRead >= 0) { + for (int i = 0; i < numRead; i++) { + serialPort.writeByte(data[i]); + } + inBytes += numRead; + try { numRead = in.read(data); } catch (IOException e) { + logger.error(e.getMessage()); numRead = -1; } - - if (numRead >= 0) { - for (int i=0; i < numRead; i++) { - serialPort.writeByte(data[i]); - } - - inBytes += numRead; - } else { - cleanupClient(); - break; - } } + logger.info("End of Stream"); + cleanupClient(); } }); incomingDataThread.start(); @@ -322,6 +462,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { clientSocket = null; } } catch (IOException e1) { + logger.error(e1.getMessage()); } try { if (in != null) { @@ -329,6 +470,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { in = null; } } catch (IOException e) { + logger.error(e.getMessage()); } try { if (out != null) { @@ -336,16 +478,11 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { out = null; } } catch (IOException e) { + logger.error(e.getMessage()); } + serialPort.deleteSerialDataObserver(serialDataObserver); - if (Cooja.isVisualized()) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - socketStatusLabel.setText("Listening on port: " + SERVER_DEFAULT_PORT); - } - }); - } + notifyClientDisconnected(null); } private boolean closed = false; @@ -353,7 +490,6 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { public void closePlugin() { closed = true; cleanupClient(); - serialPort.deleteSerialDataObserver(serialDataObserver); try { server.close(); } catch (IOException e) { From b31818521da71586e8dbef8278e349a2864614b3 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Sun, 13 Apr 2014 21:36:38 +0200 Subject: [PATCH 08/14] [cooja/serialsocket] SerialSocketServer: Allow only a single client, reject further --- .../serialsocket/SerialSocketServer.java | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index 2491cf248..9f3db29f4 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -1,6 +1,7 @@ package org.contikios.cooja.serialsocket; /* + * Copyright (c) 2014, TU Braunschweig. * Copyright (c) 2010, Swedish Institute of Computer Science. * All rights reserved. * @@ -80,6 +81,7 @@ import org.contikios.cooja.interfaces.SerialPort; * Socket to simulated serial port forwarder. Server version. * * @author Fredrik Osterlind + * @author Enrico Jorns */ @ClassDescription("Serial Socket (SERVER)") @PluginType(PluginType.MOTE_PLUGIN) @@ -101,11 +103,12 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private JLabel socketToMoteLabel; private JLabel moteToSocketLabel; private JLabel socketStatusLabel; + private JFormattedTextField serverPortField; private JButton serverStartButton; private int inBytes = 0, outBytes = 0; - private ServerSocket server; + private ServerSocket serverSocket; private Socket clientSocket; private DataInputStream in; private DataOutputStream out; @@ -135,14 +138,17 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { JLabel label = new JLabel("Listen port: "); c.gridx = 0; c.gridy = 0; + c.weightx = 0.1; + c.anchor = GridBagConstraints.EAST; socketPanel.add(label, c); NumberFormat nf = NumberFormat.getIntegerInstance(); nf.setGroupingUsed(false); - final JFormattedTextField serverPortField = new JFormattedTextField(new NumberFormatter(nf)); + serverPortField = new JFormattedTextField(new NumberFormatter(nf)); serverPortField.setColumns(5); serverPortField.setText(String.valueOf(SERVER_DEFAULT_PORT)); c.gridx++; + c.weightx = 0.0; socketPanel.add(serverPortField, c); serverStartButton = new JButton("Start"); @@ -205,7 +211,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { label = new JLabel("Status: "); statusBarPanel.add(label); - socketStatusLabel = new JLabel("Not started"); + socketStatusLabel = new JLabel("Idle"); socketStatusLabel.setForeground(Color.DARK_GRAY); statusBarPanel.add(socketStatusLabel); @@ -246,6 +252,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { socketStatusLabel.setForeground(COLOR_NEUTRAL); socketStatusLabel.setText("Listening on port " + String.valueOf(port)); serverStartButton.setEnabled(false); + serverPortField.setEnabled(false); } }); } @@ -268,8 +275,11 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void run() { - socketStatusLabel.setForeground(COLOR_NEUTRAL); - socketStatusLabel.setText("Listening on port " + String.valueOf(server.getLocalPort())); + // XXX check why needed + if (serverSocket != null) { + socketStatusLabel.setForeground(COLOR_NEUTRAL); + socketStatusLabel.setText("Listening on port " + String.valueOf(serverSocket.getLocalPort())); + } } }); } @@ -281,6 +291,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void run() { serverStartButton.setEnabled(true); + serverPortField.setEnabled(true); socketStatusLabel.setForeground(COLOR_NEUTRAL); socketStatusLabel.setText("Idle"); } @@ -300,7 +311,6 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } }); - } } @@ -355,7 +365,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { */ public void startServer(int port) { try { - server = new ServerSocket(port); + serverSocket = new ServerSocket(port); logger.info("Listening on port: " + port); notifyServerStarted(port); } catch (IOException ex) { @@ -367,10 +377,20 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { new Thread() { @Override public void run() { - while (!server.isClosed()) { + while (!serverSocket.isClosed()) { try { // wait for next client - clientSocket = server.accept(); + Socket candidateSocket = serverSocket.accept(); + + // reject connection if already one client connected + if (clientSocket != null && !clientSocket.isClosed()) { + logger.info("Refused connection of client " + candidateSocket.getInetAddress()); + candidateSocket.close(); + continue; + } + + clientSocket = candidateSocket; + in = new DataInputStream(clientSocket.getInputStream()); out = new DataOutputStream(clientSocket.getOutputStream()); out.flush(); @@ -405,7 +425,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } catch (IOException e) { logger.fatal("Listening thread shut down: " + e.getMessage()); try { - server.close(); + serverSocket.close(); } catch (IOException ex) { logger.error(ex); } @@ -486,13 +506,17 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } private boolean closed = false; + @Override public void closePlugin() { - closed = true; + closed = true; cleanupClient(); try { - server.close(); - } catch (IOException e) { + if (serverSocket != null) { + serverSocket.close(); + } + } catch (IOException ex) { + logger.error(ex); } } From f419274050ecb537297398d2838c5b4bf24a643a Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Mon, 14 Apr 2014 13:37:25 +0200 Subject: [PATCH 09/14] [cooja/serialsocket] SerialSocketClient: ClientListener interface decouples client implementation and visualization --- .../serialsocket/SerialSocketClient.java | 192 +++++++++++++----- 1 file changed, 139 insertions(+), 53 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index 83a6f1e0e..b032d02dd 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -33,6 +33,7 @@ package org.contikios.cooja.serialsocket; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; @@ -45,9 +46,10 @@ import java.net.Socket; import java.text.NumberFormat; import java.text.ParseException; import java.util.Collection; +import java.util.LinkedList; +import java.util.List; import java.util.Observable; import java.util.Observer; -import java.util.logging.Level; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -88,6 +90,8 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { private static final String SERVER_DEFAULT_HOST = "localhost"; private static final int SERVER_DEFAULT_PORT = 1234; + private final static int STATUSBAR_WIDTH = 350; + private static final Color ST_COLOR_UNCONNECTED = Color.DARK_GRAY; private static final Color ST_COLOR_CONNECTED = new Color(0, 161, 83); private static final Color ST_COLOR_FAILED = Color.RED; @@ -98,6 +102,8 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { private JLabel socketToMoteLabel; private JLabel moteToSocketLabel; private JLabel socketStatusLabel; + private JTextField serverHostField; + private JFormattedTextField serverPortField; private JButton serverSelectButton; private int inBytes = 0, outBytes = 0; @@ -123,15 +129,15 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { GridBagConstraints c = new GridBagConstraints(); JPanel serverSelectPanel = new JPanel(new GridBagLayout()); pack(); + serverSelectPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + + JLabel label = new JLabel("Host:"); c.gridx = 0; c.gridy = 0; - serverSelectPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); - - label = new JLabel("Host:"); c.gridx++; serverSelectPanel.add(label, c); - final JTextField serverHostField = new JTextField(SERVER_DEFAULT_HOST); + serverHostField = new JTextField(SERVER_DEFAULT_HOST); serverHostField.setColumns(10); c.gridx++; c.weightx = 1.0; @@ -144,7 +150,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { NumberFormat nf = NumberFormat.getIntegerInstance(); nf.setGroupingUsed(false); - final JFormattedTextField serverPortField = new JFormattedTextField(new NumberFormatter(nf)); + serverPortField = new JFormattedTextField(new NumberFormatter(nf)); serverPortField.setColumns(5); serverPortField.setText(String.valueOf(SERVER_DEFAULT_PORT)); c.gridx++; @@ -152,6 +158,8 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { serverSelectButton = new JButton("Connect"); c.gridx++; + c.weightx = 0.1; + c.anchor = GridBagConstraints.EAST; serverSelectPanel.add(serverSelectButton, c); c.gridx = 0; @@ -196,13 +204,19 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { // --- Status bar - JPanel statusBarPanel = new JPanel(new BorderLayout()); + JPanel statusBarPanel = new JPanel(new BorderLayout()) { + @Override + public Dimension getPreferredSize() { + Dimension d = super.getPreferredSize(); + return new Dimension(STATUSBAR_WIDTH, d.height); + } + }; statusBarPanel.setLayout(new BoxLayout(statusBarPanel, BoxLayout.LINE_AXIS)); statusBarPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED)); label = new JLabel("Status: "); statusBarPanel.add(label); - socketStatusLabel = new JLabel("disconnected"); + socketStatusLabel = new JLabel("Disconnected"); socketStatusLabel.setForeground(Color.DARK_GRAY); statusBarPanel.add(socketStatusLabel); @@ -220,38 +234,9 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { public void actionPerformed(ActionEvent e) { try { serverPortField.commitEdit(); + startClient(serverHostField.getText(), ((Long) serverPortField.getValue()).intValue()); } catch (ParseException ex) { - java.util.logging.Logger.getLogger(SerialSocketClient.class.getName()).log(Level.SEVERE, null, ex); - } - if (socket == null) { - // connect to serer - try { - logger.info("Connecting: " + serverHostField.getText() + ":" + serverPortField.getValue()); - socket = new Socket(serverHostField.getText(), ((Long) serverPortField.getValue()).intValue()); - in = new DataInputStream(socket.getInputStream()); - out = new DataOutputStream(socket.getOutputStream()); - out.flush(); - startSocketReadThread(in); - socketStatusLabel.setText("connected"); - socketStatusLabel.setForeground(ST_COLOR_CONNECTED); - serverSelectButton.setEnabled(false); - } catch (IOException ex) { - logger.error(ex.getMessage()); - socketStatusLabel.setText("failed"); - socketStatusLabel.setForeground(ST_COLOR_FAILED); - } - } else { - // disconnect from server - try { - logger.info("Closing connection to serer..."); - socket.close(); - socketStatusLabel.setText("disconnected"); - socketStatusLabel.setForeground(ST_COLOR_UNCONNECTED); - } catch (IOException ex) { - logger.error(ex); - socketStatusLabel.setText("failed"); - socketStatusLabel.setForeground(ST_COLOR_FAILED); - } + logger.error(ex); } } }); @@ -279,6 +264,114 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { } }); } + + if (Cooja.isVisualized()) { + addClientListener(new ClientListener() { + + @Override + public void onError(final String msg) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + socketStatusLabel.setForeground(ST_COLOR_FAILED); + socketStatusLabel.setText(msg); + } + }); + } + + @Override + public void onConnected() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + socketStatusLabel.setText("Connected"); + socketStatusLabel.setForeground(ST_COLOR_CONNECTED); + serverHostField.setEnabled(false); + serverPortField.setEnabled(false); + serverSelectButton.setEnabled(false); + } + }); + } + + @Override + public void onDisconnected() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + socketStatusLabel.setForeground(ST_COLOR_UNCONNECTED); + socketStatusLabel.setText("Disconnected"); + serverHostField.setEnabled(true); + serverPortField.setEnabled(true); + serverSelectButton.setEnabled(true); + } + }); + } + }); + } + } + + private List listeners = new LinkedList<>(); + + public interface ClientListener { + void onError(String msg); + void onConnected(); + void onDisconnected(); + } + + public void addClientListener(ClientListener listener) { + if (!listeners.contains(listener)) { + listeners.add(listener); + } + } + + private void notifyClientError(String msg) { + for (ClientListener l : listeners) { + l.onError(msg); + } + } + + private void notifyClientConnected() { + for (ClientListener l : listeners) { + l.onConnected(); + } + } + + private void notifyClientDisconnected() { + for (ClientListener l : listeners) { + l.onDisconnected(); + } + } + + + public void startClient(String host, int port) { + if (socket == null) { + // connect to serer + try { + logger.info("Connecting: " + host + ":" + port); + socket = new Socket(host, port); + in = new DataInputStream(socket.getInputStream()); + out = new DataOutputStream(socket.getOutputStream()); + out.flush(); + startSocketReadThread(in); + notifyClientConnected(); + } catch (IOException ex) { + logger.error(ex.getMessage()); + notifyClientError(ex.getMessage()); + } + } else { + // disconnect from server + try { + logger.info("Closing connection to serer..."); + socket.close(); + notifyClientDisconnected(); + } catch (IOException ex) { + logger.error(ex); + notifyClientError(ex.getMessage()); + } finally { + socket = null; + } + } + } private void startSocketReadThread(final DataInputStream in) { @@ -289,13 +382,13 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { int numRead = 0; byte[] data = new byte[1024]; logger.info("Start forwarding: socket -> serial port"); - while (true) { - numRead = -1; + while (numRead >= 0) { try { numRead = in.read(data); } catch (IOException e) { logger.error(e.getMessage()); - return; + numRead = -1; + continue; } if (numRead >= 0) { @@ -306,19 +399,12 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { if (Cooja.isVisualized()) { socketToMoteLabel.setText(inBytes + " bytes"); } - } else { - logger.warn("Incoming data thread shut down"); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - socketStatusLabel.setForeground(ST_COLOR_FAILED); - socketStatusLabel.setText("Disconnected from server"); - serverSelectButton.setEnabled(true); - } - }); - break; } } + + logger.warn("Incoming data thread shut down"); + cleanup(); + notifyClientDisconnected(); } }); incomingDataThread.start(); From 6c0e7ae15ec2e9c9d1a0fe22d68dfa1b9a6de4f1 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Mon, 14 Apr 2014 14:24:55 +0200 Subject: [PATCH 10/14] [cooja/serialsocket] Allow to stop server / disconnect client --- .../serialsocket/SerialSocketClient.java | 41 ++++++++++---- .../serialsocket/SerialSocketServer.java | 56 ++++++++++++++----- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index b032d02dd..e13400b88 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -156,7 +156,19 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { c.gridx++; serverSelectPanel.add(serverPortField, c); - serverSelectButton = new JButton("Connect"); + serverSelectButton = new JButton("Connect") { // Button for label toggeling + private final String altString = "Disconnect"; + + @Override + public Dimension getPreferredSize() { + String origText = getText(); + Dimension origDim = super.getPreferredSize(); + setText(altString); + Dimension altDim = super.getPreferredSize(); + setText(origText); + return new Dimension(Math.max(origDim.width, altDim.width), origDim.height); + } + }; c.gridx++; c.weightx = 0.1; c.anchor = GridBagConstraints.EAST; @@ -232,11 +244,16 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { @Override public void actionPerformed(ActionEvent e) { - try { - serverPortField.commitEdit(); - startClient(serverHostField.getText(), ((Long) serverPortField.getValue()).intValue()); - } catch (ParseException ex) { - logger.error(ex); + if (e.getActionCommand().equals("Connect")) { + try { + serverPortField.commitEdit(); + startClient(serverHostField.getText(), ((Long) serverPortField.getValue()).intValue()); + } catch (ParseException ex) { + logger.error(ex); + } + } else { + // close socket + cleanup(); } } }); @@ -288,7 +305,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { socketStatusLabel.setForeground(ST_COLOR_CONNECTED); serverHostField.setEnabled(false); serverPortField.setEnabled(false); - serverSelectButton.setEnabled(false); + serverSelectButton.setText("Disconnect"); } }); } @@ -302,7 +319,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { socketStatusLabel.setText("Disconnected"); serverHostField.setEnabled(true); serverPortField.setEnabled(true); - serverSelectButton.setEnabled(true); + serverSelectButton.setText("Connect"); } }); } @@ -386,13 +403,13 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { try { numRead = in.read(data); } catch (IOException e) { - logger.error(e.getMessage()); + logger.info(e.getMessage()); numRead = -1; continue; } if (numRead >= 0) { - for (int i=0; i < numRead; i++) { + for (int i = 0; i < numRead; i++) { serialPort.writeByte(data[i]); } inBytes += numRead; @@ -401,8 +418,8 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { } } } - - logger.warn("Incoming data thread shut down"); + + logger.info("Incoming data thread shut down"); cleanup(); notifyClientDisconnected(); } diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index 9f3db29f4..876c912b2 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -151,7 +151,19 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { c.weightx = 0.0; socketPanel.add(serverPortField, c); - serverStartButton = new JButton("Start"); + serverStartButton = new JButton("Start") { // Button for label toggeling + private final String altString = "Stop"; + + @Override + public Dimension getPreferredSize() { + String origText = getText(); + Dimension origDim = super.getPreferredSize(); + setText(altString); + Dimension altDim = super.getPreferredSize(); + setText(origText); + return new Dimension(Math.max(origDim.width, altDim.width), origDim.height); + } + }; c.gridx++; c.weightx = 0.1; c.anchor = GridBagConstraints.EAST; @@ -221,12 +233,16 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void actionPerformed(ActionEvent e) { - try { - serverPortField.commitEdit(); - } catch (ParseException ex) { - java.util.logging.Logger.getLogger(SerialSocketClient.class.getName()).log(Level.SEVERE, null, ex); + if (e.getActionCommand().equals("Start")) { + try { + serverPortField.commitEdit(); + } catch (ParseException ex) { + java.util.logging.Logger.getLogger(SerialSocketClient.class.getName()).log(Level.SEVERE, null, ex); + } + startServer(((Long) serverPortField.getValue()).intValue()); + } else { + stopServer(); } - startServer(((Long) serverPortField.getValue()).intValue()); } }); @@ -249,10 +265,11 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void run() { + System.out.println("onServerStarted"); socketStatusLabel.setForeground(COLOR_NEUTRAL); socketStatusLabel.setText("Listening on port " + String.valueOf(port)); - serverStartButton.setEnabled(false); serverPortField.setEnabled(false); + serverStartButton.setText("Stop"); } }); } @@ -290,8 +307,8 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void run() { - serverStartButton.setEnabled(true); serverPortField.setEnabled(true); + serverStartButton.setText("Start"); socketStatusLabel.setForeground(COLOR_NEUTRAL); socketStatusLabel.setText("Idle"); } @@ -381,16 +398,16 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { try { // wait for next client Socket candidateSocket = serverSocket.accept(); - + // reject connection if already one client connected if (clientSocket != null && !clientSocket.isClosed()) { logger.info("Refused connection of client " + candidateSocket.getInetAddress()); candidateSocket.close(); continue; } - + clientSocket = candidateSocket; - + in = new DataInputStream(clientSocket.getInputStream()); out = new DataOutputStream(clientSocket.getOutputStream()); out.flush(); @@ -416,14 +433,14 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } } }); - + inBytes = outBytes = 0; logger.info("Client connected: " + clientSocket.getInetAddress()); notifyClientConnected(clientSocket); } catch (IOException e) { - logger.fatal("Listening thread shut down: " + e.getMessage()); + logger.info("Listening thread shut down: " + e.getMessage()); try { serverSocket.close(); } catch (IOException ex) { @@ -436,6 +453,17 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } }.start(); } + + /** + * Stops server by closing server listen socket. + */ + public void stopServer() { + try { + serverSocket.close(); + } catch (IOException ex) { + logger.error(ex); + } + } private void startSocketReadThread(final DataInputStream in) { /* Forward data: virtual port -> mote */ @@ -454,7 +482,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { try { numRead = in.read(data); } catch (IOException e) { - logger.error(e.getMessage()); + logger.info(e.getMessage()); numRead = -1; } } From dea03493bd9c9aa81c76227bfe8c2c721bdd62b3 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Mon, 14 Apr 2014 15:08:53 +0200 Subject: [PATCH 11/14] [cooja/serialsocket] Support for xml import/export --- .../serialsocket/SerialSocketClient.java | 86 ++++++++++++++++++- .../serialsocket/SerialSocketServer.java | 86 ++++++++++++++++--- 2 files changed, 155 insertions(+), 17 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index e13400b88..e074437d2 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -45,11 +45,13 @@ import java.io.IOException; import java.net.Socket; import java.text.NumberFormat; import java.text.ParseException; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.logging.Level; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -426,16 +428,92 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { }); incomingDataThread.start(); } + + @Override + public Collection getConfigXML() { + List config = new ArrayList<>(); + Element element; + + // XXX isVisualized guards? + element = new Element("host"); + if (socket == null || !socket.isBound()) { + element.setText(serverHostField.getText()); + } else { + element.setText(socket.getInetAddress().getHostName()); + } + config.add(element); + + element = new Element("port"); + if (socket == null || !socket.isBound()) { + try { + serverPortField.commitEdit(); + element.setText(String.valueOf((Long) serverPortField.getValue())); + } catch (ParseException ex) { + logger.error(ex.getMessage()); + serverPortField.setText("null"); + } + } else { + element.setText(String.valueOf(socket.getPort())); + } + config.add(element); + + element = new Element("bound"); + if (socket == null) { + element.setText(String.valueOf(false)); + } else { + element.setText(String.valueOf(socket.isBound())); + } + config.add(element); + + return config; + } @Override public boolean setConfigXML(Collection configXML, boolean visAvailable) { + String host = null; + Integer port = null; + boolean bound = false; + + for (Element element : configXML) { + switch (element.getName()) { + case "host": + host = element.getText(); + break; + case "port": + port = Integer.parseInt(element.getText()); + break; + case "bound": + bound = Boolean.parseBoolean(element.getText()); + break; + default: + logger.warn("Unknwon config element: " + element.getName()); + break; + } + } + + // XXX binding might fail if server not configured yet + if (Cooja.isVisualized()) { + if (host != null) { + serverHostField.setText(host); + } + if (port != null) { + serverPortField.setText(String.valueOf(port)); + } + if (bound) { + serverSelectButton.doClick(); + } + } else { + // if bound and all set up, start client + if (host != null && port != null) { + startClient(host, port); + } else { + logger.error("Client not started due to incomplete configuration"); + } + } + return true; } - @Override - public Collection getConfigXML() { - return null; - } private void cleanup() { serialPort.deleteSerialDataObserver(serialDataObserver); diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index 876c912b2..351e485f3 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -46,6 +46,7 @@ import java.net.ServerSocket; import java.net.Socket; import java.text.NumberFormat; import java.text.ParseException; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -103,7 +104,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private JLabel socketToMoteLabel; private JLabel moteToSocketLabel; private JLabel socketStatusLabel; - private JFormattedTextField serverPortField; + private JFormattedTextField listenPortField; private JButton serverStartButton; private int inBytes = 0, outBytes = 0; @@ -144,12 +145,12 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { NumberFormat nf = NumberFormat.getIntegerInstance(); nf.setGroupingUsed(false); - serverPortField = new JFormattedTextField(new NumberFormatter(nf)); - serverPortField.setColumns(5); - serverPortField.setText(String.valueOf(SERVER_DEFAULT_PORT)); + listenPortField = new JFormattedTextField(new NumberFormatter(nf)); + listenPortField.setColumns(5); + listenPortField.setText(String.valueOf(SERVER_DEFAULT_PORT)); c.gridx++; c.weightx = 0.0; - socketPanel.add(serverPortField, c); + socketPanel.add(listenPortField, c); serverStartButton = new JButton("Start") { // Button for label toggeling private final String altString = "Stop"; @@ -235,11 +236,11 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("Start")) { try { - serverPortField.commitEdit(); + listenPortField.commitEdit(); } catch (ParseException ex) { java.util.logging.Logger.getLogger(SerialSocketClient.class.getName()).log(Level.SEVERE, null, ex); } - startServer(((Long) serverPortField.getValue()).intValue()); + startServer(((Long) listenPortField.getValue()).intValue()); } else { stopServer(); } @@ -268,7 +269,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { System.out.println("onServerStarted"); socketStatusLabel.setForeground(COLOR_NEUTRAL); socketStatusLabel.setText("Listening on port " + String.valueOf(port)); - serverPortField.setEnabled(false); + listenPortField.setEnabled(false); serverStartButton.setText("Stop"); } }); @@ -307,7 +308,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void run() { - serverPortField.setEnabled(true); + listenPortField.setEnabled(true); serverStartButton.setText("Start"); socketStatusLabel.setForeground(COLOR_NEUTRAL); socketStatusLabel.setText("Idle"); @@ -494,13 +495,72 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } @Override - public boolean setConfigXML(Collection configXML, boolean visAvailable) { - return true; + public Collection getConfigXML() { + List config = new ArrayList<>(); + Element element; + + // XXX isVisualized guards? + + element = new Element("port"); + if (serverSocket == null || !serverSocket.isBound()) { + try { + listenPortField.commitEdit(); + element.setText(String.valueOf((Long) listenPortField.getValue())); + } catch (ParseException ex) { + logger.error(ex.getMessage()); + listenPortField.setText("null"); + } + } else { + element.setText(String.valueOf(serverSocket.getLocalPort())); + } + config.add(element); + + element = new Element("bound"); + if (serverSocket == null) { + element.setText(String.valueOf(false)); + } else { + element.setText(String.valueOf(!serverSocket.isClosed())); + } + config.add(element); + + return config; } @Override - public Collection getConfigXML() { - return null; + public boolean setConfigXML(Collection configXML, boolean visAvailable) { + Integer port = null; + boolean bound = false; + + for (Element element : configXML) { + switch (element.getName()) { + case "port": + port = Integer.parseInt(element.getText()); + break; + case "bound": + bound = Boolean.parseBoolean(element.getText()); + break; + default: + logger.warn("Unknwon config element: " + element.getName()); + break; + } + } + if (Cooja.isVisualized()) { + if (port != null) { + listenPortField.setText(String.valueOf(port)); + } + if (bound) { + serverStartButton.doClick(); + } + } else { + // if bound and all set up, start client + if (port != null) { + startServer(port); + } else { + logger.error("Server not started due to incomplete configuration"); + } + } + + return true; } private void cleanupClient() { From 894a88d08ee05eb8dd9078f2870b08fd9fe99f39 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Wed, 16 Apr 2014 03:42:41 +0200 Subject: [PATCH 12/14] [cooja/serialsocket] SeriaSocketServer: Moved Observer and Handler to inner classes and let handler thread join before notifying server terminated --- .../serialsocket/SerialSocketServer.java | 156 ++++++++++-------- 1 file changed, 86 insertions(+), 70 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index 351e485f3..e3aabbc15 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -111,8 +111,6 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private ServerSocket serverSocket; private Socket clientSocket; - private DataInputStream in; - private DataOutputStream out; private Mote mote; @@ -282,13 +280,15 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { @Override public void run() { socketStatusLabel.setForeground(COLOR_POSITIVE); - socketStatusLabel.setText(String.format("Client " + client.getInetAddress() + " connected.")); + socketStatusLabel.setText("Client " + + client.getInetAddress() + ":" + client.getPort() + + " connected."); } }); } @Override - public void onClientDisconnected(final Socket client) { + public void onClientDisconnected() { SwingUtilities.invokeLater(new Runnable() { @Override @@ -338,7 +338,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { public interface ServerListener { void onServerStarted(int port); void onClientConnected(Socket client); - void onClientDisconnected(Socket client); + void onClientDisconnected(); void onServerStopped(); void onServerError(String msg); } @@ -359,9 +359,9 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } } - public void notifyClientDisconnected(Socket client) { + public void notifyClientDisconnected() { for (ServerListener listener : listeners) { - listener.onClientDisconnected(client); + listener.onClientDisconnected(); } } @@ -393,6 +393,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } new Thread() { + private Thread incomingDataHandler; @Override public void run() { while (!serverSocket.isClosed()) { @@ -409,31 +410,13 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { clientSocket = candidateSocket; - in = new DataInputStream(clientSocket.getInputStream()); - out = new DataOutputStream(clientSocket.getOutputStream()); - out.flush(); - startSocketReadThread(in); + /* Start handler for data input from socket */ + incomingDataHandler = new Thread(new IncomingDataHandler()); + incomingDataHandler.start(); /* Observe serial port for outgoing data */ - serialPort.addSerialDataObserver(serialDataObserver = new Observer() { - @Override - public void update(Observable obs, Object obj) { - try { - if (out == null) { - /*logger.debug("out is null");*/ - return; - } - - out.write(serialPort.getLastSerialData()); - out.flush(); - - outBytes++; - } catch (IOException ex) { - logger.error(ex); - cleanupClient(); - } - } - }); + serialDataObserver = new SerialDataObserver(); + serialPort.addSerialDataObserver(serialDataObserver); inBytes = outBytes = 0; @@ -450,11 +433,19 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } } cleanupClient(); + if (incomingDataHandler != null) { + // Wait for reader thread to terminate + try { + incomingDataHandler.join(500); + } catch (InterruptedException ex) { + logger.warn(ex); + } + } notifyServerStopped(); } }.start(); } - + /** * Stops server by closing server listen socket. */ @@ -466,32 +457,72 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } } - private void startSocketReadThread(final DataInputStream in) { - /* Forward data: virtual port -> mote */ - Thread incomingDataThread = new Thread(new Runnable() { - @Override - public void run() { - int numRead = 0; - byte[] data = new byte[1024]; - logger.info("Forwarder: socket -> serial port"); - while (numRead >= 0) { - for (int i = 0; i < numRead; i++) { - serialPort.writeByte(data[i]); - } - inBytes += numRead; + /* Forward data: virtual port -> mote */ + private class IncomingDataHandler implements Runnable { - try { - numRead = in.read(data); - } catch (IOException e) { - logger.info(e.getMessage()); - numRead = -1; - } + DataInputStream in; + + @Override + public void run() { + int numRead = 0; + byte[] data = new byte[1024]; + try { + in = new DataInputStream(clientSocket.getInputStream()); + } catch (IOException ex) { + logger.error(ex); + return; + } + + logger.info("Forwarder: socket -> serial port"); + while (numRead >= 0) { + for (int i = 0; i < numRead; i++) { + serialPort.writeByte(data[i]); } - logger.info("End of Stream"); + inBytes += numRead; + + try { + numRead = in.read(data); + } catch (IOException e) { + logger.info(e.getMessage()); + numRead = -1; + } + } + logger.info("End of Stream"); + cleanupClient(); + } + } + + private class SerialDataObserver implements Observer { + + DataOutputStream out; + + public SerialDataObserver() { + try { + out = new DataOutputStream(clientSocket.getOutputStream()); + } catch (IOException ex) { + logger.error(ex); + out = null; + } + } + + @Override + public void update(Observable obs, Object obj) { + try { + if (out == null) { + /*logger.debug("out is null");*/ + return; + } + + out.write(serialPort.getLastSerialData()); + out.flush(); + + outBytes++; + } catch (IOException ex) { + logger.error(ex); cleanupClient(); } - }); - incomingDataThread.start(); + } + } @Override @@ -572,25 +603,10 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { } catch (IOException e1) { logger.error(e1.getMessage()); } - try { - if (in != null) { - in.close(); - in = null; - } - } catch (IOException e) { - logger.error(e.getMessage()); - } - try { - if (out != null) { - out.close(); - out = null; - } - } catch (IOException e) { - logger.error(e.getMessage()); - } + serialPort.deleteSerialDataObserver(serialDataObserver); - notifyClientDisconnected(null); + notifyClientDisconnected(); } private boolean closed = false; From db2f4e68187d469bc53a0598cf9e9bbbe5527a9b Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Fri, 2 May 2014 21:46:20 +0200 Subject: [PATCH 13/14] [cooja/serialsocket] Write serial data only on simulation thread --- .../serialsocket/SerialSocketClient.java | 18 +++++++++++++++--- .../serialsocket/SerialSocketServer.java | 19 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index e074437d2..113730952 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -115,10 +115,12 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { private DataOutputStream out; private final Mote mote; + private final Simulation simulation; public SerialSocketClient(Mote mote, Simulation simulation, final Cooja gui) { super("Serial Socket (CLIENT) (" + mote + ")", gui, false); this.mote = mote; + this.simulation = simulation; /* GUI components */ if (Cooja.isVisualized()) { @@ -411,9 +413,19 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { } if (numRead >= 0) { - for (int i = 0; i < numRead; i++) { - serialPort.writeByte(data[i]); - } + final int finalNumRead = numRead; + final byte[] finalData = data; + /* We are not on the simulation thread */ + simulation.invokeSimulationThread(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < finalNumRead; i++) { + serialPort.writeByte(finalData[i]); + } + } + }); + inBytes += numRead; if (Cooja.isVisualized()) { socketToMoteLabel.setText(inBytes + " bytes"); diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java index e3aabbc15..fa55da74b 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketServer.java @@ -113,10 +113,12 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { private Socket clientSocket; private Mote mote; + private Simulation simulation; public SerialSocketServer(Mote mote, Simulation simulation, final Cooja gui) { super("Serial Socket (SERVER) (" + mote + ")", gui, false); this.mote = mote; + this.simulation = simulation; updateTimer.start(); @@ -475,10 +477,19 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin { logger.info("Forwarder: socket -> serial port"); while (numRead >= 0) { - for (int i = 0; i < numRead; i++) { - serialPort.writeByte(data[i]); - } - inBytes += numRead; + final int finalNumRead = numRead; + final byte[] finalData = data; + /* We are not on the simulation thread */ + simulation.invokeSimulationThread(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < finalNumRead; i++) { + serialPort.writeByte(finalData[i]); + } + inBytes += finalNumRead; + } + }); try { numRead = in.read(data); From 133311b427b5fc32479edb97daa0d9d1c70faa87 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Fri, 29 Aug 2014 00:54:09 +0200 Subject: [PATCH 14/14] [cooja/serialsocket] SeriaSocketClient: Fixed position of pack() call --- .../org/contikios/cooja/serialsocket/SerialSocketClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java index 113730952..df8a7dc22 100644 --- a/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java +++ b/tools/cooja/apps/serial_socket/java/org/contikios/cooja/serialsocket/SerialSocketClient.java @@ -132,7 +132,6 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { GridBagConstraints c = new GridBagConstraints(); JPanel serverSelectPanel = new JPanel(new GridBagLayout()); - pack(); serverSelectPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); JLabel label = new JLabel("Host:"); @@ -329,6 +328,7 @@ public class SerialSocketClient extends VisPlugin implements MotePlugin { } }); } + pack(); } private List listeners = new LinkedList<>();