セクションの複数ページをまとめています。 印刷またはPDF形式で保存...

もとのページに戻る

2024-11-14 現在

pktparser

パケットパーサ
pktparser(parser_packet)は、serparserで変換したバイト列に対して、内容の解釈を行います。
serparser_heap parser_ser;

void setup() {
    // init ser parser (heap alloc)
    parser_ser.begin(PARSER::ASCII, 256);
}

void loop() {
    int c;
    while ((c = Serial.read()) >= 0) {
        parser_ser.parse(c);
        if (parser_ser.available()) {
            // get buffer object
            auto&& payl = parser_ser.get_buf();
            // identify packet type
            auto&& typ = identify_packet_type(payl.begin(), payl.end());

            // if packet type is TWELITE standard 0x81 message
            if (typ == E_PKT::PKT_TWELITE) {
                pktparser pkt; // packet parser object
                // analyze packet data
                typ = pkt.parse<TwePacketTwelite>(payl.begin(), payl.end());

                if (typ != E_PKT::PKT_ERROR) { // success!
                    // get data object
                    auto&& atw = pkt.use<TwePacketTwelite>();

                    // display packet inforamtion
                    Serial << crlf << format("TWELITE: SRC=%08X LQI=%03d "
                        , app.u32addr_src, app.u8lqi);
	                  Serial << " DI1..4="
	                      << atw.DI1 ? 'L' : 'H' << atw.DI2 ? 'L' : 'H'
                        << atw.DI3 ? 'L' : 'H' << atw.DI4 ? 'L' : 'H';
                }
            }
        }
    }
}

上記の例は、標準アプリケーションの0x81メッセージの解釈を行っています。parser_serオブジェクトによりSerialより入力された電文をバイト列に変換します。このバイト列をまずidentify_packet_type()により電文の種別E_PKTを特定します。電文の種別が判定できたら次に.parse<TwePacketTwelite>()により電文を解析します。解析結果はTwePacketTwelite型になりますが、このオブジェクトを取り出す手続きが.use<TwePacketTwelite>()です。TwePacketTwelite型はクラスですが構造体として直接メンバー変数を参照します。

parse<T>

template <class T>
E_PKT parse(const uint8_t* p, const uint8_t* e)

バイト列を解析します。

Tには解析対象のパケット型を指定します。例えば標準アプリケーションの0x81メッセージならTwePacketTweliteを指定します。

peはバイト列の先頭と終端の次を指定します。

戻り値はE_PKT型です。エラーの場合はE_PKT::PKT_ERRORが戻ります。

user<T>

template 
T& use()

解釈したバイト列に対応するパケット型に対応するオブジェクトの参照を返します。事前にparse<T>を実行しエラーがなかった場合に呼び出すせます。

Tparse<T>で実行した型と同じもの、または基本的な情報のみ取得できるTwePacketを指定します。

1 - E_PKT

パケット種別定義

以下のパケットに対応します。

名前解説
PKT_ERRORパケット解釈前やパケット種別が特定できないなど、TwePacketには意味のあるデータが格納されていない
PKT_TWELITE標準アプリ App_Twelite の 0x81 コマンドを解釈したもの
PKT_PALTWELITE PALのシリアル形式を解釈したもの
PKT_APPIOリモコンアプリ App_IO UARTメッセージを解釈したもの
PKT_APPUARTシリアル通信アプリ App_UART 拡張書式を解釈したもの。
PKT_APPTAG無線タグアプリApp_TagのUARTメッセージを解釈したもの。センサ固有部分は解釈されずpayloadとしてバイト列を報告します。
PKT_ACT_STDアクト(Act)のサンプルなどで使用される出力書式。

2 - identify_packet_type()

パケット種別の判定

idenify_packet_type()

パケットデータのバイト列を入力として、パケットの種別を判定します。戻り値はE_PKTです。

E_PKT identify_packet_type(uint8_t* p, uint8_t u8len)

