自動耐圧試験システムの構築　その２

耐圧試験システムの構築　その１からの続き。
プログラム作成について記述する。

# 1. プログラムの作成

繰り返しになるが、最初にシステム構成について記述しておく。

# 1-1. システム構成

今回は、絶縁耐圧試験器（日置電機社製　型番3159）の耐圧試験を例にシステムを構築する。
システムは、Raspberry Pi 4B（ラズパイ）、ATOM Lite、絶縁耐圧試験器から構成した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/798486dc-0aaf-f28f-5ab7-a0d1f2119441.png)


ATOM Liteは、ラズパイに設定したWiFiアクセスポイントに接続し、ATOM Liteと絶縁耐圧試験器はレベルシフトしたRS232Cで接続した。
ラズパイとATOM Liteは、MQTTを用いてデータ交換することにした。
ラズパイに試験用データベースをMariaDBに設定し、試験項目一覧テーブルと試験結果テーブルを設けた。
ラズパイはNode-RED、ATOM LiteはUIFowでプログラムを作成した。
ユーザインタフェースは、Node-REDのダッシュボードを用いた。

# 1-2. シーケンス図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/004a2e74-76ed-f609-463e-60d273a4c5d5.png)

(1)　DBに試験項目一覧を問合せ、Node-REDのダッシュボード上に表示する
(2)　ダッシュボードで試験項目をキー入力し、OKボタンを押す。試験器に送信する設定コマンドが表示される。
(3)　送信ボタンを押すことで設定コマンド一式をMQTTでATOM Liteに送信する。
(4)　ATOM Liteは、MQTTで設定コマンドをサブスクライブしたら、設定コマンドを1つずつRS232Cで試験器に送信する。すべてのコマンドが終了したらMQTTでreadyを返す。
(5)　ラズパイは、MQTTでReadyをサブスクライブしたら、ダッシュボードのLEDを緑色にし、readyの文字を表示する。
(6)　ダッシュボードでロットナンバーを入力すると、ロットナンバーがダッシュボードに表示される。
(7)　ダッシュボードのスタートボタンを押すと、MQTTでstartがパブリッシュされる。
(8)　ATOM Liteは、MQTTでstartをサブスクライブしたら、スタートコマンドをRS232Cで試験器に送信する。環境センサの温度、湿度、気圧をJSON形式に変換し、MQTTでパブリッシュする。
(9)　ラズパイは、MQTTで温湿度データをサブスクライブしたら、flow変数に代入する。
(10)　ATOM Liteは、:STARTコマンド送信後、一定間隔で:STATコマンドを送信して試験終了を確認する。
(11)　WREADYを受信したら試験終了と判断して、試験結果問合せコマンド:MEAS:RES:WITH?を送信し、試験結果を取得し、JSON形式に変換してMQTTにパブリッシュする。　　
(12)　ラズパイは、MQTTで試験結果をサブスクライブしたら、DBに登録し、ダッシュボード上に表示する。

# 1-2.プログラムの作成

# 1-2-1.Node-REDのプログラムについて

Node-REDプログラムについて、シーケンス図に沿って説明する。

## (1)　DBに試験項目一覧を問合せ、Node-REDのダッシュボード上に表示する

Nod-REDに以下のフローを配置する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/78f8cfc2-7ba1-01cb-da73-caa570234073.png)

functionノードにSQL文をmsg.topicとして記述する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/663f5691-d181-4182-15f9-c0344960d3e0.png)

~~~md:select文を準備
msg.topic = "SELECT id, exam_mode, ref_voltage, exam_time, upper_limit, lower_limit, exam_volt_range FROM command;";
return msg;
~~~

mysqlノードを設定する。ユーザ名、パスワード、データベース名の欄を入力する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/d3fb054e-c672-a682-d536-df0774f65b38.png)

templateノードを利用して表示する文字の大きさを調整する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/dd476e15-32ca-4838-027e-197c1840cab7.png)

これを実行するとダッシュボード上に試験項目一覧が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/1207326c-c85a-48e8-bd3a-5485cc0cb57d.png)



## (2)　ダッシュボードで試験項目をキー入力し、OKボタンを押す。試験器に送信する設定コマンドが表示される。

formノードを利用して、試験一覧表のidを入力することで、試験器を送信する設定コマンドを選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/7ab539f1-722b-ed82-5e21-fe46e675fe56.png)

formノードは、OKボタンを押すと入力したidが表示されなくなるが、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/893e715a-d716-8583-4858-623cffb2c5c7.png)

idと送信するコマンドをテーブル表示することで確認できるようにした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/adfe4d82-973d-cd36-984a-128ba13c3a6c.png)

このテーブルを表示するために、formノードから出力されるidの値を元に、functionノードでSQL文を作成してデータベースを参照している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/037dd9c3-604d-3ede-fa0b-5839e9ab29d3.png)

~~~md:select文を準備
msg.topic = "select id, cmd_mode, cmd_ref_volt, cmd_exam_time, cmd_upper_limit, cmd_lower_limit, cmd_exam_volt_range from command where id = \u0027";
msg.topic += msg.payload.id;
msg.topic += "\u0027;";

