[cooja/serialsocket] SerialSocketServer: Fixed and improved server

implementation. Added visual feedback for new user interface.
This commit is contained in:
Enrico Joerns 2014-04-13 19:13:24 +02:00
parent 585db7df96
commit a18a7fa717
1 changed files with 207 additions and 71 deletions

View File

@ -44,9 +44,13 @@ import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.logging.Level;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
@ -85,12 +89,18 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
private final static int STATUSBAR_WIDTH = 350; 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 int SERVER_DEFAULT_PORT;
private final SerialPort serialPort; private final SerialPort serialPort;
private Observer serialDataObserver; private Observer serialDataObserver;
private JLabel socketStatusLabel, socketToMoteLabel, moteToSocketLabel; private JLabel socketToMoteLabel;
private JLabel moteToSocketLabel;
private JLabel socketStatusLabel;
private JButton serverStartButton; private JButton serverStartButton;
private int inBytes = 0, outBytes = 0; private int inBytes = 0, outBytes = 0;
@ -205,7 +215,12 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
@Override @Override
public void actionPerformed(ActionEvent e) { 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"); throw new RuntimeException("No mote serial port");
} }
try { if (Cooja.isVisualized()) {
logger.info("Listening on port: " + SERVER_DEFAULT_PORT); // gui updates for server status updates
if (Cooja.isVisualized()) { addServerListener(new ServerListener() {
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();
startSocketReadThread(in); @Override
if (Cooja.isVisualized()) { public void onServerStarted(final int port) {
socketStatusLabel.setText("Client connected: " + clientSocket.getInetAddress()); 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<ServerListener> 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; inBytes = outBytes = 0;
cleanupClient();
break; 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(); cleanupClient();
} catch (Exception e) { notifyServerStopped();
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();
}
} }
}); }.start();
} }
private void startSocketReadThread(final DataInputStream in) { private void startSocketReadThread(final DataInputStream in) {
@ -281,25 +425,21 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
int numRead = 0; int numRead = 0;
byte[] data = new byte[1024]; byte[] data = new byte[1024];
logger.info("Forwarder: socket -> serial port"); logger.info("Forwarder: socket -> serial port");
while (true) { while (numRead >= 0) {
numRead = -1; for (int i = 0; i < numRead; i++) {
serialPort.writeByte(data[i]);
}
inBytes += numRead;
try { try {
numRead = in.read(data); numRead = in.read(data);
} catch (IOException e) { } catch (IOException e) {
logger.error(e.getMessage());
numRead = -1; 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(); incomingDataThread.start();
@ -322,6 +462,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
clientSocket = null; clientSocket = null;
} }
} catch (IOException e1) { } catch (IOException e1) {
logger.error(e1.getMessage());
} }
try { try {
if (in != null) { if (in != null) {
@ -329,6 +470,7 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
in = null; in = null;
} }
} catch (IOException e) { } catch (IOException e) {
logger.error(e.getMessage());
} }
try { try {
if (out != null) { if (out != null) {
@ -336,16 +478,11 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
out = null; out = null;
} }
} catch (IOException e) { } catch (IOException e) {
logger.error(e.getMessage());
} }
serialPort.deleteSerialDataObserver(serialDataObserver);
if (Cooja.isVisualized()) { notifyClientDisconnected(null);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
socketStatusLabel.setText("Listening on port: " + SERVER_DEFAULT_PORT);
}
});
}
} }
private boolean closed = false; private boolean closed = false;
@ -353,7 +490,6 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
public void closePlugin() { public void closePlugin() {
closed = true; closed = true;
cleanupClient(); cleanupClient();
serialPort.deleteSerialDataObserver(serialDataObserver);
try { try {
server.close(); server.close();
} catch (IOException e) { } catch (IOException e) {