glancer_con
App_Wings(または各アプリのUART出力)を解釈して、各パケットメッセージの概要を表示します。
glancerは「glance(一目する)人」の意味の英単語です。

ファイル構成

プロジェクトフォルダ

1
examples/glancer --- Makefile
2
msc/MSC_glancer_con --- VC++ Project 定義
Copied!
プロジェクトフォルダのソースコードはglancer.cppのみです。残りの必要なコードはsrc/..を参照します。

他のプロジェクトなどに移植する場合

MWM5ライブラリ全てのファイルは必要ありません。以下に必要なファイルを列挙します。
1
twe_fmt.cpp
2
twe_fmt_actstd.cpp
3
twe_fmt_appio.cpp
4
twe_fmt_apptag.cpp
5
twe_fmt_appuart.cpp
6
twe_fmt_common.cpp
7
twe_fmt_pal.cpp
8
twe_fmt_twelite.cpp
9
twe_sercmd.cpp
10
twe_sercmd_ascii.cpp
11
twe_utils_crc8.cpp
12
13
twe_common.hpp
14
twe_fmt.hpp
15
twe_fmt_actstd.hpp
16
twe_fmt_appio.hpp
17
twe_fmt_apptag.hpp
18
twe_fmt_appuart.hpp
19
twe_fmt_common.hpp
20
twe_fmt_pal.hpp
21
twe_fmt_twelite.hpp
22
twe_sercmd.hpp
23
twe_sercmd_ascii.hpp
24
twe_stream.hpp
25
twe_utils.hpp
26
twe_utils_crc8.hpp
27
twe_utils_fixedque.hpp
28
twe_utils_simplebuffer.hpp
Copied!
上記で必要なファイル等は本ドキュメントと相違ある場合もあります。問題があればビルド時にエラーが表示されますので、エラーファイルに記述される"~not found"といったメッセージを頼りに足りないファイルをコピーしてください。

ビルド

ビルドに必要な定義

定義
内容
TWE_STDINOUT_ONLY
必須。標準入出力のみでビルドしたい場合に必要な定義です。
TWE_HAS_MILLIS
システム時間[ms]を得るmillis()を実装した場合は定義します。定義するとパケット情報にタイムスタンプ[ms]情報が含まれます。
millis() の実装には、システム時間を取得するためのOS依存のシステムコールが必要です。

ビルド方法(VC++, Windows)

examples/glancer_con というプロジェクト定義がありますので、こちらをビルドしてください。

ビルド方法 (Makefile, gcc)

make (GNU) と g++ (gcc より) が必要です。事前にインストールを済ませてください。
Makefileを確認してください。以下の定義は必要に応じて書き換えます。
定義名
CXX
g++-9 など実行名が違う場合に書き換えます。
※ Makefile 中で動作環境を判定している部分があり、この部分に定義が含まれます。
DEFINES
必要に応じて。 -D??? の定義をコンパイラに与えます。
CFLAGS
必要に応じて。コンパイルオプションをコンパイラに与えます。
makeコマンドを実行します。

millis()の実装

1
#if defined(_MSC_VER) || defined(__MINGW32__)
2
#include "windows.h"
3
# pragma comment(lib, "secur32.lib")
4
# pragma comment(lib, "winmm.lib")
5
# pragma comment(lib, "dmoguids.lib")
6
# pragma comment(lib, "wmcodecdspuuid.lib")
7
# pragma comment(lib, "msdmo.lib")
8
# pragma comment(lib, "Strmiids.lib")
9
#elif defined(__APPLE__) || defined(__linux)
10
#include <sys/time.h>
11
#endif
12
13
uint32_t millis() {
14
#if defined(_MSC_VER) || defined(__MINGW32__)
15
return (uint32_t)timeGetTime();
16
#elif defined(__APPLE__) || defined(__linux)
17
timeval time;
18
gettimeofday(&time, NULL);
19
long ms = (time.tv_sec * 1000) + (time.tv_usec / 1000);
20
return (uint32_t)ms;
21
#else
22
# warning "no millis() implementation."
23
return 0;
24
#endif
25
}
Copied!

実行例

