Examples - Telnet server2

Top  Previous  Next

//-----------------------------------------------------------------------------
// telnet.vpl, created 2018-03-15 10:39
//
// This is a VERY simple telnet server. It has only a few commands, but still
// shows how to establish a TCP/IP connection.
// When a Telnet client connects to the RTCU, a welcome message is shown,
// together with a prompt, where the telnet client then can enter commands.
// The following commands are available:
// input, on n, off n, quit and help. Input will show the state
// of 4 digital inputs, on/off n will switch digital output n to either on or off
// state, quit will close the connection, and help will show a list of available
// commands.
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
 
//  Input variables that can be configured via the configuration dialog (These are global)
VAR_INPUT
  din      : ARRAY [1..4] OF BOOL; | Digital inputs
END_VAR;
 
//  Output variables that can be configured via the configuration dialog (These are global)
VAR_OUTPUT
  dout     : ARRAY [1..4] OF BOOL; | Digital outputs
  led      : ARRAY [1..3] OF BOOL;
END_VAR;
 
//---------------------------------------------------------------------------
// Send a STRING on a TCP/IP connection
//---------------------------------------------------------------------------
FUNCTION SendData;
VAR_INPUT
  handle   : SYSHANDLE;
  str      : STRING;
END_VAR;
VAR
  txbuf    : ARRAY [0..100] OF SINT;
  len      : INT;
  size     : DINT;
END_VAR;
  len := strLen(str := str);
  strToMemory(dst := ADDR(txbuf), str := str, len := len);
  DebugFmt(message:="soSend = \1", v1 := soSend(socket:=handle, data:=ADDR(txbuf), size:=len, sent:=size));
END_FUNCTION;
 
//---------------------------------------------------------------------------
// Thread which handles client connections
//---------------------------------------------------------------------------
THREAD_BLOCK TB_TELNET_CLIENT;
VAR_INPUT
  handle   : SYSHANDLE;
END_VAR;
VAR_OUTPUT
  inuse    : BOOL;
END_VAR;
VAR
  buf      : ARRAY [1..200] OF SINT;
  size     : DINT;
  sent     : DINT;
  rc, i    : INT;
 
  extract  : strGetValues;
  str      : STRING;
  Partial  : STRING;
  Command  : STRING;
  unknown  : BOOL;
END_VAR;
 
  // The thread is in use
  inuse := TRUE;
 
  // Send welcome message
  SendData(handle := handle, str := "Welcome to the RTCU Telnet Server.$N$N");
  SendData(handle := handle, str := "Available commands:$N");
  SendData(handle := handle, str := "  <input>  : Show status of digital inputs$N");
  SendData(handle := handle, str := "  <output> : Show status of digital outputs$N");
  SendData(handle := handle, str := "  <on n>   : Set digital output n to ON$N");
  SendData(handle := handle, str := "  <off n>  : Set digital output n to OFF$N");
  SendData(handle := handle, str := "  <quit>   : Close connection$N");
  SendData(handle := handle, str := "  <help>   : Show available commands$N");
  SendData(handle := handle, str := "$NEnter command> ");
 
