App_Wings(または各アプリのUART出力)を解釈して、各パケットメッセージの概要を表示します。
glancerは「glance(一目する)人」の意味の英単語です。
ファイル構成
プロジェクトフォルダ
examples/glancer --- Makefile
msc/MSC_glancer_con --- VC++ Project 定義
プロジェクトフォルダのソースコードはglancer.cpp
のみです。残りの必要なコードはsrc/..
を参照します。
他のプロジェクトなどに移植する場合
MWM5ライブラリ全てのファイルは必要ありません。以下に必要なファイルを列挙します。
twe_fmt.cpp
twe_fmt_actstd.cpp
twe_fmt_appio.cpp
twe_fmt_apptag.cpp
twe_fmt_appuart.cpp
twe_fmt_common.cpp
twe_fmt_pal.cpp
twe_fmt_twelite.cpp
twe_sercmd.cpp
twe_sercmd_ascii.cpp
twe_utils_crc8.cpp
twe_common.hpp
twe_fmt.hpp
twe_fmt_actstd.hpp
twe_fmt_appio.hpp
twe_fmt_apptag.hpp
twe_fmt_appuart.hpp
twe_fmt_common.hpp
twe_fmt_pal.hpp
twe_fmt_twelite.hpp
twe_sercmd.hpp
twe_sercmd_ascii.hpp
twe_stream.hpp
twe_utils.hpp
twe_utils_crc8.hpp
twe_utils_fixedque.hpp
twe_utils_simplebuffer.hpp
上記で必要なファイル等は本ドキュメントと相違ある場合もあります。問題があればビルド時にエラーが表示されますので、エラーファイルに記述される"~not found"といったメッセージを頼りに足りないファイルをコピーしてください。
ビルド
ビルドに必要な定義
必須。標準入出力のみでビルドしたい場合に必要な定義です。
システム時間[ms]を得るmillis()
を実装した場合は定義します。定義するとパケット情報にタイムスタンプ[ms]情報が含まれます。
※ millis()
の実装には、システム時間を取得するためのOS依存のシステムコールが必要です。
ビルド方法(VC++, Windows)
examples/glancer_con というプロジェクト定義がありますので、こちらをビルドしてください。
ビルド方法 (Makefile, gcc)
make (GNU) と g++ (gcc より) が必要です。事前にインストールを済ませてください。
Makefile
を確認してください。以下の定義は必要に応じて書き換えます。
g++-9 など実行名が違う場合に書き換えます。
※ Makefile 中で動作環境を判定している部分があり、この部分に定義が含まれます。
必要に応じて。 -D???
の定義をコンパイラに与えます。
必要に応じて。コンパイルオプションをコンパイラに与えます。
makeコマンドを実行します。
millis()の実装
#if defined(_MSC_VER) || defined(__MINGW32__)
#include "windows.h"
# pragma comment(lib, "secur32.lib")
# pragma comment(lib, "winmm.lib")
# pragma comment(lib, "dmoguids.lib")
# pragma comment(lib, "wmcodecdspuuid.lib")
# pragma comment(lib, "msdmo.lib")
# pragma comment(lib, "Strmiids.lib")
#elif defined(__APPLE__) || defined(__linux)
#include <sys/time.h>
#endif
uint32_t millis() {
#if defined(_MSC_VER) || defined(__MINGW32__)
return (uint32_t)timeGetTime();
#elif defined(__APPLE__) || defined(__linux)
timeval time;
gettimeofday(&time, NULL);
long ms = (time.tv_sec * 1000) + (time.tv_usec / 1000);
return (uint32_t)ms;
#else
# warning "no millis() implementation."
return 0;
#endif
}
実行例
:78811501C98201015A000391000C2E00810301FFFFFFFFFB
PKT:Typ=1:Lq=201:Ad=0x8201015a(0x78):Vmv=3118:Tms=2656:App_Twelite:DI1..4=LHHH:AI1..4=(0028,----,----,----)
:78A0028201015AFFFFFFFFA8000700112233AABBCCC6
PKT:Typ=4:Lq=168:Ad=0x8201015a(0x78):Vmv=0000:Tms=5430:App_UARTMSG=0x00112233aabbcc
:8000000084811F810EFF6D04808205113008020AEB11300102035A0501000209E3010200020E3A02030004000001BE6C00
PKT:Typ=2:Lq=132:Ad=0x810eff6d(0x04):Vmv=2795:Tms=3904:PAL_AMB:TEMP=25.31:HUMD=36:LUMI=446
標準入力(通常の1バイト入力を用いているため改行が入力されるまでは入力文字列は評価されません)にアスキー形式のUARTメッセージを入力します。出力はPKT...
となります。
先頭の項目について解説します。
32bitの送信元アドレスと8bitの送信元論理アドレスが格納されます。
電源電圧です。
※ メッセージにデータがない場合は 0000 となります。
メッセージを受信したシステム時間が格納されます。
※ millis()
を実装しておく必要があります。
ソースコードの構造
ヘッダ部
#include <iostream>
#include <iomanip>
#include <string>
#include "twe_common.hpp"
#include "twe_sercmd_ascii.hpp"
#include "twe_fmt.hpp"
using namespace TWE;
using namespace TWEUTILS;
using namespace TWESERCMD;
using namespace TWEFMT;
AsciiParser parse_ascii(256);
必要な標準ライブラリヘッダとMWM5ライブラリヘッダを読み込みます。
MWM5ライブラリでは細かく名前空間が使用されていますが、利用時には煩雑ですのですべてusing namespace
により省略可能とします。
AsciiParser
のクラスインスタンスを生成しておきます。
メインループ
int c;
while ((c = std::cin.get()) >= 0) {
if (c == '\r' || c == '\n') {
parse_ascii << char_t(0x0d); // always 0x0d (this parser does not check the following 0x0a)
}
else {
parse_ascii << char_t(c);
}
if (parse_ascii) {
std::cout << "PKT";
auto&& payl = parse_ascii.get_payload(); // payload data array
auto&& pkt = newTwePacket(payl); // packet object (contains minimum information)
auto&& typ = identify_packet_type(pkt); // packet type
std::cout << ":Typ=" << int(typ);
if (typ != E_PKT::PKT_ERROR) {
// ... display common packet info
}
// display each packet information
switch (typ) {
case E_PKT::PKT_PAL: print_pal(pkt); break;
case E_PKT::PKT_ACT_STD: print_act(pkt); break;
case E_PKT::PKT_TWELITE: print_app_twelite(pkt); break;
case E_PKT::PKT_APPIO: print_app_io(pkt); break;
case E_PKT::PKT_APPUART: print_app_uart(pkt); break;
case E_PKT::PKT_APPTAG: print_app_tag(pkt); break;
default: print_unknown(payl); // e.g. UART message of App_Twelite, App_UART(simple format), or corrupted.
}
}
}
このサンプルでは標準入力から1バイトずつ入力(std::cin::get()
)して、これをASCIIパーサーに投入します。
標準入力ではシステムによって改行コードが異なりますが、ここでは'r'
または'\n'
のいずれが来ても'\r'
を投入するようにしています。本パーサーは末尾の'\r'
のみを評価するためです。
投入後書式が完了した場合はparse_ascii
はtrue
を戻します。ここでデータを解釈します。
auto&& payl = parse_ascii.get_payload();
auto&& pkt = newTwePacket(payl);
ASCIIパーサーにより得られたバイト列は.get_payload()
メソッドにより配列SmplBuf_Byte
型として取り出すことが出来ます。
このサンプルでは得られたバイト列を解釈して、どの種別のUARTメッセージなのか、何が格納されているのかを取り出せるようにします。
バイト列を解釈する手続きはnewTwePacket()
にバイト列payl
を与えます。戻り値としてクラスオブジェクトpkt
を生成します。このオブジェクトはspTwePacket
型の(メモリ管理を行う)スマートオブジェクトで、TwePacket
型のオブジェクトを格納します。
auto&& typ = identify_packet_type(pkt); // packet type
実際にはpkt
は種別に応じたTwePacket
型の派生クラスのインスタンスが格納されています。この派生クラスの種別を確認するのがidentify_pkt_type()
で、パケット種別としてE_PKT
型の値を返します。
パケット種別が得られたら、パケット種別ごとの表示関数print_???(spTwePacket)
を呼び出しています。
ここから先はパケット種別ごとに手続きが違います。以下にApp_Tweliteの解釈例、App_PALの解釈例を示します。
print_app_twelite()
void print_app_twelite(spTwePacket pkt) {
auto&& atw = refTwePacketTwelite(pkt);
std::cout << ":DI1..4="
<< char(atw.DI1 ? 'L' : 'H')
<< char(atw.DI2 ? 'L' : 'H')
<< char(atw.DI3 ? 'L' : 'H')
<< char(atw.DI4 ? 'L' : 'H');
...
App_Twelite の UARTメッセージの解釈を行います。まずspTwePacket
型のままでは、App_Twelite特有のデータにアクセスできません。
まずrefTwePacketTwelite()
を呼びます。戻り値はTwePacketTwelite
の参照型です。App_TweliteのUARTメッセージを解釈した特有のデータが含まれます。例えば.DI1
はDI1のHigh/Low状態を示します。
print_pal()
void print_pal(spTwePacket pkt) {
auto&& pal = refTwePacketPal(pkt);
PalEvent ev;
// acquire event data.
if (pal.has_PalEvent()) {
ev = pal.get_PalEvent();
}
switch(pal.get_PalDataType()) {
case E_PAL_DATA_TYPE::EVENT_ONLY:
/* イベントのみが含まれるパケットの場合 */ break;
case E_PAL_DATA_TYPE::MAG_STD:
/* MAG PAL の表示コード */ break;
case E_PAL_DATA_TYPE::MOT_STD:
/* MOT PAL の表示コード */ break;
case E_PAL_DATA_TYPE::AMB_STD:
/* AMB PAL の表示コード */ break;
case E_PAL_DATA_TYPE::EX_CUE_STD:
/* TWELITE CUE のメッセージ */ break;
}
TWELITE PALのUARTメッセージを解釈します。TWELITE PALは接続される基板によって異なるUARTメッセージが出力されます。このためデータ取得も一旦PALとして解釈してから、さらに個別のPAL基板のデータを取り出します。
auto&& pal = refTwePacketPal(pkt);
最初にPAL共通データをrefTwePacketPal()
により取り出します。
if (pal.is_PalEvent()) {
最初にパケットにイベントが含まれるかどうかの判定を行います。イベントパケットは例えば通知(NOTICE)PALの加速度センサーが静止状態から加速度を検出した(タップ)といった現象をイベント番号として伝えるものです。イベントパケットにも様々なデータが付加されますが、通常はイベント番号を参照します。
switch(pal.get_PalDataType()) {
パケットの種別を.get_PalDataType()
により判定します。
case E_PAL_DATA_TYPE::MAG_STD:
{
PalMag mag = pal.get_PalMag();
std::cout << ":STAT=" << int(mag.u8MagStat);
...
PAL基板の種別がわかったら、PAL基板の種別に応じた手続き(.get_PalMag()
など)により、PAL基板特有のデータが含まれるクラスオブジェクトを得ます。
上記の例では開閉センサーPALの磁気センサーの状態を得ています。