Skip to content

TCP/IP Packet Loss

I am currently using a TCP/IP socket to communicate with a third party controller. The communication is working quite well at this point for single transmissions. The controller that I am communicating with works on a Receive Command/Respond with data type system. One command results in one response.

The problem I am having is that a few commands have a 2 message response to 1 command. So I a two packet response and the packets are really close together on the order of less than 10ms between. The TLS module is receiving both packets, I tell this by the data byte count, but it appears that the packets are being compacted together.

For instance:

Send Message: 1 byte packet asking for measurements.

Response: Packet 1 of 73 bytes and Packet 2 of 1621 bytes is sent to the PLCNext.

The TCP Receive functions show 1694 bytes received. meaning that both packets are received.

Then this goes into a BUF_TO_STRING block to convert the BYTES to strings for parsing.

The BUF_TO_STRING is only reporting (capturing) the first 73 byte message. The rest of the message is lost.


Anyone else ever seen this or fixed this before?


Comments

  • Hi!

    Are you sure that BUF_TO_STRING can correctly convert tcp buffer to string format dedicated with PLC string type, e.g. 4 byte header and zero trerminating character? You can lost part of message just because it truncated according to internal convertion rules of BUF_TO_STRING FB. Take a look closely to this situation.

    Best regards.

  • Oleksandr - Very good question and Yes. I have other message packets that are much larger than these ones but they come in one message.

    The only problem I seem to have is when there is a two packets sent one right after the other.

  • edited July 2023

    Oleksandr might be on to something.

    What are the last two bytes in message 1, the 73 byte message, as transmitted by the third-party controller?

  • Martin -

    The last byte is a terminator byte of 00.


    73 Byte string:

    Line STRING Conversion

    0000 30 30 37 33 31 32 30 31 30 30 32 30 20 20 20 20  007312010020   

    0010 30 30 20 20 30 30 32 30 30 31 30 30 30 30 30 30  00 002001000000

    0020 30 30 30 31 32 30 31 38 2D 31 31 2D 30 32 3A 32  00012018-11-02:2

    0030 30 3A 30 33 3A 30 34 30 30 30 30 30 30 36 30 30  0:03:04000000600

    0040 31 30 30 30 30 31 30 30 30 00           100001000 



    Start of the next byte set:

    0000 31 36 32 32 31 32 30 32 30 30 31 30 20 20 20 20  162212020010   

    0010 30 30 20 20 30 30 32 30 30 32 30 30 30 30 30 30  00 002002000000

    0020 30 30 30 31 30 30 30 30 30 36 39 33 30 32 30 30  0001000006930200

    0030 30 31 31 30 32 30 30 30 30 30 30 30 30 30 30 30  0110200000000000

  • edited July 2023

    I don't think the documentation on the BUF_TO_STRING FB is especially clear on this point, but I think the FB will stop processing the buffer when it gets to a null byte, as Oleksandr has suggested.

    You can check that the second message is in the buffer by calling a second instance of the BUF_TO_STRING FB with the same buffer, but with the BUFF_OFFS input parameter set to 74. That should decode the second message.

  • edited July 2023

    If the null character turns out to be the problem, then you could do a check on the size of the string that is returned by the BUF_TO_STRING FB, and if it's shorter than you're expecting you could call a second instance of the FB, using the BUFF_OFFS parameter to decode the next message in the buffer.

  • I've confirmed that it's the null character that's the problem in this case.

    In this example, I'm converting the following buffer to a STRING:

    The first BUF_TO_STRING instance doesn't convert the whole buffer; it stops when it sees the first null character.

    The second BUF_TO_STRING instance starts where the first instance stopped, and converts the remainder of the message to a STRING.

    If there are even more messages in the buffer, this process could be repeated until all the messages have been extracted from the buffer.

  • Martin - This solution is something I already developed on my own this last week. However, how would I arrange this if I didn't know how many packets where going to show up?

    In the example above and other data there is a header that shows what is coming, see attached picture, and how many messages are in the package. do you have a recommendation on how to setup this communication to be flexible with an unknown number of packets (messages).

    Also I do have some packages or most of my packages that don't have this total number of messages, but I do have a message size. My solution shown below will handle 2 message packets, however, some of my stuff will send 3 to 6 messages.


    So how would I make this work with 1 or 2 BUF_TO_STRING block and feed it different parameters each time with the same buffer or is that even possible?

  • One way to do what you want is to use a step sequencer to "loop" through a buffer and extract multiple messages from that buffer using a single BUF_TO_STRING FB instance. Implementing a step sequencer is far easier in Structured Text than in Function Block diagram.

    Here is an example:

    // In this example:
    //
    // - The arrival of a new message is simulated
    //   using a boolean variable called "Start".
    //
    // - The total number of characters is simulated using an
    //   integer called "TotalChars".
    //
    // In a real application, these two variables
    // would be set by a TLS_RECEIVE function block instance.
    //
    // It is assumed that the buffer contains one or more "messages",
    // with each message terminated by a single null character.
    //
    // This example handles a maximum of 10 messages in a single buffer.
    
    CASE Step OF
    
        0 :  // Waiting for a new message to be sent from the partner.
            IF Start AND NOT StartMemory THEN
                Offset := 0;
                RemainingChars := TotalChars;
                MessageIndex := 0;
                Step := 1;
            END_IF
        
        1:  // Wait for the FB to be done.
            IF Done THEN
                // Put the message into the array
                Messages[MessageIndex] := Message;
                
                // Increment pointers
                MessageLength := LEN(Message);
                Offset := Offset + MessageLength + 1;
                RemainingChars := TotalChars - Offset;
                MessageIndex := MessageIndex + 1;
    
                // Check if this is the last message
                IF Offset >= TotalChars OR MessageIndex > 9 THEN
                    Step := 0;
                ELSE
                    // Continue
                    Step := 2;
                END_IF
            END_IF
    
            IF Error THEN
                Step := 0;
            END_IF
            
        2:  // This step simply toggles the Request flag.
            Step := 1;
    
        ELSE  // We should never reach here
            Step := 0;
    
    END_CASE
    
    // Outputs from the Step Sequencer
    StartMemory := Start;
    Request := (Step = 1);
    
    // Call the BUF_TO_STRING function block instance
    BUF_TO_STRING1(
        REQ := Request,
        BUF_FORMAT := FALSE,
        BUF_OFFS := Offset,
        BUF_CNT := RemainingChars,
        DONE => Done,
        ERROR => Error,
        STATUS => Status,
        BUFFER := Buffer,
        DST := Message
    );
    
    

    The variable table for the above code is:

    This code could be put into a Function Block, if that makes sense for you.

    I understand that you are working with Dan from your local Phoenix Contact office. I will send Dan the PLCnext Engineer project containing this code, and he will be able to help you to adapt it for your application if needed.

  • The example I gave solves the problem of a single buffer containing multiple messages separated by a null character.

    If the buffer contains multiple messages but without null characters separating them, then you could just put the entire buffer into a single string, check the string to see how many messages there are, and then (for example) split the string at the required location(s). Or, if you know which byte in the buffer contains the number of messages, you could read that byte before processing the buffer, and adapt the parameters on the BUF_TO_STRING FB in the above step sequencer (somehow). Dan will be able to discuss the alternatives with you.

  • Martin -

    Thank you for all the help. I was able to get this working after some frustration and great support from Phoenix Engineering.

Sign In or Register to comment.