ソニーセミコンダクタソリューションズの大橋です。
AITRIOSでは、Consoleを用いてエッジデバイスの操作や、Consoleに登録しているCommand ParameterファイルやAIモデルを制御することができますが、REST APIを直接実行して制御することもできます。
本記事では、WebブラウザからREST APIを実行してエッジデバイスとCommand Parameterの制御を試してみたので紹介します。
この記事は2024年09月27日時点の情報に基づいて作成されています。
はじめに
REST APIを実行する方法としては、Pythonを用いたり、Postmanなどのソフトウェアを用いたりする方法があります。
本記事ではそういったツールを用いず、HTML+JavaScriptを用いてREST APIを実行することを試してみました。
注意点
- ブラウザセキュリティの変更について
本記事では、ブラウザのセキュリティレベルを落として実行するためのショートカットを作成します。そのためブラウザそのものに影響を与えることはありません。
ただし、本記事で作成したショートカットで開いたブラウザを他の目的で使用することは危険が伴いますので注意してください。
- ブラウザからのREST API実行のサポートについて
本記事の内容は筆者が試しにやってみた内容を紹介しているものです。AITRIOSが公式にサポートしている内容ではありません。
前提
- 推論可能なエッジデバイスが準備できていること
推論に必要なAIモデル、Applicationがエッジデバイスにデプロイされ、Command Parameterが設定されていることを前提とします。
推論を実行すると、ConsoleのCheck Dataで画像と推論結果が確認できる状態にしておいてください。
AITRIOSにおけるREST API実行の流れ
REST APIを利用する流れについては、公式マニュアル「Console REST API 使い方ガイド」に記載がありますので、詳細についてはこちらも参照してください。簡単に流れを説明すると、
- 使用するAITRIOSプロジェクトのClient ID, Client SecretをPortalエンドポイントに送信する。
- 認証が成功すると、アクセストークンが発行される。
- ConsoleエンドポイントにREST APIコマンド (HTTPリクエスト) を送信する。このとき、ヘッダにアクセストークンを設定する。
のようになります。
なお、AITRIOSではアクセストークンの有効期限が1時間に設定されています。1時間の間であれば、アクセストークンを使い回してREST APIコマンドの実行を連続して送信することができます。
準備
- クライアントID、シークレットの取得
AITRIOSのプロジェクトに接続するためのクライアントID、シークレットの取得についてはPortal User Manualの「クライアントアプリ用のClient Secretを発行する」に記載があります。AITRIOSのPortalにログインするアカウントにAdminロールが割り当てられている必要がありますので注意してください。 - Portalエンドポイント、Consoleエンドポイントの確認
Portalエンドポイント、Consoleエンドポイントの情報はこちらに記載があります(参照するためにはAITRIOSのPortalにログインする必要があります)。Portalエンドポイントは共通ですが、Consoleエンドポイントはお使いのAITRIOS Consoleによって異なりますので上記資料を確認してください。 - HTMLファイルの準備
本記事では1つのHTMLファイルを作成・修正していきます。まずは空のHTMLファイルを作成しておきます。ファイル名は自由に設定いただいて構いませんが、ここではREST_API.htmlとします。
テキストエディタを使って以下のようなファイルを作成してください。
<html>
<head>
<title>ブラウザでREST APIを実行するサンプル</title>
</head>
<body>
</body>
</html>
上記の<body>~</body>にコードを実装していきます。
- ブラウザ設定
ブラウザからJavaScriptでREST APIを実行する場合、CORS制約により異なるドメインのAPIを実行することができません。ここではブラウザのセキュリティレベルを落としてAPIを実行するための設定をしたショートカットを作成します。本記事ではブラウザとしてChromeを使用します。
本設定はブラウザのセキュリティを無効化します。
あくまでもブラウザでAPIを実行してみる実験用途で設定していますので、この設定をした状態で他サイトの閲覧などの用途には利用しないでください。
- Chromeの起動ショートカットを作成します。Chromeの実行ファイル"chrome.exe"を右クリックして「ショートカットの作成」をクリックします。
- ショートカットのリンク先に以下のように引数を設定します。ファイルパスは自分の環境に読み替えてください。C:\testは自由に変更しても構いません
C:\Users\{ユーザー名}\AppData\Local\Google\Chrome\Application\chrome.exe --disable-web-security --user-data-dir=C:\test
Chromeショートカットのプロパティ設定
設定が成功すれば、作成したショートカットでChromeを起動すると下図のように「--disable-web-securityを使用しています。」というメッセージが表示されます。本記事で作成するREST_API.htmlを実行する場合は、ここで起動したChromeにHTMLファイルをドラッグ&ドロップして動作を確認してください。
アクセストークンを取得する
それではいよいよREST_API.htmlを実装していきます。まずはアクセストークンを取得してみましょう。アクセストークンを取得するために必要なクライアントIDやシークレットはWebフォームで入力できるようにします。REST_API.htmlの<body>直下に以下のように記述します。
<h1>Settings</h1>
<div>
<input type="button" onclick="Authentication()" value="token取得">
</div>
auth_url: <input type="text" id="auth_url" required size="55" value=""><br>
base_url: <input type="text" id="base_url" required size="45" value=""><br>
client_id: <input type="text" id="client_id" required size="20" value=""><br>
secret: <input type="text" id="secret" required size="70" value=""><br>
token: <span id="access_token"></span><br>
device_id: <input type="text" id="device_id" required size="40" value=""><br>
保存してブラウザで開くと下図のようになります。
token取得のフォーム
device_idはアクセストークンの取得に必要な情報ではありませんが、このあとの多くのREST APIの実行で必要になるパラメータなので、ここで設定できるようにしています。もちろん、個別のREST APIの実装の中で都度device_idを設定するようにしても構いません。
次に、"token取得"ボタンを押下したときに、tokenを取得するためのHTTPリクエスト送信処理をJavaScriptで記述します。HTTPリクエストを送信するために、XMLHttpRequestを使用します。REST_API.htmlに以下の記述を追加します。
<script type="text/javascript">
function Authentication() {
auth_url = document.getElementById('auth_url').value
client_id = document.getElementById('client_id').value
secret = document.getElementById('secret').value
//client_id:secretをbase64エンコード
authorization = btoa(client_id + ':' + secret)
data = 'grant_type=client_credentials&scope=system'
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
document.getElementById('access_token').innerHTML = JSON.parse(res)['access_token'];
};
xhr.onerror = function () {
alert('token取得エラー');
}
xhr.open('post', document.getElementById('auth_url').value, true);
xhr.setRequestHeader('accept', 'application/json');
xhr.setRequestHeader('authorization', 'Basic ' + authorization);
xhr.setRequestHeader('cache-control', 'no-cache');
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
xhr.send(data);
};
</script>
フォームに入力されたclient_id(クライアントID)、secret(シークレット)をauthorization(Consoleエンドポイント)に送信してアクセストークンを取得し、フォーム上のaccess_token欄に表示しています。
client_idとsecretはコロン(:)でつないでbase64エンコードする必要があります。このあたりの仕様は「Console REST API 使い方ガイド」に記載があります。
それでは正しくアクセストークンが取得できるか実際に動かしてみましょう。実行するときは前述したようにCORSを無効化したブラウザを使うことを忘れないようにしてください。繰り返しになりますが、セキュリティを落としているので他の用途で使わないようにしましょう。
アクセストークン取得
上図のように、"token:"の欄に文字列が表示されていれば成功です。
REST APIを実行する~GetDirectImage編~
さて、AITRIOSにおけるREST APIの実行の流れの復習です。
- 使用するAITRIOSプロジェクトのClient ID, Client SecretをPortalエンドポイントに送信する。
- 認証が成功すると、アクセストークンが発行される。
- ConsoleエンドポイントにREST APIコマンド (HTTPリクエスト) を送信する。このとき、ヘッダにアクセストークンを設定する。
これまでの作業でアクセストークンの発行までが完了しました。これでようやくAITRIOSが用意しているREST API群を実行する準備が整いました。
最初に実行するREST APIはGetDirectImageです。これはエッジデバイスのプレビュー画像を取得するためのAPIです。ConsoleのPreviewに相当します。REST APIのリファレンスを確認しておきましょう。
GetDirectImageのAPIリファレンス
画面右側に "GET /devices/{device_id}/images/latest"の記載があります。HTTPリクエストのメソッドがGETで、Consoleエンドポイント+/devices/{device_id}/images/latest にリクエストを送信すれば良いことが分かります。また{device_id}は画面左側に記載しているPATH PARAMETERSに説明があります。お使いのエッジデバイスのデバイスID (sid-xxxxや、Aid-xxxx) を指定すれば良いことが分かります。
device_idの入力フォームは作成済みです。これをAPIリファレンスに従って送信し、GetDirectImageを実行します。REST_API.htmlに下記コードを追加します。
<h1>Preview</h1>
<form name="GetDirectImage_form">
<div>
<input type="button" onclick="GetDirectImage()" value="更新">
<img id="preview_image" src="" />
</div>
</form>
<script type="text/javascript">
function GetDirectImage() {
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
if (res.length > 0) {
document.getElementById("preview_image").src = "data:image/png;base64," + JSON.parse(res)['contents'];
}
};
xhr.onerror = function () {
alert('error!');
}
xhr.open('get', document.getElementById('base_url').value + '/devices/' + document.getElementById('device_id').value + '/images/latest', true);
xhr.setRequestHeader('Authorization', 'Bearer ' + document.getElementById('access_token').innerHTML);
xhr.send();
};
</script>
簡単にコードの説明をします。
xhr.openの行で、第1引数にHTTPリクエストのメソッド'get'を指定し、第2引数にはリクエストの送信先を指定しています。リクエスト送信先の{device_id}には、フォームに入力されたdevice_idを挿入しています。
xhr.setRequestHeaderの行で、アクセストークンを渡しています。
xhr.sendの行でリクエストの送信をしています。
実行結果は変数resに格納されますが、欲しいのは画像データ部分のみです。リファレンスの記載を見ると、base64エンコードされた画像データが'contents'の値としてJSONに格納されていることが分かります。そこでJSON.parse(res)['contents']により画像データ部分を抜き出し、最後にデコードして表示しています。
それでは実行してみましょう。
エッジデバイスが撮影している画像が表示されれば成功です。
基本的には他のAPIについても、xhr.openでのメソッドの変更、リクエスト送信先の変更、パラメータの指定を各APIに合わせて送信すれば実行することができます。
REST APIを実行する~GetCommandParameterFile編~
次にCommand Parameterを設定するためのREST APIを実行できるようにしていきます。ここではすでにエッジデバイスにBindされているCommand Parameterを修正するコードを実装していきます。まずは修正したいCommand Parameterのファイル名を指定して、該当するCommand Parameterの設定値をテキストボックスに表示してみましょう。REST APIのリファレンスを確認しておきます。
GetCommandParameterFileのAPIリファレンス
画面右側に "GET /command_parameter_files"の記載があります。HTTPリクエストのメソッドがGETで、Consoleエンドポイント+/command_parameter_files にリクエストを送信すれば良いことが分かります。また、引き渡すべきパラメータはありません。
返り値を確認すると、parameter_listというリストが返り、リストの1要素の中にはparameterやfile_nameという要素が格納されていることが分かります。GetCommandParameterFileでは、Consoleに登録されている全てのCommand Parameterの情報を取得するので、その中から欲しいCommand Parameterの情報だけを抜き出すようにします。REST_API.htmlに以下のコードを追加してください。
<h1>CommandParameter</h1>
<form name="GetCommandParameterFile_form">
<div>
<label for="command_parameter_file_name">ファイル名: </label>
<input type="text" id="command_parameter_file_name" required size="40">
<input type="button" onclick="GetCommandParameterFile()" value="取得">
<input type="button" onclick="UpdateCommandParameterFile()" value="更新">
</div>
<div>
<textarea cols="55" rows="10" id="command_parameter"></textarea>
</div>
</form>
<script type="text/javascript">
function GetCommandParameterFile() {
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
if (res.length > 0) {
//(1) CommandParameterのリストを取得
var parameter_list = JSON.parse(res)['parameter_list'];
//(2) (1) から、フォームに入力されたファイル名のCommandParameterを絞り込む
var target_param = parameter_list.filter(param => param.file_name === document.getElementById('command_parameter_file_name').value)[0];
//(3) (2) から、設定値部分を抜き出してフォームに表示する
document.getElementById('command_parameter').value = JSON.stringify(target_param['parameter'], null, 4);
}
};
xhr.onerror = function () {
alert('error!');
}
xhr.open('get', document.getElementById('base_url').value + '/command_parameter_files', true);
xhr.setRequestHeader('Authorization', 'Bearer ' + document.getElementById('access_token').innerHTML);
xhr.send();
};
</script>
フォームには取得ボタン、更新ボタン、Command Parameterの表示・編集テキストボックスを配置しています。更新ボタン押下時の処理は次節で行います。取得ボタンを押下したときの処理をjavaScriptで記載しています。
xhr.open、xhr.setRequestHeader、xhr.sendについては前節のGetDirectImageと同じで、APIリファレンスに沿ってリクエストを送信しているだけです。
Command Parameterの設定を抜き出す部分についてはAPIリファレンスの返り値を確認すれば、どの部分を抜き出せば良いかが判断できます。実行してみましょう。"ファイル名"欄に使用しているデバイスにバインドしているCommand Parameterのファイル名を指定して"取得"ボタンを押下します。
Command Parameter取得
上図のようにテキストエリアにCommand Parameterファイルの中身が表示されていれば成功です。
REST APIを実行する~UpdateCommandParameterFile編~
次に、"更新"ボタンを押下したときに、登録されているCommand Parameter fileの中身を更新するコードを作成します。まずはUpdateCommandParameterFileのAPIリファレンスを確認します。
UpdateCommandParameterFileのAPIリファレンス
HTTPリクエストのメソッドがPATCH、リクエストの送信先は"/command_parameter_files/{file_name}"であることが分かります。また、送信パスに含めるパラメータ"file_name"の他、JSON形式でパラメータ"parameter"を渡す必要があることが分かります。
必要なボタンやテキストエリアは作成済みなので、JavaScriptでHTTPリクエスト部分を記述します。
<script type="text/javascript">
function UpdateCommandParameterFile() {
var data = {
parameter: btoa(document.getElementById('command_parameter').value),
comment: "updated by REST_API_SAMPLE",
}
var json_text = JSON.stringify(data);
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
if (res.length > 0) {
alert(res);
}
};
xhr.onerror = function () {
alert('error!');
}
xhr.open('PATCH', document.getElementById('base_url').value + '/command_parameter_files/' + document.getElementById('command_parameter_file_name').value, true);
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xhr.setRequestHeader('Authorization', 'Bearer ' + document.getElementById('access_token').innerHTML);
xhr.send(json_text);
};
</script>
これまでに実行したREST APIと異なるのは、HTTPリクエストのメソッドがPATCHであること、JSONでパラメータを渡していることです。メソッドはxhr.openの第1引数の指定を変更するだけです。JSONでパラメータを渡すために、xhr.setRequestHeaderを追加しています。また、パラメータとして渡すJSONについては、var dataでデータを定義し、JSON.stringify(data); としてJSONオブジェクトに変更し、xhr.sendの引数に設定して送信する流れとなります。また、UpdateCommandParameterFileは正常終了するとレスポンスとして{"result":"Success"}を返すことがリファレンスに記載されていますので、確認のためにalert(res)でAPIのレスポンスをポップアップで表示するようにしています。それでは"取得"ボタンでCommand Parameterを取得し、テキストエリアに表示されているCommand Parameterを少し変更して"更新"ボタンを押してみましょう。
UpdateCommandParameterFile実行結果
無事、"Success"が返ってきたら成功です。念のため、テキストエリアのCommand Parameterを手動で削除して、"取得"ボタンを押下して再度Command Parameterを確認してみましょう。修正したCommand Parameterが表示されるはずです。
これまでに、REST APIのメソッドに合わせたHTTPリクエストの送信方法、パラメータを送信パスに含めて送る方法とJSONで送る方法を説明しました。ここまでできれば、あとはこれらの組み合わせで実現できます。
REST APIを実行する~Start/StopUploadInferenceResult編~
ここでは推論を開始するStartUploadInferenceResultと、推論を停止するStopUploadInferenceResultを作成します。実行に必要なパラメータはdevice_idのみで、レスポンスも実行結果を返しているだけなので必要な情報を抜き出すなどの処理も不要です。APIリファレンスを確認し、これまでに作成したコードを参考にすれば推論の"開始"、"停止"ボタン押下時の処理を書けると思いますのでやってみましょう。
<h1>Inference Contorol</h1>
<form action="" method="post" name="StartUploadInferenceResult_form">
<div>
<input type="button" onclick="StartUploadInferenceResult()" value="推論開始">
<input type="button" onclick="StopUploadInferenceResult()" value="推論停止">
</div>
</form>
<script type="text/javascript">
function StartUploadInferenceResult() {
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
if (res.length > 0) {
alert(res)
}
};
xhr.onerror = function () {
alert('error!')
}
xhr.open('post', document.getElementById('base_url').value + '/devices/' + document.getElementById('device_id').value + '/inferenceresults/collectstart', true);
xhr.setRequestHeader('Authorization', 'Bearer ' + document.getElementById('access_token').innerHTML);
xhr.send();
};
function StopUploadInferenceResult() {
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
if (res.length > 0) {
alert(res)
}
};
xhr.onerror = function () {
alert('error!')
}
xhr.open('post', document.getElementById('base_url').value + '/devices/' + document.getElementById('device_id').value + '/inferenceresults/collectstop', true);
xhr.setRequestHeader('Authorization', 'Bearer ' + document.getElementById('access_token').innerHTML);
xhr.send();
};
</script>
REST APIを実行する~おまけ~
REST APIのGetInferenceResultsを使って最新のメタデータを取得して表示するコードと、取得したメタデータを削除するコードを作成してみました。
<h1>Insight</h1>
<form name="GetInferenceResults_form">
<div>
<input type="button" onclick="GetInferenceResults()" value="取得">
<input type="button" onclick="DeleteInferenceResults()" value="削除">
id:<span id="metadata_id"></span>
</div>
<div>
<textarea cols="70" rows="10" id="InferenceResult"></textarea>
</div>
</form>
<script type="text/javascript">
function GetInferenceResults() {
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
if (res.length > 0) {
document.getElementById('InferenceResult').value = JSON.stringify(JSON.parse(res)[0]["inference_result"], null, 4);
document.getElementById('metadata_id').innerHTML = JSON.stringify(JSON.parse(res)[0]["inference_result"]["id"]).replace(/\"/g, "");
}
};
xhr.onerror = function () {
alert('error!');
}
url = new URL(document.getElementById('base_url').value + '/devices/' + document.getElementById('device_id').value + '/inferenceresults');
url.searchParams.set('NumberOfInferenceresults', 1);
url.searchParams.set('raw', 1);
xhr.open('get', url, true);
xhr.setRequestHeader('Authorization', 'Bearer ' + document.getElementById('access_token').innerHTML);
xhr.send();
};
</script>
<script type="text/javascript">
function DeleteInferenceResults() {
xhr = new XMLHttpRequest;
xhr.onload = function () {
var res = xhr.responseText;
if (res.length > 0) {
alert(res);
}
};
xhr.onerror = function () {
alert('error!');
}
url = new URL(document.getElementById('base_url').value + '/devices/' + document.getElementById('device_id').value + '/inferenceresults');
url.searchParams.set('item_ids', document.getElementById('metadata_id').innerHTML);
xhr.open('delete', url, true);
xhr.setRequestHeader('Authorization', 'Bearer ' + document.getElementById('access_token').innerHTML);
xhr.send();
};
</script>
最終的にできたREST_API.htmlを使うと、Command Parameterの変更→推論の開始→出力されるメタデータの確認→推論の停止→Command Parameterの変更→…といった処理が1画面上で簡単に実行できますね。Consoleで同じことをやろうとすると、推論の実行・停止画面と、メタデータの確認画面が別なので、少し時間がかかると思います。Command Parameterの修正もローカルファイルの編集とアップロード作業が必要です。
REST APIを使いこなせるようになると、自分がやりたい作業に特化したアプリケーションを作成することも可能になります。
おわりに
いかがだったでしょうか。本記事では「ブラウザでREST APIを実行してエッジデバイスを制御してみた」ということでHTML + JavaScriptを使うためにブラウザのセキュリティを落としてしまったので実業務での応用は難しいかもしれません。しかし、あるAPIの動作を確認したい場合に、専用ソフトウェアがなくてもお手軽に実行してレスポンスを確認することができますので、ぜひ試してみてください。
困った時は
もし、記事の途中でうまくいかなかった場合は、気軽にこの記事にコメントいただいたり、以下のサポートのページもご覧ください。
コメントのお返事にはお時間を頂く可能性もありますがご了承ください。
また、記事の内容以外で AITRIOS についてお困りごとなどあれば以下よりお問い合わせください。