How to use structure with dynamically changing siz

2019-03-05 07:19发布

Question for C only, C++ and vectors do not solve problem.

I have such structure:

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

(EDIT: U8 is uint8_t (unsigned char) and so on)

For example, I've received packet(hex):

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

where

head = 0x24

len = 0x0B 0x00

id = 0x07 0x00 0x00 0x00

data = 0xAA

end = 0x0D

crc = 0x16 0x1C

I can copy it from incoming buffer like this

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

and work futher with it.

Is it possible to use my structure if field "DATA" is longer than 1 byte?

How can I handle something like this?

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

Length of packet will be always known (2 and 3 bytes have info about length).

EDIT: Under "handle" I mean that I want to do next:

  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
    }

  }

And also (if possible ofc) I would like to send "anwers" using same structure:

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
}

First problem - I cant get size of all structure automatically, since pointer will be always 4 bytes... Second problem - I sure that I will lose rsp data since I don't know where is end of it.

3条回答
神经病院院长
2楼-- · 2019-03-05 07:40

Is it possible to use my structure if field "DATA" is longer than 1 byte?

No, since it has only room for 1 data byte. But you can use a slightly modified version of your structure.

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

Of course, you'd have to adapt the copying accordingly:

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

Regarding the added problems, it's necessary to pass the data length to 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
}
查看更多
冷血范
3楼-- · 2019-03-05 07:57

You can't have dynamic buffer in middle of a struct.

Another way to solve the problem is divide the struct to two pieces. For example (notice that data is here a flexible array member):

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;

Then use

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

Assuming that __packed allows to use unaligned access to members of __packed structs.

查看更多
forever°为你锁心
4楼-- · 2019-03-05 08:05

You should split your processing function into two distinct functions:

  • One which will discard everything until it finds the head byte. This byte usually is a constant byte, marking the start of a packet. This is done this way, in order to avoid start to read in the middle of a previously sent packet (think i.e. the startup order of the sender and the listener devices).

    Once it finds the start of the packet, it can read the header, len and id and receive all the data storing it into your Buffer variable, until it reads the end byte or there is a buffer overflow, in which case it would just discard the data and start again.

    NOTE that into the Buffer variable only should be written the actual data. All the other fields (len, id and so) can be stored in different variables, or in a struct containing only the Packet information, but no data. This way, you spit the application data from the transmission information.

    Note also that this function does not interpret the idfield, nor the datafield. It just sends this information to the other funciton, which will do the processing or discard if the id or the dataare not correct / known.

  • Once the end byte is found, you can pass the information to the actual processing function. Its header would be something like:

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

    and the call to it would be:

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

A more complete example could be:

//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);

For sending, just use the same format:

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

This function sould prepare the Packet header from info and read the data from data.


And finally, a word of caution: casting (or memcpy) a received packet directly into a struct is almost never a good idea. You have to take into account not only the zero holes (using the __packet attribute), but also the endianness and the data format representation of the sender and receiver systems, since if they are different, you would end up with the wrong values.

查看更多
登录 后发表回答