Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

TCP Socket communication

ZKid
2018-11-08
2021-08-26
  • ZKid

    ZKid - 2018-11-08

    I am writing a client reading data from another device using TCP socket (STREAM type). The Codesys program runs in PLC PiXtend (Raspberry Pi based PLC). The socket is created and connected successfully. The function sending data (SysSockSend) returns success and 81 bytes of sent data. Subsequent call to of SysSockRecv() return RTS_IEC_RESULT = 529 (Socket has been gracefully closed. No more sends/receives allowed.). What is wrong in my code???

    VAR
       sDataSend   :STRING;
       sDataRecv   :STRING(4095);
       // IP Socket support
       addressPointer    :POINTER TO SOCKADDRESS;
        address           :SOCKADDRESS;
        ip                  :STRING(16) := '10.0.0.138';
        port               :WORD := 1367;
       iecCreateResult      :RTS_IEC_RESULT;
       iecConnectResult   :RTS_IEC_RESULT;
       iecSendResult        :RTS_IEC_RESULT;
       iecRecvResult        :RTS_IEC_RESULT;
       iecResultClose      :RTS_IEC_RESULT;
       iecResultShutdown   :RTS_IEC_RESULT;
       
        iecSocketId           :RTS_IEC_HANDLE := 0;
       xiSentBytes           :__XINT;
       xiRecvBytes           :__XINT;
    END_VAR
      ...
       // Create address
       SysSockInetAddr(ip, ADR(address.sin_addr));
       address.sin_family := SOCKET_AF_INET;
       address.sin_port := SysSockHtons(port);
       addressPointer := ADR(address);
       
       // Create socket
       iecSocketId := SysSockCreate(SOCKET_AF_INET, SOCKET_STREAM, 0, ADR(iecCreateResult));
       IF iecSocketId = RTS_INVALID_HANDLE THEN
         // Socket creation failed
         ...
       ELSE
          // Connect socket
          iecConnectResult := SysSockConnect(iecSocketId, addressPointer, SIZEOF(address));
          // Send data    
           xiSentBytes := SysSockSend(iecSocketId, ADR(sDataSend), SIZEOF(sDataSend), 1, ADR(iecSendResult));
          IF xiSentBytes = 0 THEN
             // ... failed
             ...
          ELSE
             // Shudown sending data
             iecResultShutdown := SysSockShutdown(iecSocketId, SOCKET_SD_SEND);
             
             // Receive data
             // THIS CALL FAILS: iecRecvResult = 529
             xiRecvBytes := SysSockRecv(iecSocketId, ADR(sDataRecv), SIZEOF(sDataRecv), 1, ADR(iecRecvResult));
             IF xiRecvBytes = 0 THEN
                // ... failed
                ...
             ELSE
                // ... success: process result
                ...
             END_IF
          END_IF
       END_IF
       
       // Close socket after sending/getting data 
       iecResultClose := SysSockClose(iecSocketId);
    END_IF 
    
     
  • ZKid

    ZKid - 2018-11-08

    Additional information:
    When I try to use simple Python script for same operation, it works well. In Codesys I only created same thing using another programming language.

    import socket
    from datetime import datetime
    def netcat(host, port, content):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((host, int(port)))
        s.sendall(content.encode())
        s.shutdown(socket.SHUT_WR)
        result = ""
        while True:
           data = s.recv(4096)
           if not data:
                break
           result = result + data.decode()
        s.close()
        print(result)
    HOST="10.0.0.138"
    PORT=1367
    netcat(HOST, PORT, "{\"id\":1,\"method\":\"sensor-list\",\"params\":[]}")
    

    The Python script reads the result in a loop, my Codesys program reads it in 1 call without the loop. But an expected response is so short, that it must easily fit into a buffer in 1 call. Anyway I tried to read it in a loop too, but the result was 100% same. So the missing loops doesn't seem to be a reason of problem.

     
  • ZKid

    ZKid - 2018-11-12

    No answer...… Hmmm..... I tried to rewrite using library Network/CAA Net Base Service 3.5.13.0. The code is below. Connection is OK. Sending data seems to send data successfully. Reading data... after some timeout (10s): "TCP_RECEIVE_ERROR" (code 6012).

    PROGRAM POU_TCP_IFACE
    VAR_INPUT
       xSend      :BOOL := FALSE;
       sDataSend      :STRING;
    END_VAR
    VAR
       strIPAddr      : NBS.IP_ADDR;
       uiPort      : UINT;
       
       nwClient       : NBS.TCP_Client;   
       nwTCPWrite    : NBS.TCP_Write;
       nwTCPRead    : NBS.TCP_Read;
       
       sDataRecv      :STRING := '';            // Final result value
       sDataBuff       :STRING(4095);            // Buffer for composing result string
       xBusy      :BOOL := FALSE;
       xSuccess      :BOOL := FALSE;
       xError      :BOOL := FALSE;
       iErrorCode      :INT := 0;   
       
       iStep         :INT := 0;
    END_VAR
    // If the send command is active and the system is busy, ignore the send request 
    IF xSend THEN
       IF xBusy THEN
           xSend := FALSE;
       END_IF   
    END_IF
    IF xSend THEN
       strIPAddr.sAddr := '10.0.0.138';
       uiPort := 1367;
       
       // Init status variables
       xSend := FALSE;
       xBusy := TRUE;
       xSuccess := FALSE;
       xError := FALSE;
       iErrorCode := 0;   
       sDataRecv := '';
       iStep := 0;
       nwClient(xEnable := FALSE);
       nwTCPRead(xEnable := FALSE);
       nwTCPWrite(xExecute := FALSE);
    END_IF 
    IF xBusy THEN
       CASE iStep OF
          0: // Prepare connection
             nwClient(xEnable := TRUE, ipAddr := strIPAddr, uiPort := uiPort, udiTimeOut := 10000000);   
             IF nwClient.xActive THEN
                // Successfuly connected
                iStep := 1;            // JUMP TO SEND DATA
             ELSIF nwClient.xError THEN
                // Error shile connection creation
                nwClient.xEnable := FALSE;
                xError := TRUE;
                iErrorCode := 1;      // Error creating connection: reason - 1
                xBusy := FALSE;
             ELSIF NOT nwClient.xActive AND NOT nwClient.xError AND nwClient.xDone THEN
                nwClient.xEnable := FALSE;
                xError := TRUE;      
                iErrorCode := 2;      // Error creating connection: reason - 2
                xBusy := FALSE;
             END_IF
             
          1: // Send data
             IF nwTCPWrite.xBusy = FALSE AND nwTCPWrite.xError = FALSE AND nwTCPWrite.xDone = FALSE THEN
                nwTCPWrite.hConnection := nwClient.hConnection;
                nwTCPWrite.pData := ADR(sDataSend);
                nwTCPWrite.szSize := INT_TO_UDINT(LEN(sDataSend));
                nwTCPWrite.udiTimeOut := 10000000;
                
                nwTCPWrite.xExecute := TRUE;
             END_IF
          
             IF nwTCPWrite.xDone = TRUE THEN
                // Data IS SENT!
                nwTCPWrite.xExecute := FALSE;
                iStep := 2;         // JUMP TO READ
             ELSIF nwTCPWrite.xError THEN
                // Send data ERROR
                nwTCPWrite.xExecute := FALSE;
                xError := TRUE;      
                iErrorCode := 3;      // Data write error
                xBusy := FALSE;
             ELSE
                nwTCPWrite();   
             END_IF   
          
          2: // Read data
             nwTCPRead(xEnable := TRUE,
                        hConnection := nwClient.hConnection,
                      szSize := SIZEOF(sDataBuff),
                      pData  := ADR(sDataBuff));
                      
             IF nwTCPRead.xReady AND nwTCPRead.eError = 0 THEN
                IF nwTCPRead.szCount > 0 THEN
                   sDataRecv := sDataBuff;
                ELSE
                   sDataRecv := '';
                END_IF
                xError := FALSE;
                iErrorCode := 0;      // No error
                xSuccess := TRUE; 
                xBusy := FALSE;
             ELSIF nwTCPRead.eError <> 0 THEN
                xError := TRUE;
                iErrorCode := 4;      // Data read error 
                xBusy := FALSE;
             END_IF
       END_CASE;
    END_IF
    
     
  • ZKid

    ZKid - 2018-11-14

    I tried to run the program under SIMULATION mode on my development PC. Simultaneously I ran also Wireshark to capture network communication between simu-PLC and the second device. Unfortunately under simulation mode it doesn't work. No socket is opened The function returns zero socket handle but the result error code is also zero. Incredible! Therefore I cannot capture network traffic between PLC and controlled device. Crazy world!!!!!!

     
  • pato

    pato - 2021-08-26

    Hey zkid, I am sorry your question was not answered and I hope you found the solution.
    If you don't mind since you seem to have a good understanding of the TCP sockets. I am working with modbus TCP and it uses the MainTask as its bus cycle task.
    This task has a very small code but when checking its max cycle time, sometimes it reaches to 21ms!
    So my theory is, do we open and close TCP connection in each modbus cycle transmission? As that would explain reaching 21ms sometimes, right?.
    But I dont know how or where to check that.
    Is the TCP socket always on or always off? (once the connection is established)

     

Log in to post a comment.