Ewellix昇降軸
前書き
このチュートリアルでは、Ewellix LIFTKITプラグインを作成し、顧客がLIFTKITリフティングカラムの機能をJAKAアプリケーションに統合できるようにします。 開始する前に理解しておくべき知識/ドキュメントは以下の通りです:
- ビジュアルプログラミングの概念;
- JavaScriptのプログラミング知識;
- Node-REDノードとフローレポジトリの使用;
- JAKA AddOn紹介ドキュメント:(具体的な名称またはリンク)
本チュートリアルでは、JAKAロボットアームコントローラ内部に完全なAddOn環境がインストールされており、コントローラとAPPのバージョンがAddOnの作成要件を満たしていることを前提とします。
プラグイン機能の説明
Ewellix LIFTKITプラグインの具体的な機能は以下の通りです:
- node-redを通して3つのカスタムコマンドブロックを作成: (1)リフティングカラムの位置を取得し、その値を数値型変数に保存して後で呼び出せるようにする; (2)リフティングカラムを絶対位置まで移動させる。動作モードはブロッキングと非ブロッキングに分かれる; (3)リフティングカラムの動作を停止する。
- node-redを通してフロントエンドインターフェースとの連携を実現: (1)リフティングカラムのパラメータ取得と設定。これには、インターフェース言語表示、リフティングカラムのIP、ポート番号、仮想リミット、タイプが含まれる; (2)フロントエンドインターフェースの制御ボタンを用いてリフティングカラムを操作する。これには、絶対位置への移動、仮想リミットの最大値までの上昇、仮想リミットの最小値までの下降、現在の動作の停止が含まれる。
準備作業
- まず、AddOn開発テンプレート「JAKA_AddOn」を事前にダウンロードする必要があります。このプロジェクトファイルには、さまざまなタイプのAddOn開発に使用される機能が含まれています。
- JAKA_AddOnフォルダ内の設定ファイルJAKA_AddOn_config.iniを修正します。 (1)設定ファイル内のnameをname = JAKA_LiftKitに変更;
(2)descriptionを“JAKA LiftKit Command”に変更;
(3)設定ファイル名とフォルダ名はaddOn名と一致している必要があるため、設定ファイルの名前をJAKA_LiftKit_config.iniに変更;
(4)urlは現時点では変更せず、後ほどフロントエンドとバックエンドの連携部分で修正する;
(5)LiftKitの設定情報を追加:リフティングカラムのIP、ポート番号、および言語を含む。これら3つのパラメータはフロントエンドで設定された後、設定ファイルに渡されます。設定ファイルから値を読み取りますが、ここでは一旦以下のパラメータとして固定します。``` ini [LIFIKIT_CONFIG] lang = cn ip = 172.30.0.61 port = 50001

3. フォルダ名「JAKA_AddOn」を「JAKA_LiftKit」に変更し、JAKA_LiftKit.tar.gz 形式に圧縮します。
4. JAKAアプリを開き、「設定」→「システム設定」→「アドオン」を順にタップし、「アドオン」で以下の+ボタンをクリックして JAKA_LiftKit.tar.gz ファイルをアップロードします。
## カスタムコマンドブロックの作成
この部分を作成した後の完全なコマンドブロックのスタイルと Node-RED ノードのデプロイは以下の通りです。次に、これらの機能を1つずつ実現していきます。

### 4.1 位置取得コマンドブロック
#### 4.1.1 作成手順
操作手順は以下のとおりです。
1. アプリのアドオン管理画面で、JAKA_LiftKit の歯車ボタンをクリックし、現在のポート番号を確認します。

2. 本例ではロボットのIPは172.30.3.123で、AddOnのポート番号は10010です。ブラウザのアドレスバーに「172.30.3.123:10010」と入力して開発者ページにアクセスします。
3. JAKA_LiftKit_config.ini 設定ファイルからリフターのIPとポート番号を読み取ります。
(1)開発者ページの左側のノードメニューで「inject」「read file」「function」「debug」の4つのノードを見つけ、連結してデプロイします。
(3)「read file」ノードをダブルクリックし、プロパティ設定で、ファイル名にコントローラ内の JAKA_LiftKit_config.ini ファイルの保存位置を入力します。/usr/etc/jkzuc/configs/JAKA/AddOns/JAKA_LiftKit/JAKA_LiftKit_config.ini。エンコーディングを utf8 に設定し、「完了」ボタンをクリックします。

(4)IPとポート番号を読み取る関数を作成します:
コードは次のとおりです:
```javascript
// payload 属性は設定情報を含む文字列です
let configString = msg.payload;
// 正規表現で設定文字列内のIPとポートをマッチング
const ipRegex = /ip\s*=\s*([\d.]+)/;
const portRegex = /port\s*=\s*(\d+)/;
// カスタム関数:指定された文字列 str から正規表現 regex に一致する結果を抽出
function extractValue(regex, str) {
const matches = str.match(regex);
return matches ? matches[1] : null;
}
const ipValue = extractValue(ipRegex, configString);
const portValue = extractValue(portRegex, configString);
// グローバル変数を設定してIPとPortを保存
global.set("global_ip", ipValue);
global.set("global_port", portValue);
// IPアドレスとポート番号の値を取得
msg.payload = [ipValue, portValue];
msg.ipValue = ipValue;
msg.portValue = portValue;
return msg
(5)すべて設定が完了したら再デプロイし、最後にDebugで出力が昇降柱のIPとポート番号であるかを確認します。

(6)このノードの下にNode-REDのコメントノードを追加して、可読性を向上させることができます。

- 開発者ページの左側にあるノードメニューで「JAKA AddOn」を見つけ、その中の「Customized-commands」を中央の作業エリアにドラッグします。「デプロイ」をクリックし、ページ上部に「デプロイ成功」と表示されたら次のステップに進みます。

- 作業エリア内のブロックをダブルクリックして、コマンドブロック開発ページに入ります。このページでは、コマンドブロックのスタイル、リンク、および実現する機能を定義できます。
(1)基本設定: 

