4

私が取り組んでいる組み込みアプリケーションでは、シリアルポートを介して送信するために、いくつかの異なるオブジェクトからのデータをパケット化する必要があることがよくあります。同様に、データはいくつかの異なるオブジェクトに書き込む必要があるシリアルポートに送られます。

互換性の理由から、パケット内のデータの順序付けでは、1つのオブジェクトに関連付けられたすべてのデータをパケット内に連続して配置することはできません。そのため、各オブジェクトのデータなどを簡単にベクトル化して、すべてをパケットに入れることはできません。

パケットクラスが送受信されたパケットを作成/デコードするために使用する非常に多くのゲッター/ミューテーターを使用して、適切な機能を実現できます。しかし、これはあまりエレガントではないようです。

パケットクラスを、データをプル/書き込みするクラスのフレンドにすることはできますが、オブジェクト指向の原則に違反するため、フレンドの使用は避けるように常に言われていました。

理想的には、実際のアプリケーションを処理するクラスは、パケットクラスについて何も知らず(そして、それ専用のゲッター/ミューテーターを提供する必要はありません)、更新の間にパケットが入った場合、単に新しいデータを持ちます。

起動時に関連するメンバー変数への参照またはポインターをパケットクラスに渡すことができるかもしれないと思いましたが、すべてのメンバーが同じサイズではないため、これも注意が必要です。おそらく、サイズ情報も渡すことができますか?パケットクラスがコンストラクターに対して膨大な量の引数を取る必要がないように、メンバーへのvoidポインターとメンバーペアのサイズのリストをベクトル化する方法はありますか?

この問題をどれだけうまく説明したかはわかりませんが、それが役立つ場合は、説明と詳細情報を提供できます。アイデアをありがとうございました。

現在のtxパケットクラスの省略例:

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]));
}

この例にはヘッダーやチェックサムなどは含まれていませんが、頭痛の原因となっているものの基本的な考え方がわかるはずです。

4

2 に答える 2

2

したがって、私が理解している場合、任意の順序でバッファに書き出す必要がある任意の数のメンバーを持つクラスCがあります。次に、同じメカニズムが逆に発生します。

シリアル->Packet.buffer()->オブジェクト(およびリバース)

私の最初の考えはboost::tuple、タイプを使用して、パケット内のデータの順序に対応させることでした。(ただし、おそらく50以上のメンバー/エントリがタプルに含まれているため、コンパイル時に大きな打撃を受ける可能性があります)

例えば

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

また、クラスでタプルを引数として取り、データからエントリを入力する必要があります。したがって、バッファに書き込むのではなく、タプルに保持されているフィールドを列挙して、「適切な」コードを記述できるようにすることができます。

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

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

注逆にも機能します。

これがあなたがやろうとしていたことに完全に適合するかどうかはわかりませんが、それはいくつかの新しい考えを引き起こす可能性がありますか?

于 2012-10-26T14:12:50.300 に答える
1

ここで必要なのは構成です。

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
};

ここでの考え方は、必要に応じて複数のパケットを作成し、それらを1つの大きなパケットとして送信することです(composition_packet)。ここでのもう1つの概念は、パケットがそれ自体のシリアル化のみを担当し、知っておくべきデータ型は担当しないということです。パケットへのデータは、シリアル化する方法を知っているオブジェクトによってすでにシリアル化されている必要があります。


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;
   }

};
于 2012-10-26T14:38:43.497 に答える