05-14-2015 03:32 PM
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);
05-15-2015 01:58 PM
Hello,
This may be helpful: http://forums.ni.com/t5/LabWindows-CVI/TCP-callback-in-own-thread/m-p/257182/highlight/true#M18179
05-19-2015 08:47 AM - edited 05-19-2015 08:48 AM
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.
05-19-2015 12:06 PM - edited 05-19-2015 12:09 PM
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;
05-19-2015 01:31 PM
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
}
05-19-2015 02:03 PM
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. 🙂