注:中央の空白入力ボックスは、プログラムまたはシステムの数値型変数を格納するために使用され、取得した位置値はその変数によって受け取られます。
(2)編集画面プレビュー:プレビューページ、設定は不要です。
(3)リンク:デフォルト
(4)スクリプト生成: 方法1:スクリプト生成方法としてTemplate Syntaxを選択します。Jks Templateでスクリプトを作成します。スクリプト内容が少なく直接定義できる場合は、この方法を使用することを推奨します。 方法2:スクリプト生成方法としてFunctionを選択します。カスタム関数による生成方法です。昇降ポールのIPやポート番号などのパラメータは可変のため、ここでは関数方式で実現します。選択後に「完了」をクリックします。
6. 左側のノード機能バーから「http in」、「http response」、2つの「function」、「debug」ノードをドラッグします。前の「get_position」ノードを追加し、以下の図のように接続してデプロイします:
7. 「http in」を編集し、リクエスト方式を「GET」、URLをカスタムコマンドブロックの名前に設定し、編集後「完了」をクリックします。
8. APPを再起動し、プログラミング制御ページの拡張内でカスタムコマンドブロックLiftKit_GetPosを見つけ、ブロックをメインプログラムにドラッグして保存します。保存成功メッセージが表示されれば、スクリプト要求が成功したことを意味します。同時に、node-redインターフェースのデバッグウィンドウでAPPから返された情報を確認できます。入力ボックスにプログラム数値型変数、システム数値型変数、または手動入力値を入れた場合、それぞれの時にAPPが返す情報を見てみましょう。 


「indePos」は前述のカスタムコマンド基本設定で入力ボックスに設定した属性名であり、「type」が0の場合は手動入力を意味し、「type」が1の場合はドラッグされた変数を意味します。取得される位置値は変数であるため、その値を格納するための数値型プログラム変数またはシステム変数を作成する必要があります。
- 最初の「function」ノードを編集し、以下のコードを入力して受信データストリームを前処理します。
// 受信したデータを前処理する
var value = msg.payload.indexPos;
var selectObj = JSON.parse(value);
msg.payload = selectObj;
return msg;
- 2つ目の「function」ノードを編集し、以下のコードを入力してjksスクリプトを生成します:
// 正常に返された場合、error_code = 0
msg.error_code = 0;
var LIFTKIT_ip = global.get("global_ip");
var LIFTKIT_port = global.get("global_port");
var value = msg.payload.value;
var type = msg.payload.type;
// typeが0の場合、ユーザーが取得値を受け取る変数を設定していないことを示し、エラーコードは-1を返す
if(type == 0){
msg.error_code = -1;
msg.error_msg = "位置値を受け取るプログラム変数を設定してください。";
}
// typeが1の場合、ユーザーは取得値を受け取る変数を設定済みであり、ソケットを介して昇降柱に位置取得コマンドを送信し、返されたデータをプログラム/システム変数に格納する
if (type == 1){
var jks_txt = ''
jks_txt += `LIFTKIT_clinet = socket_open("${LIFTKIT_ip}", ${LIFTKIT_port})\n`;
jks_txt += `get_position = "get_position\\n"\n`;
jks_txt += `get_position_str = socket_send(LIFTKIT_clinet, get_position)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet, 0)\n`;
jks_txt += `recv_data_cut = ""\n`;
jks_txt += `formatStr = "get_position,OK,%s"\n`;
jks_txt += `cutting = sscanf(recv_data, formatStr, recv_data_cut)\n`;
jks_txt += `position_data = recv_data_cut\n`;
jks_txt += `arrayOut = [0,1,2]\n`;
jks_txt += `resLen = get_array_from_string(position_data," ",arrayOut)\n`;
jks_txt += `position_data = arrayOut[0]\n`
jks_txt += `${value} = position_data\n`;
jks_txt += `socket_close(LIFTKIT_clinet)\n`;
msg.jks = jks_txt;
}
return msg;
- 各ノードの設定が完了した後、接続してデプロイします。

4.1.2 APPテスト
JAKA APPを開き、プログラミング画面の拡張モジュール内でLiftKit_GetPosを見つけます。プログラム実行終了後、変数は初期値を表示するため、変数の変化をよりよく観察できるようにプログラム内に遅延を追加します。 
この時、リフティングポールが返す位置は180 mmであり、このカスタムコマンドが有効になっていることを示しています。
4.1.3 拡張
前節で作成したコマンドブロックタイプはコマンドブロック型に属します。ここでは、同じ機能を実現するために数値型のコマンドを作成します。 
- 開発者ページの左側にあるノードメニューで「JAKA AddOn」を見つけ、その中の「Customized-commands」を中央の作業エリアにドラッグします。「デプロイ」をクリックし、ページ上部に「デプロイ成功」と表示されたら次のステップに進みます。

- 作業エリア内のブロックをダブルクリックして、コマンドブロック開発ページに入ります。このページでは、コマンドブロックのスタイル、リンク、実現する機能を定義することができます。 (1)基本設定:

(2)編集画面プレビュー:プレビューページ、設定は不要です。
(3)リンク:デフォルトを使用。
(4)スクリプト生成:Functionを選択します。選択後に「完了」をクリックします。
3. 左側のノード機能バーから「http in」、「http response」、「function」ノードをドラッグします。「liftkit_positon」ノードを追加し、以下の図のように接続してデプロイします:
4. 「http in」ノードを編集し、リクエスト方法を「GET」、URLをカスタムコマンドブロックの名前に設定します。編集後、「完了」をクリックします。
5. 「function」ノードを編集し、このデータブロック上で位置値を取得し、その位置パラメータをデータブロック上に保存するようにします。
var LIFTKIT_ip = global.get("global_ip");
var LIFTKIT_port = global.get("global_port");
var jks_txt = ''
jks_txt += `LIFTKIT_clinet = socket_open("${LIFTKIT_ip}", ${LIFTKIT_port})\n`;
jks_txt += `get_position = "get_position\\n"\n`;
jks_txt += `get_position_str = socket_send(LIFTKIT_clinet, get_position)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet, 0)\n`;
jks_txt += `recv_data_cut = ""\n`;
jks_txt += `formatStr = "get_position,OK,%s"\n`;
jks_txt += `cutting = sscanf(recv_data, formatStr, recv_data_cut)\n`;
jks_txt += `position_data = recv_data_cut\n`;
jks_txt += `arrayOut = 0\n`;
jks_txt += `resLen = sscanf(position_data,"%f",arrayOut)\n`;
jks_txt += `position_data = arrayOut\n`
jks_txt += `socket_close(LIFTKIT_clinet)\n`;
jks_txt += `return position_data`;
msg.jks = jks_txt;
return msg;
- APP上でテストします。 テスト手順は次の通りです:
(1) APPを再起動し、プログラム制御ページの拡張内でカスタムコマンドブロックLiftKit Position、条件判断命令、比較命令、および数値出力設定命令を見つけます。同時に、I/Oインターフェースの制御盤DO1がオフ状態になっていることを確認してください。