1
:78811501C98201015A000391000C2E00810301FFFFFFFFFB
2
PKT:Typ=1:Lq=201:Ad=0x8201015a(0x78):Vmv=3118:Tms=2656:App_Twelite:DI1..4=LHHH:AI1..4=(0028,----,----,----)
3
:78A0028201015AFFFFFFFFA8000700112233AABBCCC6
4
PKT:Typ=4:Lq=168:Ad=0x8201015a(0x78):Vmv=0000:Tms=5430:App_UARTMSG=0x00112233aabbcc
5
:8000000084811F810EFF6D04808205113008020AEB11300102035A0501000209E3010200020E3A02030004000001BE6C00
6
PKT:Typ=2:Lq=132:Ad=0x810eff6d(0x04):Vmv=2795:Tms=3904:PAL_AMB:TEMP=25.31:HUMD=36:LUMI=446
Copied!
標準入力(通常の1バイト入力を用いているため改行が入力されるまでは入力文字列は評価されません)にアスキー形式のUARTメッセージを入力します。出力はPKT...となります。
先頭の項目について解説します。
項目
内容
Typ=
Lq=
LQI値が格納されます。
Ad=
32bitの送信元アドレスと8bitの送信元論理アドレスが格納されます。
Vmv=
電源電圧です。
※ メッセージにデータがない場合は 0000 となります。
Tms=
メッセージを受信したシステム時間が格納されます。
millis()を実装しておく必要があります。

ソースコードの構造

解説 Parser についても参照ください。

ヘッダ部

1
#include <iostream>
2
#include <iomanip>
3
#include <string>
4
5
#include "twe_common.hpp"
6
#include "twe_sercmd_ascii.hpp"
7
#include "twe_fmt.hpp"
8
9
using namespace TWE;
10
using namespace TWEUTILS;
11
using namespace TWESERCMD;
12
using namespace TWEFMT;
13
14
AsciiParser parse_ascii(256);
Copied!
必要な標準ライブラリヘッダとMWM5ライブラリヘッダを読み込みます。
MWM5ライブラリでは細かく名前空間が使用されていますが、利用時には煩雑ですのですべてusing namespaceにより省略可能とします。
AsciiParserのクラスインスタンスを生成しておきます。

メインループ