// Main loop
WHILE TRUE DO
  // Wait for data
  rc := soRecv(
              socket  := handle,
              data    := ADDR(buf),
              maxsize := SIZEOF(buf),
              size    := size
              );
  IF rc < 1 THEN
    DebugFmt(message := "soRecv = \1", v1 := rc);
    soClose(socket := handle);
    inuse := FALSE;
    RETURN;
  END_IF;
 
  // Echo the received data
  rc := soSend(socket := handle, data:=ADDR(buf), size := size, sent := sent);
  IF rc < 1 THEN
    DebugFmt(message := "soRecv = \1", v1 := rc);
    soClose(socket := handle);
    inuse := FALSE;
    RETURN;
  END_IF;
 
  // Convert the received data to a string
  str := strFromMemory(src := ADDR(buf), len := INT(size));
  DebugMsg(message := str);
 
  // concatenate the received data to command string
  Partial := strConcat(str1 := Partial, str2 := str);
  i := strFind(str1 := Partial, str2 := "$R");
  IF i > 0 THEN
    // We now have a complete command assembled in PartialCommand
    Command := strLeft(str := Partial, length := i - 1);
    DebugMsg(message := strConcat(str1 := "Command=", str2 := Command));
    Partial := "";
    unknown := ON;
 
    // Check if it is a "ON" command
    extract(format := "ON \1", str := Command);
    IF extract.match AND extract.v1 >= 1 AND extract.v1 <= 8 THEN
        unknown := OFF;
        dout[extract.v1] := ON;
        str := strFormat(format := "Setting output \1 to ON", v1 := extract.v1);
        DebugMsg(message := str);
        SendData(handle := handle, str := strConcat(str1 := str, str2 := "$N"));
    END_IF;
 
    // Check if it is a "OFF" command
    extract(format := "OFF \1", str := Command);
    IF extract.match AND extract.v1 >= 1 AND extract.v1 <= 8 THEN
        unknown := OFF;
        dout[extract.v1] := OFF;
        str := strFormat(format := "Setting output \1 to OFF", v1 := extract.v1);
        DebugMsg(message := str);
        SendData(handle := handle, str := strConcat(str1 := str, str2 := "$N"));
    END_IF;
 
    // Check if it is a "INPUT" command
    IF strCompare(str1 := "INPUT", str2 := Command) = 0 THEN
        unknown := OFF;
        FOR i := 1 TO 4 DO
          str := strFormat(format := "$NInput \1 is \2", v1 := i, v2 := INT(din[i]));
          SendData(handle := handle, str := str);
        END_FOR;
    END_IF;
 
    // Check if it is a "OUTPUT" command
    IF strCompare(str1 := "OUTPUT", str2 := Command) = 0 THEN
        unknown := OFF;
        FOR i := 1 TO 4 DO
          str := strFormat(format := "$NOutput \1 is \2", v1 := i, v2 := INT(dout[i]));
          SendData(handle := handle, str := str);
        END_FOR;
    END_IF;
 
    // Check if it is a "HELP" command
    IF strCompare(str1 := "HELP", str2 := Command) = 0 THEN
        unknown := OFF;
        SendData(handle := handle, str := "Available commands:$N");
        SendData(handle := handle, str := "  <input>  : Show status of digital inputs$N");
        SendData(handle := handle, str := "  <output> : Show status of digital outputs$N");
        SendData(handle := handle, str := "  <on n>   : Set digital output n to ON$N");
        SendData(handle := handle, str := "  <off n>  : Set digital output n to OFF$N");
        SendData(handle := handle, str := "  <quit>   : Close connection$N");
        SendData(handle := handle, str := "  <help>   : Show available commands$N");
    END_IF;
 
    // Check if it is a "QUIT" command
    IF strCompare(str1 := "QUIT", str2 := Command) = 0 THEN
        unknown := OFF;
        SendData(handle := handle, str := "$NGoodbye.$N");
 
        // Diconnect
        soClose(socket := handle);
    END_IF;
 
    // Unknown command
    IF unknown THEN
        SendData(handle := handle, str := "Unknown command$N");
    END_IF;
 
    // Send a new prompt
    SendData(handle := handle, str := "$NEnter command> ");
  END_IF;
 
END_WHILE;
END_THREAD_BLOCK;
 
//  These are the global variables of the program
VAR
  client   : ARRAY [1..10] OF TB_TELNET_CLIENT;
END_VAR;
 

//---------------------------------------------------------------------------
// Show information about incoming connection
//---------------------------------------------------------------------------
FUNCTION ShowInfo;
VAR_INPUT
  handle   : SYSHANDLE;
END_VAR;
VAR
  local    : STRING;
  remote   : STRING;
  host     : STRING;
  port     : DINT;
  iface    : SINT;
END_VAR;
  // Get information
  soStatus(socket := handle, local := local, remote := remote);
  soAddrInetGet(address := remote, host := host, port := port);
  iface := soAddrToInterface(address := local);
 
  // Show information
  DebugMsg(message := "Incoming connection");
  DebugFmt(message := "    interface  = \1", v1 := iface);
  DebugMsg(message := "    IP address = " + host);
  DebugFmt(message := "    IP port    = \4", v4 := port);
END_FUNCTION;
 
//---------------------------------------------------------------------------
// Thread which listens for incoming connections
//---------------------------------------------------------------------------
THREAD_BLOCK TB_TELNET_SERVER;
VAR_INPUT
  port     : DINT;