(2)下図に示す手順に従って設定を行い、「保存」をクリックしてください。
(3)実際の効果として昇降柱の位置が180mmになった場合、プログラム実行後に制御盤のDO1状態が開いている必要があります。効果をより分かりやすくするために、デバッグアシスタントを使用してTCPサーバー(上昇する列)をシミュレートし、ロボットをTCPクライアントとして扱います。プログラムの実行時、ロボットはまず「get_position\n」コマンドを送信して、昇降コラムの現在の状態を照会します。昇降コラムが正常な状態であれば、「get_position,OK,180\n」コマンドで応答します。ここで、180は昇降コラムの現在の位置値です。その後、ソケット(TCP)通信が閉じられ、プログラムが終了します。APP I/Oインターフェース上の制御盤DO1が開放状態に変更されました。これは、カスタムコマンドが有効になったことを示しています。 

4.2 移動命令ブロック
4.2.1 作成手順
- 開発者ページの左側のノードメニューバーで、JAKA AddOn を見つけ、Customized-commands を中央の作業領域にドラッグして、「デプロイ」をクリックします。
- 作業エリア内のブロックをダブルクリックして、コマンドブロック開発ページに入ります。 (1)基本設定:

注: 「LiftKit_MoveTo」コマンドにおいて、「Block」はコマンドのブロック機能が有効になっているかどうかを示し、「False」は非ブロック、「True」はブロックを意味します。デフォルト値は「False」で、これはブロック機能が無効になっていることを意味します。ブロッキングが有効になっている場合、上昇する柱が指定された高さに達するまで、ロボットは次のステップに進むことができません。ブロッキングが無効になっている場合、ロボットは上昇する柱が移動している間でも、次のステップのコマンドを実行して移動することができます。
(2)リンク:デフォルトを使用する。
(3)スクリプト生成:関数メソッドを選択し、「完了」をクリックします。
3. 「http in」、「http response」、「function」、および「debug」ノードを、左側のノード機能バーにドラッグアンドドロップします。下記の図に示すように、ノードを接続して展開してください。
4. 「http in」ノードを編集し、リクエスト方法を「GET」、URLをカスタムコマンドブロックの名前に設定します。編集後、「完了」をクリックします。
5. アプリを再起動し、プログラミング制御ページの拡張機能からカスタム命令ブロック「LiftKit_MoveTo」を見つけて、その命令ブロックをメインプログラムにドラッグして保存します。保存が成功したことを示すメッセージが表示されれば、スクリプト要求が成功したことを意味します。一方、アプリから返される情報は、Node-REDインターフェースのデバッグウィンドウで確認できます。
カスタムディレクティブブロックを設定する際、「select」はドロップダウンリストの属性名です。「select」では、「value」が0の場合、現在のドロップダウンリストは「False」に選択され、「value」が0の場合、現在のドロップダウンリストは「True」に選択されます。「numPos」は入力ボックスの属性名です。「numPos」の「値」は0であり、これは入力ボックスに現在入力されている値が0であることを意味します。“type”の意味については、3.1節の位置取得命令ブロックの説明を参照してください。 6. 最初の“function”ノードを編集し、関数名を任意に設定します。ここでは“get_data”と名付けます。以下のコードを入力して、送信データを前処理し、JavaScriptオブジェクトに変換します。
var value_pos = msg.payload.numPos;
var select = msg.payload.select;
var selectObj1 = JSON.parse(value_pos);
var selectObj2 = JSON.parse(select);
msg.payload = [selectObj1, selectObj2];
msg.selectObj1 = selectObj1;
msg.selectObj2 = selectObj2;
return msg;
- 2番目の“function”ノードを編集し、関数名を“data_process”とします。以下のコードを入力して、送信データをさらに処理します。
var value_pos = msg.payload[0].value;
var type_pos = msg.payload[0].type;
var value_select = msg.payload[1].value;
var type_select = msg.payload[1].type;
msg.payload = [value_pos, type_pos, value_select, type_select];
msg.value_pos = value_pos;
msg.type_pos = type_pos;
msg.value_select = value_select;
msg.type_select = type_select;
return msg;
- 3番目の“function”ノードを編集し、関数名を“move_to”とします。以下のコードを入力して、昇降ポールにsocket命令を送信し、指定位置への移動を実現します。
msg.error_code = 0;
var LIFTKIT_ip = global.get("global_ip");
var LIFTKIT_port = global.get("global_port");
var value_pos = msg.payload[0];
var type_pos = msg.payload[1];
var value_select = msg.payload[2];
var type_select = msg.payload[3];
// 仮想リミットはフロントエンドの設定に基づき、後ほど詳しく説明します
// var l_min = global.get("global_virtualLimits_min");
// var l_max = global.get("global_virtualLimits_max");
// ここでは一旦 l_min = 0、l_max = 900 に固定する
var l_min = 0;
var l_max = 900;
// 入力欄のデータが手動入力か、プログラム/システム変数で保存されたものかを判定する
if (type_pos == 0){
value_pos = parseFloat(value_pos);
// 入力欄が手動入力の場合、昇降柱の仮想リミットを超えていないかを判定する
if (value_pos >= l_min && value_pos <= l_max) {
// 仮想リミットを超えていなければ移動コマンドを送信する
var jks_txt = ``
jks_txt += `LIFTKIT_clinet = socket_open("${LIFTKIT_ip}", ${LIFTKIT_port})\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = 1\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `while(condition):\n`;
jks_txt += `get_status = "get_status\\n"\n`;
jks_txt += `get_status_str = socket_send(LIFTKIT_clinet,get_status)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet,0)\n`;
jks_txt += `recv_data_cut = recv_data\n`;
jks_txt += `separator_cutting = ""\n`;
jks_txt += `formatStr = "get_status,OK,%s"\n`;
jks_txt += `cutting = sscanf(recv_data,formatStr,recv_data_cut)\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = strcmp(recv_data_cut,Str_READY)\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `end\n`;
jks_txt += `sleep(1)\n`;
jks_txt += `moveTo = "moveTo_absolutePosition,${value_pos}\\n"\n`;
jks_txt += `moveTo_str = socket_send(LIFTKIT_clinet,moveTo)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet,0)\n`;
jks_txt += `sleep(1)\n`;
jks_txt += `if(${value_select} == 1)\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = 1\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `while(condition):\n`;
jks_txt += `get_status = "get_status\\n"\n`;
jks_txt += `get_status_str = socket_send(LIFTKIT_clinet,get_status)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet,0)\n`;
jks_txt += `recv_data_cut = recv_data\n`;
jks_txt += `separator_cutting = ""\n`;
jks_txt += `formatStr = "get_status,OK,%s"\n`;
jks_txt += `cutting = sscanf(recv_data,formatStr,recv_data_cut)\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = strcmp(recv_data_cut,Str_READY)\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `end\n`;
jks_txt += `socket_close(LIFTKIT_clinet)\n`;
jks_txt += `else:\n`;
jks_txt += `socket_close(LIFTKIT_clinet)\n`;
jks_txt += `end\n`;
msg.jks = jks_txt;
}
// 仮想リミットを超えた場合、エラーメッセージを表示する
else {
msg.error_code = -1;
msg.error_msg = "位置値が仮想リミット範囲外です";
}
}
// 入力ボックス内はプログラム/システム変数であり、移動コマンドを送信する
else {
var jks_txt = ``
jks_txt += `LIFTKIT_clinet = socket_open("${LIFTKIT_ip}", ${LIFTKIT_port})\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = 1\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `while(condition):\n`;
jks_txt += `get_status = "get_status\\n"\n`;
jks_txt += `get_status_str = socket_send(LIFTKIT_clinet,get_status)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet,0)\n`;
jks_txt += `recv_data_cut = recv_data\n`;
jks_txt += `separator_cutting = ""\n`;
jks_txt += `formatStr = "get_status,OK,%s"\n`;
jks_txt += `cutting = sscanf(recv_data,formatStr,recv_data_cut)\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = strcmp(recv_data_cut,Str_READY)\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `end\n`;
jks_txt += `sleep(1)\n`;
jks_txt += `bufferIn = ${value_pos}\n`;
jks_txt += `bufferOut = ""\n`;
jks_txt += `resLen = sprintf(bufferOut,"%f",bufferIn)\n`;
jks_txt += `position_data = bufferOut\n`
jks_txt += `Str_move = "moveTo_absolutePosition,"\n`
jks_txt += `concatStr = string_concat(Str_move,bufferOut)\n`
jks_txt += `concatStr = string_concat(concatStr,"\\n")\n`
jks_txt += `moveTo = concatStr\n`;
jks_txt += `moveTo_str = socket_send(LIFTKIT_clinet,moveTo)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet,0)\n`;
jks_txt += `sleep(1)\n`;
jks_txt += `if(${value_select} == 1)\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = 1\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `while(condition):\n`;
jks_txt += `get_status = "get_status\\n"\n`;
jks_txt += `get_status_str = socket_send(LIFTKIT_clinet,get_status)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet,0)\n`;
jks_txt += `recv_data_cut = recv_data\n`;
jks_txt += `separator_cutting = ""\n`;
jks_txt += `formatStr = "get_status,OK,%s"\n`;
jks_txt += `cutting = sscanf(recv_data,formatStr,recv_data_cut)\n`;
jks_txt += `Str_READY = "READY\\n"\n`;
jks_txt += `compare_value = strcmp(recv_data_cut,Str_READY)\n`;
jks_txt += `condition_value = 0\n`;
jks_txt += `condition = (compare_value != condition_value)\n`;
jks_txt += `end\n`;
jks_txt += `socket_close(LIFTKIT_clinet)\n`;
jks_txt += `else:\n`;
jks_txt += `socket_close(LIFTKIT_clinet)\n`;
jks_txt += `end\n`;
msg.jks = jks_txt;
}
return msg;
- 各ノードの設定が完了した後、接続してデプロイする(デバッグの場合は削除を選択してもよい)。

4.2.2 アプリテスト
JAKAアプリを開き、プログラミング画面の拡張モジュールでLiftKit_MoveToを見つける。 ケース1:「Block」で「False」を選択する。入力ボックスに適切な位置値を直接入力するか、プログラム変数を通して適切な位置値を入力する。プログラムを保存して実行し、リフトコラムを絶対位置100mmの位置まで移動させる。 
実際の昇降柱を接続すると、実際の動作として昇降柱が絶対位置100 mmの場所まで移動します。効果を確認しやすくするために、ここではデバッグアシスタントを使用してTCPサーバー(昇降柱)をシミュレートし、ロボットをTCPクライアントとして動作させます。
プログラムを実行すると、ロボットはまず「get_status\n」コマンドを送信して昇降柱の現在状態を問い合わせます。昇降柱が準備完了状態であれば「get_status,OK,READY\n」を返信し、「READY」状態を受信したときのみ、ロボットは「moveTo_absolutePosition,100\n」コマンドを送信します。送信後、昇降柱は「moveTo_absolutePosition,OK\n」を返信し、最後にソケット(TCP)通信を閉じて、プログラムが終了します。 ケース2:「Block」を「True」に設定します。入力欄に適切な位置値を直接入力するか、プログラム変数を介して適切な位置値を設定します。ここでは変数の初期値を200に設定し、プログラムを保存・実行して昇降柱を絶対位置200 mmの場所まで移動させます。 
プログラムを実行すると、ロボットはまず「get_status\n」コマンドを送信して昇降柱の現在状態を問い合わせます。昇降柱が準備完了状態であれば「get_status,OK,READY\n」を返信します。「READY」状態を受信した場合にのみ、ロボットは「moveTo_absolutePosition,200\n」コマンドを送信し、送信後、昇降柱は「moveTo_absolutePosition,OK\n」を返信します。その後、再度「get_status\n」コマンドを送信して昇降柱の現在状態を問い合わせ、昇降柱が移動中の場合は「get_status,OK,Moving\n」を返します。移動が完了し停止状態になった場合は「get_status,OK,READY\n」を返し、この返信によって昇降柱の移動完了を確認してから、プログラムは次の命令に進みます。 以上のテストが成功すれば、このカスタムコマンドブロックが有効であることを示します。
4.3 停止コマンドブロック
4.3.1 作成手順
- 開発者ページの左側のノードメニューバーで、JAKA AddOn を見つけ、Customized-commands を中央の作業領域にドラッグして、「デプロイ」をクリックします。
- 作業エリア内のブロックをダブルクリックして、コマンドブロック開発ページに入ります。 (1)基本設定:

(2)リンク:パスはデフォルトを使用します。 (3)スクリプト生成:Function方式を選択し、「完了」をクリックします。 
- 下図のように「http in」「http response」「function」「debug」および前の「get_position」ノードを接続してデプロイします:

- 「http in」ノードを編集し、リクエスト方式を「GET」、URLをカスタムコマンドブロック名stop_movingに設定し、編集後に「完了」をクリックします。
- APPを再起動し、プログラミング制御ページの拡張機能でカスタムコマンドブロックLiftKit_StopMoveを見つけ、メインプログラムにドラッグして保存します。保存成功のメッセージが表示されたら、スクリプトリクエストが正常に完了したことを示します。同時に、node-redインターフェースのデバッグウィンドウでAPPから返された情報を確認できます。返されたオブジェクトが空なのは、このコマンドブロックにテキスト属性が1つしかないためです。

- 関数をダブルクリックし、関数名は任意ですが、ここでは「stop_moving」とします。コントローラーで以下のコードを入力してjksスクリプトを生成します:
var LIFTKIT_ip = global.get("global_ip");
var LIFTKIT_port = global.get("global_port");
var jks_txt = ``
jks_txt += `LIFTKIT_clinet = socket_open("${LIFTKIT_ip}", ${LIFTKIT_port})\n`;
jks_txt += `stop_moving = "stop_moving\\n"\n`;
jks_txt += `stop_moving_str = socket_send(LIFTKIT_clinet, stop_moving)\n`;
jks_txt += `recv_data = socket_recv(LIFTKIT_clinet, 0)\n`;
jks_txt += `socket_close(LIFTKIT_clinet)\n`;
msg.jks = jks_txt;
return msg;
- すべてのノードの設定が完了したら、接続してデプロイします(ここでDebugは削除を選択します)。

4.3.2 アプリテスト
JAKA APPを開き、プログラミング画面の拡張モジュールでLiftKit_StopMoveを見つけます。ここでは移動コマンドと組み合わせて使用する必要があり、非ブロッキング移動状態です。まずリフトコラムを300 mmまで移動させ、移動が完了する前に停止コマンドを送信します。
実際のリフトコラムを接続すると、リフトコラムは300 mmまで移動する前に停止します。停止位置はコラム自体の遅延によって異なります。効果を確認しやすくするために、ここではデバッグアシスタントを使用してTCPサーバー(昇降柱)をシミュレートし、ロボットをTCPクライアントとして動作させます。
プログラムを実行すると、ロボットはまず「get_status\n」コマンドを送信して昇降柱の現在状態を問い合わせます。昇降柱が準備完了状態であれば「get_status,OK,READY\n」を返信します。「READY」状態を受信した場合のみ、ロボットは「moveTo_absolutePosition,300\n」コマンドを送信し、送信後にリフトコラムは「moveTo_absolutePosition,OK\n」コマンドを返します。その後、ロボットは「stop_moving\n」コマンドを送信して動作を停止し、リフトコラムは「stop_moving,OK\n」と返します。 以上のテストが成功すれば、このカスタムコマンドブロックが有効であることを示します。
フロントエンドとバックエンドの連携
この部分は、実際のリフトコラムを接続してテストを行うことで、仮想リミット、モデル、現在の状態、現在位置、ストロークなどの情報をリアルタイムで取得できます。
5.1 準備作業
- 事前にフロントエンドプロジェクトフォルダー「dist」をダウンロードしておきます。このフォルダーをAddOnパッケージのJAKA_LiftKitディレクトリ内に配置します。

- JAKA_LiftKitフォルダー内の設定ファイルJAKA_LiftKit_config.iniのurlパスをhttp://localhost/JAKA_LiftKit/dist/に変更します。

- JAKA_LiftKit_config.iniを保存し、JAKA_LiftKitフォルダーをJAKA_LiftKit.tar.gz形式に圧縮します。
- JAKA APPを開き、「設定」→「システム設定」→「アドオン」を順にクリックし、「アドオン」で既存のJAKA_LiftKitパッケージを削除してから、新しいJAKA_LiftKit.tar.gzファイルをアップロードします。
- アドオンJAKA_LiftKitパッケージの右側の操作オプションにある地球アイコンをクリックすると、フロントエンド画面が開きます。

- デバッグを容易にするために、ブラウザーでフロントエンド画面を開くこともできます。現在のロボットIPは172.30.3.123です。したがって、ブラウザーのアドレスバーに172.30.3.123/addon/JAKA_LiftKit/dist/を入力します。表示されたページに情報の誤り、欠落、設定ボタンの無反応などがあっても正常です。これは、まだバックエンドサービスを作成していないためです。

5.2 初期化画面
フロントエンド画面の言語、リフトコラムのIP、port初期パラメータはJAKA_AddOn_config.iniから取得するため、事前にファイルから読み取る必要があります。現在の状態、現在位置、最大ストローク、仮想リミット、タイプおよびタイプリストはリフティングポールから取得されるため、事前にTCP通信を確立し、リフティングポールに関連コマンドを送信してこれらの情報を取得する必要があります。この部分の作成後、node-redノードの配置は以下のようになります。次に、これらの機能を一歩ずつ完成させていきます。 
- 左側のノードメニュー欄で「inject」、「read file」、2つの「function」、「write file」、2つの「debug」を見つけ、これらのノードを接続して配置します。
(1)「inject」ノードを編集し、0.1秒後に即時実行を選択します。 (2)「read file」ノードを編集し、ファイルパスを次のように設定します: /usr/etc/jkzuc/configs/JAKA/AddOns/JAKA_LiftKit/JAKA_LiftKit_config.ini エンコーディングはutf8を選択します。 (3)「function 12」ノードを編集し、「extract_addon_port」に改名します。正規表現を使用してaddonポート番号を抽出し、出力します。 コードは次のとおりです:
let configString = msg.payload;
const portRegex = /portal\s*=\s*(\d+)/
function extractValue(regex, str) {
const matches = str.match(regex);
return matches ? matches[1] : null;
}
const portValue = extractValue(portRegex, configString);
const jsonObject = { "PORT": portValue };
msg.payload = jsonObject;
return msg
(4)「write file」ノードを編集し、ファイルパスを次のように設定します: /usr/etc/jkzuc/configs/JAKA/AddOns/JAKA_LiftKit/dist/server_config.json 行動はファイル上書きを選択し、エンコーディングはutf8を選択します。
(5)「function 13」ノードを編集し、「extract_ip/lang/port」に改名します。同様に正規表現を使用してリフティングポールのIPアドレス、ポート番号、デフォルト言語を抽出して出力します。コードは次のとおりです:
let configString = msg.payload;
const langRegex = /lang\s*=\s*([^\s]+)/;
const ipRegex = /ip\s*=\s*([\d.]+)/;
const portRegex = /port\s*=\s*(\d+)/;
function extractValue(regex, str) {
const matches = str.match(regex);
return matches ? matches[1] : null;
}
const ipValue = extractValue(ipRegex, configString);
const langValue = extractValue(langRegex, configString);
const portValue = extractValue(portRegex, configString);
// グローバル変数として設定し、後続の呼び出しを容易にする
global.set("global_ip", ipValue);
global.set("global_lang", langValue);
global.set("global_port", portValue);
msg.payload = [ipValue, langValue, portValue];
msg.ipValue = ipValue;
msg.langValue = langValue;
msg.portValue = portValue;
return msg
(6)すべての編集が完了したら、接続して配置し、debugデバッグ画面には次の情報が出力されます。
2. リフティングポールパラメータの取得:左側のノードメニュー欄で「inject」、8つの「function」、4つの「tcp request」、1つの「tcp out」ノードを見つけ、これらのノードを下図のように接続して配置します。
(1)「inject」ノードを編集し、0.1秒後に即時実行を選択します。
(2)「function 4」ノードを編集し、名前を「get_stroke」に変更します。このノードはリフティングポールにストローク取得コマンドを送信するために使用します。コードは次のとおりです:
msg.payload = "get_stroke\n"
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(3)「tcp request」ノードを編集し、名前を変更して選択文字列を返します。前のステップでmsg.hostおよびmsg.portプロパティを使ってtcpホストまたはポートを設定しているため、tcpホストまたはポートは空のままにしておくことができます。
(4)「function 8」ノードを編集し、名前を「return_get_stroke」に変更します。これはtcpサーバーからの返信メッセージを受信・処理し、ストローク値を取得するために使用されます。
var get_stroke_data = msg.payload;
var data = get_stroke_data.split(",");
global.set("global_stroke", data[2]);
msg.payload = global.get("global_stroke");
return msg;
注:「return_get_stroke」関数ノードの後にdebugノードを追加してストローク値を出力できますが、ここではデモを行いません。
(5)「function 5」ノードを編集し、名前を「get_virLimit」に変更します。このノードは昇降コラムに仮想リミット取得コマンドを送信するために使用されます。コードは次のとおりです:
msg.payload = "get_virtualLimits\n"
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(6)「function 9」ノードを編集し、名前を「return_get_virLimit」に変更します。これはtcpサーバーからの返信メッセージを受信・処理し、仮想リミット値を取得するために使用されます。コードは次のとおりです:
var get_stroke_data = msg.payload;
var data = get_stroke_data.split(",");
global.set("global_virtualLimits_min", data[2]);
global.set("global_virtualLimits_max", data[3]);
msg.payload = global.get("global_virtualLimits_min");
return msg;
(7)「function 6」ノードを編集し、名前を「get_types」に変更します。このノードは昇降コラムにタイプ取得コマンドを送信するために使用されます。コードは次のとおりです:
msg.payload = "get_type\n"
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(8)「function 10」ノードを編集し、名前を「return_get_types」に変更します。これはtcpサーバーからの返信メッセージを受信・処理し、現在の昇降コラムタイプを取得するために使用されます。コードは次のとおりです:
var get_stroke_data = msg.payload;
var data = get_stroke_data.split(",");
global.set("global_types", data[2]);
msg.payload = global.get("global_types", data[2]);
return msg;
(9)「function 7」ノードを編集し、名前を「get_typesAvailable」に変更します。このノードは昇降コラムに利用可能なタイプの取得コマンドを送信するために使用されます。コードは次のとおりです:
msg.payload = "get_typesAvailable\n";
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(10)「function 11」ノードを編集し、名前を「return_get_typesAvailable」に変更します。これはtcpサーバーからの返信メッセージを受信・処理し、現在の昇降コラムで利用可能なタイプを取得するために使用されます。コードは次のとおりです:
var get_stroke_data = msg.payload;
var data = get_stroke_data.split(",");
const filteredArray = data.filter(item => item.includes('LIFTKIT-'));
global.set("global_typesAvailable", filteredArray);
msg.payload = global.get("global_typesAvailable");
return msg;
(11)「tcp out」ノードを編集し、名前を変更して、タイプは「TCP応答」を選択します。 (12)各ノードを編集した後、再デプロイします。
3. UIの初期化:左側のノードメニューから「http in」、「function」、「http response」ノードを見つけ、下図のように接続してデプロイします。
(1)「http in」ノードを編集:リクエスト方法を「POST」にし、URLを「/init」とします。「完了」をクリックします。
(2)「function」ノードを編集し、名前を「init」に変更します。コードは以下の通りです:
msg.payload={
ip: global.get("global_ip"),
lang: global.get("global_lang"),
port: global.get("global_port"),
ewelli_params: {
stroke: global.get("global_stroke"),
virtualLimits: [global.get("global_virtualLimits_min"), global.get("global_virtualLimits_max")],
types: global.get("global_types"),
typesAvailable: global.get("global_typesAvailable")
}
}
return msg;
(3)「http response」ノードを編集し、ステータスコードを200に設定します。
(4)各ノードを編集・接続した後、再デプロイして実際の昇降柱に正常に接続します(注:実際のロボットアームに接続している場合のみ、正しいストローク、タイプ、仮想リミット情報が自動的に返されます)。フロントエンド画面をリフレッシュすると完全な情報が表示されます。
5.3 ハートビート監視
この部分では、昇降柱の状態、位置、ストロークおよび仮想リミットを監視および更新します。この部分を作成した後のNode-REDノードのデプロイ構成は以下の通りです: 
- 左側のノードメニューから「http in」、5つの「function」、「http response」、2つの「tcp request」、2つの「tcp out」ノードを見つけ、上図の順に接続してデプロイします。
(1)「http in」ノードを編集:リクエスト方法を「POST」にし、URLを「/heartbeat1」とします。「完了」をクリックします。
(2)1つ目の「function」ノードを編集し、名前を「get_status」に変更します。このノードは昇降柱に状態取得コマンドを送信するために使用されます。コードは次のとおりです:
msg.payload = "get_status\n";
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(3)2つ目の「function」ノードを編集し、名前を「return_get_status」に変更します。このノードはTCPサーバーからの応答メッセージを受信して処理し、現在の状態を取得します。コードは次のとおりです:
var get_status_data = msg.payload;
var data = get_status_data.split(",");
global.set("get_status_data", data[2]);
msg.payload = data[2];
return msg;
(4)3番目の「function」ノードを編集し、名前を「get_position」に変更します。このノードは昇降ポールに位置取得コマンドを送信するために使用されます。コードは次のとおりです:
msg.payload = "get_position\n";
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(5)4番目の「function」ノードを編集し、名前を「return_get_position」に変更します。このノードはTCPサーバーからの返信メッセージを受信して処理し、現在位置を取得するために使用されます。コードは次のとおりです:
var get_position_data = msg.payload;
var data = get_position_data.split(",");
global.set("get_position_data",data[2]);
msg.payload = data[2];
return msg;
(6)5番目の「function」ノードを編集し、名前を「heartbeat1」に変更します。このノードは昇降ポールの現在状態、位置、最大ストローク、最大仮想リミット、最小仮想リミットを監視および更新するために使用されます。コードは次のとおりです:
msg.payload = {
status: global.get("get_status_data"),
position: global.get("get_position_data"),
stroke: global.get("global_stroke"),
min_limit: global.get("global_virtualLimits_min"),
max_limit: global.get("global_virtualLimits_max")
}
return msg;
(7)「http response」ノードを編集し、ステータスコードを200に設定します。
(8)2つの「tcp request」ノードを編集し、名前を変更して、選択文字列を返すようにします。
(9)2つの「tcp out」ノードを編集し、名前を変更し、タイプを「応答TCP」に設定します。
5.4 パラメータ設定
この部分は、フロントエンドの設定ボタンを有効にするためのものであり、昇降ポールのIPアドレス、ポート番号、言語(中国語/英語)、仮想リミット、選択タイプを含みます。この部分を作成した後のNode-REDノードのデプロイ構成は以下の通りです: 
- 言語の設定: (1)左側のノードメニューで「http in」「switch」3つの「function」「http reponse」「read file」「write file」ノードを見つけ、以下の図の順序でこれらのノードを接続してデプロイします。

(2)「http in」ノードを編集:リクエスト方式を「POST」に設定し、URLを「/set_param」にして、「完了」をクリックします。
(3)「switch」ノードを編集し、名前を「switch_set」に変更します。プロパティはmsg.payload.paramを選択し、左下で合計4つのフローを追加して、それぞれの出力を「set_lang」「set_ip」「set_virtualLimits」「set_type」に設定します。
(4)1番目の「function」ノードを編集し、「set_lang」に名前を変更し、送信データフローの前処理を行います:
var setlang = msg.payload.data.lang
global.set("global_setlang", setlang);
msg.setportValue = global.get("global_setlang");
return msg;
(5)「read file」ノードを編集し、プロパティ設定でファイル名を JAKA_LiftKit_config.ini にし、コントローラー内の保存場所を指定します。/usr/etc/jkzuc/configs/JAKA/AddOns/JAKA_LiftKit/JAKA_LiftKit_config.ini。エンコーディングは utf8 を選択し、「完了」ボタンをクリックします。
(6)2つ目の「function」ノードを編集し、「update_/lang」に名前を変更します。正規表現を使用して、設定ファイルからインターフェース言語を抽出します:
let configString = msg.payload;
var newLang = global.get("global_setlang");
const langRegex = /lang\s*=\s*([^\s]+)/;
function extractValue(regex, str) {
const matches = str.match(regex);
return matches ? matches[1] : null;
}
const langValue = extractValue(langRegex, configString);
msg.payload = configString;
return msg;
(7)「write file」ノードを編集し、ファイルパスを以下のように設定します: /usr/etc/jkzuc/configs/JAKA/AddOns/JAKA_LiftKit/JAKA_LiftKit_config.ini 動作は「上書きファイル」を選択し、エンコーディングは utf8 を選びます。
(8)3つ目の「function」ノードを編集し、「return_set」に名前を変更して、フロントエンドに設定成功のメッセージを返します。
msg.payload = "0";
return msg;
(9)「http response」ノードを編集し、ステータスコードを200に設定します。
- IP/ポートの設定: (1)左側のノードメニューから「function」「http reponse」「read file」「write file」の3つのノードを見つけ、図の順序に従ってこれらのノードを接続してデプロイします。
(2)1つ目の「function」ノードを編集し、「set_ip」に名前を変更して、転送データストリームを前処理します:
var setip = msg.payload.data.ip
var setport = msg.payload.data.port
global.set("global_setip", setip);
global.set("global_setport", setport);
msg.payload = [global.get("global_setip"), global.get("global_setport")];
msg.setipValue = global.get("global_setip");
msg.setportValue = global.get("global_setport");
return msg;
(3)「read file」ノードを編集し、プロパティ設定でファイル名を JAKA_LiftKit_config.ini にし、コントローラー内の保存場所を指定します。/usr/etc/jkzuc/configs/JAKA/AddOns/JAKA_LiftKit/JAKA_LiftKit_config.ini。エンコーディングは utf8 を選択し、「完了」ボタンをクリックします。
(4)2つ目の「function」ノードを編集し、「update_ip/port」に名前を変更して、正規表現を使用して設定ファイルからリフトコラムのIPとポート番号を抽出します:
let configString = msg.payload;
var newIP = global.get("global_setip");
var newPort = global.get("global_setport");
const ipRegex = /ip\s*=\s*([\d.]+)/;
const portRegex = /port\s*=\s*(\d+)/;
function extractValue(regex, str) {
const matches = str.match(regex);
return matches ? matches[1] : null;
}
const ipValue = extractValue(ipRegex, configString);
const portValue = extractValue(portRegex, configString);
configString = configString.replace(ipRegex, `ip = ${newIP}`);
configString = configString.replace(portRegex, `port = ${newPort}`);
msg.payload = configString;
return msg;
(5)「write file」ノードを編集し、ファイルパスを次のように設定します: /usr/etc/jkzuc/configs/JAKA/AddOns/JAKA_LiftKit/JAKA_LiftKit_config.ini 動作は「上書きファイル」を選択し、エンコーディングは utf8 を選びます。
(6)3番目の「function」ノードを編集し、「update_ip/port」に名前を変更して、フロントエンドに設定成功のメッセージを返します。
msg.payload = "0";
return msg;
(7)「http response」ノードを編集し、ステータスコードを200に設定します。 3. 仮想リミットを設定します: (1)左側のノードメニューで、2つの「function」、「tcp repuest」、「tcp out」、「http response」ノードを見つけ、下図の順序でこれらのノードを接続してデプロイします。
(2)1番目の「function」ノードを編集し、「set_virtualLimits」に名前を変更して、TCPサーバーに仮想リミット取得コマンドを送信します:
const l_min = parseFloat(msg.payload.data.min_limit);
const l_max = parseFloat(msg.payload.data.max_limit);
global.set("global_l_min", l_min);
global.set("global_l_max", l_max);
var set_l_min = global.get("global_l_min");
var set_l_max = global.get("global_l_max");
const send_msg_virtualLimits = `set_virtualLimits,${set_l_min},${set_l_max}\n`;
msg.payload = send_msg_virtualLimits;
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(3)「tcp request」ノードを編集し、名前を変更し、選択文字列を返します。 (4)「tcp out」ノードを編集し、名前を変更し、タイプを「TCP応答」に設定します。 (5)2番目の「function」ノードを編集し、「return_set_virtualLimit」に名前を変更して、受信した仮想リミットを処理してグローバル変数を更新し、フロントエンドに設定成功のメッセージを返します。
if (msg.payload == 'set_virtualLimits,OK\n')
{
global.set("global_virtualLimits_min", global.get("global_l_min"));
global.set("global_virtualLimits_max", global.get("global_l_max"));
msg.payload = "0";
}
else
{
msg.payload = "1";
}
return msg;
(6)「http response」ノードを編集し、ステータスコードを200に設定します。 4. タイプを設定: (1)左側のノードメニューから2つの「function」ノード、「tcp repuest」、「http response」ノードを見つけ、下図の順にこれらのノードを接続してデプロイします。
(2)最初の「function」ノードを編集し、「set_type」に名前を変更し、TCPサーバーにタイプ取得コマンドを送信します:
const types = msg.payload.data.types;
global.set("global_types", types);
var set_types = global.get("global_types");
const send_msg = `set_type,${set_types}\n`;
msg.payload = send_msg;
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(3)「tcp request」ノードを編集し、名前を変更し、選択文字列を返します。 (4)「tcp out」ノードを編集し、名前を変更し、タイプを「TCP応答」に設定します。 (5)2つ目の「function」ノードを編集し、「return_set_types」に名前を変更して、設定成功のメッセージをフロントエンドに返します。
if (msg.payload == 'set_type,OK\n')
{
msg.payload = "0";
}
else
{
msg.payload = "1";
}
return msg;
(6)「http response」ノードを編集し、ステータスコードを200に設定します。
5.5 モーションコントロール
この部分は、前端のモーションコントロールボタン(移動、上昇(仮想リミットの最大値まで上昇)、下降(仮想リミットの最小値まで下降)、停止)を有効にするためのものです。この部分を作成した後のNode-REDノードのデプロイ構成は以下の通りです: 
移動、上昇、下降はいずれもTCPサーバーに絶対位置への移動コマンドを送信するため、これら3つの出力は同一の出力ストリームで処理できます。
昇降ポールの移動/上昇/下降を制御:
(1)左側のノードメニューから「http in」、「switch」、2つの「function」、「http reponse」、「read file」、「write file」ノードを見つけ、下図の順にこれらのノードを接続してデプロイします。
(2)「http in」ノードを編集します:リクエストメソッドを「POST」に設定し、URLを「/move_control」とし、「完了」をクリックします。
(3)「switch」ノードを編集し、名前を「switch_set」に変更し、プロパティをmsg.payload.paramに設定します。左下で4つの出力ストリームを追加し、それぞれ「move」、「up」、「down」、「stop」とします。 
(4)最初の「function」ノードを編集し、「move/up/down」に名前を変更し、送信データストリームの事前処理を行います:
const move_data = parseFloat(msg.payload.data);
global.set("global_move_data", move_data);
var position = global.get("global_move_data");
const send_msg = `moveTo_absolutePosition,${position}\n`;
msg.payload = send_msg;
msg.host = global.get("global_ip");
msg.port = global.get("global_port");
return msg;
(5)「tcp request」ノードを編集し、名前を変更し、戻り値を文字列に設定します。
(6)「tcp out」ノードを編集し、名前を変更し、タイプを「TCPに応答」に設定します。
(7)2番目の「function」ノードを編集し、「return_move」に名前を変更し、フロントエンドに設定成功の情報を返します。
if (msg.payload == 'moveTo_absolutePosition,OK\n')
{
msg.payload = "0";
}
else
{
msg.payload = "1";
}
return msg;
(8)「http response」ノードを編集し、ステータスコードを200に設定します。 これで、フロントエンドと連携するバックエンドサービスの作成が完了しました。ノードの配置は以下の通りです。 
5.6 アプリテスト
前提条件:実機の昇降柱に接続済みであること。ここでは、EWELLIX社製のLIFTKIT-601型昇降柱を使用しています。実際の昇降柱に接続することで、仮想リミット、モデル、現在の状態、現在位置、ストロークなどの情報をリアルタイムで取得できます。 具体的なテスト手順:
アドオンJAKA_LiftKitパッケージの右側の操作オプションにある地球アイコンをクリックすると、フロントエンド画面が開きます。

言語切替:右上の言語切替をクリックすると、画面が英語から中国語に変わるのが確認できます。
通信設定:正しい昇降柱のIPとポートを入力し、下の「設定」ボタンをクリックすると、「パラメータ設定成功」のメッセージが表示されます。
仮想リミット:仮想リミットの最大値と最小値を入力し、下の「設定」ボタンをクリックすると、「パラメータ設定成功」のメッセージが表示されます。
タイプ設定:ドロップダウンで現在「LIFTKIT-601」「LIFTKIT-602」「LIFTKIT-00」の3種類が表示されます。「LIFTKIT-601」を選択し、下の「設定」ボタンをクリックすると、「パラメータ設定成功」のメッセージが表示されます。
手動制御 (1)目標位置に200を入力し、「移動」ボタンをクリックすると、昇降柱が200 mmの位置まで移動して停止します。パラメータは右側の「パラメータ表示」モジュールにリアルタイムで表示されます。昇降柱の現在の状態や位置をリアルタイムで監視できます。 (2)目標位置に400を入力して「移動」ボタンをクリックし、その直後に「停止」をクリックすると、昇降柱は400 mmの位置に到達する前に停止します。 (3)「上昇」ボタンを長押しすると、昇降柱が動き出し、最大仮想リミット900 mmに達すると動作が停止します。 (4)「下降」ボタンを長押しすると、昇降柱が動き出し、最小仮想リミット0 mmに達すると動作が停止します。 上記のテストが成功したことにより、Node-redで作成したフロントエンドとバックエンドの連携機能が有効になっていることを示しています。
