Парсинг полных сообщений от последовательного порта

Я пытаюсь прочитать полные сообщения от моего GPS через последовательный порт.

Сообщение, которое я ищу, начинается с:

0xB5 0x62 0x02 0x13

Так я читаю с последовательного порта вот так

while (running !=0)
{

int n = read (fd, input_buffer, sizeof input_buffer); 


for (int i=0; i<BUFFER_SIZE; i++)
{   



if (input_buffer[i]==0xB5  && input_buffer[i+1]== 0x62 && input_buffer[i+2]== 0x02 && input_buffer[i+3]== 0x13    && i<(BUFFER_SIZE-1) )
     { 

            // process the message.
     }

}

Проблема в том, что мне нужно получить полное сообщение. Половина сообщения может находиться в буфере за одну итерацию. А вторая половина может прийти в сообщение на следующей итерации.

Кто-то предложил освободить буфер от полного сообщения. И затем я перемещаю оставшиеся данные в буфере в начало буфера.

Как мне сделать это или каким-либо другим способом, чтобы убедиться, что я получаю каждое полное выбранное сообщение, которое приходит?

редактировать//

Я хочу определенный класс и ID. Но я также могу прочитать в длину

2 ответа

Решение

Вы можете разбить прочитанное на три части. Найти начало сообщения. Тогда получите ДЛИНУ. Тогда прочитайте остальную часть сообщения.

// Should probably clear these in case data left over from a previous read
input_buffer[0] = input_buffer[1] = 0;

// First make sure first char is 0xB5
do {
    n = read(fd, input_buffer, 1); 
} while (0xB5 != input_buffer[0]);

// Check for 2nd sync char
n = read(fd, &input_buffer[1], 1);

if (input_buffer[1] != 0x62) {
     // Error
     return;
}

// Read up to LENGTH
n = read(fd, &input_buffer[2], 4); 

// Parse length
//int length = *((int *)&input_buffer[4]);
// Since I don't know what size an int is on your system, this way is better
int length = input_buffer[4] | (input_buffer[5] << 8);

// Read rest of message
n = read(fd, &input_buffer[6], length);

// input_buffer should now have a complete message

Вы должны добавить проверку ошибок...

Чтобы минимизировать накладные расходы на выполнение многих системных вызовов read() с небольшим количеством байтов, используйте в своем коде промежуточный буфер.
Операторы read() должны находиться в режиме блокировки, чтобы избежать кода возврата нулевых байтов.

#define BLEN    1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;

static unsigned char getbyte(void)
{
    if ((rp - rbuf) >= bufcnt) {
        /* buffer needs refill */
        bufcnt = read(fd, rbuf, BLEN);
        if (bufcnt <= 0) {
            /* report error, then abort */
        }
        rp = rbuf;
    }
    return *rp++;
}

Для правильного кода инициализации termios для последовательного терминала, см. Этот ответ. Вам следует увеличить параметр VMIN до значения, близкого к значению BLEN.

Теперь вы можете легко получить доступ к полученным данным по байтам за один раз с минимальными потерями производительности.

#define MLEN    1024  /* choose appropriate value for message protocol */
unsigned char mesg[MLEN];

while (1) {
    while (getbyte() != 0xB5)
        /* hunt for 1st sync */ ;
retry_sync:
    if ((sync = getbyte()) != 0x62) {
        if (sync == 0xB5)
            goto retry_sync;
        else    
            continue;    /* restart sync hunt */
    }

    class = getbyte();
    id = getbyte();

    length = getbyte();
    length += getbyte() << 8;

    if (length > MLEN) {
        /* report error, then restart sync hunt */
        continue;
    }
    for (i = 0; i < length; i++) {
        mesg[i] = getbyte();
        /* accumulate checksum */
    }

    chka = getbyte();
    chkb = getbyte();
    if ( /* valid checksum */ ) 
        break;    /* verified message */

    /* report error, and restart sync hunt */
}

/* process the message */
switch (class) {
case 0x02:
    if (id == 0x13) {
        ... 
...
Другие вопросы по тегам