Как использовать структуру с динамически изменяющимся размером данных? - PullRequest
0 голосов
/ 25 сентября 2018

Вопрос только для C, C ++ и векторы не решают проблему.

У меня есть такая структура:

typedef __packed struct Packet_s
{
  U8  head;
  U16 len;
  U32 id;
  U8  data;
  U8  end;
  U16 crc;
} Packet_t, *Packet_p;

( EDIT : U8 - это uint8_t (без знака)char) и т. д.)

Например, я получил пакет (в шестнадцатеричном формате):

24 0B 00 07 00 00 00 AA 0D 16 1C

где

голова = 0x24

len = 0x0B 0x00

id = 0x07 0x00 0x00 0x00

данные = 0xAA

end = 0x0D

crc = 0x16 0x1C

Я могу скопировать его из входного буфера вот так

U8 Buffer[SIZE]; // receives all bytes here
memcpy(&Packet, &Buffer, buffer_len);

и работать с ним дальше.

Можно ли использовать мою структуру, если поле "DATA" длиннее 1 байта?

Как я могу обработать что-то подобное?

24 0F 00 07 00 00 00 AA BB CC DD EE 0D BD 66

Длина пакета будет всегда известна (2 и 3 байта имеют информацию о длине).

РЕДАКТИРОВАТЬ: Под "ручкой" я имею в виду, что я хочу сделать следующее:

  if (isCRCmatch() )
  {
    if(Packet.id == SPECIAL_ID_1)
    {
      // do something
    }

    if(Packet.id == SPECIAL_ID_2)
    {
      // do other 
    }

    if(Packet.data[0] == 0xAA)
    {
      // do one stuff
    }

    if(Packet.data[1] == 0xBB && Packet.id == SPECIAL_ID_3 )
    {
      // do another stuff
    }

  }

А также(если возможно ofc) Я хотел бы отправить "anwers", используя ту же структуру:

U8 rspData[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};

SendResponce(Packet.id, rspData);

void SendResponce (U8 id_rsp, uint8_t* data_rsp)
{
  Packet_t ResponceData;
  uint16_t crc;
  uint8_t *data;

  ResponceData.head  = 0x24;
  ResponceData.len   = sizeof(ResponceData); // HERE IS PROBLEM ONE
  ResponceData.id   = id_rsp;
  ResponceData.data  = *data_rsp; // HERE IS PROBLEM TWO
  ResponceData.end   = 0x0D; // symbol '\r'

  data = (uint8_t*)&ResponceData;
  crc = GetCrc(data, sizeof(ResponceData)-2); // last 2 bytes with crc

  ResponceData.crc = crc;//(ack_crc >> 8 | ack_crc);

  SendData((U8*)&ResponceData, sizeof(ResponceData));  // Send rsp packet
}

Первая проблема - я не могу получить размер всей структуры автоматически, так как указатель всегда будет 4 байта ... Вторая проблема- Я уверен, что потеряю данные rsp, так как не знаю, где их конец.

Ответы [ 3 ]

0 голосов
/ 25 сентября 2018

У вас не может быть динамического буфера в середине структуры.

Еще один способ решения проблемы - разделить структуру на две части.Например (обратите внимание, что data здесь гибкий элемент массива ):

typedef __packed struct Packet_s
{
  U8  head;
  U16 len;
  U32 id;
  U8  data[];
} Packet_t, *Packet_p;

typedef __packed struct PacketEnd_s
{
  U8  end;
  U16 crc;
} PacketEnd_t, *PacketEnd_p;

Затем используйте

Packet_t *pPacket = (Packet_t *)&Buffer;
PacketEnd_t *pPacketEnd = (PacketEnd_t *)( count pointer here by using pPacket->len );

Предполагая, что __packed позволяет использовать доступ без выравниваниячленам __ упакованных структур.

0 голосов
/ 25 сентября 2018

