//----------------------------------------------------------------------------- // 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. //----------------------------------------------------------------------------- INCLUDErtcu.inc // Input variables that can be configured via the configuration dialog (These are global) VAR_INPUT din : ARRAY [1..4] OFBOOL; | Digital inputs END_VAR; // Output variables that can be configured via the configuration dialog (These are global) VAR_OUTPUT dout : ARRAY [1..4] OFBOOL; | Digital outputs led : ARRAY [1..3] OFBOOL; END_VAR; //--------------------------------------------------------------------------- // Send a STRING on a TCP/IP connection //--------------------------------------------------------------------------- FUNCTIONSendData; VAR_INPUT handle : SYSHANDLE; str : STRING; END_VAR; VAR txbuf : ARRAY [0..100] OFSINT; 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_BLOCKTB_TELNET_CLIENT; VAR_INPUT handle : SYSHANDLE; END_VAR; VAR_OUTPUT inuse : BOOL; END_VAR; VAR buf : ARRAY [1..200] OFSINT; 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 WHILETRUEDO // Wait for data rc := soRecv( socket := handle, data := ADDR(buf), maxsize := SIZEOF(buf), size := size ); IFrc < 1THEN 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); IFrc < 1THEN 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"); IFi > 0THEN // 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); IFextract.matchANDextract.v1 >= 1ANDextract.v1 <= 8THEN 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); IFextract.matchANDextract.v1 >= 1ANDextract.v1 <= 8THEN 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 IFstrCompare(str1 := "INPUT", str2 := Command) = 0THEN unknown := OFF; FORi := 1TO4DO 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 IFstrCompare(str1 := "OUTPUT", str2 := Command) = 0THEN unknown := OFF; FORi := 1TO4DO 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 IFstrCompare(str1 := "HELP", str2 := Command) = 0THEN 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 IFstrCompare(str1 := "QUIT", str2 := Command) = 0THEN unknown := OFF; SendData(handle := handle, str := "$NGoodbye.$N"); // Diconnect soClose(socket := handle); END_IF; // Unknown command IFunknownTHEN 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] OFTB_TELNET_CLIENT; END_VAR;
//--------------------------------------------------------------------------- // Show information about incoming connection //--------------------------------------------------------------------------- FUNCTIONShowInfo; 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_BLOCKTB_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] OFSINT; END_VAR; // Setup strToMemory(dst := ADDR(buf), str := "EXIT$N", len := 6); // Build listen address soAddrInetSet(address := address, port := port); WHILETRUEDO // Open listen socket IFNOTinuseTHEN // Create socket rc := soCreate(socket := handle); IFrc < 1THEN DebugFmt(message := "soCreate = \1", v1 := rc); RETURN; END_IF; // Bind socket rc := soBind(socket := handle, address := address); IFrc < 1THEN DebugFmt(message := "soBind = \1", v1 := rc); soClose(socket := handle); RETURN; END_IF; // Start listening rc := soListen(socket := handle); IFrc < 1THEN DebugFmt(message := "soListen = \1", v1 := rc); soClose(socket := handle); RETURN; END_IF; inuse := TRUE; END_IF; // Accept rc := soAccept(socket := handle, remote := remote); IFrc = 1THEN // Find free thread FORi := 1TO10DO IFNOTclient[i].inuseTHEN EXIT; END_IF; END_FOR; // Start thread IFNOTclient[i].inuseTHEN // 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; PROGRAMTelnet; 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) WHILENOTnetConnected(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); IFval_con <> old_conTHEN IFval_conTHEN 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; FORi := 1TO10DO IFclient[i].inuseTHENled[3] := ON; END_IF; END_FOR; // Delay Sleep(delay := 250); END; END_PROGRAM;