TCP with CAA Net Base Services

dFx
2018-02-26
2022-12-20
  • dFx

    dFx - 2018-02-26

    Hello forum users,

    I had a TCP communication to do with some external Equipment, and it is working well, using the exemple provided in codesys help.
    To make it easy, i'm using chained TCP_Server, TCP_Connection and TCP_Read (not TCP_ReadBuffer) as descripted in help.

    But (yes there is a but) I face a strange behaviour when clients disconnects. TCP_Read block is outputing error before TCP_Connection detects normal/abnormal connection ending.
    Is that normal behaviour ?

    Regards,
    dFx

     
  • Basta87 - 2018-02-27

    dFx hat geschrieben:
    Hello forum users,
    I had a TCP communication to do with some external Equipment, and it is working well, using the exemple provided in codesys help.
    To make it easy, i'm using chained TCP_Server, TCP_Connection and TCP_Read (not TCP_ReadBuffer) as descripted in help.
    But (yes there is a but) I face a strange behaviour when clients disconnects. TCP_Read block is outputing error before TCP_Connection detects normal/abnormal connection ending.
    Is that normal behaviour ?
    Regards,
    dFx

    Hello,

    try it to configurate the xEnable parameter of the TCP_Read Functionblock.

    Example:
    xEnable := sTcpClient.xActive AND NOT sSendMsg.Q AND NOT sTcpWrite.xBusy,

    Best regards

     
  • dFx

    dFx - 2018-02-27

    Thanks for your reply @Basta87

    I don't see any reference about sequencing Read/Write in the NBS manual.

    Anyway, I will try it on site and report the result as soon as possible.

    This is my call order for NBS function blocks:

    TCP_Server([...]);
    TCP_Connection([...]);
    TCP_Read(xEnable := TCP_Connection.xActive,[...]);
    
     
  • Basta87 - 2018-02-27

    Can you post the complete call of the function blocks?

     
  • dFx

    dFx - 2018-03-01

    Hope this will help :

    //----------------------------------------------------------------------------------------
    // Server Call
    //----------------------------------------------------------------------------------------
    TCP.fbTCP_Server_V2(
       Enable            := TRUE, 
       IPAddr            := TCP.IPAddress, 
       Port            := TCP.uiPort, 
       SendDataOrder      := , 
       SendDataBuffer      := TCP.abySendBuffer, 
       SendDataSize      := TCP.udiSendSize, 
       IsActive         => , 
       IsConnected         => , 
       InError            => , 
       ReceivedDataTrigger   => , 
       ReceivedDataBuffer   => , 
       ReceivedDataSize   => , 
       SendDataDone      => ,
       BlockState         => , 
       LastErrorBlock      => , 
       LastError         => );
    
    // TCP server v2 code
    // Manages TCP server with 1 connection with R/W capabilities
    FUNCTION_BLOCK TCPServer_FB
    VAR_INPUT
       Enable : BOOL := FALSE; // Enable TCP server 
       IPAddr : NBS.IP_ADDR := STRUCT(sADDR := '0.0.0.0'); // IP-Address of the TCP server 
       Port : UINT := 0; // port OF the TCP server  
       SendDataOrder : BOOL := FALSE; // Data send order (delayed till connection active)  
       SendDataBuffer : ARRAY [1.._udiMaxComBuffer] OF BYTE; // Byte buffer containing data to be sent  
       SendDataSize : UDINT := 0; // Size of data to be sent  
    END_VAR
    VAR_OUTPUT
       IsActive : BOOL := FALSE; // TCP Server block running  
       IsConnected : BOOL := FALSE; // TCP Client connected  
       InError : BOOL := FALSE; // Indicates that an error has occurred during operation  
       ReceivedDataTrigger : BOOL := FALSE; // Raising edge indicating data received
       ReceivedDataBuffer : ARRAY [1.._udiMaxComBuffer] OF BYTE; // received data buffer  
       ReceivedDataSize : UDINT := 0; // Receive size  
       SendDataDone : BOOL := FALSE; // Data was send correctly
       BlockState : TCPServerStatus_ENUM; // Litteral block status
       LastErrorBlock : TCPServerErrorBlock_ENUM; // Name of block raising last occured error
       LastError : NBS.ERROR; // Last occured error   
    END_VAR
    VAR
       TCP_Server : NBS.TCP_Server; // TCP_Server block
       TCP_Connection : NBS.TCP_Connection; // TCP_Connection block (1 client can connect)
       TCP_Read : NBS.TCP_Read; // TCP_Read block (reads on first connection)
       TCP_Write : NBS.TCP_Write; // TCP_Write block (writes on first connection)
       _Enable : BOOL; // Internal Enable to handle restart on errors
       RWError : BOOL; // Error occured during Read or Write
       ErrorCounter : CAA.COUNT; // Internal error counter for debbug purposes
    END_VAR
    VAR CONSTANT
       _udiMaxComBuffer : UDINT := 1500;
    END_VAR
    // Auto recovery from RW error
    IF Enable AND NOT _Enable THEN
       _Enable := TRUE;
    ELSE
       IF NOT Enable THEN
          _Enable := FALSE;
          ErrorCounter := 0;
          LastErrorBlock := TCPServerErrorBlock_ENUM.NONE;
          LastError := NBS.ERROR.NO_ERROR;
       END_IF
    END_IF
    // read/Write error on previous scan
    RWError := (
                TCP_Read.xError AND
                (TCP_Read.eError = NBS.ERROR.TCP_RECEIVE_ERROR)
             ) OR 
             (
                TCP_Write.xError AND
                (
                   (TCP_Read.eError = NBS.ERROR.TCP_SEND_ERROR) OR
                   (TCP_Read.eError = NBS.ERROR.TIME_OUT)
                )
             );
    // RW Error management
    IF RWError AND Enable THEN
       // Something was wrong with read/write
       // Reset full block
       _Enable := FALSE;
    END_IF
    // TCP Server
    // This block handle the socket open/close
    TCP_Server(
       xEnable:= _Enable, 
       ipAddr:= IPAddr, 
       uiPort:= Port,
       xDone=> , 
       xBusy=> , 
       xError=> ,  
       eError=> , 
       hServer=> 
    );
    // TCP Connection
    // This block handle client connection
    // If connection is properly closed, xDone is set
    // Then restart connection block to wait for another client
    // RW Errors may need connection to be restart (client disconnected improperly is not detected from TCP_Connection block
    TCP_Connection(
       xEnable:= TCP_Server.xBusy AND (NOT TCP_Connection.xDone), 
       xDone=> , 
       xBusy=> , 
       xError=> , 
       hServer:= TCP_Server.hServer, 
       eError=> , 
       xActive=> , 
       hConnection=> 
    );
    // TCP Read
    // This block allows to listen for data on opened socket
    TCP_Read(
       xEnable:= TCP_Connection.xActive, 
       xDone=> , 
       xBusy=> , 
       xError=> , 
       hConnection:= TCP_Connection.hConnection, 
       szSize:= _udiMaxComBuffer, 
       pData:= ADR(ReceivedDataBuffer), 
       eError=> , 
       xReady=> ReceivedDataTrigger, 
       szCount=> ReceivedDataSize
    );
    // TCP Write
    // This block allows to send data on opened socket
    TCP_Write(
       xExecute:= SendDataOrder, 
       udiTimeOut:= 500000, // µs -> 500ms
       hConnection:= TCP_Connection.hConnection, 
       szSize:= SendDataSize, 
       pData:= ADR(SendDataBuffer), 
       xDone=> SendDataDone, 
       xBusy=> , 
       xError=> , 
       eError=> 
    );
    // Generic error management
    // Save last error 
    IF TCP_Server.xError THEN
       ErrorCounter := ErrorCounter + 1;
       LastError := TCP_Server.eError;
       LastErrorBlock := TCPServerErrorBlock_ENUM.SERVER;
    ELSE
       IF TCP_Connection.xError THEN
          ErrorCounter := ErrorCounter + 1;
          LastError := TCP_Connection.eError;
          LastErrorBlock := TCPServerErrorBlock_ENUM.CONNECTION;
       ELSE
          IF TCP_Read.xError THEN
             ErrorCounter := ErrorCounter + 1;
             LastError := TCP_Read.eError;
             LastErrorBlock := TCPServerErrorBlock_ENUM.READ;
          ELSE
             IF TCP_Write.xError THEN
                ErrorCounter := ErrorCounter + 1;
                LastError := TCP_Write.eError;
                LastErrorBlock := TCPServerErrorBlock_ENUM.WRITE;
             END_IF
          END_IF
       END_IF
    END_IF
    // outputs
    IsConnected := TCP_Connection.xActive;
    IsActive := TCP_Server.xBusy;
    InError := TCP_Server.xError;
    // Block status
    IF NOT _Enable THEN
       BlockState := TCPServerStatus_ENUM.DISABLED;
    ELSE
       IF _Enable AND NOT IsActive THEN
          BlockState := TCPServerStatus_ENUM.SERVER_INIT;
       ELSE
          IF IsActive AND NOT IsConnected THEN
             BlockState := TCPServerStatus_ENUM.WAIT_CONNECTION;
          ELSE
             IF IsConnected AND ReceivedDataTrigger  THEN
                BlockState := TCPServerStatus_ENUM.DATA_RECEIVED;
             ELSE
                IF IsConnected AND NOT SendDataOrder THEN
                   BlockState := TCPServerStatus_ENUM.CLIENT_CONNECTED;
                ELSE
                   IF IsConnected AND SendDataOrder THEN
                      BlockState := TCPServerStatus_ENUM.DATA_WRITTEN;
                   END_IF
                END_IF
             END_IF
          END_IF
       END_IF
    END_IF
    

    EDIT : I also face another linked problem when using a switch : When my PC is connected as TCP client via a switch, and I disconnect my cable from the switch, there is no TCP error outputed. Even writing ends ok ...
    Writing isn't the usual method to detect a down link on a tcp connection ?

    To face this, I ping my Partner which is not very reliable (need to know Partner IP address). I do it outside the block, and then I reset Enable flag.

     
  • Anonymous - 2018-03-08

    Originally created by: luciano.assirelli

    dFx hat geschrieben:
    Hello forum users,
    I had a TCP communication to do with some external Equipment, and it is working well, using the exemple provided in codesys help.
    To make it easy, i'm using chained TCP_Server, TCP_Connection and TCP_Read (not TCP_ReadBuffer) as descripted in help.
    But (yes there is a but) I face a strange behaviour when clients disconnects. TCP_Read block is outputing error before TCP_Connection detects normal/abnormal connection ending.
    Is that normal behaviour ?
    Regards,
    dFx

    Hi all, and thanks for existing.
    I'm apparently facing the same problem. I need to implement a server accepting a single client.
    To simplify things at the beginning, I implemented a simple echo server, which TCP_Writes the same
    buffer contents, as soon as some contents arrives thru TCP_Read. And everything looks ok:
    once the connection is established, echo is working.
    Things get complcated when I "close" the connection, either from the client or server side: before reconnect,
    the connection handle is properly at zero; after reconnect, the connection handle provided by the
    TCP_Connection function block appears to be the same as before disconnect; and this is may not
    be a problem, but the TCP_Read executed on that handle gives a permanent TCP_RECEIVE_ERROR (6012).
    I have no other options than disable/enable the TCP_Server fb, but nothing changes. By the way, even if
    this worked, I suppose that it would be not applicable if one had to implement a multiple client solution.
    The only operation that really reset things is a controller warm reset.

    Just to put some other meat on the grill... I have been using a proprietary TCP server implementation
    from ELAU (now Schneider Electric), and it gave me the option to filter incoming connection based on the IP address.
    How could this be done with CAA netbase? Or, more simply spelled: how can I obtain the IP address of an incoming connection?

    I will really appreciate any comments!

     
  • tom vg - 2018-03-12

    hi Everyone !

    I'm implementing a TCP read write but is quit hard to get it operational. I'm working with Somachine (Codesys environment for Schneider Electric PLC's), In the example from the CAA Net base services is a 'Createmessage fb. I'm not retrieving this in the libs that i have installed. Can someone tell me where to find it?

    gr Tom

    IMG: Knipsel.PNG

     
  • dFx

    dFx - 2018-09-28

    Create message is an exemple of user function that transfert the data into your buffer.

     
  • matyzs.v - 2018-12-10

    luciano.assirelli hat geschrieben:
    Things get complcated when I "close" the connection, either from the client or server side: before reconnect,
    the connection handle is properly at zero; after reconnect, the connection handle provided by the
    TCP_Connection function block appears to be the same as before disconnect; and this is may not
    be a problem, but the TCP_Read executed on that handle gives a permanent TCP_RECEIVE_ERROR (6012).
    I have no other options than disable/enable the TCP_Server fb, but nothing changes. By the way, even if
    this worked, I suppose that it would be not applicable if one had to implement a multiple client solution.
    The only operation that really reset things is a controller warm reset.

    HI,
    I have the same problem. Did you find some solution please?

     
  • dFx

    dFx - 2018-12-11

    dFx hat geschrieben:
    To face this, I ping my Partner which is not very reliable (need to know Partner IP address). I do it outside the block, and then I reset Enable flag.

     
  • Anonymous - 2019-07-18

    Originally created by: rettore84

    luciano.assirelli hat geschrieben:
    Hi all, and thanks for existing.
    I'm apparently facing the same problem. I need to implement a server accepting a single client.
    To simplify things at the beginning, I implemented a simple echo server, which TCP_Writes the same
    buffer contents, as soon as some contents arrives thru TCP_Read. And everything looks ok:
    once the connection is established, echo is working.
    Things get complcated when I "close" the connection, either from the client or server side: before reconnect,
    the connection handle is properly at zero; after reconnect, the connection handle provided by the
    TCP_Connection function block appears to be the same as before disconnect; and this is may not
    be a problem, but the TCP_Read executed on that handle gives a permanent TCP_RECEIVE_ERROR (6012).
    I have no other options than disable/enable the TCP_Server fb, but nothing changes. By the way, even if
    this worked, I suppose that it would be not applicable if one had to implement a multiple client solution.
    The only operation that really reset things is a controller warm reset.
    Just to put some other meat on the grill... I have been using a proprietary TCP server implementation
    from ELAU (now Schneider Electric), and it gave me the option to filter incoming connection based on the IP address.
    How could this be done with CAA netbase? Or, more simply spelled: how can I obtain the IP address of an incoming connection?
    I will really appreciate any comments!

    Not sure if someone is still having issues with this and getting error 6012. This was happening whenever the client was disconnecting from Codesys Server. I had always to do a warm/cold restart. My solution was not to use the Handler FB that comes in the example project. Take all the logic that it is inside the Handler FB, and put it outside. You don't need the Handler FB. It will work.

     
  • arihje - 2019-07-31

    I also have this problem. I am using code similar to what dFx posted, not like the example with the
    Handler FB, and for a single client it works fine resetting the server each time the client disconnects.
    Luckily, I only need a server for one client in my current application.

    I cannot find anything documenting what causes a TCP_RECEIVE_ERROR (6012).
    It surely does not mean that a message was not received correctly, because the messages always come
    through correctly.
    The error is raised if I physically disconnect the server from the network, but also when the client closes
    the connection correctly by setting the TCP FIN flag. I have checked with Wireshark that a full normal
    handshake for closing the connection is performed, but still get the error.

     
  • AndreasR - 2019-08-09

    matyzs.v hat geschrieben:
    HI,
    I have the same problem. Did you find some solution please?

    I have the same problem as well. Did anyone find some solution please?

     
  • maoravni - 2020-05-05

    I have a project that was compiled on an old version of codesys. I recently upgraded my entire project to use all latest library and bumped into the same error (6012).

    My problem is even worse, as on newer versions of the library (3.5.12.0/3.5.15.0) I don't even receive data from the network.

    My temporary solution was to downgrade only the NBS library to an older version - that seemed to solve my issue. I've gone back to version 3.5.6.0 of the library.

    Can anyone from codesys comment on this?

     

    Last edit: maoravni 2020-05-05
  • ddenton - 2021-05-14

    Hey guys!

    I've been struggling with this too. To defeat the Tcp_Read/Write error, I cleared my Tx and Rx (send and receive) buffers every cycle. I guess this makes sense, since overwriting a larger message with a small one without clearing first would be...messy.

    I have large sized arrays (65,500 bytes), so I had to use the following method to zero out all my arrays without hitting a watchdog timer exception:

    Mem.MemFill(pMemoryBlock:= ADR(arrRx),uiLength := SIZEOF(arrRx),byFillValue := 0);
    Mem.MemFill(pMemoryBlock:= ADR(arrTx),uiLength := SIZEOF(arrTx),byFillValue := 0);

    Hope this helps!

     
    • masmith1553 - 2022-12-20

      Is there a was to flush the TCP port before a TCP Read?

       

      Last edit: masmith1553 2022-12-20

Log in to post a comment.