Вы должны разделить вашу функцию обработки на две отдельные функции:

  • Одна, которая отбросит все, пока не найдет байт head.Этот байт обычно является постоянным байтом, отмечающим начало пакета.Это делается таким образом, чтобы избежать начала чтения в середине ранее отправленного пакета (например, порядок запуска отправителя и устройства-слушателя).

    Как только он находит начало пакета, он может прочитать заголовок len и id и получить все данные, хранящие его, в вашу переменную Buffer, пока он не прочитает байт end или не произойдет переполнение буфера, в этом случае он просто отброситданные и начать заново.

    Обратите внимание, что в переменную Buffer должны быть записаны только фактические данные.Все остальные поля (len, id и т. Д.) Могут храниться в разных переменных или в структуре, содержащей только Packet information, но без данных.Таким образом, вы выплевываете данные приложения из информации о передаче.

    Также обратите внимание, что эта функция не интерпретирует ни поле id, ни поле data.Он просто отправляет эту информацию в другую функцию, которая выполнит обработку или сброс, если id или data не верны / известны.

  • Однажды endбайт найден, вы можете передать информацию в актуальную функцию processing.Его заголовок будет выглядеть примерно так:

    void processPacket(U8 *data, U32 id, U16 len);
    

    , и вызов будет выглядеть так:

    void receiveFrame() {
        //Receive head
        //Receive id
        //Receive len
        //Fill in Buffer with the actual data
    
        //Call the processPacket function
        processPacket(&Buffer[0], id, len);
    }
    

Более полный пример может быть:

//It is not packet, since you fill it reading from the buffer and assigning
//to it, not casting the buffer into it.
//It has no data field. The data is in a different variable.
typedef struct Packet_s
{
    U8  head;
    U16 len;
    U32 id;
    U8  end;
    U16 crc;
} PacketInfo_t;

U8 Buffer[MAX_BUFFER_SIZE];
PacketInfo_t info;
void receiveFrame() {
    info.head=//Receive head
    info.len=//Receive len
    info.id=//Receive id
    //Fill the buffer variable
    processPacket(&Buffer[0], &info);
}
void processPacket(U8 *data, PacketInfo_t *info);

Для отправки просто используйте тот же формат:

void sendPacket(U8 *data, PacketInfo_t *info);

Эта функция может подготовить заголовок пакета из info и прочитать данные из data.


И, наконец, предостережение: приведение (или memcpy) полученного пакета непосредственно в структуру почти никогда не является хорошей идеей.Вы должны принять во внимание не только нулевые дыры (используя атрибут __packet), но также порядковый номер и представление формата данных систем отправителя и получателя, так как, если они различаются, вы получите неправильные значения.

0 голосов
/ 25 сентября 2018

Можно ли использовать мою структуру, если поле "DATA" длиннее 1 байта?

Нет, поскольку в нем есть только место для 1 data байта.Но вы можете использовать слегка модифицированную версию своей структуры.

typedef __packed struct Packet_s
{
  U8  head;
  U16 len;
  U32 id;
  U8  data[DATALENMAX]; // define appropriately
  U8  end;
  U16 crc;
} Packet_t, *Packet_p;

Конечно, вам придется соответствующим образом адаптировать копирование:

memcpy(&Packet, &Buffer, buffer_len), memmove(&Packet.end, &Packet.data[buffer_len-7-3], 3);

Что касается добавленных проблем, необходимопередать длину данных в SendResponce():

SendResponce(rspData, sizeof rspData);

void SendResponce(uint8_t* data_rsp, int datalen)
{
  Packet_t ResponceData;
  uint16_t crc;
  uint8_t *data;

  ResponceData.head  = 0x24;
  ResponceData.len   = 7+datalen+3; // HERE WAS PROBLEM ONE
  ResponceData.id   = SPECIAL_ID_X;
  memcpy(ResponceData.data, data_rsp, datalen); // HERE WAS PROBLEM TWO
  ResponceData.data[datalen] = 0x0D; // symbol '\r'
  data = (uint8_t*)&ResponceData;
  crc = GetCrc(data, 7+datalen+1); // last 2 bytes with crc
  ResponceData.crc = crc;//(ack_crc >> 8 | ack_crc);
  memmove(ResponceData.data+datalen+1, &ResponceData.crc, 2);
  SendData((U8*)&ResponceData, ResponceData.len);  // Send rsp packet
}
...