特定のパケットであると解釈できなかった場合はE_PKT::PKT_ERRORが戻ります。

3 - TwePacket

パケット型
パケット型の基底クラスですが、メンバー構造体commonにはアドレス情報など共通情報が含まれます。
class TwePacket {
	public:
		static const E_PKT _pkt_id = E_PKT::PKT_ERROR;

		struct {
			uint32_t tick;     // 解釈実行時のシステム時刻[ms]
			uint32_t src_addr; // 送信元アドレス(シリアル番号)
			uint8_t src_lid;   // 送信元アドレス(論理アドレス)
			uint8_t lqi;       // LQI
			uint16_t volt;     // 電圧[mV]
		} common;
};

3.1 - TwePacketTwelite

App_Tweliteからのパケット
TwePacketTweliteクラスは、標準アプリApp_Tweliteの0x81コマンドを解釈したものです。
class TwePacketTwelite : public TwePacket, public DataTwelite { ... };

パケットデータ内の諸情報はparse<TwePacketTwelite>()実行後にパケット情報がDataTweliteに格納されます。

DataTwelite構造体

struct DataTwelite {
		//送信元のシリアル#
		uint32_t u32addr_src;

		// 送信元の論理ID
		uint8_t u8addr_src;

		// 宛先の論理ID
		uint8_t u8addr_dst;

		// 送信時のタイムスタンプ
		uint16_t u16timestamp;

		// 低レイテンシ送信時のフラグ
		bool b_lowlatency_tx;

		// リピート中継回数
		uint16_t u8rpt_cnt;

		// LQI値
		uint16_t u8lqi;

		// DIの状態 (true がアクティブ Lo,GND)
		bool DI1, DI2, DI3, DI4;
		// DIの状態ビットマップ (LSBから順にDI1,2,3,4)
		uint8_t DI_mask;

		// DIアクティブならtrue (過去にアクティブになったことがある)
		bool DI1_active, DI2_active, DI3_active, DI4_active;
		// DIのアクティブビットマップ(LSBから順にDI1,2,3,4)
		uint8_t DI_active_mask;

		// モジュールの電源電圧[mV]
		uint16_t u16Volt;

		// AD値 [mV]
		uint16_t u16Adc1, u16Adc2, u16Adc3, u16Adc4;
		// ADがアクティブ(有効)なら 1 になるビットマップ (LSBから順にAD1,2,3,4)
		uint8_t Adc_active_mask;
};

3.2 - TwePacketIO

App_IOからのパケット
TwePacketAppIOクラスは、標準アプリApp_IOのシリアルメッセージ(0x81)を解釈したものです。
class TwePacketAppIO : public TwePacket, public DataAppIO { ... };

パケットデータ内の諸情報はparse<TwePacketIO>()実行後にDataTweliteに格納されます。

DataAppIO構造体

struct DataAppIO {
		//送信元のシリアル#
		uint32_t u32addr_src;

		// 送信元の論理ID
		uint8_t u8addr_src;

		// 宛先の論理ID
		uint8_t u8addr_dst;

		// 送信時のタイムスタンプ
		uint16_t u16timestamp;

		// 低レイテンシ送信時のフラグ
		bool b_lowlatency_tx;

		// リピート中継回数
		uint16_t u8rpt_cnt;

		// LQI値
		uint16_t u8lqi;

		// DIの状態ビットマップ (LSBから順にDI1,2,3,4,...)
		uint8_t DI_mask;

		// DIのアクティブ(使用なら1)ビットマップ(LSBから順にDI1,2,3,4,...)
		uint8_t DI_active_mask;

		// DIが割り込み由来かどうかのビットマップ(LSBから順にDI1,2,3,4,...)
		uint16_t DI_int_mask;
};

3.3 - TwePacketUART

App_UARTからのパケット
TwePacketAppUartクラスは、App_UARTの拡張書式を親機・中継機アプリApp_Wingsで受信したときの形式です。
class TwePacketAppUART : public TwePacket, public DataAppUART

