TCP/IP Sockets

Es wird die Implementierung einer TCP/IP Kommunikation vorgestellt. Die Schnittstelle wird durch das Erstellen einer Socket-Verbindung realisiert. Hierf�r wird auf die Standardfunktionalit�ten von JavaScript (insb. StreamWriter) zur�ckgegriffen.

1. Einleitung


Mittels Sockets kann eine Kommunikation zum Datenaustausch zwischen dem Roboter und weiteren via Netzwerk erreichbaren Teilnehmern (Anlage, Kamera, etc.) hergestellt werden. Die Kommunikation erfolgt bidirektional. Das hei�t, Daten k�nnen empfangen sowie gesendet werden.

In dem folgenden Artikel wird beschrieben, wie eine solche Kommunikation �ber Sockets aufgebaut und Daten �bertragen werden k�nnen.

Der Roboter agiert hierbei als Client und die Gegenstelle als Server. 

2. Aufbau der Verbindung

Der Roboter wird mittels eines Netzwerkkabels mit der Gegenstelle verbunden.

F�r den Verbindungsaufbau wird die IP-Adresse der Gegenstelle sowie der Port ben�tigt. Es ist darauf zu achten, dass die IP-Adressen der beiden Teilnehmer sich im selben Bereich befinden. Das hei�t, die vordersten 9 Stellen der IP-Adressen m�ssen gleich sein und die letzten 3 Stellen m�ssen sich voneinander unterscheiden.

In folgendem Artikel ist beschrieben, wie sich die IP-Adresse des Roboters einstellen l�sst. IP-Adresse �ndern

Mittels des folgenden Befehls kann eine Verbindung �ber einen Socket hergestellt werden. Der Roboter agiert hierbei als Client und die Gegenstelle als Server.

var IP_Adresse = "10.125.1.122";
var Port = 3000;

var socket = new java.net.Socket(IP_Adresse, Port);

 Das gleiche funktioniert auch mit Timeout: 

var IP_Adresse = "10.125.1.122";
var Port = 3000;

var socket = new java.net.Socket();
socket.connect(new  java.net.InetSocketAddress(IP_Adresse, Port), 10000); //10s timeout

3. Informationen senden

Mittels der folgenden Funktion kann per StreamWriter eine Nachricht als Zeichenkette �ber den Socket gesendet werden.

function schreibeNachricht(socket, nachricht) 
{
        var printWriter = 
              new java.io.PrintWriter(
                       new java.io.OutputStreamWriter(
                               socket.getOutputStream()));
        printWriter.print(nachricht);
       printWriter.flush();
}

Die Funktion wird �ber folgende Zeile aufgerufen. In der Klammer wird als zweites Argument die gew�nschte Nachricht �bergeben.

var Nachricht = "Hello World!";
schreibeNachricht(socket, Nachricht);

3.1.  Senden von raw-Daten

Sollen raw-Daten, zum Beispiel in Form eines hexadezimalen Byte Arrays,  �ber den Socket gesendet werden, kann folgende Funktion genutzt werden.

function schreibeNachricht(socket, nachricht)
{
    var jNachricht = Java.to(nachricht,"byte[]");
    
    var dout = new java.io.DataOutputStream(socket.getOutputStream());
    dout.write(jNachricht);
    dout.flush();
}

Die Funktion wird �ber folgende Zeile aufgerufen. In der Klammer wird als zweites Argument die gew�nschte Nachricht �bergeben. Hierbei wird 01hex 00hex 00hex 00hex �bertragen.

var connect_cam =[0x01, 0x00, 0x00, 0x00];
schreibeNachricht(socket, connect_cam);

4. Informationen lesen

Mittels der folgenden Funktion kann per StreamReader eine Nachricht als Zeichenkette �ber den Socket gelesen werden.

function leseNachricht(socket) 
{
    var bufferedReader =
        new java.io.BufferedReader(
            new java.io.InputStreamReader(
                socket.getInputStream()));
    var charArrayType = Java.type("char[]");
    var buffer = new charArrayType(200);
    var anzahlZeichen = bufferedReader.read(buffer, 0, 200);
    var nachricht = new java.lang.String(buffer);
    return nachricht;
}

Die Funktion wird �ber folgende Zeile aufgerufen. In die Variable result wird die Nachricht geschrieben. Diese wird anschlie�end als Meldung in horstFX ausgegeben.

var result = leseNachricht(socket);
show_info(result);

4.1. Einlesen von raw-Daten

Mittels der folgenden Funktion k�nnen raw-Daten dezimal codiert als Byte-Array �ber den Socket gelesen werden.

