1. 排他的操作

排他的操作とは、現在操作しているユーザーに操作する権限を占有させ、他のユーザーと競合しないように、他のユーザーからの操作を制限する機能を言います。R-CPSではNode-REDのダッシュボードを使ってデータの表示や操作を行いますので、複数のユーザーが同時に操作してしまう競合が発生し易くなります。Node-REDのダッシュボードを使った、排他的操作の事例をを解説します。

1-1.機能概要

ダッシュボードを表示すると、接続元個別の識別子:msg.scokectidがサーバーであるNode-REDから付与されます。そして、ダッシュボードのスイッチやスライダーを操作すると、操作値(msg.payload)と共に、誰が操作したのかを知らせるために、msg.socketidがノードから出力されます。この機能を使い、ユーザー端末を識別し、特定の端末からの操作だけを許可するようにフローを記述します。

具体的には、グローバル変数:global.socketidを、識別のために使用します。これに設定されている値で未接続と接続中を判断します。
未接続の場合には、初めに接続ボタンを押した端末のmsg.socketidが設定されて、接続中に移行します。
接続中の場合には、global.socketidと等しいmsg.socketidを持つ端末しか切断ボタンを含めて操作することはできません。

以下、もう少し詳しく動作を説明します。

global.socketidの初期値には、ダミーのsocketidである”demae0000000000“を設定します。このダミーのsocketidが、global.socketidに設定されているときは、未接続状態を意味します。状態の表示は、未接続です。

そして、ダッシュボードの「接続」ボタンが押されると、その端末のmsg.socketidを取得し、global.socketidが初期値”demae0000000000“ならば、未接続と判断し上書きます。そして、状態が、接続中に移ります。

接続中にスライダーやスイッチを押された場合、その端末のmsg.socketidとglobal.socketidを比較し、同じ場合のみ操作を反映します。異なるmsg.socketidの端末から操作しても、出力には反映されず、2秒後に事前の状態に戻ります。

接続中に他の端末から「接続」ボタンが押されると、接続要求が出されたことが表示されますが、接続中はglobal.socketidは変わりません。
同様に、接続中に他の端末から「切断」ボタンが押されても、無視され、global.socketidは変わりません。

global.socketidと同じmsg.socketidを持つ端末から、「切断」ボタンが押されるまでは接続中が維持されます。そして、「切断」ボタンが押されると、global.socketidは初期値と同じ”demae0000000000“に戻され、未接続の状態になります。
もし、 global.socketidと同じmsg.socketidを持つ端末が「切断」を押さずにダッシュボードから抜けた場合には、抜けたことを検出して、 global.socketidを初期値と同じ”demae0000000000“に戻し、未接続の状態に初期化します。

1-2.フローの概要説明

以下にフローを示します。フローのJSONファイルは、Appendixに載せています。

 

フローの肝としては、2つあります。

1) global変数

以下の3つのglobal変数を使用しています。global.switchとglobal.sliderは、許可されていない端末からの操作を2秒後に元に戻すために使用されます。
global変数に関しては、こちら(ノード間のデータの受け渡し(Context))を参照ください。
global.socketid : ダミーid “demae0000000000”か、未接続状態で最初に「接続」ボタンを押した端末のmsg.scoketidを保持
global.switch : global.socketidと同じmsg.socketidを持つ端末が設定したswicthの状態 “true” or “false”
global.slider : global.socketidと同じmsg.socketidを持つ端末が設定したsliderの操作値 0~10の整数

2) ui-controlノード

ui-controlノードは、ダッシュボードのタブやグループなどの表示切替などの制御に使用されるノードですので、ダッシュボードへの接続(connect)、切断(lost)、タブの移動(change)などの情報を出力してくれます。これを、「切断」ボタンを押さないまま、ダッシュボードから抜けていった端末を検出するのに使用します。
ダッシュボードを抜けた端末があった場合のui-controlノードの出力を以下に示します。payload: “lost”, scoketid: “NOVvHuLWieGVWT-MAAAB”のように表示されます。これを利用して、msg.payload:”lost”の場合のmsg.socketid:が、global.socketidと同じだったら、「切断」し忘れと判断して、global.socket.idを初期化します。

 

2. ユーザー制限

ユーザー制限とは、ユーザーIDとパスワードでの認証により、限られたユーザーだけに操作することを可能にする機能です。

ここでは、上記の排他制御にユーザー制限を追加します。

2-1. ユーザー制限の方法

ダッシュボードの閲覧にはユーザー制限を設けず、操作を行うことにのみユーザー制限をかけることから、socketidの認証記録簿方式の以下の様なユーザー制限の方法を行います。

①「接続」ボタンを押した際に、socketidの認証記録簿にscoketidが登録されているかどうかをまず確認します。
②登録されている場合には、socketidによる排他制御に移ります。
③登録されていない場合には、ユーザー認証画面に移動し、ユーザーIDとパスワードを入力してもらいます。ユーザー認証OKの場合(ユーザーIDとパスワードが正しい)は、認証が出来た旨を表示し、socketidを記録します。その後、 socketidによる排他制御に移ります。
④認証記録簿からのsocketidの削除は、利用者がダッシュボードから抜けた際に行います。ですので、一度認証すると、抜けるまで「接続」ボタンを押しても、認証画面に移ることはありません。
⑤認証記録簿は、10人の利用者までのsocketidを記録できます。11人目の利用者が認証OKしても、「参加者が10名を超えました」と表示して、認証できません。誰か利用者がダッシュボードから抜けると、socketid記録簿に空きができますので、認証できるようになります。

2-2. socketid記録簿の実装

socketid記録簿の実装に関して説明します。global変数の配列global.mem_id[]を使用します。11個の配列を使用します。
利用者が登録されていない配列には、すべて”non”を入れておきます。そのため、”non”が入っている配列は、socketidが登録されていないことを意味します。
そして、初めの人が認証OKになると、global.mem_id[0]にsocketidを保存します。2人目のsocketidは、global.mem_id[1]に保存されます。5人目の人はglobal.mem_id[4]に、10人目の人はglobal_mem_id[9]にsocketidが保存されます。

具体的な動作を説明します。

a)global.mem_id[]の初期化
Node-REDの起動直後に、global.mem_id=[“non”, “non”, “non”, “non”, “non”, “non”, “non”, “non”, “non”, “non”, “non”];のようにすべて”non”を代入します。

b)global.mem_id[]への追加
global.mem_id[]をgetし、global.mem_id[0]から順に、”non”かどうかを調べていきます。初めに”non”が出た場所に、socketidを追加します。追加後、global.mem_id[]にsetします。

c) mem_id[]にsocketidが登録されているかの確認 global.mem_id[]をgetし、global.mem_id[0]から順に、sockteidが一致するかどうか“non”がでるまで調べていきます。”non”がでてきたら登録されていません。

d) mem_id[]からのsocktidの削除
i) mem_id[]をgetし、global.mem_id[0]から順に調べて、何番目に”non”がでるかを確認します。例えば、global.mem_id[5]に”non”があれば、5人の利用者のsocketidが登録されています。global.mem_id[10]に”non”があれば、10人の利用者のsocktidが登録されています(定員一杯です)。
ii) 次に、mem_id[]の何番目に削除するsocktidが登録されているかを調べます。
iii) 削除するsocktidが登録されていた番号の次の番号から最初の”non”までのデータを登録されていた番号以降に上書きします。これで、削除が完了します。
以下に、sockeidの削除の例を示します。

 

2-3. Node-REDのフロー

次にNode-REDのフローを説明します。下の図の左側は上記で説明した排他的操作のみのフローです。右側はそれに、ユーザー認証を追加したフローです。
追加した部分のポイントを説明します。

①のfunctionノードは、Node-RED起動時に、mem_id[]の初期化とusernameとpasswordの設定を行っています。
usenameとpasswordは、必要に応じて、変更してください。

var id = ["non", "non", "non", "non", "non", "non", "non", "non", "non", "non", "non"];
global.set("mem_id", id);
global.set("username","r-cps-member");
global.set("password","r-msm-pdh");
return msg;

②のfunctionノードとswitchノードは、「接続」ボタンを押した利用者のsocketidが、 socketidの認証記録簿に存在するかどうかを調べています。存在すれば、
排他制御のcheck idのノードへ移動し、存在しなければ、登録者の人数が10名に達しているかどうかのチェックを経て、③の認証画面への移行へ移ります。②のfunctionノードでは、上のsocktid記録簿の実装の「c) mem_id[]にsocketidが登録されているかの確認」で述べた内容を実装しています。
存在していれば、is_member=“true”、存在しなければ、is_member=“false”を返します。次段のswitchノードで行先を振り分けます。
もう一つの機能は、記録簿の中の”non”の位置をmsg.numで返します。次の満席のswitchノードで使用します。msg.num=10であれば、定員一杯であるので、「参加者が10名に達しました」と表示して認証画面に移行できません。

var mem_id = global.get("mem_id");
var max = 10;
var num = 10;
var i;
var is_member = false;

// confirm current number of member
for (i=0; i<max; i++){
    if (mem_id[i] == "non"){
        num = i;
        break;
    }
}


// check socketid registered or not
for (i=0; i<num; i++){
    if (msg.socketid == mem_id[i]){
        is_member = true;
        break;
    }
}

msg.num = num;
msg.is_member = is_member;

return msg;

③のchangeノードとui-controlノードで、認証画面を表示させます。
changeノードで、msg.payloadに、移動先のタブ名を、JSONフォーマット{“tab”:”認証画面”}で記載します。それを、ui-controlノードに渡すことで、タブの移動を実現します。ui-controlノードは、出力を「タブ変更もしくはグループイベントのみ」とします。

 

④のformノードで、認証画面を作成します。
・formノードを配置するタブとグループを新規に作成します。
・フォームの要素にユーザーIDとパスワードの欄を作成します。この時、必須にチェックを入れてください。

これで、認証画面が出来上がりました。

ユーザーidとパスワードが、①の初期化ノードで設定したglobal.username, global.passwordと一致するかswitchノードで確認して一致すれば、
⑤のfunctionノードで、socketid記録簿に追加します。ここでは、上のsocketid記録簿の実装の「b) global.mem_id[]への追加」で述べた内容を実装しています。

var mem_id = global.get("mem_id");
var max = 10;
var num = 10;
var i;

// confirm current number of member
for (i=0; i<max; i++){
    if (mem_id[i] == "non"){
        num = i;
        break;
    }
}

// store socket id
mem_id[i] = msg.socketid;

// socketid registered
global.set("mem_id", mem_id);

msg.mem_id = mem_id;
return msg;

