質問

In an embedded application I am working on, I frequently need to packetize data from several different objects to be sent out over a serial port. Similarly, data comes in on the serial port that needs to be written to several different objects.

For compatibility reasons, the ordering of the data in the packets does not allow for all the data associated with one object to be placed consecutively in the packet. So I can not easily vectorize, etc. data from each object and then put all that in a packet.

I can achieve proper functionality using a very large number of getters/mutators that the packet class uses to create/decode transmitted/received packets. But this does not seem very elegant.

I could make the packet class a friend of the classes it pulls/writes data from/to, but I was always told to avoid using friends as that sort of violates object-oriented principles.

Ideally the classes that handle the actual application would not know anything about the packet class (and not have to provide getters/mutators exclusively for it) and they simply would have new data if a packet came in between updates.

I thought that maybe I could pass references or pointers to the relevant member variables to the packet class at startup, but that is also tricky because all the members are not the same size. Perhaps I could also pass the size information as well? Would there be a way to vectorize a list of void pointer to member and size of member pairs so that the packet class does not need to take a gigantic amount of arguments to its constructor?

I am not sure how well I have described this problem, but I can certainly provide clarifications and more information if that would help. Thanks very much for any ideas.

Abbreviated example of tx packet class currently:

class PacketTx {
private:
  uint8_t buffer[MAX_PACKET_SIZE];   // MAX_PACKET_SIZE is 200
public:
  PacketTx(object1 *firstObject,
           object2 *secondObject,
           object3 *thirdObject
           // ...etc...
           )

  void sendPacket(void);
};

void PacketTx::sendPacket(void) {
  uint32_t i = 0;

  int16_t tempVar1 = firstObject->getVar1();
  uint32_t tempVar2 = secondObject->getVar2();
  uint8_t tempVar3 = firstObject->getVar3();
  int32_t tempVar4 = thirdObject->getVar4();
  // ...etc...

  memcpy(buffer + i, &tempVar1, sizeof(tempVar1));
  i += sizeof(tempVar1);

  memcpy(buffer + i, &tempVar2, sizeof(tempVar2));
  i += sizeof(tempVar2);

  memcpy(buffer + i, &tempVar3, sizeof(tempVar3));
  i += sizeof(tempVar3);

  memcpy(buffer + i), &tempVar4, sizeof(tempVar4));
  i += sizeof(tempVar4);

  // ...etc...

  for(uint32_t j = 0; j < i; ++j)
    putc(static_cast<char>(buffer[i]));
}

This example doesn't include header, checksum, etc., but it should give a basic idea of what is causing me a headache.

役に立ちましたか?

解決

So If I understand you have Some class C which has an arbitrary number of members which need to be written out into a buffer in an arbitrary order. Then the same mechanism occurs in reverse.

Serial -> Packet.buffer() -> Object (and reverse)

My initial thought was using boost::tuple and having the types correspond to the order of the data in the packet. (But with possibly 50+ members/entries in tuple that could be a big compile time hit)

e.g

tuple<float, int, std::string> a(1.0f,  2, std::string("Some Words");
ostr << a;

That would also require your classes to take a tuple as an argument and fill in the entries from data. So rather than write to a buffer there could be an enumeration of the fields held in the tuple so that "nice" code could be written:

enum Fields {Price,Amount,Reason};
typedef boost::tuple<...> MyTuple;

void MyClass::getData( MyTuple& t )
{
    t<Price>() = mPrice;
    t<Amount>() = mAmount;
    t<Reason>() = mSomeReason;
}

Note Would work in reverse as well.

Not sure if this fits completely with what you were trying to do but it may trigger some new thoughts?

他のヒント

Composition is what you'll need here:

class packet {
    enum PacketType {
        type1,
        ...
    };
    std::vector<unsigned char> buffer;
    PacketType id;
public:
    void setData(std::vector<unsigned char> buffer);
    void setType(PacketType type);
    std::vector<usigned char> getData();
    PacketType getType();
    //serialize Packet;
};

class composition_packet {
    //other packet related detail.
    std::vector<packet> data;
public:
    void addPacket(packet p);
    void send();
    //serialize composition_packet
};

The idea here is to make multiple packets as needed but send them as one big packet (composition_packet). The other concept here is the packet is only responsible for serializing itself and not data types that it should know anything about. Data to the packet must be already serialized by an object that know how to serialize it.


struct foo {
   std::string str;
   uint_8 u8;
   uint_32 u32;

   packet getPacket() {
       //simple dumb serialization
       std::stringstream ss(str);
       ss << "|";  //delimit character
       ss << u8 << "|";
       ss << u32;
       std::string s(ss.str());
       std::vector<unsigned char> data(s.begin(), s.end());
       packet p;
       p.setType(packet::fooType);
       p.setData(data);
       return p;
   }

};
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top