return msg;
~~~


ATOM Liteに送信するコマンド一式をflow変数commandに配列として代入し、一時保存した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/5a9c4d2f-ca0f-cba1-d1c0-f7770be5972d.png)

~~~md:設定コマンドをflow変数に代入
flow.set("command", [msg.payload[0].cmd_mode,vmsg.payload[0].cmd_exam_time, msg.payload[0].cmd_upper_limit, msg.payload[0].cmd_lower_limit, msg.payload[0].cmd_exam_volt_range]);
return msg;
~~~

また、試験結果をデータベースへ登録際に、idも記録するために、グローバル変数に代入する。
グローバル変数は、このフローのみに限定するために、フロー変数exam_idを設定して代入した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/d6e137b1-dab2-f08d-8cf3-56cd3a2a1afc.png)

~~~md:試験項目をflow変数に代入
flow.set("command", [msg.payload[0].cmd_mode,vmsg.payload[0].cmd_exam_time, msg.payload[0].cmd_upper_limit, msg.payload[0].cmd_lower_limit, msg.payload[0].cmd_exam_volt_range]);
return msg;
~~~

## (3)　送信ボタンを押すことで設定コマンド一式をMQTTでATOM Liteに送信する。

コマンド送信ボタンを押すことで、buttonノードに接続されたfunctionノードで、flow変数commandから配列を取り出し、トピック3159/settingでMQTTにパブリッシュした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/666079ef-f1e7-bb03-f2f6-4c8af0d1c016.png)

flow変数commandから配列を取り出す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/6611beb9-5a85-d334-6b19-83efae6e34b8.png)

~~~md:設定コマンドの取り出し
msg.payload = flow.get("command");
return msg;
~~~

<!---
  コメント
 (4)　ATOM Liteは、MQTTで設定コマンドをサブスクライブしたら、設定コマンドを1つずつRS232Cで試験器に送信する。すべてのコマンドが終了したらMQTTでreadyを返す。
-->

## (5)　ラズパイは、MQTTでReadyをサブスクライブしたら、ダッシュボードのLEDを緑色にし、readyの文字を表示する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/580023f4-0b33-dcee-10d2-ae397527980e.png)

startをMQTTにパブリッシュする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/50596194-dba4-7988-6d58-615b299c8b92.png)

試験開始日時をflow変数に代入

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/9106f3bb-c5c4-3d7f-2a03-d6888987804d.png)

~~~md:試験開始日時をフロー変数に代入
const dt = new Date();
var date = dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate();
var time = dt.getHours() + ':' + dt.getMinutes() + ':' + dt.getSeconds();
var date_time = date +" "+ time;
flow.set("exam_date", date_time);
msg.payload = date_time;
return msg;
~~~


## (6)　ダッシュボードでロットナンバーを入力すると、ロットナンバーがダッシュボードに表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/dc472687-23e1-b9f3-4bf9-347d599ce884.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/411d61c4-315f-9865-7b5e-a7b26971bf12.png)

~~~md:ロット番号をフロー変数に代入
flow.set("lot_no", msg.payload.lot_no);
msg.payload = msg.payload.lot_no;
return msg;
~~~

## (7)　ダッシュボードのスタートボタンを押すと、MQTTでstartがパブリッシュされる。

## (9) 環境センサの値を受け取ったら、flow変数に代入する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/a29e296e-848a-b756-afae-28c9a2c073c5.png)

ATOM Liteから3159/tempのタグでMQTT送信されてくる環境センサの値（温度、湿度、気圧）をflow変数tmo, hum, atmに代入する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/5828275a-fc10-0e32-e1b3-269273892e8b.png)

~~~md:環境センサの値をフロー変数に代入
flow.set("tmp", msg.payload.tmp);
flow.set("hum", msg.payload.hum);
flow.set("atm", msg.payload.atm);
msg.payload = [msg.payload.tmp, msg.payload.hum, msg.payload.atm]
return msg;
~~~

また、試験結果をデータベースに登録するための準備として、試験結果保存テーブルresultから一番最後に登録したid番号を取得する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/cccd1f8b-10b1-9f08-942e-96d749893eb0.png)

~~~md:最新データのid番号を取得するselect文
msg.topic = "SELECT id FROM `result` ORDER BY id DESC LIMIT 1;";
return msg;
~~~


取得したid番号に１を加算して、flow変数に代入する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/ea131e76-e6b9-eb92-9496-53f2d86e3efd.png)

~~~md:DBに登録するidをフロー変数に代入
var a = msg.payload[0].id;
a++; 
flow.set("id", a);
msg.payload = a;
return msg;
~~~

## (12)　ラズパイは、試験結果を受信後、DBに登録し、ダッシュボード上に表示する。

ATOM Liteから3159/resultのタグでMQTT送信されてくる試験結果をデータベースに登録し、登録されたか確認するために最新登録のデータを取得してtableノードに送りダッシュボードに表示している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/cad03d6e-8bef-804a-9d52-f23ae1c6e606.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/9b6ecd8a-4451-7ffb-f536-f3520f2f453c.png)