ユーザーidとパスワードが、①の初期化ノードで設定したglobal.username, global.passwordと一致しなかった場合と、一致した後にsocketid記録簿への追加が終わったら、それぞれ、「認証できませんでした」と「認証できました」の表示をnotificationノードで出してから、⑥のchangeノードとui-controlノードで、”認証画面“から、”操作テスト”の画面に戻ります。
changeノードで、msg.payloadに、移動先のタブ名を、JSONフォーマット{“tab”:”操作テスト”}で記載します。ui-controlノードの設定は上と同じです。

最後に、利用者が抜けた場合のsocketid記録簿からの削除の処理です。
⑦のfunctionノードで実装しています。上のsocktid記録簿の実装の「d) mem_id[]からのsocktidの削除」で説明した内容を実装しています。
赤枠で囲った部分で、抜けた利用者のsocketidの記録簿内位置を探して、見つかったら、一つずつ、大きい位置のsocktidをずらして上書きしていきます。最後は、“non”が上書きされ、一つsocketidが減ります。

var mem_id = global.get("mem_id");
var max = 10;
var num = 10;
var own = 0;
var i, j;

// confirm current number of member
for (i=0; i<max; i++){
    if (mem_id[i] == "non"){
        num = i;
        break;
    }
}

// check socketid registered or not
for (i=0; i<num; i++){
    if (msg.socketid == mem_id[i]){
        own = i;
        // shift socketid 
        for (j = own; j < num; j++) {
            mem_id[j] = mem_id[j + 1];
        }
        break;
    }
}

global.set("mem_id", mem_id);

msg.mem_id = mem_id;
return msg;

 

3. ユーザー制限(2): 個別ユーザー名対応と操作ログの保存

2章のユーザー制限では、一つのユーザーIDとパスワードを知っている人だけが操作できるというユーザー制限を実現しました。
ここでは、一人一人のユーザーに個別のユーザー名とパスワードを付与し、それを使って、誰がいつダッシュボードを操作したかと最終設定値をログに保存してみます。

3-1. 動作の概要

2章のユーザー制限のフローに、個別ユーザー名対応と操作ログの記録対応を追加する方針で進めます。追加する機能の概要を以下に説明します。
2章のユーザー制限のフローの仕様には、以下の様な特徴があります。
1) 一度ユーザー認証を済ませると、ダッシュボードを抜けるまで認証が有効。「接続」ボタンを押すたびに認証を要求されることは無い。
2) その代わりに、認証を済ませた待機者は最大10名

3-1-1. 個別ユーザー名とパスワードの認証

ユーザー名とパスワードが複数あることから、それらをあらかじめファイルに記載しておき、起動時に読み込む方式にします。Node-RED起動直後に、ファイルを読み込み、グローバル変数userに2次元配列で保存します。そして、接続ボタンが押されたら、認証画面に移行しユーザー名とパスワードを入力してもらい、グローバル変数userの中に両方共に一致するものがあるかどうかを調べます。一致すれば、socketid記録簿にsocktidを保存します。そして、この時にユーザー名記録簿の同じ番号にユーザー名も保存します。ユーザー名とsocketidの対応をとるのは、操作ログの作成のためです。ユーザー名とsocketidは認証時にしか対応が取れないので、配列の添え字で対応させて保存します。

【使用するglobal変数】
1) global.user_name:ユーザ名とパスワードが記載されたファイル名(パスを含む)
2) global.user[][2]: ユーザー名とパスワードを保管しておく2次元配列   配列のlengthは、ファイルを読み込んだ時点で決まります。global.user[i][0]が、i番目のユーザー名、global.user[i][1]が、i番目のパスワード
3) global.mem_id[11]:認証できたユーザーのsocketidを保存する配列(認証済みの人のsocketidの管理用。最大10名)
4) global.info[11]:認証できたユーザーのユーザー名を保存する配列。配列の添え字が同じglobal.mem_id[j]とglobal.info[j]は、同じユーザーのsocketidとユーザー名を示します(これも最大10名分)

3-1-2. ログデータの記録

ログデータとして記録するのは、以下の5つです。
1) ユーザー名
2) socketid
3) 操作開始時刻(操作が可能になった時刻)
4) 操作終了時刻(「切断」ボタンを押した時刻もしくは、ダッシュボードを抜けた時)
5) 操作データ:switchノードのデータ(true or false)とsliderノードのデータ(0~10の整数)

・操作開始時:他の人が接続していない状態で「接続」ボタンを押して操作が可能になった時に、ユーザー名とsocketidと現在の時刻を操作開始時刻として、global変数に保存します。

・操作終了時:「切断」ボタンを押したとき、もしくは「切断」せずにダッシュボードを抜けた時に、現在の時刻を操作終了時刻として、global変数に保存します。

そして、操作開始時に保存したユーザー名、socketid、操作開始時刻と今回の操作終了時刻、そして現在の操作データをcsv形式に変換してglobal変数log_nameで指定されたファイルに保存します。

【使用するglobal変数】
1)login_user
2)login_time
3)switch:switchノードのデータ(true or false)
4)slider:sliderノードのデータ(0~10の整数)
5)log_name:logファイル名

3-1-3. ログデータの参照

ログデータとしてグローバル変数log_nameで指定したファイルに保存したデータを、読み込みデバッグ画面に表示させる機能です。
csvファイルを読み込み、オブジェクトの配列に保存して、最後にdebugノードで出力します。

【使用するglobal変数】
1)log_name:logファイル名
2)log_data:読み出したデータを保管するオブジェクトの配列

3-2. Node-REDのフロー

次に、「動作の概要」で説明した内容を実装したNode-REDのフローの説明を行います。下に、フローの全体像を示します。右側の青い一点鎖線で括った部分が今回追加したフローのメイン部分です。Link inノードとLink outノードを使って、左側と右側を繋いでいます。左側の図の赤い番号を付けたLink outから、右側の図の同じ番号に接続しています。

「3-1-1. 個別ユーザー名とパスワードの認証」で動作概要を説明した機能を、④と⑤の部分で実現しています。
「3-1-2. ログデータの記録」で動作概要を説明した機能を、⑥の部分で実現しています。
「3-1-3. ログデータの参照」で動作概要を説明した機能を、⑦の部分で実現しています。

以下、それぞれのフローのポイントを説明していきます。

3-2-1. 個別ユーザー名とパスワードの認証

④の部分では、大きく2つの機能を実現しています。global変数の初期化と、global変数user_nameで指定されたファイルからユーザ名とパスワードを読み出しglobal変数userに保管することです。

1) global変数の初期化

初期化のfunctionノードの記述を次に示します。各種global変数の初期化を行っています。最初の2つのファイル名に関しては、環境に合わせて変更する必要があります。また、ユーザー名とパスワードを記載したファイルのファイル名user_nameは、msg.filenameとして、次段のread fileノードに渡します。

// file for username and password
var user_name = "/home/pi/Documents/userkanri.txt";

// log file name with full path
var log_name = "/home/pi/Documents/user_log.csv";

// initialize routine  # Don't touch!!
var id = ["non", "non", "non", "non", "non", "non", "non", "non", "non", "non", "non"];
var user = new Array();
global.set("user", user);
global.set("mem_id", id);
var info = new Array();
for(var i=0; i<10; i++){
    info[i] = "username";
}
global.set("info",info);
global.set("login_time","19991231125959");
global.set("login_user","username");
global.set("log_name",log_name);
msg.filename = user_name;
global.set("log_data",new Array());
return msg;

2) ユーザー名とパスワードの保管

ユーザー名とパスワードの保管は、read fileノードとcsvファイルノードとswitchノードとfunctionノードの4つで実現しています。

read fileノードで、初期化のノードからmsg.filenameにセットされたファイルを開いて1行毎読み出します。csvノードで読み込んだデータをオブジェクトに変換します。switchノードでは、usernameがキーとして含まれているかをチェックします。その後、functionノードで、global変数userにmsg.payload.usernameとmsg.payload.passwordを保管します。

ユーザー名とパスワードを記載したファイルは、以下の例のように1行目にusername,passwordというヘッダーを記載し、2行目から実際のユーザー名とパスワードを“、”で区切り、1行毎に記載するフォーマットで書く必要があります。

username,password
r-cps-user01,r-cps-pass01
r-cps-user02,r-cps-pass02
r-cps-user03,r-cps-pass03
r-cps-user04,r-cps-pass04

以下の様に、2次元配列として保存されます。参照は、user[0][0]=“r-cps-user01”, user[1][1]=“r-cps-pass02”のように行います。

下の段のcompleteノードとchangeノードとdebugノードは、読み込みがうまく行ったかを確認するためのデバッグ用ノードです。

⑤の部分では、ユーザー認証が終わった時点で、ユーザー名をglobal変数infoに保管します。前段のfunctionノードで、soketidをglobal変数mem_idに保存しています。その時に保存に使用した配列の添え字を、msg.mem_numとして送ってもらっており、その値を使って同じ配列番号に保管します。
これによって、socketidとユーザ名が1対1で対応して保管できます。デバッグ用に、msg.payloadにsockettidとユーザー名を代入しています。

var info = global.get("info");

var num = msg.mem_num;

// save login name
info[num] = msg.payload.username;

global.set("info",info);

msg.payload = {"socketid":msg.mem_id[num], "username":info[num]};
return msg;
3-2-2. ログデータの記録

⑥の部分では、ログとして保存するのに必要なデータを取得し、「切断」ボタンが押された際に、global変数log_nameで指定されたファイルに保存します。

1)操作開始時

link inノードが2つあります。上側(②)が、未接続状態時に「接続」ボタンが押され、操作が開始されたときのパスです。
まず、Date/Time formatterノードで現在の時刻を取得し、msg.datetimeに乗せます。次段のlog in functionノードでは、global変数mem_idの中からsocketidを見つけます。その時の添え字を使って、ユーザー名をglobal変数infoから取得して、global変数login_userにセットします。合わせてmsg.datetimeをglobal変数login_timeにセットします。以上が操作開始時のフローです。

var mem_id = global.get("mem_id");
var info = global.get("info");
var max = 10;
var num = 10;
var i;

// confirm current number of member
for (i=0; i<max; i++){
    if (mem_id[i] == "non"){
        num = i;
        break;
    }
}


// check socketid registered or not
for (i=0; i<num; i++){
    if (msg.socketid == mem_id[i]){
        global.set("login_user", info[i]);
        break;
    }
}

global.set("login_time", msg.datetime);

msg.payload ={"login_user":info[i], "socket_id":msg.socketid, "login_time":msg.datetime};

return msg;

2)操作終了時

下側(③)のlink inノードが、接続中に「切断」ボタンが押された、もしくは、ダッシュボードから抜けた(ui-controlで”lost”検出された)ときのパスです。
まず、Date/Time formatterノードで現在の時刻を取得し、msg.datetimeに乗せます。これは操作開始時と同じです。次段のlog outのfunctionノードでは、ログファイルに保存するためのデータを集めて、msg.payloadオブジェクトに乗せます。