END_VAR;
VAR
  handle   : SYSHANDLE;
  remote   : SYSHANDLE;
  inuse    : BOOL;
  address  : STRING;
 
  rc       : INT;
  i        : INT;
  size     : DINT;
  buf      : ARRAY [1..10] OF SINT;
END_VAR;
 
// Setup
strToMemory(dst := ADDR(buf), str := "EXIT$N", len := 6);
 
// Build listen address
soAddrInetSet(address := address, port := port);
 
WHILE TRUE DO
  // Open listen socket
  IF NOT inuse THEN
    // Create socket
    rc := soCreate(socket := handle);
    IF rc < 1 THEN
        DebugFmt(message := "soCreate = \1", v1 := rc);
        RETURN;
    END_IF;
 
    // Bind socket
    rc := soBind(socket := handle, address := address);
    IF rc < 1 THEN
        DebugFmt(message := "soBind = \1", v1 := rc);
        soClose(socket := handle);
        RETURN;
    END_IF;
 
    // Start listening
    rc := soListen(socket := handle);
    IF rc < 1 THEN
        DebugFmt(message := "soListen = \1", v1 := rc);
        soClose(socket := handle);
        RETURN;
    END_IF;
 
    inuse := TRUE;
  END_IF;
 
  // Accept
  rc := soAccept(socket := handle, remote := remote);
  IF rc = 1 THEN
    // Find free thread
    FOR i := 1 TO 10 DO
        IF NOT client[i].inuse THEN
          EXIT;
        END_IF;
    END_FOR;
 
    // Start thread
    IF NOT client[i].inuse THEN
        // Show information
        ShowInfo(handle := remote);
 
        // Start thread
        client[i](handle := remote);
    ELSE
        // Send denied
        soSend(
              socket  := remote,
              data    := ADDR(buf),
              size    := 6,
              sent    := size
              );
        soClose(socket := remote);
    END_IF;
  ELSE
    DebugFmt(message := "soAccept = \1", v1 := rc);
    soClose(socket := handle);
    inuse := FALSE;
  END_IF;
 
  Sleep(delay := 250);
END_WHILE;
END_THREAD_BLOCK;
 
PROGRAM Telnet;
VAR
  telnet      : TB_TELNET_SERVER;
  val_con     : BOOL;
  old_con     : BOOL;
  i           : INT;
 
  iface       : SINT;
  port        : DINT;
END_VAR;
 
  // Welcome
  DebugMsg(message := "----------------------------------------");
  DebugMsg(message := "  RTCU Telnet Server.");
  DebugMsg(message := "----------------------------------------");
 
  // Initialize
  DebugMsg(message := "Initialize...");
  iface    := _NET_IFACE_LAN1;
  port     := 5020;
 
  // Open network interface
  netOpen(iface := iface);
 
  // Wait for connection to the Internet
  // (This is actually not needed, as the soListen() can be called before
  // the connection is established)
  WHILE NOT netConnected(iface := iface) DO
    DebugMsg(message := "   Waiting for network connection");
    Sleep(delay := 3000);
  END_WHILE;
  old_con := ON;
 
  // Start server
  telnet(port := port);
 
  // Show our assigned IP address (this is the one the telnet client should connect to)
  DebugMsg(message := strConcat(str1 := "My IP Address= ", str2 := sockIPToName(ip := sockGetLocalIP(iface := iface))));
  DebugFmt(message := "My IP Port=    \4", v4 := port);
  DebugMsg(message := "----------------------------------------");
  DebugMsg(message := "Running.");
 
BEGIN
  // Update interface status
  val_con := netConnected(iface := iface);
  IF val_con <> old_con THEN
    IF val_con THEN
        DebugMsg(message := "Network: Connected");
    ELSE
        DebugMsg(message := "Network: Connection lost");
    END_IF;
  END_IF;
  old_con := val_con;
 
  // Update LEDS
  led[1]  := val_con;
  led[2]  := telnet._running;
  led[3]  := OFF;
  FOR i := 1 TO 10 DO
    IF client[i].inuse THEN led[3] := ON; END_IF;
  END_FOR;
 
  // Delay
  Sleep(delay := 250);
END;
END_PROGRAM;