[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

View File

@ -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,39 +233,148 @@ 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);
// gui updates for server status updates
addServerListener(new ServerListener() {
@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);
}
server = new ServerSocket(SERVER_DEFAULT_PORT);
});
}
@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 != null) {
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);
if (Cooja.isVisualized()) {
socketStatusLabel.setText("Client connected: " + clientSocket.getInetAddress());
}
} catch (IOException e) {
logger.fatal("Listening thread shut down: " + e.getMessage());
server = null;
cleanupClient();
break;
}
}
}
}.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() {
@ -266,11 +390,31 @@ public class SerialSocketServer extends VisPlugin implements MotePlugin {
out.flush();
outBytes++;
} catch (IOException e) {
} catch (IOException ex) {
logger.error(ex);
cleanupClient();
}
}
});
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);
}
}
}
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 {
logger.info("End of Stream");
cleanupClient();
break;
}
}
}
});
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) {