~~~md:DB追記用select文
//"INSERT INTO `result` VALUES ('id', 'lot_no', 'exam_date', 'exam_id', 'ret_volt', 'ret_current', 'ret_time', exam_result', 'tempature', 'humidity', 'atmosphere');
msg.topic = "INSERT INTO `result` VALUES ( \u0027";
msg.topic += flow.get("id");
msg.topic += "\u0027, \u0027";
msg.topic += flow.get("lot_no");
msg.topic += "\u0027, \u0027";
msg.topic += flow.get("exam_date");
msg.topic += "\u0027, \u0027";
msg.topic += flow.get("exam_id");
msg.topic += "\u0027, \u0027";
msg.topic += msg.payload.VOLT;
msg.topic += "\u0027, \u0027";
msg.topic += msg.payload.CURR;
msg.topic += "\u0027, \u0027";
msg.topic += msg.payload.TIME;
msg.topic += "\u0027, \u0027";
msg.topic += msg.payload.RSLT;
msg.topic += "\u0027, \u0027";
msg.topic += flow.get("tmp");
msg.topic += "\u0027, \u0027";
msg.topic += flow.get("hum");
msg.topic += "\u0027, \u0027";
msg.topic += flow.get("atm");
msg.topic += "\u0027 );";

return msg;
~~~

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/1a844790-88e9-0520-1457-944dd2758cdf.png)

~~~md:DBに登録するしたデータの確認用select文
msg.topic = "select * from result where id = \u0027";
msg.topic += flow.get("id");
msg.topic += "\u0027;";
return msg;
~~~

# 1-2-2.UIFlowプログラムについて

初期化処理では、Raspberry PiのWiFiアクセスポイントへの接続、UARTの初期化、MQTTサーバへの接続、変数の初期化などの処理を行っている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/9fdefc0e-bc96-95f4-5939-67222a35af80.png)

## (4)　ATOM Liteは、MQTTで設定コマンドをサブスクライブしたら、設定コマンドを1つずつRS232Cで試験器に送信する。すべてのコマンドが終了したらMQTTでreadyを返す。

MQTTのトピック3159/settingで試験器の設定コマンド配列が送られてくると、送信データの両端の[]を削除し、カンマを分割文字にリストを作成する。その後、setting変数を１にする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/9ce04f33-f443-0178-c451-81e5f97c93f7.png)

ループ内では、MQTTをサブスクライブしたときに１を設定する変数をif文でチェックする構造にした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/cfcd80fd-84fd-5be7-f5db-9991403548ae.png)

変数settingが１である場合、settingサブルーチンを実行する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/e3fd9f63-5613-639b-4629-a54bbbd0a436.png)

settingサブルーチンでは、最初に絶縁耐圧試験試験器を初期化する*RSTコマンドをRS232Cへ送信する。
RS232Cのバッファをスキャンして試験器からの応答OKが確認できたら、RaspberryPiから送信された試験器の設定コマンドを順番にRS232Cに送信し、最後のコマンドに対する応答OKが確認できたら、MQTTのトピック3159/examに"ready"をパブリッシュする。

## (8)　ATOM Liteは、MQTTでstartをサブスクライブしたら、スタートコマンドをRS232Cで試験器に送信する。環境センサの温度、湿度、気圧をJSON形式に変換し、MQTTでパブリッシュする。

トピック3159/startをサブスクライブして受信を確認したら、変数start, status, contimueを１にする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/ca1ff996-0dec-1b67-f6cc-c940859ca19d.png)

変数startが１なので、ループでサブルーチンstartが呼び出され実行される。
試験器にRS232Cで試験開始コマンド:SRARTを送信し、応答OKが確認されたら、MQTTのトピック3159/returnに"working"をパブリッシュする。
環境センサの値をサブルーチンtemp2Map2JSONを実行してJSON形式に変換し、MQTTのトピック3159/tempにパブリッシュする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/7ba2dc28-8df7-9f95-9a48-ab06ef41ffff.png)

サブルーチンtemp2Map2JSON

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/fe0ac6da-d132-de6a-3161-9a92f4c606ea.png)

## (10)　ATOM Liteは、:STARTコマンド送信後、一定間隔で:STAT?コマンドを送信して試験終了を確認する。

変数statusが１なので、ループでサブルーチンstatusが呼び出され実行される。

サブルーチンstatusでは、試験器にRS232Cで状態確認コマンド:STAT?を送信し、応答がWTESTであれば、MQTTのトピック3159/returnに"working"をパブリッシュする。応答がWREADYになるまで繰り返す。
応答がWREADYであれば、試験終了と判断し、試験結果問合せコマンド:MEAS:RES:WITH?を試験器に送信し、試験結果を取得する。
取得した試験結果は、サブルーチンresult2Map2JSONでJSON形式に変換してMQTTのトピック3159/resultにパブリッシュする。
また、同時にMQTTのトピック3159/returnに"ready"をパブリッシュする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/8b70607e-b85b-22ef-70a8-ba5195d61ebf.png)

サブルーチンresult2Map2JSON

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36206/206f0d31-dd12-95b9-3b5b-dee54e7434fe.png)



以上で、自動耐圧試システムが完成する。