“login_user”:global.login_user
“socket_id”:socketid
“login_time”:global.login_time
“logout_time”:msg.datetime
“switch”:global.switch
“slider”:global.slider

次のcsvノードでオブジェクトをcsv形式に変換します。その次のchangeノードで、ログファイル名を指定したglobal変数log_nameをmsg.filenameにセットします。
そして、write fileノードで、ファイルに書き込みます。

var login_user = global.get("login_user");
var login_time = global.get("login_time");
var socketid = msg.socketid;
var _switch = global.get("switch");
var _slider = global.get("slider");

msg.payload ={"login_user":login_user, "socket_id":socketid, "login_time":login_time, "logout_time":msg.datetime, "switch":_switch, "slider":_slider};

return msg;
3-2-3. ログデータの参照

⑦の部分では、injectノードを押すとログファイルを開いて、csvデータを読み込んで、配列にセットします。csvファイルのデータを配列に読み込むフローに関しては、こちら(csvファイルのデータを配列に読込む)を参照してください。
ポイントとしては、以下の3つになります。

1)read fileにログファイル名として、global変数log_nameをmsg.filenameとして渡しています。
2)ログデータをファイルに記録するところで、1行目にヘッダを付けています。デプロイのたびにヘッダが1行記録されるので、読み込んだ時に、ヘッダを読み飛ばすためのswitchノードを付けています。
3)また、completeノードを使っているのは、配列functionをそのまま出力するとcsvファイルの行数分データが出力されるので、ファイルを読み終わったことを検出して、1秒待って、安定した時点で、global変数log_dataを読み出しています。

 

Appendix. Node-REDのフローファイル

1) 排他的操作のみ