1
int c;
2
while ((c = std::cin.get()) >= 0) {
3
if (c == '\r' || c == '\n') {
4
parse_ascii << char_t(0x0d); // always 0x0d (this parser does not check the following 0x0a)
5
}
6
else {
7
parse_ascii << char_t(c);
8
}
9
10
if (parse_ascii) {
11
std::cout << "PKT";
12
auto&& payl = parse_ascii.get_payload(); // payload data array
13
auto&& pkt = newTwePacket(payl); // packet object (contains minimum information)
14
auto&& typ = identify_packet_type(pkt); // packet type
15
std::cout << ":Typ=" << int(typ);
16
17
if (typ != E_PKT::PKT_ERROR) {
18
// ... display common packet info
19
}
20
21
// display each packet information
22
switch (typ) {
23
case E_PKT::PKT_PAL: print_pal(pkt); break;
24
case E_PKT::PKT_ACT_STD: print_act(pkt); break;
25
case E_PKT::PKT_TWELITE: print_app_twelite(pkt); break;
26
case E_PKT::PKT_APPIO: print_app_io(pkt); break;
27
case E_PKT::PKT_APPUART: print_app_uart(pkt); break;
28
case E_PKT::PKT_APPTAG: print_app_tag(pkt); break;
29
default: print_unknown(payl); // e.g. UART message of App_Twelite, App_UART(simple format), or corrupted.
30
}
31
}
32
}
Copied!
このサンプルでは標準入力から1バイトずつ入力(std::cin::get())して、これをASCIIパーサーに投入します。
標準入力ではシステムによって改行コードが異なりますが、ここでは'r'または'\n'のいずれが来ても'\r'を投入するようにしています。本パーサーは末尾の'\r'のみを評価するためです。
1
if (parse_ascii) {
Copied!
投入後書式が完了した場合はparse_asciitrueを戻します。ここでデータを解釈します。
1
auto&& payl = parse_ascii.get_payload();
2
auto&& pkt = newTwePacket(payl);
Copied!
ASCIIパーサーにより得られたバイト列は.get_payload()メソッドにより配列SmplBuf_Byte型として取り出すことが出来ます。
このサンプルでは得られたバイト列を解釈して、どの種別のUARTメッセージなのか、何が格納されているのかを取り出せるようにします。
バイト列を解釈する手続きはnewTwePacket()にバイト列paylを与えます。戻り値としてクラスオブジェクトpktを生成します。このオブジェクトはspTwePacket型の(メモリ管理を行う)スマートオブジェクトで、TwePacket型のオブジェクトを格納します。
1
auto&& typ = identify_packet_type(pkt); // packet type
Copied!
実際にはpktは種別に応じたTwePacket型の派生クラスのインスタンスが格納されています。この派生クラスの種別を確認するのがidentify_pkt_type()で、パケット種別としてE_PKT型の値を返します。
パケット種別が得られたら、パケット種別ごとの表示関数print_???(spTwePacket)を呼び出しています。
ここから先はパケット種別ごとに手続きが違います。以下にApp_Tweliteの解釈例、App_PALの解釈例を示します。
1
void print_app_twelite(spTwePacket pkt) {
2
auto&& atw = refTwePacketTwelite(pkt);
3
std::cout << ":DI1..4="
4
<< char(atw.DI1 ? 'L' : 'H')
5
<< char(atw.DI2 ? 'L' : 'H')
6
<< char(atw.DI3 ? 'L' : 'H')
7
<< char(atw.DI4 ? 'L' : 'H');
8
...
Copied!
App_Twelite の UARTメッセージの解釈を行います。まずspTwePacket型のままでは、App_Twelite特有のデータにアクセスできません。
まずrefTwePacketTwelite()を呼びます。戻り値はTwePacketTweliteの参照型です。App_TweliteのUARTメッセージを解釈した特有のデータが含まれます。例えば.DI1はDI1のHigh/Low状態を示します。
1
void print_pal(spTwePacket pkt) {
2
auto&& pal = refTwePacketPal(pkt);
3
PalEvent ev;
4
5
// acquire event data.
6
if (pal.has_PalEvent()) {
7
ev = pal.get_PalEvent();
8
}
9
10
switch(pal.get_PalDataType()) {
11
case E_PAL_DATA_TYPE::EVENT_ONLY:
12
/* イベントのみが含まれるパケットの場合 */ break;
13
14
case E_PAL_DATA_TYPE::MAG_STD:
15
/* MAG PAL の表示コード */ break;
16
17
case E_PAL_DATA_TYPE::MOT_STD:
18
/* MOT PAL の表示コード */ break;
19
20
case E_PAL_DATA_TYPE::AMB_STD:
21
/* AMB PAL の表示コード */ break;
22
23
case E_PAL_DATA_TYPE::EX_CUE_STD:
24
/* TWELITE CUE のメッセージ */ break;
25
}
Copied!
TWELITE PALのUARTメッセージを解釈します。TWELITE PALは接続される基板によって異なるUARTメッセージが出力されます。このためデータ取得も一旦PALとして解釈してから、さらに個別のPAL基板のデータを取り出します。
1
auto&& pal = refTwePacketPal(pkt);
Copied!
最初にPAL共通データをrefTwePacketPal()により取り出します。
1
if (pal.is_PalEvent()) {
Copied!
最初にパケットにイベントが含まれるかどうかの判定を行います。イベントパケットは例えば通知(NOTICE)PALの加速度センサーが静止状態から加速度を検出した(タップ)といった現象をイベント番号として伝えるものです。イベントパケットにも様々なデータが付加されますが、通常はイベント番号を参照します。
1
switch(pal.get_PalDataType()) {
Copied!
パケットの種別を.get_PalDataType()により判定します。
1
case E_PAL_DATA_TYPE::MAG_STD:
2
{
3
PalMag mag = pal.get_PalMag();
4
std::cout << ":STAT=" << int(mag.u8MagStat);
5
...
Copied!
PAL基板の種別がわかったら、PAL基板の種別に応じた手続き(.get_PalMag()など)により、PAL基板特有のデータが含まれるクラスオブジェクトを得ます。
上記の例では開閉センサーPALの磁気センサーの状態を得ています。
最終更新 1mo ago