intdash SDKを使って開発したプログラムを自動で実行したいみなさん、
こんにちは、ソリューションアーキテクトの伊勢です。
これまで本シリーズではプログラムをコマンドで手動起動してきましたが、検証や運用では自動で起動したい場面が多いはずです。
今回はWebhookとLambdaを組み合わせて、計測完了直後にプログラムを起動する方法を紹介します。
はじめに
全体構成
SDK入門②の距離算出プログラムが計測完了直後に起動されるようにします。

計測イベントをWebhookリクエストとしてAPI Gatewayに送信します。
Webhookリクエストを受けたAPI GatewayがLambdaを起動します。
レスポンス返却Lambdaは、リクエストが計測完了のときに距離算出Lambdaを起動します。1
距離算出Lambdaは、プログラムの完了をSlackで通知します。
Webhookとは
システム内イベントをシステム外にHTTPで通知する仕組みです。
これでintdash内の変化をintdash外からリアルタイムに知ることができます。
イベントドリブンなシステム連携により、効率的なワークフローを実現できます。
今回はよりシンプルな使い方を紹介します。
- Lambda関数をインラインコードで作成
- intdash SDKのLambdaレイヤーを作成
- ローカルPCでテストコードによる動作確認2
AWS Lambdaとは
言わずと知れたサーバレスでコードを実行できるコンピューティングサービスです。
従量課金で手軽であり、作成した関数をイベントで起動できます。
今回はWebhookリクエストをAPI Gatewayで受けてLambdaを起動します。
Lambdaレイヤーとは
Lambda関数の実行環境に共有ライブラリや依存ファイルを追加する仕組みです。
これにより、Lambda関数が軽量化され、コードの再利用性が向上します。
今回はintdash SDKや依存するPythonパッケージを登録します。
API Gatewayとは
APIエンドポイントへのアクセス仲介サービスです。
WebリクエストをバックエンドのLambdaやEC2などにルーティングします。
やってみた
AWS構築
起動される側(全体構成の右側)から順に構築していきます。
距離算出Lambda
カスタムLambdaレイヤー作成
Lambdaレイヤーとして登録するためのZIPファイルを作成します。
ローカルPCでLambdaレイヤーのディレクトリ構造にSDK入門①、SDK入門②のPython依存パッケージをインストールします。3
mkdir -p path/to/workdir/python/lib/python3.12/site-packages pip3.12 install pydantic python-dateutil urllib3 -t path/to/workdir/python/lib/python3.12/site-packages pip3.12 install protobuf -t path/to/workdir/python/lib/python3.12/site-packages

以下をZIPにまとめます。
cp -r intdash gen path/to/workdir/python/lib/python3.12/site-packages cd path/to/workdir find . -name "*.pyc" -delete find . -name "__pycache__" -type d -exec rm -r {} + zip -r intdash_sdk.zip python ls -l intdash_sdk.zip python

ZIPファイルをアップロードしてレイヤーを作成します。

Lambda関数作成
サンプルプログラムからLambda関数のZIPファイルを作成します。
今回のサンプルプログラムはLambdaで実行されることを想定しています。4
cd lesson6/intdash-distance/src find . -name "*.pyc" -delete find . -name "__pycache__" -type d -exec rm -r {} + zip -r ../deployment_package.zip .

Lambda関数を作成します。
作成したZIPファイルをアップロードします。

タイムアウト設定がデフォルト3秒だと途中終了してしまうため、5分に延長します。

プログラムに渡す環境変数を設定します。
API_TOKEN
: サーバー環境にアクセスするAPIトークンAPI_URL
: サーバー環境のURLFETCH_SIZE
: データポイントを何件ずつ処理するかORIGIN_LAT
: 基準点(緯度)ORIGIN_LON
: 基準点(経度)SLACK_URL
: 通知先Slack

Lambdaレイヤーを追加します。
- intdash SDK: 作成したカスタムLambdaレイヤー
requests
: 公開ARNを指定

