# glancer\_con

App\_Wings（または各アプリのUART出力)を解釈して、各パケットメッセージの概要を表示します。

{% hint style="info" %}
glancerは「glance（一目する）人」の意味の英単語です。
{% endhint %}

## ファイル構成

### プロジェクトフォルダ

```
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
```

{% hint style="warning" %}
上記で必要なファイル等は本ドキュメントと相違ある場合もあります。問題があればビルド時にエラーが表示されますので、エラーファイルに記述される"～not found"といったメッセージを頼りに足りないファイルをコピーしてください。
{% endhint %}

## ビルド

### ビルドに必要な定義

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

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

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

##

### ビルド方法 (Makefile, gcc)

make (GNU) と g++ (gcc より) が必要です。事前にインストールを済ませてください。

`Makefile`を確認してください。以下の定義は必要に応じて書き換えます。

| 定義名     |                                                                                    |
| ------- | ---------------------------------------------------------------------------------- |
| CXX     | <p>g++-9 など実行名が違う場合に書き換えます。</p><p>※ Makefile 中で動作環境を判定している部分があり、この部分に定義が含まれます。</p> |
| DEFINES | 必要に応じて。 `-D???` の定義をコンパイラに与えます。                                                    |
| CFLAGS  | 必要に応じて。コンパイルオプションをコンパイラに与えます。                                                      |

makeコマンドを実行します。

### millis()の実装

```cpp
#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
```

標準入力(通常の１バイト入力を用いているため改行が入力されるまでは入力文字列は評価されません)にアスキー形式のUARTメッセージを入力します。出力は`PKT...`となります。

先頭の項目について解説します。

| 項目   | 内容                                                                               |
| ---- | -------------------------------------------------------------------------------- |
| Typ= | [パケットの種別](https://mwm5.twelite.info/references/parser/twefmt/twepacket/e_pkt)です。 |
| Lq=  | LQI値が格納されます。                                                                     |
| Ad=  | 32bitの送信元アドレスと8bitの送信元論理アドレスが格納されます。                                             |
| Vmv= | <p>電源電圧です。</p><p>※ メッセージにデータがない場合は 0000 となります。</p>                               |
| Tms= | <p>メッセージを受信したシステム時間が格納されます。</p><p>※ <code>millis()</code>を実装しておく必要があります。</p>     |

## ソースコードの構造

{% hint style="info" %}
解説 [Parser ](https://mwm5.twelite.info/getting-started/using-library/parser)についても参照ください。
{% endhint %}

### ヘッダ部

```cpp
#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`](https://mwm5.twelite.info/references/parser/twesercmd/asciiparser)のクラスインスタンスを生成しておきます。

### メインループ

```cpp
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.
		}
  }
}
```

このサンプルでは標準入力から１バイトずつ入力(`std::cin::get()`)して、これをASCIIパーサーに投入します。

標準入力ではシステムによって改行コードが異なりますが、ここでは`'r'`または`'\n'`のいずれが来ても`'\r'`を投入するようにしています。本パーサーは末尾の`'\r'`のみを評価するためです。

```cpp
	if (parse_ascii) {
```

投入後書式が完了した場合は`parse_ascii`は`true`を戻します。ここでデータを解釈します。

```cpp
auto&& payl = parse_ascii.get_payload();
auto&& pkt = newTwePacket(payl);
```

ASCIIパーサーにより得られたバイト列は`.get_payload()`メソッドにより配列`SmplBuf_Byte`型として取り出すことが出来ます。

このサンプルでは得られたバイト列を解釈して、どの種別のUARTメッセージなのか、何が格納されているのかを取り出せるようにします。

バイト列を解釈する手続きは`newTwePacket()`にバイト列`payl`を与えます。戻り値としてクラスオブジェクト`pkt`を生成します。このオブジェクトは`spTwePacket`型の（メモリ管理を行う）スマートオブジェクトで、`TwePacket`型のオブジェクトを格納します。

```cpp
auto&& typ = identify_packet_type(pkt);  // packet type
```

実際には`pkt`は種別に応じた`TwePacket`型の派生クラスのインスタンスが格納されています。この派生クラスの種別を確認するのが`identify_pkt_type()`で、パケット種別として`E_PKT`型の値を返します。

パケット種別が得られたら、パケット種別ごとの表示関数`print_???(spTwePacket)`を呼び出しています。

ここから先はパケット種別ごとに手続きが違います。以下にApp\_Tweliteの解釈例、App\_PALの解釈例を示します。

### print\_app\_twelite()

```cpp
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`](https://mwm5.twelite.info/references/parser/twefmt/packet-types/twepackettwelite)の参照型です。App\_TweliteのUARTメッセージを解釈した特有のデータが含まれます。例えば`.DI1`はDI1のHigh/Low状態を示します。

### print\_pal()

```cpp
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基板のデータを取り出します。

```cpp
auto&& pal = refTwePacketPal(pkt);
```

最初にPAL共通データを`refTwePacketPal()`により取り出します。

```cpp
if (pal.is_PalEvent()) {
```

最初にパケットにイベントが含まれるかどうかの判定を行います。イベントパケットは例えば通知(NOTICE)PALの加速度センサーが静止状態から加速度を検出した（タップ）といった現象をイベント番号として伝えるものです。イベントパケットにも様々なデータが付加されますが、通常はイベント番号を参照します。

```cpp
switch(pal.get_PalDataType()) {
```

パケットの種別を`.get_PalDataType()`により判定します。

```cpp
case E_PAL_DATA_TYPE::MAG_STD:
{
			PalMag mag = pal.get_PalMag();
			std::cout << ":STAT=" << int(mag.u8MagStat);
			...
```

PAL基板の種別がわかったら、PAL基板の種別に応じた手続き(`.get_PalMag()`など)により、PAL基板特有のデータが含まれるクラスオブジェクトを得ます。

上記の例では開閉センサーPALの磁気センサーの状態を得ています。
