LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

TCP Callback on Newline Character

I have a TCP connection to a device that sends its short message (< 100 characters) in chunks.  Unfortunately these chunks seem to be random.  I am having some difficulty tokenizing the data and getting it into five floats.  Is there any way I can make this TCP callback fire only when a newline character is received, or a better method of putting the message together?  I'm going to be dealing with much larger TCP messages for other devices, so it will be good to get this issue resolved on a small scale problem.

 

For whatever reason, my logic is allowing incomplete messages to be tokenized.  The message leads with BARO and ends with \r\n.

 

                    receiveBuf[dataSize] = '\0';
                    if (strstr(receiveBuf,"\n") == NULL)
                        strcpy(Vaisala.Message, receiveBuf);
                    else
                    {
                        if ((strlen(Vaisala.Message) + strlen(receiveBuf) < 100) || (strstr(receiveBuf,"BARO") == NULL))
                        {
                            strcat(Vaisala.Message, receiveBuf);
                            Vaisala.Message[strlen(Vaisala.Message) - 2] = '\0';
                            token = strtok(Vaisala.Message, "\t");
                            token = strtok(NULL, "\t");                              
                            Vaisala.Barometric = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.DryBulb = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.WetBulb = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.Dewpoint = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.RH = atof(token);
                        }
                        else
                            memset(Vaisala.Message, '\0', 100);

Programming Data Acquisition and Control in Measurement Studio and Labwindows/CVI
0 Kudos
Message 1 of 6
(4,995 Views)

Michael,  Can you edit your post to provide 6 - 10  typical lines of the input you are seeing?  Seeing what you are seeing may help to see problems with the way it is being parsed.

0 Kudos
Message 3 of 6
(4,913 Views)

I found a workaround that I don't like. 

 

1. If the message starts with B, start a new string.

2. If the message does not have \n, concatenate the message.

3. If the message has a \n, parse the data.

 

Seems to work well but I won't always have such fixed strings so I'd like a callback that fires when the \n is received.

 

 

         case TCP_DATAREADY:
            if ((dataSize = ClientTCPRead (VaisalaTCPConnectionHandle, receiveBuf, dataSize, 1000)) < 0)
                {
                // Error
                }
            else
                {
                    receiveBuf[dataSize] = '\0';
                    if ((strstr(receiveBuf,"\n") == NULL) && (receiveBuf[0] == 'B'))
                        strcpy(Vaisala.Message, receiveBuf);
                    else if (strstr(receiveBuf,"\n") == NULL)
                        strcat(Vaisala.Message, receiveBuf);             
                    else
                    {
                        if ((strlen(Vaisala.Message) + strlen(receiveBuf) < 100) || (strstr(receiveBuf,"BARO") == NULL))
                        {
                            strcat(Vaisala.Message, receiveBuf);
                            Vaisala.Message[strlen(Vaisala.Message) - 2] = '\0';
                            token = strtok(Vaisala.Message, "\t");
                            token = strtok(NULL, "\t");                              
                            Vaisala.Barometric = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.RH = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.Dewpoint = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.DryBulb = atof(token);
                            token = strtok(NULL, "\t");  
                            token = strtok(NULL, "\t");                              
                            Vaisala.WetBulb = atof(token);
                        }
                        else
                            memset(Vaisala.Message, '\0', 100);
                    }
                        
                // Process data here
                }
            break;

 

Serial.jpg

Programming Data Acquisition and Control in Measurement Studio and Labwindows/CVI
0 Kudos
Message 4 of 6
(4,904 Views)

I write my recv() function so that it searches for a unique termination character, similar to your "\n", and I imagine you can do the same using ClientTCPRead(); : (following is an adaptation from function panel sample code)

char * buffer;
int messageSize;
int bytesToRead;
int bytesRead;

/* Find messageSize and allocate buffer appropriately... */

bytesToRead = messageSize;

while (bytesToRead > 0)

{

    bytesRead = ClientTCPRead (connectionHandle, &buffer[messageSize - bytesToRead], bytesToRead, 0);

    bytesToRead -= bytesRead;

}

// test

if((str[0]=='B')&&(str[0]=='A')&&(str[0]=='R')&&(str[0]=='O')&&(strstr(str, "\n"))) parse(buffer, data);

 

 

Where parse() and data are defined as:

 

typedef struct {
    char type[20];
    float a[5];//number of numeric fields in your example
}DATA;

DATA;

int parse(char *str, DATA *data)
{
    char *tok;
    char *dup;
    char *dummy;
    int i=0;
    
    if((str[0]=='B')&&(str[1]=='A')&&(str[2]=='R')&&(str[3]=='O')&&(strstr(str, "\n")))
    {
        //parse
        tok = strtok(str, " \n\r\t");
        strcpy(data->type, tok);
        while(tok)
        {
            tok = strtok(NULL, " \n\r\t");
            if(tok)
            {
                data->a[i] = strtod(tok, &dummy);
            }
            i++;
        }
        return 1;//parsed
    }
    return 0;//nothing to parse
}

 

0 Kudos
Message 5 of 6
(4,897 Views)

This is very similar to my approach.  I guess this is how it has to be done in C.  I'll admit I've been spoiled with Visual Basic .NET for over 10 years.  🙂

Programming Data Acquisition and Control in Measurement Studio and Labwindows/CVI
0 Kudos
Message 6 of 6
(4,890 Views)