レスポンス返却Lambda
Lambda関数作成
intdashにレスポンスを返却するためのLambda関数を作成します。
lesson6/invoke-distance/src/lambda_function.py
の内容を貼り付けます。

プログラムに渡す環境変数を設定します。
SECRET_KEY
: あとでWebhook設定に登録する任意の文字列

API Gateway作成
POSTリクエストを受けるAPI Gatewayを作成します。
レスポンス返却Lambdaと統合します。

Webhook設定
設定はintdash APIで管理され、SDKでも参照・更新メソッドが提供されます。
ラップするCLIツールも作ってみました。
このツールを使って今回使う設定を登録・確認します。5

設定登録
API GatewayにWebhookリクストを送信する設定を追加します。
リクエストボディはこのようなJSONです。6
{ "url": "https://example.execute-api.ap-northeast-1.amazonaws.com/webhook", "secret": "stringstringstringstringstringst", "edges_event": false, "measurements_event": true, "users_event": false, "edge_connections_event": false, "project_edges_event": false, "project_members_event": false }
secret
にはレスポンス返却Lambdaの環境変数SECRET_KEY
を設定します。7
計測イベントを送信するようにmeasurements_event
のみtrue
にします。8
JSONファイルをsrc_path
に指定します。
python lesson6/cli/src/hook_cli.py --api_url https://example.intdash.jp --api_token <YOUR_API_TOKEN> --project_uuid <YOUR_PROJECT_UUID> import --src_path=<YOUR_SRC_PATH>
設定確認
登録結果を一覧で確認します。
python lesson6/cli/src/hook_cli.py --api_url https://example.intdash.jp --api_token <YOUR_API_TOKEN> --project_uuid <YOUR_PROJECT_UUID> list

計測
計測対象
せっかくなので、その場でプログラムを手動実行できない環境で試しました。
iOSアプリ intdash Motion で飛行中のGPSを計測します。

エアバスA320の最大巡航速度はマッハ0.82、国内線の高度はおよそ1万mです。
当社がこれまで計測した環境としては最速かつ最高度ではないかと思います。
1回の計測距離として最長かもしれません。
計測開始
iPhoneはフライトモードにしてあるため、計測がオフラインで開始されます。

機内Wifiが使える機体なら、リアルタイム計測も可能かもしれません。

計測完了
着陸後、Motionから計測を遅延アップロードします。

数十秒ほどでSlackに通知がありました。

PCからData Visualizerにリンクするとプレイバック再生が始まります。
成田空港を基準点とした算出距離が計測とあわせて表示されます。9