パケットデータ内の諸情報はparse<TwePacketUART>()実行後にDataAppUARTに格納されます。

DataAppUART構造体

struct DataAppUART {
		/**
		 * source address (Serial ID)
		 */
		uint32_t u32addr_src;

		/**
		 * source address (Serial ID)
		 */
		uint32_t u32addr_dst;

		/**
		 * source address (logical ID)
		 */
		uint8_t u8addr_src;

		/**
		 * destination address (logical ID)
		 */
		uint8_t u8addr_dst;

		/**
		 * LQI value
		 */
		uint8_t u8lqi;

		/**
		 * Response ID
		 */
		uint8_t u8response_id;

		/**
		 * Payload length
		 */
		uint16_t u16paylen;

		/**
		 * payload
		 */
##if MWX_PARSER_PKT_APPUART_FIXED_BUF == 0
		mwx::smplbuf_u8_attach payload;
##else
		mwx::smplbuf_u8<MWX_PARSER_PKT_APPUART_FIXED_BUF> payload;
##endif
	};

payloadはデータ部分ですが、マクロ定義によってデータ格納の方法が変わります。

MWX_PARSER_PKT_APPUART_FIXED_BUFの値が0としてコンパイルした場合は、payloadはパケット解析を行うバイト列を直接参照します。元のバイト列の値が変更されるとpayload中のデータは破壊されます。

MWX_PARSER_PKT_APPUART_FIXED_BUFの値を0より大きい値として定義した場合は、payloadにはその値(バイト数)のバッファが確保されます。ただしシリアル電文のデータがバッファサイズを超えた場合はparse<TwePacketAppUART>()は失敗しE_PKT::PKT_ERRORを戻します。

3.4 - TwePacketPAL

App_PALからのパケット
TwePacketPalクラスは、TWELITE PALのパケットデータを解釈したものです。このクラスはTWELITE PAL(センサーデータなど上り方向)共通に取り扱います。
class TwePacketPal : public TwePacket, public DataPal { ... };

PAL共通データはDataPalに定義されています。

PALの各センサー基板特有のデータを取り出すためのジェネレータ関数を用意しています。

DataPal構造体

PALは接続されるセンサーなどによってパケットデータ構造が異なりますが、DataPalでは共通部のデータ構造を保持します。

struct DataPal {
	uint8_t u8lqi;        // LQI値

	uint32_t u32addr_rpt; // 中継器のアドレス

	uint32_t u32addr_src; // 送信元のアドレス
	uint8_t u8addr_src;   // 送信元の論理アドレス

	uint16_t u16seq;      // シーケンス番号

	E_PAL_PCB u8palpcb;		// PAL基板の種別
	uint8_t u8palpcb_rev;	// PAL基板のレビジョン
	uint8_t u8sensors;		// データに含まれるセンサーデータの数 (MSB=1はエラー)
	uint8_t u8snsdatalen; // センサーデータ長(バイト数)

	union {
		const uint8_t *au8snsdata; // センサーデータ部への参照
		uint8_t _pobj[MWX_PARSER_PKT_APPPAL_FIXED_BUF]; // 各センサーオブジェクト
	};
};

PALのパケットデータ構造は大まかに2つのブロックからなり、全てのPAL共通部と個別のデータ部になります。個別のデータ部は、パケットの解釈を行わずそのまま格納しています。取り扱いを単純化するため32バイトを超えるデータは動的に確保するuptr_snsdataに格納します。

個別のデータ部は、PalBaseをベースクラスに持つ構造体に格納されます。この構造体は、TwePacketPalに定義されるジェネレータ関数により生成されます。

parse<TwePacketPAL>()実行時にMWX_PARSER_PKT_APPPAL_FIXED_BUFに収まるサイズであれば、センサー個別のオブジェクトを生成します。