function leseNachricht(socket, size)
{
    var ByteArray = Java.type("byte[]");
    var dIn = new java.io.DataInputStream(socket.getInputStream());

    var message = new ByteArray(size);
    dIn.readFully(message, 0, message.length); 

    var jsMessage =  Java.from(message);
    return jsMessage;
}

Die Funktion wird �ber folgende Zeile aufgerufen. In die Variable result wird die Nachricht geschrieben. Diese wird anschlie�end als Meldung in horstFX ausgegeben.

var result = leseNachricht(socket);
show_info(result);

4.2. Einlesen bis zu einem bestimmten Zeichen (z.B. carriage return)

Mittels der folgenden Funktion kann die Socket bis zu einem definierten Zeichen ausgelesen werden.

Oft wird am Ende eines zu �bertragenden Strings ein Trennzeichen (Semikolon, Zeilenumbruch, etc.) geschrieben. Mit der folgenden Funktion wird die Socket solange ausgelesen, bis das definierte Trennzeichen erkannt/eingelesen wurde. Dadurch wird sichergestellt, dass die gesamten Informationen �bertragen wurden.

function leseNachricht(socket, msgSperator)
{
    var ByteHelper = Java.type("de.fruitcore.robot.core.util.ByteHelper");

    var ByteArray = Java.type("byte[]");
    var dIn = new java.io.DataInputStream(socket.getInputStream());
   
    var full_msg = new ByteHelper();;
   
    var buffer = new ByteArray(1);
       
    while(1){
        dIn.readFully(buffer, 0, buffer.length);
        full_msg.putByte(buffer[0]);
        if(msgSperator == buffer[0]){
            break;
        }
    }

    var nachricht = new java.lang.String(full_msg.toBytes(), java.nio.charset.StandardCharsets.UTF_8);

    return nachricht;
}

Die Funktion wird �ber folgende Zeile aufgerufen. In der Variable delimiter wird das Trennzeichen definiert, bis zu dem die Socket ausgelesen werden soll. In die Variable result wird die Nachricht geschrieben.

var delimiter = "\r".charCodeAt(0); //Trennzeichen, z.B. CR Carriage Return (ggf. anpassen)
var result = leseNachricht(socket, delimiter);

4.3.  Einlesen der Nachricht bis zu einem bestimmten Timeout

Ein Timeout, falls einige Zeit keine Nachricht von der Kamera gesendet wird, kann �ber folgende Zeile definiert werden. Ansonsten k�nnte das Programm nicht abgebrochen werden, da der Roboter st�ndig auf die Nachricht wartet.

// 10 sek Timeout beim Einlesen der Nachricht ueber einen Socket
socket.setSoTimeout(10000);

Die Funktion zum Einlesen der Nachricht kann dazu folgenderma�en angepasst werden.

function leseNachricht(Socket) {
    var bufferedReader =
        new java.io.BufferedReader(
            new java.io.InputStreamReader(
                socket.getInputStream()));
    var charArrayType = Java.type("char[]");
    var buffer = new charArrayType(1000);
    try {
        var anzahlZeichen = bufferedReader.read(buffer, 0, 1000);  
        var nachricht = new java.lang.String(buffer);
    } catch (e) {
        var nachricht = "Timeout: Keine Nachricht erhalten";
    }
    return nachricht;
}

5. Sicherstellen, dass der Socket geschlossen wird

var IP_Adresse = "10.125.1.122";
var Port = 3000;
 
var socket = new java.net.Socket();
socket.connect(new  java.net.InetSocketAddress(IP_Adresse, Port), 10000); //10s timeout
LOG.info("connected");

try {
//#############code begin#############
  
//#############code end #############
} finally {
  socket.close();
}

Durch den try finally Block wird sichergestellt, das socket.close() aufgerufen wird. Das Funktioniert bei Programmierfehlern oder auch beim Abbrechen �ber den Abbrechen-Button.
Die gesamte Logik muss nun zwischen ##code begin## und ##code end ## Programmiert werden. Au�erdem muss darauf geachtet werden, dass Funktionen au�erhalb des try finally Block�s definiert werden.

6. Testen der Kommunikation

Mittels verschiedener Programme l�sst sich die Kommunikation sehr einfach testen. Es muss lediglich Server ausgew�hlt sowie die IP-Adresse des Roboters und der Port eingetragen werden. 

Basis Progamm f�r Socketverbindungen

Ein Anwendungsbeispiel f�r eine TCP/IP Kommunikation ist unser Component Kit "SICK PLOC2D" f�r die Teilevereinzelung im 2D Bereich. Die Dokumentation finden Sie hier.