サンプルプログラム説明
Webhook設定ツール
一覧
List Project Hookの結果のitems
配列を返します。
return self.api.list_project_webhooks(
self.project_uuid, per_page=per_page
).items
取得
List Project Hookでhook_uuid
を指定しています。10
hooks = self.api.list_project_webhooks( self.project_uuid, hook_uuid=[hook_uuid], ) return hooks.items[0]
登録
引数hook_uuid
の指定有無でCreate/Update Project Hookを使い分けます。
if hook_uuid: hook = self.api.update_project_webhook( self.project_uuid, hook_uuid, hook_project_update_request=hook_src ) else: hook = self.api.create_project_webhook( self.project_uuid, hook_project_create_request=hook_src )
テスト
指定したresource_type
、action
をHookリクエストとして送信します。11
return self.api.test_project_webhook( self.project_uuid, hook_uuid, hook_test_request={"resource_type": resource_type, "action": action}, )
距離算出
距離算出プログラム
APIアクセス部はSDK入門②と同じです。
Lambdaにあわせてメイン関数をlambda_hander
に変更してあります。
定数は環境変数としてLambda設定から与えられるようにしてあります。
api_url = os.getenv("API_URL", "https://example.intdash.jp") api_token = os.getenv("API_TOKEN", "<YOUR_API_TOKEN>") fetch_size = int(os.getenv("FETCH_SIZE", 100)) origin_lat = float(os.getenv("ORIGIN_LAT", 35.6878973)) origin_lon = float(os.getenv("ORIGIN_LON", 139.7170926)) origin = (origin_lat, origin_lon) # 会社 slack_url = os.getenv("SLACK_URL", "<YOUR_SLACK_WEBHOOK_URL>")
テストコードから環境変数を与えて起動しています。12
os.environ["API_URL"] = "https://example.intdash.jp" os.environ["API_TOKEN"] = "<YOUR_API_TOKEN>" os.environ["FETCH_SIZE"] = "100" os.environ["ORIGIN_LAT"] = "35.6878973" os.environ["ORIGIN_LON"] = "139.7170926" os.environ["SLACK_URL"] = "<YOUR_SLACK_WEBHOOK_URL>"
なお、距離算出Lambaが計測を完了するときにもWebhookリクエストが送信されます。これにより、距離算出Lambdaがもう一度起動されるため、無限ループの防止が必要です。
位置情報を含むデータポイントだけを取得します。
stream = api.list_project_data_points( project_uuid=self.project_uuid, name=self.meas_uuid, data_id_filter=["#:1/gnss_coordinates"], start=self.start, limit=fetch_size, time_format="ns", )
データポイントが0件の場合は作成した計測を削除します。
# 計測削除(GPSデータなし) if not count: self.writer.delete_measurement() logging.info(f"Deleted measurement: {measurement_dst.uuid}") return
レスポンス返却プログラム
intdash APIとは直接関わりません。
距離算出Lambdaを起動します。
response = lambda_client.invoke( FunctionName="intdash-distance", InvocationType="Event", Payload=json.dumps(body_dict), )
起動は0.1秒程度で終わり、すぐにレスポンスを返却します。
return { "statusCode": 200, "body": json.dumps({"message": "Webhook received and Distance Lambda invoked"}), }
おわりに
今回は起動方法を実際の利用シーンに近づけてみました。
intdash SDKのLambdaレイヤーに登録しておけば、複数のLambda関数に適用できて便利です。
なお、Lambdaは最大実行時間が15分のため、もっと長い処理はFargateやEC2インスタンスを利用することになります。
リンク
本シリーズの過去記事はこちらからご覧ください。
- SDK入門①〜社用車で走ったとこ全部見せます〜 :REST APIでデータ取得
- SDK入門②〜データ移行ツールの作り方〜:REST APIでデータ送信
- SDK入門③〜RTSPで映像配信するぞ〜:リアルタイムAPIでデータ取得
- SDK入門④〜YOLOで物体検知しちゃう〜:リアルタイムAPIでデータ送信
- SDK入門⑤〜iPadでData Visualizerを見る会〜:リアルタイムAPIでキャプチャデータ送信
-
10秒以内にレスポンスを返すため、Lambdaを二段構成にしています。intdashはWebhookリクエストのステータスを管理しており、10秒以内に通知先からレスポンスが返るとステータスを成功
succeeded
に移行します。↩ - テストコードはGitHubで公開しています。↩
- 前回までにインストールしたパッケージをすべてZIP化すると、Lambdaレイヤーの上限10MBを超えてしまうためです。↩
- サンプルプログラムはGitHubで公開しています。↩
- CLIの使い方はGitHubで解説します。↩
-
サンプルプログラム内にテンプレート
lesson6/cli/config/hook.json
を用意しています。↩ - HMAC検証でWebhookリクエストの改ざんを防止します。↩
-
間違えやすいですが、各項目名は
xxx_events
ではなくxxx_event
です。↩ - 計測は1時間ほどで終了しています。Motionログイン時にサーバーで払い出すアクセストークンの有効期限によるものです。↩
-
設定を1件取得する
get_project_webhook
は現行バージョンでは動作しません。次バージョンにて改修予定です。↩ -
SDK入門①で紹介しているintdash SDK for Python生成の手順では
created
/updated
/deleted
以外のaction
がエラーになるようです。その場合は、curlコマンドで直接Test Hook APIを叩いて確認します。↩ - テストコードの使い方はGitHubで解説します。↩