|
//-----------------------------------------------------------------------------
// Program: Socket example program using the so API.
// Demonstrates both blocking and non-blocking mode.
//
// Description:
// This program demonstrates how to establish a TCP/IP connection over the
// LAN network using the SO API in both blocking and non-blocking mode.
//
// Prerequisites:
// - A TCP Echo Server (e.g., the supplied Windows tool 'echos.exe') must be running
// on the target PC/Server to echo received data back to the RTCU.
// - The 'host' and 'server_port' variables below must be updated to match
// the IP address and port of the PC running 'echos.exe'.
//
// Key Logic:
// 1. Network Setup: Initializes the network and waits for connection.
// 2. Socket Config: Creates a TCP stream and in either non-blocking or
// blocking mode using 'soConfigNonblock'.
// When NON_BLOCKING is defined the non-blocking mode will be used.
// 3. Data Handling:
// - Checks 'soRecv' for data.
// - If rc = 1, data is processed (Echoed back to server).
// - If rc = -4, the program continues (no data waiting).
// - If rc < 0 (other errors), the connection is reset.
// 4. Lifecycle: Connects to '192.168.1.100:5005', echoes 100 packets, then disconnects.
//
// I/O Mapping:
// - Output 'cnetwork': High when cellular network is connected.
// - Output 'toggle': Toggles state every time a data packet is received.
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
// Define this for non-blocking mode:
//#DEFINE NON_BLOCKING
// Input variables
VAR_INPUT
END_VAR;
// Output variables
VAR_OUTPUT
toggle : BOOL; | LED that indicates when data is received from echo host
cnetwork : BOOL; | LED that indicates that a network connection is present.
END_VAR;
// Global variables
VAR
host : STRING := "192.168.1.100";
server_port : INT := 5005;
// Test string
teststr1 : STRING := "Hello world. This is a test of the RTCU so API Interface";
END_VAR;
//-----------------------------------------------------------------------------
// Main Program
//-----------------------------------------------------------------------------
PROGRAM test;
VAR
handle : SYSHANDLE; // Handle to the socket
rc : INT; // Return codes
buffer : ARRAY[0..300] OF SINT; // Receive buffer
net_addr : STRING; // Socket Address string
iter : INT; // Iteration counter
rx_len : DINT; // Length of received data (Output from soRecv)
tx_len : DINT; // Length of sent data (Output from soSend)
// State Flags
connected : BOOL := FALSE; // True if TCP connection is established
socket_created : BOOL := FALSE; // True if soCreate has been called successfully
await_connection : BOOL := FALSE; //True if non-blocking mode is waiting for connection.
#IFDEF NON_BLOCKING THEN
//Used for soStatus in non-blocking mode
local,remote : STRING;
#END_IF;
END_VAR;
DebugMsg(message:="Network 'so' API Socket test-program started.");
//---------------------------------------------------------------------------
// 1. Network Setup: Initializes the network and waits for connection.
//---------------------------------------------------------------------------
// Open the LAN network connection
rc := netOpen(iface:=_NET_IFACE_LAN1);
DebugFmt(message:="netOpen()=\1", v1:=rc);
// Wait for network connected
WHILE NOT netConnected(iface:=_NET_IFACE_LAN1) DO
DebugMsg(message:="Waiting for network connection");
Sleep(delay:=2500);
END_WHILE;
BEGIN
// Show the network connected status on an LED
cnetwork := netConnected(iface:=_NET_IFACE_LAN1);
//---------------------------------------------------------------------------
// 2. Connection Logic
//---------------------------------------------------------------------------
//Check for not connected and network available:
IF NOT connected AND cnetwork THEN
// 2a. Create the socket
IF NOT socket_created THEN
// Create socket
rc := soCreate(socket:=handle, type:=_SO_TYPE_STREAM, protocol:=_SO_PROT_TCP);
IF rc=1 THEN
socket_created := TRUE;
#IFDEF NON_BLOCKING THEN
// Configure Socket as NON-BLOCKING
// This ensures soRecv returns immediately if no data is present.
rc := soConfigNonblock(socket:=handle, enable:=TRUE);
IF rc < 0 THEN
DebugFmt(message:="soConfigNonblock failed rc=\1", v1:=rc);
ELSE
DebugMsg(message:="Socket configured as Non-Blocking.");
END_IF;
#END_IF
ELSE
DebugFmt(message:="soCreate failed rc=\1", v1:=rc);
END_IF;
END_IF;
// 2b. Connect
IF socket_created AND NOT await_connection THEN
DebugMsg(message:="Connecting to " + host + "...");
// Create address string (IP + Port)
rc := soAddrInetSet(address:=net_addr, host:=host, port:=server_port);
IF rc < 0 THEN
DebugFmt(message:="soAddrInetSet failed rc=\1", v1:=rc);
ELSE
// Connect
rc := soConnect(socket:=handle, address:=net_addr);
IF rc = 1 THEN
connected := TRUE;
iter := 0;
DebugMsg(message:="Socket Connected!");
// "Prime" the conversation - Send initial data
strToMemory(dst:=ADDR(buffer), str:=teststr1, len:=strLen(str:=teststr1));
rc := soSend(socket:=handle, data:=ADDR(buffer), size:=strLen(str:=teststr1), sent:=tx_len);
ELSIF rc = -4 THEN
//"Would-block" in Non-blocking mode
#IFDEF NON_BLOCKING THEN
await_connection:=TRUE;
#END_IF;
ELSE
// Connect failed
DebugFmt(message:="soConnect()=\1", v1:=rc);
soClose(socket:=handle);
socket_created := FALSE;
connected := FALSE;
END_IF;
END_IF;
END_IF;
#IFDEF NON_BLOCKING THEN
IF await_connection THEN
rc := soStatus(socket:=handle,local:=local,remote:=remote);
IF rc = 4 THEN
//awaiting connection. Throttle a bit.
Sleep(delay:=250);
ELSIF rc = 5 THEN
//Connected.
DebugMsg(message:="Socket Connected! local="+local+" remote="+remote);
await_connection:=FALSE;
connected:=TRUE;
iter:=0;
// "Prime" the conversation - Send initial data
strToMemory(dst:=ADDR(buffer), str:=teststr1, len:=strLen(str:=teststr1));
rc := soSend(socket:=handle, data:=ADDR(buffer), size:=strLen(str:=teststr1), sent:=tx_len);
ELSE
//everything we consider an error.
soClose(socket:=handle);
socket_created:=FALSE;
await_connection:=FALSE;
END_IF;
END_IF;
#END_IF;
END_IF;
//---------------------------------------------------------------------------
// 3. Data Handling Logic (Non-Blocking)
//---------------------------------------------------------------------------
IF connected THEN
// Receive data
// In non-blocking mode soRecv will NOT wait.
rc := soRecv(socket:=handle, data:=ADDR(buffer), maxsize:=SIZEOF(buffer), size:=rx_len);
IF rc = 1 THEN
// --- Success (Data received or Connection Closed) ---
IF rx_len > 0 THEN
// Data actually arrived
iter := iter + 1;
DebugFmt(message:="\1 - data received len=\4", v1:=iter, v4:=rx_len);
toggle := NOT toggle;
IF iter < 100 THEN
// Echo data back
rc := soSend(socket:=handle, data:=ADDR(buffer), size:=rx_len, sent:=tx_len);
// Make a small pause. This is not necessary, but to avoid flooding the debug output window.
Sleep(delay:=250);
ELSE
DebugMsg(message:="Limit reached. Disconnecting...");
soClose(socket:=handle);
socket_created := FALSE;
connected := FALSE;
END_IF;
ELSE
// rx_len = 0 with rc = 1 implies connection closed gracefully by remote
DebugMsg(message:="Connection closed by remote (0 bytes).");
soClose(socket:=handle);
socket_created := FALSE;
connected := FALSE;
END_IF;
ELSIF rc = -4 THEN
// --- NO DATA AVAILABLE RIGHT NOW ---
// This is the expected behavior in non-blocking mode when the buffer is empty.
// We simply do nothing and loop around.
// (DebugMsg is commented out to avoid flooding the log)
// DebugMsg(message:="No data...");
// IMPORTANT: Since we are not blocking, we must sleep a little to yield CPU time.
// Without this, the loop spins infinitely fast.
Sleep(delay:=100);
ELSE
// --- ERROR ---
// Any other error code < 0 means a real failure
DebugFmt(message:="Receive Error or Connection Lost rc=\1", v1:=rc);
soClose(socket:=handle);
socket_created := FALSE;
connected := FALSE;
END_IF;
IF NOT cnetwork THEN
DebugFmt(message:="Network connection down!");
soClose(socket:=handle);
socket_created := FALSE;
connected := FALSE;
END_IF;
END_IF;
END;
END_PROGRAM;
|