[
    {
        "id": "9624ddaf8e7b3698",
        "type": "tab",
        "label": "排他制御",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "ad5b1d37877d52ee",
        "type": "ui_switch",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "label": "switch",
        "tooltip": "",
        "group": "c56c2f84eee38f59",
        "order": 7,
        "width": 0,
        "height": 0,
        "passthru": true,
        "decouple": "false",
        "topic": "topic",
        "topicType": "msg",
        "style": "",
        "onvalue": "true",
        "onvalueType": "bool",
        "onicon": "",
        "oncolor": "",
        "offvalue": "false",
        "offvalueType": "bool",
        "officon": "",
        "offcolor": "",
        "animate": false,
        "x": 110,
        "y": 440,
        "wires": [
            [
                "d3dd68a84bd2ee59"
            ]
        ]
    },
    {
        "id": "066ef102062a6dc8",
        "type": "ui_button",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "group": "c56c2f84eee38f59",
        "order": 5,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "接続",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "",
        "payloadType": "str",
        "topic": "topic",
        "topicType": "msg",
        "x": 110,
        "y": 280,
        "wires": [
            [
                "a364df582bdb65bd",
                "5e001c2f7435054a"
            ]
        ]
    },
    {
        "id": "6580298aa247f490",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "排他制御",
        "info": "",
        "x": 80,
        "y": 40,
        "wires": []
    },
    {
        "id": "59b75511d3bb0aaa",
        "type": "ui_text",
        "z": "9624ddaf8e7b3698",
        "group": "c56c2f84eee38f59",
        "order": 3,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "接続要求",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 720,
        "y": 240,
        "wires": []
    },
    {
        "id": "f3d968583e2847bd",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "read id",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "socketid",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 160,
        "wires": [
            [
                "c5e54309c7f7bce3"
            ]
        ]
    },
    {
        "id": "c97ff4710e0ffbfb",
        "type": "ui_button",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "group": "c56c2f84eee38f59",
        "order": 6,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "切断",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "",
        "payloadType": "str",
        "topic": "topic",
        "topicType": "msg",
        "x": 110,
        "y": 180,
        "wires": [
            [
                "2d29b5872f72f234"
            ]
        ]
    },
    {
        "id": "2967777faab35bab",
        "type": "inject",
        "z": "9624ddaf8e7b3698",
        "name": "初期化",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 240,
        "y": 220,
        "wires": [
            [
                "90e66fce09272b50"
            ]
        ]
    },
    {
        "id": "c5e54309c7f7bce3",
        "type": "ui_text",
        "z": "9624ddaf8e7b3698",
        "group": "c56c2f84eee38f59",
        "order": 2,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "操作可能",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 720,
        "y": 160,
        "wires": []
    },
    {
        "id": "a364df582bdb65bd",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "check id",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "demae0000000000",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 240,
        "y": 260,
        "wires": [
            [
                "d68a8951689519d6",
                "8d17f7f04fa27519"
            ]
        ]
    },
    {
        "id": "d3dd68a84bd2ee59",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "Judge",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            },
            {
                "t": "neq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 230,
        "y": 440,
        "wires": [
            [
                "729bd4a3291ee25d"
            ],
            [
                "4e20b95c94917b94"
            ]
        ]
    },
    {
        "id": "5e001c2f7435054a",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "display id",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "socketid",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 280,
        "wires": [
            [
                "59b75511d3bb0aaa"
            ]
        ]
    },
    {
        "id": "8b589828c0b36b10",
        "type": "ui_text",
        "z": "9624ddaf8e7b3698",
        "group": "c56c2f84eee38f59",
        "order": 4,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "状態",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 710,
        "y": 340,
        "wires": []
    },
    {
        "id": "a0208d9f0c132d2c",
        "type": "ui_slider",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "label": "slider",
        "tooltip": "",
        "group": "c56c2f84eee38f59",
        "order": 8,
        "width": 0,
        "height": 0,
        "passthru": true,
        "outs": "all",
        "topic": "topic",
        "topicType": "msg",
        "min": 0,
        "max": 10,
        "step": 1,
        "x": 110,
        "y": 620,
        "wires": [
            [
                "6b54522fd8bd7754"
            ]
        ]
    },
    {
        "id": "504973adb30b1a56",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "未接続",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "未接続",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 570,
        "y": 320,
        "wires": [
            [
                "8b589828c0b36b10"
            ]
        ]
    },
    {
        "id": "d68a8951689519d6",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "接続中",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "接続中",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 570,
        "y": 360,
        "wires": [
            [
                "8b589828c0b36b10"
            ]
        ]
    },
    {
        "id": "19e298710e52f945",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 31",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 560,
        "y": 560,
        "wires": []
    },
    {
        "id": "eeee16370fe62739",
        "type": "ui_gauge",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "group": "68a74b5e3cb81569",
        "order": 2,
        "width": 0,
        "height": 0,
        "gtype": "gage",
        "title": "gauge",
        "label": "units",
        "format": "{{value}}",
        "min": 0,
        "max": 10,
        "colors": [
            "#00b500",
            "#e6e600",
            "#ca3838"
        ],
        "seg1": "",
        "seg2": "",
        "x": 570,
        "y": 600,
        "wires": []
    },
    {
        "id": "6b54522fd8bd7754",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "Judge",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            },
            {
                "t": "neq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 230,
        "y": 620,
        "wires": [
            [
                "c6dbb33518226fb1"
            ],
            [
                "e1027b676fc5ea9e"
            ]
        ]
    },
    {
        "id": "b2a18a8c82a7f1cb",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 32",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 560,
        "y": 420,
        "wires": []
    },
    {
        "id": "d6ca2d5a9b099f4f",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "read slider",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "slider",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 250,
        "y": 680,
        "wires": [
            [
                "a0208d9f0c132d2c"
            ]
        ]
    },
    {
        "id": "c6a37e9657b90baf",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "read switch",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "switch",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 250,
        "y": 500,
        "wires": [
            [
                "ad5b1d37877d52ee"
            ]
        ]
    },
    {
        "id": "4e20b95c94917b94",
        "type": "delay",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "pauseType": "delay",
        "timeout": "2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 100,
        "y": 500,
        "wires": [
            [
                "c6a37e9657b90baf"
            ]
        ]
    },
    {
        "id": "e1027b676fc5ea9e",
        "type": "delay",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "pauseType": "delay",
        "timeout": "2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 100,
        "y": 680,
        "wires": [
            [
                "d6ca2d5a9b099f4f"
            ]
        ]
    },
    {
        "id": "a2a31d783183bcbd",
        "type": "inject",
        "z": "9624ddaf8e7b3698",
        "name": "20秒毎",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "20",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 640,
        "y": 200,
        "wires": [
            [
                "59b75511d3bb0aaa"
            ]
        ]
    },
    {
        "id": "e58f52290cef7435",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 36",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 240,
        "y": 80,
        "wires": []
    },
    {
        "id": "287b49bdf06d04da",
        "type": "ui_ui_control",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "events": "all",
        "x": 100,
        "y": 100,
        "wires": [
            [
                "13fdcb314bdb2b29",
                "e58f52290cef7435"
            ]
        ]
    },
    {
        "id": "90e66fce09272b50",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "id初期化",
        "rules": [
            {
                "t": "set",
                "p": "socketid",
                "pt": "global",
                "to": "demae0000000000",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 160,
        "wires": [
            [
                "f3d968583e2847bd",
                "504973adb30b1a56",
                "0865d69acac9245b"
            ]
        ]
    },
    {
        "id": "8d17f7f04fa27519",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "set id",
        "rules": [
            {
                "t": "set",
                "p": "socketid",
                "pt": "global",
                "to": "socketid",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 260,
        "wires": [
            [
                "f3d968583e2847bd"
            ]
        ]
    },
    {
        "id": "729bd4a3291ee25d",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "set switch",
        "rules": [
            {
                "t": "set",
                "p": "switch",
                "pt": "global",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 420,
        "wires": [
            [
                "b2a18a8c82a7f1cb"
            ]
        ]
    },
    {
        "id": "c6dbb33518226fb1",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "set slider",
        "rules": [
            {
                "t": "set",
                "p": "slider",
                "pt": "global",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 600,
        "wires": [
            [
                "eeee16370fe62739",
                "19e298710e52f945"
            ]
        ]
    },
    {
        "id": "da7b75a16f43986c",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "Switch制御",
        "info": "",
        "x": 100,
        "y": 380,
        "wires": []
    },
    {
        "id": "bd0d0849d1eef2f5",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "Slider制御",
        "info": "",
        "x": 100,
        "y": 580,
        "wires": []
    },
    {
        "id": "13fdcb314bdb2b29",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "lost検出",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "lost",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 240,
        "y": 120,
        "wires": [
            [
                "8778866f5540b29e"
            ]
        ]
    },
    {
        "id": "8778866f5540b29e",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "切断忘れ",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 380,
        "y": 120,
        "wires": [
            [
                "90e66fce09272b50"
            ]
        ]
    },
    {
        "id": "2d29b5872f72f234",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "check id",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            },
            {
                "t": "neq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 240,
        "y": 180,
        "wires": [
            [
                "90e66fce09272b50"
            ],
            [
                "0865d69acac9245b"
            ]
        ]
    },
    {
        "id": "0865d69acac9245b",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "clear",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 220,
        "wires": [
            [
                "59b75511d3bb0aaa"
            ]
        ]
    },
    {
        "id": "c56c2f84eee38f59",
        "type": "ui_group",
        "name": "排他制御",
        "tab": "b5de217d24b3769b",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "68a74b5e3cb81569",
        "type": "ui_group",
        "name": "ファイル保存",
        "tab": "b5de217d24b3769b",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "b5de217d24b3769b",
        "type": "ui_tab",
        "name": "操作テスト",
        "icon": "dashboard",
        "order": 6,
        "disabled": false,
        "hidden": false
    }
]

2) 排他制御+ユーザー制限

[{"id":"9624ddaf8e7b3698","type":"tab","label":"排他制御+ユーザ認証","disabled":false,"info":"","env":[]},{"id":"ad5b1d37877d52ee","type":"ui_switch","z":"9624ddaf8e7b3698","name":"","label":"switch","tooltip":"","group":"c56c2f84eee38f59","order":7,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"x":110,"y":560,"wires":[["d3dd68a84bd2ee59"]]},{"id":"066ef102062a6dc8","type":"ui_button","z":"9624ddaf8e7b3698","name":"","group":"c56c2f84eee38f59","order":5,"width":0,"height":0,"passthru":false,"label":"接続","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":110,"y":400,"wires":[["4dee3c0539dc31f5"]]},{"id":"6580298aa247f490","type":"comment","z":"9624ddaf8e7b3698","name":"排他制御","info":"","x":80,"y":40,"wires":[]},{"id":"59b75511d3bb0aaa","type":"ui_text","z":"9624ddaf8e7b3698","group":"c56c2f84eee38f59","order":3,"width":0,"height":0,"name":"","label":"接続要求","format":"{{msg.payload}}","layout":"row-spread","x":720,"y":280,"wires":[]},{"id":"f3d968583e2847bd","type":"change","z":"9624ddaf8e7b3698","name":"read id","rules":[{"t":"set","p":"payload","pt":"msg","to":"socketid","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":200,"wires":[["c5e54309c7f7bce3"]]},{"id":"c97ff4710e0ffbfb","type":"ui_button","z":"9624ddaf8e7b3698","name":"","group":"c56c2f84eee38f59","order":6,"width":0,"height":0,"passthru":false,"label":"切断","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":110,"y":220,"wires":[["2d29b5872f72f234"]]},{"id":"2967777faab35bab","type":"inject","z":"9624ddaf8e7b3698","name":"初期化","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":100,"y":180,"wires":[["90e66fce09272b50","3979fd3c78aa9a41"]]},{"id":"c5e54309c7f7bce3","type":"ui_text","z":"9624ddaf8e7b3698","group":"c56c2f84eee38f59","order":2,"width":0,"height":0,"name":"","label":"操作可能","format":"{{msg.payload}}","layout":"row-spread","className":"","x":720,"y":200,"wires":[]},{"id":"a364df582bdb65bd","type":"switch","z":"9624ddaf8e7b3698","name":"check id","property":"socketid","propertyType":"global","rules":[{"t":"eq","v":"demae0000000000","vt":"str"},{"t":"neq","v":"demae0000000000","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":240,"y":300,"wires":[["d68a8951689519d6","8d17f7f04fa27519"],["36adac1a0492a813"]]},{"id":"d3dd68a84bd2ee59","type":"switch","z":"9624ddaf8e7b3698","name":"Judge","property":"socketid","propertyType":"global","rules":[{"t":"eq","v":"socketid","vt":"msg"},{"t":"neq","v":"socketid","vt":"msg"}],"checkall":"true","repair":false,"outputs":2,"x":230,"y":560,"wires":[["729bd4a3291ee25d"],["4e20b95c94917b94"]]},{"id":"5e001c2f7435054a","type":"change","z":"9624ddaf8e7b3698","name":"display id","rules":[{"t":"set","p":"payload","pt":"msg","to":"socketid","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":320,"wires":[["59b75511d3bb0aaa"]]},{"id":"8b589828c0b36b10","type":"ui_text","z":"9624ddaf8e7b3698","group":"c56c2f84eee38f59","order":4,"width":0,"height":0,"name":"","label":"状態","format":"{{msg.payload}}","layout":"row-spread","x":710,"y":380,"wires":[]},{"id":"a0208d9f0c132d2c","type":"ui_slider","z":"9624ddaf8e7b3698","name":"","label":"slider","tooltip":"","group":"c56c2f84eee38f59","order":8,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":0,"max":10,"step":1,"x":110,"y":740,"wires":[["6b54522fd8bd7754"]]},{"id":"504973adb30b1a56","type":"change","z":"9624ddaf8e7b3698","name":"未接続","rules":[{"t":"set","p":"payload","pt":"msg","to":"未接続","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":360,"wires":[["8b589828c0b36b10"]]},{"id":"d68a8951689519d6","type":"change","z":"9624ddaf8e7b3698","name":"接続中","rules":[{"t":"set","p":"payload","pt":"msg","to":"接続中","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":400,"wires":[["8b589828c0b36b10"]]},{"id":"19e298710e52f945","type":"debug","z":"9624ddaf8e7b3698","name":"debug 31","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":560,"y":680,"wires":[]},{"id":"eeee16370fe62739","type":"ui_gauge","z":"9624ddaf8e7b3698","name":"","group":"68a74b5e3cb81569","order":2,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"units","format":"{{value}}","min":0,"max":10,"colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":570,"y":720,"wires":[]},{"id":"6b54522fd8bd7754","type":"switch","z":"9624ddaf8e7b3698","name":"Judge","property":"socketid","propertyType":"global","rules":[{"t":"eq","v":"socketid","vt":"msg"},{"t":"neq","v":"socketid","vt":"msg"}],"checkall":"true","repair":false,"outputs":2,"x":230,"y":740,"wires":[["c6dbb33518226fb1"],["e1027b676fc5ea9e"]]},{"id":"b2a18a8c82a7f1cb","type":"debug","z":"9624ddaf8e7b3698","name":"debug 32","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":560,"y":540,"wires":[]},{"id":"d6ca2d5a9b099f4f","type":"change","z":"9624ddaf8e7b3698","name":"read slider","rules":[{"t":"set","p":"payload","pt":"msg","to":"slider","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":250,"y":800,"wires":[["a0208d9f0c132d2c"]]},{"id":"c6a37e9657b90baf","type":"change","z":"9624ddaf8e7b3698","name":"read switch","rules":[{"t":"set","p":"payload","pt":"msg","to":"switch","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":250,"y":620,"wires":[["ad5b1d37877d52ee"]]},{"id":"4e20b95c94917b94","type":"delay","z":"9624ddaf8e7b3698","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":100,"y":620,"wires":[["c6a37e9657b90baf"]]},{"id":"e1027b676fc5ea9e","type":"delay","z":"9624ddaf8e7b3698","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":100,"y":800,"wires":[["d6ca2d5a9b099f4f"]]},{"id":"a2a31d783183bcbd","type":"inject","z":"9624ddaf8e7b3698","name":"20秒毎","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"20","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":640,"y":240,"wires":[["59b75511d3bb0aaa"]]},{"id":"e58f52290cef7435","type":"debug","z":"9624ddaf8e7b3698","name":"debug 36","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":240,"y":80,"wires":[]},{"id":"287b49bdf06d04da","type":"ui_ui_control","z":"9624ddaf8e7b3698","name":"","events":"all","x":100,"y":100,"wires":[["13fdcb314bdb2b29","e58f52290cef7435"]]},{"id":"90e66fce09272b50","type":"change","z":"9624ddaf8e7b3698","name":"id初期化","rules":[{"t":"set","p":"socketid","pt":"global","to":"demae0000000000","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":200,"wires":[["f3d968583e2847bd","504973adb30b1a56","0865d69acac9245b"]]},{"id":"8d17f7f04fa27519","type":"change","z":"9624ddaf8e7b3698","name":"set id","rules":[{"t":"set","p":"socketid","pt":"global","to":"socketid","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":300,"wires":[["f3d968583e2847bd"]]},{"id":"729bd4a3291ee25d","type":"change","z":"9624ddaf8e7b3698","name":"set switch","rules":[{"t":"set","p":"switch","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":540,"wires":[["b2a18a8c82a7f1cb"]]},{"id":"c6dbb33518226fb1","type":"change","z":"9624ddaf8e7b3698","name":"set slider","rules":[{"t":"set","p":"slider","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":720,"wires":[["eeee16370fe62739","19e298710e52f945"]]},{"id":"da7b75a16f43986c","type":"comment","z":"9624ddaf8e7b3698","name":"Switch制御","info":"","x":100,"y":500,"wires":[]},{"id":"bd0d0849d1eef2f5","type":"comment","z":"9624ddaf8e7b3698","name":"Slider制御","info":"","x":100,"y":700,"wires":[]},{"id":"13fdcb314bdb2b29","type":"switch","z":"9624ddaf8e7b3698","name":"lost検出","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"lost","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":240,"y":120,"wires":[["8778866f5540b29e","278ec74755d2d931"]]},{"id":"8778866f5540b29e","type":"switch","z":"9624ddaf8e7b3698","name":"切断忘れ","property":"socketid","propertyType":"global","rules":[{"t":"eq","v":"socketid","vt":"msg"}],"checkall":"true","repair":false,"outputs":1,"x":400,"y":140,"wires":[["90e66fce09272b50"]]},{"id":"2d29b5872f72f234","type":"switch","z":"9624ddaf8e7b3698","name":"check id","property":"socketid","propertyType":"global","rules":[{"t":"eq","v":"socketid","vt":"msg"},{"t":"neq","v":"socketid","vt":"msg"}],"checkall":"true","repair":false,"outputs":2,"x":240,"y":220,"wires":[["90e66fce09272b50"],["0865d69acac9245b"]]},{"id":"0865d69acac9245b","type":"change","z":"9624ddaf8e7b3698","name":"clear","rules":[{"t":"set","p":"payload","pt":"msg","to":"","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":260,"wires":[["59b75511d3bb0aaa"]]},{"id":"3979fd3c78aa9a41","type":"function","z":"9624ddaf8e7b3698","name":"初期化","func":"var id = [\"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\"];\nglobal.set(\"mem_id\", id);\nglobal.set(\"username\",\"test-user\");\nglobal.set(\"password\",\"test-password\");\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":230,"y":160,"wires":[[]]},{"id":"4dee3c0539dc31f5","type":"function","z":"9624ddaf8e7b3698","name":"check id","func":"var mem_id = global.get(\"mem_id\");\nvar max = 10;\nvar num = 10;\nvar i;\nvar is_member = false;\n\n// confirm current number of member\nfor (i=0; i<max; i++){\n    if (mem_id[i] == \"non\"){\n        num = i;\n        break;\n    }\n}\n\n\n// check socketid registered or not\nfor (i=0; i<num; i++){\n    if (msg.socketid == mem_id[i]){\n        is_member = true;\n        break;\n    }\n}\n\nmsg.num = num;\nmsg.is_member = is_member;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":240,"y":400,"wires":[["b43c421a968ba039"]]},{"id":"b43c421a968ba039","type":"switch","z":"9624ddaf8e7b3698","name":"member?","property":"is_member","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":380,"y":400,"wires":[["5e001c2f7435054a","a364df582bdb65bd"],["133470df1e98dc54"]]},{"id":"a5e3f4f6352e8f7c","type":"ui_ui_control","z":"9624ddaf8e7b3698","name":"認証へ","events":"change","x":570,"y":460,"wires":[[]]},{"id":"21d1e93119832dc8","type":"change","z":"9624ddaf8e7b3698","name":"認証へ","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"tab\":\"認証画面\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":460,"wires":[["a5e3f4f6352e8f7c"]]},{"id":"5816be9c07043ada","type":"function","z":"9624ddaf8e7b3698","name":"store id","func":"var mem_id = global.get(\"mem_id\");\nvar max = 10;\nvar num = 10;\nvar i;\n\n// confirm current number of member\nfor (i=0; i<max; i++){\n    if (mem_id[i] == \"non\"){\n        num = i;\n        break;\n    }\n}\n\n// store socket id\nmem_id[i] = msg.socketid;\n\n// socketid registered\nglobal.set(\"mem_id\", mem_id);\n\nmsg.mem_id = mem_id;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":900,"wires":[["8018ae89832c410c","f5fcc419c1256484","81d42cd238d23df4"]]},{"id":"0d6c188b5829f4a9","type":"switch","z":"9624ddaf8e7b3698","name":"pass","property":"payload.password","propertyType":"msg","rules":[{"t":"eq","v":"password","vt":"global"},{"t":"neq","v":"password","vt":"global"}],"checkall":"true","repair":false,"outputs":2,"x":330,"y":920,"wires":[["5816be9c07043ada","79a6c1643e7416cf"],["a6edc64ef1ff368d","79a6c1643e7416cf"]]},{"id":"2172f9310d3398fc","type":"switch","z":"9624ddaf8e7b3698","name":"user","property":"payload.username","propertyType":"msg","rules":[{"t":"eq","v":"username","vt":"global"},{"t":"neq","v":"username","vt":"global"}],"checkall":"true","repair":false,"outputs":2,"x":210,"y":940,"wires":[["0d6c188b5829f4a9"],["a6edc64ef1ff368d","79a6c1643e7416cf"]]},{"id":"59f8caa3b319d315","type":"ui_ui_control","z":"9624ddaf8e7b3698","name":"操作へ","events":"change","x":590,"y":980,"wires":[[]]},{"id":"a6edc64ef1ff368d","type":"change","z":"9624ddaf8e7b3698","name":"残念","rules":[{"t":"set","p":"payload","pt":"msg","to":"認証できませんでした","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":940,"wires":[["2caa54e89853cf80"]]},{"id":"bd25dcdf6fac48c5","type":"ui_form","z":"9624ddaf8e7b3698","name":"","label":"","group":"9258a40ced27eba1","order":0,"width":"6","height":"3","options":[{"label":"ユーザID","value":"username","type":"text","required":true,"rows":null},{"label":"パスワード","value":"password","type":"text","required":true,"rows":null}],"formValue":{"username":"","password":""},"payload":"","submit":"submit","cancel":"cancel","topic":"topic","topicType":"msg","splitLayout":"","className":"","x":90,"y":940,"wires":[["2172f9310d3398fc"]]},{"id":"2caa54e89853cf80","type":"ui_toast","z":"9624ddaf8e7b3698","position":"top left","displayTime":"3","highlight":"","sendall":true,"outputs":0,"ok":"OK","cancel":"","raw":false,"className":"","topic":"","name":"通知","x":730,"y":940,"wires":[]},{"id":"c0f9d30a34e2a0d9","type":"comment","z":"9624ddaf8e7b3698","name":"認証","info":"","x":90,"y":900,"wires":[]},{"id":"8018ae89832c410c","type":"change","z":"9624ddaf8e7b3698","name":"OK","rules":[{"t":"set","p":"payload","pt":"msg","to":"認証できました","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":900,"wires":[["2caa54e89853cf80"]]},{"id":"278ec74755d2d931","type":"function","z":"9624ddaf8e7b3698","name":"check id","func":"var mem_id = global.get(\"mem_id\");\nvar max = 10;\nvar num = 10;\nvar own = 0;\nvar i, j;\n\n// confirm current number of member\nfor (i=0; i<max; i++){\n    if (mem_id[i] == \"non\"){\n        num = i;\n        break;\n    }\n}\n\n// check socketid registered or not\nfor (i=0; i<num; i++){\n    if (msg.socketid == mem_id[i]){\n        own = i;\n        // shift socketid \n        for (j = own; j < num; j++) {\n            mem_id[j] = mem_id[j + 1];\n        }\n        break;\n    }\n}\n\nglobal.set(\"mem_id\", mem_id);\n\nmsg.mem_id = mem_id;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":100,"wires":[["062f012abfb1cb7e"]]},{"id":"062f012abfb1cb7e","type":"debug","z":"9624ddaf8e7b3698","name":"debug 37","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":540,"y":100,"wires":[]},{"id":"f5fcc419c1256484","type":"debug","z":"9624ddaf8e7b3698","name":"debug 38","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":600,"y":820,"wires":[]},{"id":"79a6c1643e7416cf","type":"change","z":"9624ddaf8e7b3698","name":"操作へ","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"tab\":\"操作テスト\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":980,"wires":[["59f8caa3b319d315"]]},{"id":"133470df1e98dc54","type":"switch","z":"9624ddaf8e7b3698","name":"満席","property":"num","propertyType":"msg","rules":[{"t":"neq","v":"10","vt":"num"},{"t":"eq","v":"10","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":330,"y":480,"wires":[["21d1e93119832dc8"],["362480522c9df186"]]},{"id":"6fe7d9d5b8edae46","type":"ui_toast","z":"9624ddaf8e7b3698","position":"top left","displayTime":"3","highlight":"","sendall":true,"outputs":0,"ok":"OK","cancel":"","raw":false,"className":"","topic":"","name":"満席","x":570,"y":500,"wires":[]},{"id":"362480522c9df186","type":"change","z":"9624ddaf8e7b3698","name":"満席","rules":[{"t":"set","p":"payload","pt":"msg","to":"参加者が10名に達しました","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":500,"wires":[["6fe7d9d5b8edae46"]]},{"id":"81d42cd238d23df4","type":"link out","z":"9624ddaf8e7b3698","name":"OK","mode":"link","links":["ede040597906f361"],"x":565,"y":860,"wires":[]},{"id":"ede040597906f361","type":"link in","z":"9624ddaf8e7b3698","name":"link in 1","links":["81d42cd238d23df4"],"x":135,"y":280,"wires":[["a364df582bdb65bd"]]},{"id":"36adac1a0492a813","type":"change","z":"9624ddaf8e7b3698","name":"接続中","rules":[{"t":"set","p":"payload","pt":"msg","to":"他の方が接続中です。しばらくお待ちください。","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":190,"y":360,"wires":[["ffd6107f94293144"]]},{"id":"ffd6107f94293144","type":"ui_toast","z":"9624ddaf8e7b3698","position":"top right","displayTime":"3","highlight":"","sendall":true,"outputs":0,"ok":"OK","cancel":"","raw":false,"className":"","topic":"","name":"接続中","x":310,"y":360,"wires":[]},{"id":"c56c2f84eee38f59","type":"ui_group","name":"排他制御","tab":"b5de217d24b3769b","order":2,"disp":true,"width":"6","collapse":false,"className":""},{"id":"68a74b5e3cb81569","type":"ui_group","name":"ファイル保存","tab":"b5de217d24b3769b","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"9258a40ced27eba1","type":"ui_group","name":"認証","tab":"7be2d49e0eb0264b","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"b5de217d24b3769b","type":"ui_tab","name":"操作テスト","icon":"dashboard","order":6,"disabled":false,"hidden":false},{"id":"7be2d49e0eb0264b","type":"ui_tab","name":"認証画面","icon":"dashboard","disabled":false,"hidden":false}]

3) 排他制御+ユーザー制限(2):複数ユーザー名とログファイル記録

[
    {
        "id": "9624ddaf8e7b3698",
        "type": "tab",
        "label": "排他制御+ユーザ認証+log",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "ad5b1d37877d52ee",
        "type": "ui_switch",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "label": "switch",
        "tooltip": "",
        "group": "c56c2f84eee38f59",
        "order": 7,
        "width": 0,
        "height": 0,
        "passthru": true,
        "decouple": "false",
        "topic": "topic",
        "topicType": "msg",
        "style": "",
        "onvalue": "true",
        "onvalueType": "bool",
        "onicon": "",
        "oncolor": "",
        "offvalue": "false",
        "offvalueType": "bool",
        "officon": "",
        "offcolor": "",
        "animate": false,
        "x": 110,
        "y": 560,
        "wires": [
            [
                "d3dd68a84bd2ee59"
            ]
        ]
    },
    {
        "id": "066ef102062a6dc8",
        "type": "ui_button",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "group": "c56c2f84eee38f59",
        "order": 5,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "接続",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "",
        "payloadType": "str",
        "topic": "topic",
        "topicType": "msg",
        "x": 110,
        "y": 400,
        "wires": [
            [
                "4dee3c0539dc31f5"
            ]
        ]
    },
    {
        "id": "6580298aa247f490",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "排他制御",
        "info": "",
        "x": 80,
        "y": 40,
        "wires": []
    },
    {
        "id": "59b75511d3bb0aaa",
        "type": "ui_text",
        "z": "9624ddaf8e7b3698",
        "group": "c56c2f84eee38f59",
        "order": 3,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "接続要求",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 720,
        "y": 280,
        "wires": []
    },
    {
        "id": "f3d968583e2847bd",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "read id",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "socketid",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 200,
        "wires": [
            [
                "c5e54309c7f7bce3"
            ]
        ]
    },
    {
        "id": "c97ff4710e0ffbfb",
        "type": "ui_button",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "group": "c56c2f84eee38f59",
        "order": 6,
        "width": 0,
        "height": 0,
        "passthru": false,
        "label": "切断",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "",
        "payloadType": "str",
        "topic": "topic",
        "topicType": "msg",
        "x": 110,
        "y": 220,
        "wires": [
            [
                "2d29b5872f72f234"
            ]
        ]
    },
    {
        "id": "2967777faab35bab",
        "type": "inject",
        "z": "9624ddaf8e7b3698",
        "name": "初期化",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 100,
        "y": 180,
        "wires": [
            [
                "90e66fce09272b50",
                "ced2afe73aa72f86"
            ]
        ]
    },
    {
        "id": "c5e54309c7f7bce3",
        "type": "ui_text",
        "z": "9624ddaf8e7b3698",
        "group": "c56c2f84eee38f59",
        "order": 2,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "操作可能",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "className": "",
        "x": 720,
        "y": 200,
        "wires": []
    },
    {
        "id": "a364df582bdb65bd",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "check id",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "demae0000000000",
                "vt": "str"
            },
            {
                "t": "neq",
                "v": "demae0000000000",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 240,
        "y": 300,
        "wires": [
            [
                "d68a8951689519d6",
                "8d17f7f04fa27519"
            ],
            [
                "36adac1a0492a813"
            ]
        ]
    },
    {
        "id": "d3dd68a84bd2ee59",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "Judge",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            },
            {
                "t": "neq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 230,
        "y": 560,
        "wires": [
            [
                "729bd4a3291ee25d"
            ],
            [
                "4e20b95c94917b94"
            ]
        ]
    },
    {
        "id": "5e001c2f7435054a",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "display id",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "socketid",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 320,
        "wires": [
            [
                "59b75511d3bb0aaa"
            ]
        ]
    },
    {
        "id": "8b589828c0b36b10",
        "type": "ui_text",
        "z": "9624ddaf8e7b3698",
        "group": "c56c2f84eee38f59",
        "order": 4,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "状態",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 710,
        "y": 380,
        "wires": []
    },
    {
        "id": "a0208d9f0c132d2c",
        "type": "ui_slider",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "label": "slider",
        "tooltip": "",
        "group": "c56c2f84eee38f59",
        "order": 8,
        "width": 0,
        "height": 0,
        "passthru": true,
        "outs": "all",
        "topic": "topic",
        "topicType": "msg",
        "min": 0,
        "max": 10,
        "step": 1,
        "x": 110,
        "y": 740,
        "wires": [
            [
                "6b54522fd8bd7754"
            ]
        ]
    },
    {
        "id": "504973adb30b1a56",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "未接続",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "未接続",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 570,
        "y": 360,
        "wires": [
            [
                "8b589828c0b36b10"
            ]
        ]
    },
    {
        "id": "d68a8951689519d6",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "接続中",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "接続中",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 570,
        "y": 400,
        "wires": [
            [
                "8b589828c0b36b10"
            ]
        ]
    },
    {
        "id": "19e298710e52f945",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 31",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 560,
        "y": 680,
        "wires": []
    },
    {
        "id": "eeee16370fe62739",
        "type": "ui_gauge",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "group": "68a74b5e3cb81569",
        "order": 2,
        "width": 0,
        "height": 0,
        "gtype": "gage",
        "title": "gauge",
        "label": "units",
        "format": "{{value}}",
        "min": 0,
        "max": 10,
        "colors": [
            "#00b500",
            "#e6e600",
            "#ca3838"
        ],
        "seg1": "",
        "seg2": "",
        "diff": false,
        "className": "",
        "x": 570,
        "y": 720,
        "wires": []
    },
    {
        "id": "6b54522fd8bd7754",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "Judge",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            },
            {
                "t": "neq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 230,
        "y": 740,
        "wires": [
            [
                "c6dbb33518226fb1"
            ],
            [
                "e1027b676fc5ea9e"
            ]
        ]
    },
    {
        "id": "b2a18a8c82a7f1cb",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 32",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 560,
        "y": 540,
        "wires": []
    },
    {
        "id": "d6ca2d5a9b099f4f",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "read slider",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "slider",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 250,
        "y": 800,
        "wires": [
            [
                "a0208d9f0c132d2c"
            ]
        ]
    },
    {
        "id": "c6a37e9657b90baf",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "read switch",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "switch",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 250,
        "y": 620,
        "wires": [
            [
                "ad5b1d37877d52ee"
            ]
        ]
    },
    {
        "id": "4e20b95c94917b94",
        "type": "delay",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "pauseType": "delay",
        "timeout": "2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 100,
        "y": 620,
        "wires": [
            [
                "c6a37e9657b90baf"
            ]
        ]
    },
    {
        "id": "e1027b676fc5ea9e",
        "type": "delay",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "pauseType": "delay",
        "timeout": "2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 100,
        "y": 800,
        "wires": [
            [
                "d6ca2d5a9b099f4f"
            ]
        ]
    },
    {
        "id": "a2a31d783183bcbd",
        "type": "inject",
        "z": "9624ddaf8e7b3698",
        "name": "20秒毎",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "20",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 640,
        "y": 240,
        "wires": [
            [
                "59b75511d3bb0aaa"
            ]
        ]
    },
    {
        "id": "e58f52290cef7435",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 36",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 240,
        "y": 80,
        "wires": []
    },
    {
        "id": "287b49bdf06d04da",
        "type": "ui_ui_control",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "events": "all",
        "x": 100,
        "y": 100,
        "wires": [
            [
                "13fdcb314bdb2b29",
                "e58f52290cef7435"
            ]
        ]
    },
    {
        "id": "90e66fce09272b50",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "id初期化",
        "rules": [
            {
                "t": "set",
                "p": "socketid",
                "pt": "global",
                "to": "demae0000000000",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 200,
        "wires": [
            [
                "f3d968583e2847bd",
                "504973adb30b1a56",
                "0865d69acac9245b"
            ]
        ]
    },
    {
        "id": "8d17f7f04fa27519",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "set id",
        "rules": [
            {
                "t": "set",
                "p": "socketid",
                "pt": "global",
                "to": "socketid",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 300,
        "wires": [
            [
                "f3d968583e2847bd",
                "139943774e4cd120"
            ]
        ]
    },
    {
        "id": "729bd4a3291ee25d",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "set switch",
        "rules": [
            {
                "t": "set",
                "p": "switch",
                "pt": "global",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 540,
        "wires": [
            [
                "b2a18a8c82a7f1cb"
            ]
        ]
    },
    {
        "id": "c6dbb33518226fb1",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "set slider",
        "rules": [
            {
                "t": "set",
                "p": "slider",
                "pt": "global",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 720,
        "wires": [
            [
                "eeee16370fe62739",
                "19e298710e52f945"
            ]
        ]
    },
    {
        "id": "da7b75a16f43986c",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "Switch制御",
        "info": "",
        "x": 100,
        "y": 500,
        "wires": []
    },
    {
        "id": "bd0d0849d1eef2f5",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "Slider制御",
        "info": "",
        "x": 100,
        "y": 700,
        "wires": []
    },
    {
        "id": "13fdcb314bdb2b29",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "lost検出",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "lost",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 240,
        "y": 120,
        "wires": [
            [
                "8778866f5540b29e",
                "278ec74755d2d931"
            ]
        ]
    },
    {
        "id": "8778866f5540b29e",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "切断忘れ",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 400,
        "y": 140,
        "wires": [
            [
                "90e66fce09272b50",
                "8de94317fcf035e7"
            ]
        ]
    },
    {
        "id": "2d29b5872f72f234",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "check id",
        "property": "socketid",
        "propertyType": "global",
        "rules": [
            {
                "t": "eq",
                "v": "socketid",
                "vt": "msg"
            },
            {
                "t": "neq",
                "v": "socketid",
                "vt": "msg"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 240,
        "y": 220,
        "wires": [
            [
                "90e66fce09272b50",
                "8de94317fcf035e7"
            ],
            [
                "0865d69acac9245b",
                "711c442e587074ee"
            ]
        ]
    },
    {
        "id": "0865d69acac9245b",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "clear",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 260,
        "wires": [
            [
                "59b75511d3bb0aaa"
            ]
        ]
    },
    {
        "id": "4dee3c0539dc31f5",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "check id",
        "func": "var mem_id = global.get(\"mem_id\");\nvar max = 10;\nvar num = 10;\nvar i;\nvar is_member = false;\n\n// confirm current number of member\nfor (i=0; i<max; i++){\n    if (mem_id[i] == \"non\"){\n        num = i;\n        break;\n    }\n}\n\n\n// check socketid registered or not\nfor (i=0; i<num; i++){\n    if (msg.socketid == mem_id[i]){\n        is_member = true;\n        break;\n    }\n}\n\nmsg.num = num;\nmsg.is_member = is_member;\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 240,
        "y": 400,
        "wires": [
            [
                "b43c421a968ba039"
            ]
        ]
    },
    {
        "id": "b43c421a968ba039",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "member?",
        "property": "is_member",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            },
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 380,
        "y": 400,
        "wires": [
            [
                "5e001c2f7435054a",
                "a364df582bdb65bd"
            ],
            [
                "133470df1e98dc54"
            ]
        ]
    },
    {
        "id": "a5e3f4f6352e8f7c",
        "type": "ui_ui_control",
        "z": "9624ddaf8e7b3698",
        "name": "認証へ",
        "events": "change",
        "x": 570,
        "y": 460,
        "wires": [
            []
        ]
    },
    {
        "id": "21d1e93119832dc8",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "認証へ",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\"tab\":\"認証画面\"}",
                "tot": "json"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 460,
        "wires": [
            [
                "a5e3f4f6352e8f7c"
            ]
        ]
    },
    {
        "id": "5816be9c07043ada",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "store id",
        "func": "var mem_id = global.get(\"mem_id\");\nvar max = 10;\nvar num = 10;\nvar i;\n\n// confirm current number of member\nfor (i=0; i<max; i++){\n    if (mem_id[i] == \"non\"){\n        num = i;\n        break;\n    }\n}\n\n// store socket id\nmem_id[num] = msg.socketid;\n\n// socketid registered\nglobal.set(\"mem_id\", mem_id);\n\nmsg.mem_num = num;\nmsg.mem_id = mem_id;\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 500,
        "y": 880,
        "wires": [
            [
                "81d42cd238d23df4",
                "1355945a196cd2ca"
            ]
        ]
    },
    {
        "id": "59f8caa3b319d315",
        "type": "ui_ui_control",
        "z": "9624ddaf8e7b3698",
        "name": "操作へ",
        "events": "change",
        "x": 630,
        "y": 1000,
        "wires": [
            []
        ]
    },
    {
        "id": "a6edc64ef1ff368d",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "残念",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "認証できません",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 510,
        "y": 960,
        "wires": [
            [
                "2caa54e89853cf80",
                "6a9c802414f3a28d"
            ]
        ]
    },
    {
        "id": "bd25dcdf6fac48c5",
        "type": "ui_form",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "label": "",
        "group": "9258a40ced27eba1",
        "order": 0,
        "width": "6",
        "height": "3",
        "options": [
            {
                "label": "ユーザID",
                "value": "username",
                "type": "text",
                "required": true,
                "rows": null
            },
            {
                "label": "パスワード",
                "value": "password",
                "type": "text",
                "required": true,
                "rows": null
            }
        ],
        "formValue": {
            "username": "",
            "password": ""
        },
        "payload": "",
        "submit": "submit",
        "cancel": "cancel",
        "topic": "topic",
        "topicType": "msg",
        "splitLayout": "",
        "className": "",
        "x": 90,
        "y": 940,
        "wires": [
            [
                "41995ab37fecc948"
            ]
        ]
    },
    {
        "id": "2caa54e89853cf80",
        "type": "ui_toast",
        "z": "9624ddaf8e7b3698",
        "position": "top left",
        "displayTime": "3",
        "highlight": "",
        "sendall": false,
        "outputs": 0,
        "ok": "OK",
        "cancel": "",
        "raw": false,
        "className": "",
        "topic": "",
        "name": "通知",
        "x": 630,
        "y": 940,
        "wires": []
    },
    {
        "id": "c0f9d30a34e2a0d9",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "認証",
        "info": "",
        "x": 90,
        "y": 900,
        "wires": []
    },
    {
        "id": "8018ae89832c410c",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "OK",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "認証できました",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 510,
        "y": 920,
        "wires": [
            [
                "2caa54e89853cf80",
                "6a9c802414f3a28d"
            ]
        ]
    },
    {
        "id": "278ec74755d2d931",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "check id",
        "func": "var mem_id = global.get(\"mem_id\");\nvar max = 10;\nvar num = 10;\nvar own = 0;\nvar i, j;\n\n// confirm current number of member\nfor (i=0; i<max; i++){\n    if (mem_id[i] == \"non\"){\n        num = i;\n        break;\n    }\n}\n\n// check socketid registered or not\nfor (i=0; i<num; i++){\n    if (msg.socketid == mem_id[i]){\n        own = i;\n        // shift socketid \n        for (j = own; j < num; j++) {\n            mem_id[j] = mem_id[j + 1];\n        }\n        break;\n    }\n}\n\nglobal.set(\"mem_id\", mem_id);\n\nmsg.mem_id = mem_id;\nmsg.mem_num = own;\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 400,
        "y": 100,
        "wires": [
            [
                "062f012abfb1cb7e"
            ]
        ]
    },
    {
        "id": "062f012abfb1cb7e",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 37",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 540,
        "y": 100,
        "wires": []
    },
    {
        "id": "f5fcc419c1256484",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 38",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 760,
        "y": 880,
        "wires": []
    },
    {
        "id": "79a6c1643e7416cf",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "操作へ",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\"tab\":\"操作テスト\"}",
                "tot": "json"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 510,
        "y": 1000,
        "wires": [
            [
                "59f8caa3b319d315"
            ]
        ]
    },
    {
        "id": "133470df1e98dc54",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "満席",
        "property": "num",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "10",
                "vt": "num"
            },
            {
                "t": "eq",
                "v": "10",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 330,
        "y": 480,
        "wires": [
            [
                "21d1e93119832dc8"
            ],
            [
                "362480522c9df186"
            ]
        ]
    },
    {
        "id": "6fe7d9d5b8edae46",
        "type": "ui_toast",
        "z": "9624ddaf8e7b3698",
        "position": "top left",
        "displayTime": "3",
        "highlight": "",
        "sendall": false,
        "outputs": 0,
        "ok": "OK",
        "cancel": "",
        "raw": false,
        "className": "",
        "topic": "",
        "name": "満席",
        "x": 570,
        "y": 500,
        "wires": []
    },
    {
        "id": "362480522c9df186",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "満席",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "参加者が10名に達しました",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 500,
        "wires": [
            [
                "6fe7d9d5b8edae46"
            ]
        ]
    },
    {
        "id": "81d42cd238d23df4",
        "type": "link out",
        "z": "9624ddaf8e7b3698",
        "name": "OK",
        "mode": "link",
        "links": [
            "ede040597906f361"
        ],
        "x": 595,
        "y": 820,
        "wires": []
    },
    {
        "id": "ede040597906f361",
        "type": "link in",
        "z": "9624ddaf8e7b3698",
        "name": "link in 1",
        "links": [
            "81d42cd238d23df4"
        ],
        "x": 115,
        "y": 300,
        "wires": [
            [
                "a364df582bdb65bd"
            ]
        ]
    },
    {
        "id": "36adac1a0492a813",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "接続中",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "他の方が接続中です。しばらくお待ちください。",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 190,
        "y": 360,
        "wires": [
            [
                "ffd6107f94293144"
            ]
        ]
    },
    {
        "id": "ffd6107f94293144",
        "type": "ui_toast",
        "z": "9624ddaf8e7b3698",
        "position": "top right",
        "displayTime": "3",
        "highlight": "",
        "sendall": false,
        "outputs": 0,
        "ok": "OK",
        "cancel": "",
        "raw": false,
        "className": "",
        "topic": "",
        "name": "接続中",
        "x": 310,
        "y": 360,
        "wires": []
    },
    {
        "id": "3979fd3c78aa9a41",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "初期化",
        "func": "// file for username and password\nvar user_name = \"/home/pi/Documents/userkanri.txt\";\n\n// log file name with full path\nvar log_name = \"/home/pi/Documents/user_log.csv\";\n\n// initialize routine  # Don't touch!!\nvar id = [\"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\", \"non\"];\nvar user = new Array();\nglobal.set(\"user\", user);\nglobal.set(\"mem_id\", id);\nvar info = new Array();\nfor(var i=0; i<10; i++){\n    info[i] = \"username\";\n}\nglobal.set(\"info\",info);\nglobal.set(\"login_time\",\"19991231125959\");\nglobal.set(\"login_user\",\"username\");\nglobal.set(\"log_name\",log_name);\nmsg.filename = user_name;\nglobal.set(\"log_data\",new Array());\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 210,
        "y": 1100,
        "wires": [
            [
                "f9c35b3fa0a4eb93"
            ]
        ]
    },
    {
        "id": "ced2afe73aa72f86",
        "type": "link out",
        "z": "9624ddaf8e7b3698",
        "name": "Initialize",
        "mode": "link",
        "links": [
            "a9ffebea6e68125e"
        ],
        "x": 205,
        "y": 160,
        "wires": []
    },
    {
        "id": "a9ffebea6e68125e",
        "type": "link in",
        "z": "9624ddaf8e7b3698",
        "name": "link in 2",
        "links": [
            "ced2afe73aa72f86"
        ],
        "x": 125,
        "y": 1100,
        "wires": [
            [
                "3979fd3c78aa9a41"
            ]
        ]
    },
    {
        "id": "f9c35b3fa0a4eb93",
        "type": "file in",
        "z": "9624ddaf8e7b3698",
        "name": "userkanri.txt",
        "filename": "filename",
        "filenameType": "msg",
        "format": "lines",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 350,
        "y": 1100,
        "wires": [
            [
                "cacc80e023a74043"
            ]
        ]
    },
    {
        "id": "cacc80e023a74043",
        "type": "csv",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "sep": ",",
        "hdrin": true,
        "hdrout": "none",
        "multi": "one",
        "ret": "\\n",
        "temp": "",
        "skip": "0",
        "strings": true,
        "include_empty_strings": "",
        "include_null_values": "",
        "x": 490,
        "y": 1100,
        "wires": [
            [
                "d9c0baa861008737"
            ]
        ]
    },
    {
        "id": "d9c0baa861008737",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "username",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "hask",
                "v": "username",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 260,
        "y": 1140,
        "wires": [
            [
                "9249c6f3cca45e5e"
            ]
        ]
    },
    {
        "id": "9249c6f3cca45e5e",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "append",
        "func": "var user = global.get(\"user\");\nvar len = user.length;\nuser[len] = new Array(msg.payload.username, msg.payload.password);\nglobal.set(\"user\",user);\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 400,
        "y": 1140,
        "wires": [
            []
        ]
    },
    {
        "id": "41995ab37fecc948",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "認証",
        "func": "var user = global.get(\"user\");\nvar max = user.length;\nvar is_member = false;\n\n// check username & password\nfor (var i=0; i<max; i++){\n    if (msg.payload.username == user[i][0] && msg.payload.password == user[i][1]){\n        is_member = true;\n        break;\n    }\n}\nmsg.payload.is_member = is_member;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 210,
        "y": 940,
        "wires": [
            [
                "b11d379a01770491",
                "b93eb0dc3d794cb5"
            ]
        ]
    },
    {
        "id": "b11d379a01770491",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "property": "payload.is_member",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            },
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 330,
        "y": 940,
        "wires": [
            [
                "5816be9c07043ada",
                "79a6c1643e7416cf",
                "8018ae89832c410c"
            ],
            [
                "79a6c1643e7416cf",
                "a6edc64ef1ff368d"
            ]
        ]
    },
    {
        "id": "1355945a196cd2ca",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "記録",
        "func": "var info = global.get(\"info\");\n\nvar num = msg.mem_num;\n\n// save login name\ninfo[num] = msg.payload.username;\n\nglobal.set(\"info\",info);\n\nmsg.payload = {\"socketid\":msg.mem_id[num], \"username\":info[num]};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 880,
        "wires": [
            [
                "f5fcc419c1256484"
            ]
        ]
    },
    {
        "id": "b8f656d397bc6038",
        "type": "moment",
        "z": "9624ddaf8e7b3698",
        "name": "日時",
        "topic": "",
        "input": "",
        "inputType": "date",
        "inTz": "Asia/Tokyo",
        "adjAmount": 0,
        "adjType": "days",
        "adjDir": "add",
        "format": "YYYYMMDDHHmmss",
        "locale": "ja-JP",
        "output": "datetime",
        "outputType": "msg",
        "outTz": "Asia/Tokyo",
        "x": 210,
        "y": 1280,
        "wires": [
            [
                "f065fb14d8154c01"
            ]
        ]
    },
    {
        "id": "139943774e4cd120",
        "type": "link out",
        "z": "9624ddaf8e7b3698",
        "name": "log in",
        "mode": "link",
        "links": [
            "34c6bb2031b1235c"
        ],
        "x": 535,
        "y": 280,
        "wires": []
    },
    {
        "id": "34c6bb2031b1235c",
        "type": "link in",
        "z": "9624ddaf8e7b3698",
        "name": "link in 3",
        "links": [
            "139943774e4cd120"
        ],
        "x": 125,
        "y": 1280,
        "wires": [
            [
                "b8f656d397bc6038"
            ]
        ]
    },
    {
        "id": "f065fb14d8154c01",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "log in",
        "func": "var mem_id = global.get(\"mem_id\");\nvar info = global.get(\"info\");\nvar max = 10;\nvar num = 10;\nvar i;\n\n// confirm current number of member\nfor (i=0; i<max; i++){\n    if (mem_id[i] == \"non\"){\n        num = i;\n        break;\n    }\n}\n\n\n// check socketid registered or not\nfor (i=0; i<num; i++){\n    if (msg.socketid == mem_id[i]){\n        global.set(\"login_user\", info[i]);\n        break;\n    }\n}\n\nglobal.set(\"login_time\", msg.datetime);\n\nmsg.payload ={\"login_user\":info[i], \"socket_id\":msg.socketid, \"login_time\":msg.datetime};\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 330,
        "y": 1280,
        "wires": [
            [
                "9412d1f46ea1ed1d"
            ]
        ]
    },
    {
        "id": "9412d1f46ea1ed1d",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 40",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 460,
        "y": 1300,
        "wires": []
    },
    {
        "id": "8de94317fcf035e7",
        "type": "link out",
        "z": "9624ddaf8e7b3698",
        "name": "log out",
        "mode": "link",
        "links": [
            "8ef2970ce8b1474d"
        ],
        "x": 555,
        "y": 160,
        "wires": []
    },
    {
        "id": "8ef2970ce8b1474d",
        "type": "link in",
        "z": "9624ddaf8e7b3698",
        "name": "link in 4",
        "links": [
            "8de94317fcf035e7"
        ],
        "x": 125,
        "y": 1340,
        "wires": [
            [
                "e757ef1c674e0c60"
            ]
        ]
    },
    {
        "id": "e757ef1c674e0c60",
        "type": "moment",
        "z": "9624ddaf8e7b3698",
        "name": "日時",
        "topic": "",
        "input": "",
        "inputType": "date",
        "inTz": "Asia/Tokyo",
        "adjAmount": 0,
        "adjType": "days",
        "adjDir": "add",
        "format": "YYYYMMDDHHmmss",
        "locale": "ja-JP",
        "output": "datetime",
        "outputType": "msg",
        "outTz": "Asia/Tokyo",
        "x": 210,
        "y": 1340,
        "wires": [
            [
                "35d86ceafebdd112"
            ]
        ]
    },
    {
        "id": "35d86ceafebdd112",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "log out",
        "func": "var login_user = global.get(\"login_user\");\nvar login_time = global.get(\"login_time\");\nvar socketid = msg.socketid;\nvar _switch = global.get(\"switch\");\nvar _slider = global.get(\"slider\");\n\nmsg.payload ={\"login_user\":login_user, \"socket_id\":socketid, \"login_time\":login_time, \"logout_time\":msg.datetime, \"switch\":_switch, \"slider\":_slider};\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 330,
        "y": 1340,
        "wires": [
            [
                "9412d1f46ea1ed1d",
                "c08af49593b3018c"
            ]
        ]
    },
    {
        "id": "6a9c802414f3a28d",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 41",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 760,
        "y": 940,
        "wires": []
    },
    {
        "id": "b93eb0dc3d794cb5",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 42",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 320,
        "y": 1000,
        "wires": []
    },
    {
        "id": "fa7be12cd3b493bf",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "初期化",
        "info": "",
        "x": 90,
        "y": 1060,
        "wires": []
    },
    {
        "id": "2cf86b2057dff257",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "user",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "user",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 370,
        "y": 1180,
        "wires": [
            [
                "6abf84fad253dda2",
                "f9e7153c0be5dd6d"
            ]
        ]
    },
    {
        "id": "7642ea3289f4172b",
        "type": "complete",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "scope": [
            "f9c35b3fa0a4eb93"
        ],
        "uncaught": false,
        "x": 230,
        "y": 1180,
        "wires": [
            [
                "2cf86b2057dff257"
            ]
        ]
    },
    {
        "id": "6abf84fad253dda2",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 39",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 620,
        "y": 1140,
        "wires": []
    },
    {
        "id": "f9e7153c0be5dd6d",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "info",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "info",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 490,
        "y": 1180,
        "wires": [
            [
                "6abf84fad253dda2"
            ]
        ]
    },
    {
        "id": "9ea3d49792f87d4c",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "ログデータ記録",
        "info": "",
        "x": 120,
        "y": 1240,
        "wires": []
    },
    {
        "id": "bc5d36ccc92dfd92",
        "type": "file",
        "z": "9624ddaf8e7b3698",
        "name": "write file",
        "filename": "filename",
        "filenameType": "msg",
        "appendNewline": true,
        "createDir": true,
        "overwriteFile": "false",
        "encoding": "none",
        "x": 720,
        "y": 1340,
        "wires": [
            []
        ]
    },
    {
        "id": "e41161d94530a0c7",
        "type": "comment",
        "z": "9624ddaf8e7b3698",
        "name": "ログデータ参照",
        "info": "",
        "x": 120,
        "y": 1400,
        "wires": []
    },
    {
        "id": "1d4fd9a53b236dd3",
        "type": "inject",
        "z": "9624ddaf8e7b3698",
        "name": "ログ参照",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 1440,
        "wires": [
            [
                "a6dc347bdf54e1bf"
            ]
        ]
    },
    {
        "id": "9ee76930aadc0625",
        "type": "file in",
        "z": "9624ddaf8e7b3698",
        "name": "log read",
        "filename": "filename",
        "filenameType": "msg",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 380,
        "y": 1440,
        "wires": [
            [
                "41f0e463dfea64ad"
            ]
        ]
    },
    {
        "id": "537cfcf432aae7a2",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "file_name",
        "rules": [
            {
                "t": "set",
                "p": "filename",
                "pt": "msg",
                "to": "log_name",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 580,
        "y": 1340,
        "wires": [
            [
                "bc5d36ccc92dfd92",
                "da4c8088816dab48"
            ]
        ]
    },
    {
        "id": "a6dc347bdf54e1bf",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "file_name",
        "rules": [
            {
                "t": "set",
                "p": "filename",
                "pt": "msg",
                "to": "log_name",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 240,
        "y": 1440,
        "wires": [
            [
                "9ee76930aadc0625"
            ]
        ]
    },
    {
        "id": "00ec3f7d482dbea8",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 43",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 660,
        "y": 1520,
        "wires": []
    },
    {
        "id": "da4c8088816dab48",
        "type": "debug",
        "z": "9624ddaf8e7b3698",
        "name": "debug 44",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 720,
        "y": 1300,
        "wires": []
    },
    {
        "id": "c08af49593b3018c",
        "type": "csv",
        "z": "9624ddaf8e7b3698",
        "name": "csv",
        "sep": ",",
        "hdrin": "",
        "hdrout": "once",
        "multi": "one",
        "ret": "\\n",
        "temp": "login_user,socket_id,login_time,logout_time,switch,slider",
        "skip": "0",
        "strings": true,
        "include_empty_strings": "",
        "include_null_values": "",
        "x": 450,
        "y": 1340,
        "wires": [
            [
                "537cfcf432aae7a2"
            ]
        ]
    },
    {
        "id": "41f0e463dfea64ad",
        "type": "csv",
        "z": "9624ddaf8e7b3698",
        "name": "csv",
        "sep": ",",
        "hdrin": "",
        "hdrout": "once",
        "multi": "one",
        "ret": "\\n",
        "temp": "login_user,socket_id,login_time,logout_time,switch,slider",
        "skip": "0",
        "strings": true,
        "include_empty_strings": "",
        "include_null_values": "",
        "x": 510,
        "y": 1440,
        "wires": [
            [
                "b5fc2f5a95f0e59f"
            ]
        ]
    },
    {
        "id": "6d3de56e13dec0b1",
        "type": "function",
        "z": "9624ddaf8e7b3698",
        "name": "配列",
        "func": "var data = global.get(\"log_data\");\ndata.push(msg.payload);\nglobal.set(\"log_data\", data);\nmsg.payload = data;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 510,
        "y": 1480,
        "wires": [
            []
        ]
    },
    {
        "id": "b5fc2f5a95f0e59f",
        "type": "switch",
        "z": "9624ddaf8e7b3698",
        "name": "del",
        "property": "payload.login_user",
        "propertyType": "msg",
        "rules": [
            {
                "t": "neq",
                "v": "login_user",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 390,
        "y": 1480,
        "wires": [
            [
                "6d3de56e13dec0b1"
            ]
        ]
    },
    {
        "id": "711c442e587074ee",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "使用中",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "他の方が使用中です。切断はできません。",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 150,
        "y": 260,
        "wires": [
            [
                "14146fd4a816d51b"
            ]
        ]
    },
    {
        "id": "14146fd4a816d51b",
        "type": "ui_toast",
        "z": "9624ddaf8e7b3698",
        "position": "top right",
        "displayTime": "3",
        "highlight": "",
        "sendall": false,
        "outputs": 0,
        "ok": "OK",
        "cancel": "",
        "raw": false,
        "className": "",
        "topic": "",
        "name": "使用中",
        "x": 270,
        "y": 260,
        "wires": []
    },
    {
        "id": "a739c7a734dfac5e",
        "type": "complete",
        "z": "9624ddaf8e7b3698",
        "name": "",
        "scope": [
            "9ee76930aadc0625"
        ],
        "uncaught": false,
        "x": 270,
        "y": 1520,
        "wires": [
            [
                "570a1ffa2b23a7f1"
            ]
        ]
    },
    {
        "id": "2beac260907d827c",
        "type": "change",
        "z": "9624ddaf8e7b3698",
        "name": "出力",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "log_data",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 530,
        "y": 1520,
        "wires": [
            [
                "00ec3f7d482dbea8"
            ]
        ]
    },
    {
        "id": "570a1ffa2b23a7f1",
        "type": "delay",
        "z": "9624ddaf8e7b3698",
        "name": "1秒",
        "pauseType": "delay",
        "timeout": "1",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 410,
        "y": 1520,
        "wires": [
            [
                "2beac260907d827c"
            ]
        ]
    },
    {
        "id": "c56c2f84eee38f59",
        "type": "ui_group",
        "name": "排他制御",
        "tab": "b5de217d24b3769b",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "68a74b5e3cb81569",
        "type": "ui_group",
        "name": "ファイル保存",
        "tab": "b5de217d24b3769b",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "9258a40ced27eba1",
        "type": "ui_group",
        "name": "認証",
        "tab": "7be2d49e0eb0264b",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "b5de217d24b3769b",
        "type": "ui_tab",
        "name": "操作テスト",
        "icon": "dashboard",
        "order": 6,
        "disabled": false,
        "hidden": false
    },
    {
        "id": "7be2d49e0eb0264b",
        "type": "ui_tab",
        "name": "認証画面",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]