// This file is part of PleoCommand:
// Interactively control Pleo with psychobiological parameters
//
// Copyright (C) 2010 Oliver Hoffmann - Hoffmann_Oliver@gmx.de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Boston, USA.
package pleocmd.pipe.in;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import pleocmd.Log;
import pleocmd.StringManip;
import pleocmd.cfg.ConfigInt;
import pleocmd.exc.ConfigurationException;
import pleocmd.exc.FormatException;
import pleocmd.exc.InputException;
import pleocmd.pipe.data.Data;
import pleocmd.pipe.data.MultiFloatData;
public final class TcpIpInput extends Input { // NO_UCD
private final ConfigInt cfgTimeoutConn;
private final ConfigInt cfgTimeoutRead;
private final ConfigInt cfgPort;
private Socket socket;
private ServerSocket serverSocket;
private DataInputStream in;
public TcpIpInput() {
addConfig(cfgPort = new ConfigInt("Port", 19876, 1, 65535));
addConfig(cfgTimeoutConn = new ConfigInt("Connection-Timeout (sec)",
60, 0, 3600));
addConfig(cfgTimeoutRead = new ConfigInt("Read-Timeout (sec)", 10, 0,
3600));
constructed();
}
public TcpIpInput(final int port) {
this();
try {
cfgPort.setContent(port);
} catch (final ConfigurationException e) {
throw new IllegalArgumentException("Cannot set port", e);
}
}
@Override
protected void init0() throws IOException {
serverSocket = new ServerSocket();
serverSocket.setPerformancePreferences(0, 2, 1);
serverSocket.setReuseAddress(true);
serverSocket.setSoTimeout(3000);
serverSocket.bind(new InetSocketAddress(cfgPort.getContent()));
socket = null;
in = null;
}
@Override
protected void close0() throws IOException {
if (in != null) in.close();
if (socket != null) socket.close();
if (serverSocket != null) serverSocket.close();
}
@Override
public String getOutputDescription() {
return MultiFloatData.IDENT;
}
@Override
protected String getShortConfigDescr0() {
return String.format("%d [%ds, %ds]", cfgPort.getContent(),
cfgTimeoutConn.getContent(), cfgTimeoutRead.getContent());
}
@Override
protected Data readData0() throws IOException, InputException {
if (socket == null || !socket.isConnected() || socket.isInputShutdown()) {
if (in != null) in.close();
if (socket != null) socket.close();
final int cnt = Math.max(1, cfgTimeoutConn.getContent() / 3);
for (int i = 1;; ++i)
try {
Log.info("Waiting for TCP/IP connection ...");
socket = serverSocket.accept();
break;
} catch (final IOException e) {
if (getPipe().isInitPhaseInterrupted()) return null;
if (i == cnt) throw e;
}
socket.setSoTimeout(cfgTimeoutRead.getContent() * 1000);
in = new DataInputStream(socket.getInputStream());
}
try {
final Data data = Data.createFromBinary(in);
if (Log.canLogDetail())
Log.detail("<html>Read from TCP/IP: %s",
StringManip.printSyntaxHighlightedBinary(data));
return new MultiFloatData(data);
} catch (final FormatException e) {
throw new InputException(this, false, e, "Cannot read from TCP/IP");
} catch (final IOException e) {
in.close();
socket.close();
socket = null;
throw new InputException(this, false, e, "Cannot read from TCP/IP");
}
}
public static String help(final HelpKind kind) {
switch (kind) {
case Name:
return "TCP/IP Input";
case Description:
return "Reads Data blocks from a TCP/IP connection";
case Config1:
return "Port number of the client";
case Config2:
return "Connection Timeout in seconds (0 means infinite)";
case Config3:
return "Timeout for reading in seconds (0 means infinite)";
default:
return null;
}
}
@Override
public String isConfigurationSane() {
try {
final ServerSocket ss = new ServerSocket();
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress(cfgPort.getContent()));
ss.close();
} catch (final IOException e) {
return "Port is already in use";
}
return null;
}
@Override
protected int getVisualizeDataSetCount() {
return 0;
}
}