収まらない場合はau8snsdataに解析時のバイト列の参照が保存されます。この場合、解析に用いたバイト列のデータが書き換えられた場合は、センサー個別のオブジェクトは生成できなくなります。

PalBase

PALの各センサーのデータ構造体はすべてPalBaseを継承します。センサーデータの格納状況u32StoredMaskが含まれます。

	struct PalBase {
		uint32_t u32StoredMask; // 内部的に利用されるデータ取得フラグ
	};

PalEvent

PALイベントは、センサーなどの情報を直接送るのではなく、センサー情報を加工し一定の条件が成立したときに送信される情報です。例えば加速度センサーの静止状態から一定以上の加速度が検出された場合などです。

	struct PalEvent {
		uint8_t b_stored;       // 格納されていたら true
		uint8_t u8event_source; // 予備
		uint8_t u8event_id;     // イベントID
		uint32_t u32event_param;// イベントパラメータ
	};

イベントデータが存在する場合はTwePacketPal.is_PalEvent()trueになることで判定でき、.get_PalEvent()によりPalEventデータ構造を得られます。

ジェネレータ関数

センサーPALの各種データを取り出すためのジェネレータ関数です。

void print_pal(pktparser& pkt) {
	auto&& pal = pkt.use<TwePacketPal>();
	if (pal.is_PalEvent()) {
		PalEvent obj = pal.get_PalEvent();
	} else
	switch(pal.u8palpcb) {
	case E_PAL_PCB::MAG:
	  {
		  // generate pal board specific data structure.
		  PalMag obj = pal.get_PalMag();
	  } break;
  case E_PAL_PCB::AMB:
	  {
		  // generate pal board specific data structure.
		  PalAmb obj = pal.get_PalAmb();
	  } break;
	  ...
	default: ;
	}
}

ジェネレータ関数を利用するには、まずpktがイベントかどうか判定(.is_PalEvent())します。イベントの場合はget_PalEvent()を持ちます。それ以外はu8palpcbに応じてオブジェクトを生成します。

get_PalMag()

PalMag get_PalMag()

// MAG
struct PalMag : public PalBase {
    uint16_t u16Volt;         // モジュール電圧[mV]
    uint8_t u8MagStat;        // 磁気スイッチの状態 [0:磁石なし,1,2]
    uint8_t bRegularTransmit; // MSB flag of u8MagStat
};

.u8palpcb==E_PAL_PCB::MAGの場合、開閉センサーパルのデータPalMagを取り出します。

get_PalAmb()

PalAmb get_PalAmb()

// AMB
struct PalAmb : public PalBase {
    uint16_t u16Volt;       // モジュール電圧[mV]
    int16_t i16Temp;        // 温度(100倍値)
    uint16_t u16Humd;       // 湿度(100倍値)
    uint32_t u32Lumi;       // 照度(Lux相当)
};

.u8palpcb==E_PAL_PCB::AMBの場合、環境センサーパルのデータPalAmbを取り出します。

get_PalMot()

PalMot get_PalMot()

// MOT
struct PalMot : public PalBase {
    uint16_t u16Volt;  // モジュール電圧[mV]
    uint8_t u8samples; // サンプル数
    uint8_t u8sample_rate_code; // サンプルレート (0: 25Hz, 4:100Hz)
    int16_t i16X[16]; // X 軸
    int16_t i16Y[16]; // Y 軸
    int16_t i16Z[16]; // Z 軸
};

.u8palpcb==E_PAL_PCB::MOTの場合、動作センサーパルのデータPalMotを取り出します。

get_PalEvent()

PalEvent get_PalEvent()

// PAL event
struct PalEvent {
    uint8_t b_stored;        // trueならイベント情報あり
    uint8_t u8event_source;  // イベント源
    uint8_t u8event_id;      // イベントID
    uint32_t u32event_param; // 24bit、イベントパラメータ
};

.is_PalEvent()trueの場合PalEvent(PALイベント)を取り出します。