メインコンテンツまでスキップ

Integration API(非同期)を使用したシンプルな最適化ワークフロー

このガイドでは、Stateless APIをエミュレートするIntegration APIを使用して、完全な最適化サイクルを実行する自動化ワークフローの使用方法について説明します。長時間実行される最適化(例:1時間以上)をサポートするために、ワークフローは「開始(Start)」、「ステータス(Status)」、「結果(Results)」の3つのアクションに分割されています。

なぜStateless APIとしてIntegration APIを使用するのか?

Stateless APIは最大限の柔軟性を提供しますが、ステートレスなワークフローをエミュレートするためにIntegration APIを使用すると、統合のスピードと機能へのアクセスの面で大きな利点があります。このアプローチは、ステートフルなシステムの堅牢性を活用しながら、「ファイア・アンド・フォーゲット(撃ちっ放し)」方式の最適化リクエストの単純さを維持します。

テンプレートによる統合の複雑さの軽減

シミュレーションテンプレートガイドで詳述されているように、Integration APIを使用すると、複雑な最適化設定(コスト関数、制約、車両プロファイル)を「テンプレート」として一度だけ事前に設定できます。

  • 時間の節約: すべてのリクエストに対して巨大なJSON設定オブジェクトを構築して検証する必要がありません。
  • メンテナンス: 運用上の変更(例:サービス時間やコストの調整)は、統合コードを変更することなくテンプレート内で実行できます。
  • 自動調整: システムは、定期的な運用のためにタイムウィンドウのシフトを自動的に処理します。

機能のスーパーセットへのアクセス

はじめにで概説されているように、Integration APIは、生のStateless APIよりも幅広い機能をサポートしています。

  • 高度なパイプライン: システムによって管理される多段階の最適化パイプライン(例:ウェーブプランニング、マルチDC)へのアクセス。
  • 可視化とデバッグ: データは一時的に「シミュレーション」として保存されるため、SWATのダッシュボードツールを使用して、ルートを可視化し、未割り当ての注文をデバッグし、パフォーマンスを分析できます。これは、純粋に一時的なStateless APIでは利用できない機能です。
  • 永続オブジェクト: 一時データ(日次注文)と永続データ(車両マスターデータ)を混合するオプションがあり、ペイロードサイズを削減できます。

簡素化された非同期ワークフロー

長時間実行される最適化(1時間以上)の場合、Integration APIのネイティブなジョブ管理(プロセッサ)は、ステータスを追跡して結果を取得するための堅牢な方法を提供します。このワークフローでは、これをラップして、シンプルな非同期呼び出しのように感じられるようにしています。

ワークフローの概要

クライアントは最適化を開始し、ステータスをポーリングして、最後に結果を取得します。

プロセスフロー

APIの使用法

認証

すべてのAPIリクエストには、ヘッダーにAPIトークンを含める必要があります。 Key: x-api-key Value: <your_api_token>

開始アクション (Start Action)

データのアップロードと最適化の開始を行い、ワークフローを開始します。

基礎となるAPIの詳細なドキュメントについては、以下を参照してください。

  • ordersの設定については、注文のアップロードを参照してください。オブジェクトの形式はbookingsオブジェクトと一致します。
  • vehiclesの設定については、車両の一括アップロードを参照してください。オブジェクトの形式は、Vehicle bulk upload操作のpayload.vehiclesオブジェクトと一致します。
  • デフォルトのテンプレート最適化設定を上書きするためのオプションの最適化設定の設定について。オブジェクトの形式はoptimization_settingsオブジェクトと一致します。
ヒント

各注文と車両には一意のUIDが必要です。

本文には、以下のオプションパラメータを含めることができます。

  • simulation_template: デフォルトの自動選択されたものを上書きして使用するカスタムシミュレーションテンプレートを指定します。
  • date_of_service: 最適化を開始する特定の日付を定義します。デフォルトでは、最適化の開始時間は、提供された車両と注文の中で見つかった最も早い既知のタイムスタンプになります。このタイムスタンプは、特に注文や車両の可能な限り早い時間が不明なシナリオにおいて、車両の稼働時間を制限するために重要です。
警告

各最適化の実行により、新しいシミュレーションが作成されます。削除リクエストでクリーンアップされない限り、SWATシステムに保持されます。SWATは現在、同じテンプレートから作成された同じ開始時間の複数のシミュレーションを持つことをサポートしていません。送信されたデータは、以前に作成されたシミュレーションに含まれてしまう可能性があります。結果は依然として生成される可能性がありますが、データの分離が壊れ、予期しない結果が生じる可能性があります。

エンドポイント: POST https://windmill.d.gcprod.swatrider.com/api/r/start_optimization

リクエストボディ:

{
"project": 839,
"orders":
[
{
"uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"demand": {
"fruit": 1
},
"max_pickup_time": "2031-01-01T23:00:00+07:00",
"min_pickup_time": "2031-01-01T00:00:00+07:00",
"max_dropoff_time": "2031-01-01T23:00:00+07:00",
"min_dropoff_time": "2031-01-01T00:00:00+07:00",
"pickup_location_lat": 1.3238751960077857,
"pickup_location_lon": 103.99996384867768,
"pickup_service_time": 0,
"dropoff_location_lat": 1.3258751960077857,
"dropoff_location_lon": 103.79996384867766,
"dropoff_service_time": 300,
"pickup_location_name": "Hub",
"dropoff_location_name": "Grocery shop"
},
{
"uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde64",
"demand": {
"carton": 1
},
"max_pickup_time": "2031-01-01T23:00:00+07:00",
"min_pickup_time": "2031-01-01T00:00:00+07:00",
"max_dropoff_time": "2031-01-01T23:00:00+07:00",
"min_dropoff_time": "2031-01-01T00:00:00+07:00",
"pickup_location_lat": 1.3228751960077858,
"pickup_location_lon": 103.89996384867766,
"pickup_service_time": 0,
"dropoff_location_lat": 1.3238751960077857,
"dropoff_location_lon": 103.78996384867766,
"dropoff_service_time": 300,
"pickup_location_name": "Hub",
"dropoff_location_name": "Grocery shop"
}
],
"vehicles": [
{
"lat": 0,
"lon": 0,
"speed": 0,
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e96978",
"capacity": {
"fruit": 10
},
"routing_engine_settings": {
"curb": true,
"speed": null,
"profile": "van",
"time_factor": 1,
"road_network": "van",
"make_depot_zero": true,
"intermediate_curb": true
}
}
],
"optimization_setting": [],
"inventory": [],
// "simulation_template": 123, - Optional
// "date_of_service": "2031-01-01T17:00:00.000Z" - Optional
}

レスポンス:

{
"status": "started",
"processor_id": "12345",
"simulation_id": "67890"
}

ステータスアクション (Status Action)

Start Actionから返されたprocessor_idを使用して、最適化の進行状況を確認します。

注記

最適化時間はシミュレーションテンプレートで設定可能であり、非常に大規模な最適化問題の場合、数秒から数時間まで変化する可能性があります。

エンドポイント: POST https://windmill.d.gcprod.swatrider.com/api/r/optimization_status

リクエストボディ:

{
"processor_id": "12345"
}

レスポンス:

{
"start_time": "2025-09-09T22:00:00",
"end_time": "2025-09-10T06:00:00",
"current_status": "completed",
"id": 12345
}

結果アクション (Results Action)

ステータスが「completed」になったら、最終結果を取得します。これにより、割り当てられたノードと各車両の詳細なルート情報、割り当てられた注文、移動区間(travel legs)が返されます。rejected_bookingsには、車両に割り当てることができなかった注文のリストが含まれます。

Nodeオブジェクト構造の詳細なドキュメントについては、以下を参照してください。

注記

include_navigationは、システムに移動のターンバイターンナビゲーションを強制的に返させますが、これは非常に冗長になる可能性があります。不要な場合は、このパラメータを省略するか、falseに設定してください。移動時間とポリラインは引き続き含まれますが、ターンバイターンナビゲーションは提供されません。

エンドポイント: POST https://windmill.d.gcprod.swatrider.com/api/r/optimization_results

リクエストボディ:

{
"simulation_id": 67890,
"include_navigation": false
}
警告

ルートシーケンスのパスは、polyline5形式で返されます。Googleの公式ドキュメントを参照してください。正しい結果を表示するには、ポリラインを含むJSON文字列を最初にアンエスケープする必要があります。

レスポンス:

Details
{
"vehicles": {
"2b1e3b65-2c04-4fa2-a2d7-467901e96978": [
{
"id": 16269806,
"lat": 1.3238751960077857,
"lon": 103.99996384867767,
"uid": "705654c4-f51f-4416-99f4-b8e6d85669ee",
"data": {
"product_kind": null
},
"slack": 0,
"demand": {
"fruit": 1
},
"status": "assigned",
"node_type": "pickup",
"break_info": null,
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"display_name": "Hub",
"geofence_ids": [],
"open_time_ts": "2030-12-31T17:00:00+00:00",
"scheduled_ts": "2030-12-31T17:00:00+00:00",
"service_time": 0,
"time_windows": null,
"close_time_ts": "2031-01-01T16:00:00+00:00",
"location_name": "Hub",
"vehicle_labels": null,
"vehicle_characteristics": {}
},
{
"summary": "Tanah Merah Coast Road, Pan-Island Expressway",
"distance": 30381.7,
"duration": 2454.6,
"polyline": "ydaGsjwyRbR`GtNrE|Bt@hF~A??xCiJDQ?OEOIMSKkDgAwLyDYKUK{Aw@k@[]Oe@Qo@SiGmB}JaDwGuBuIqCiJwCECCCAI@EDW??`AZhF`BRFfBj@TFpGrBzGvBvBr@dGlBhf@pOjA^LBf@N?@H@??XLEJw@vBQh@y@fCITY~@g@bBa@lAELoAzDy@bCeAdDuEhNu@bCSp@Wv@M\\]hAGPGROb@[bASx@G\\W~AKj@Qz@e@|AoBbGm@nB]~@i@~A{AzESf@S`@gDpG_@r@Yf@Ud@EJiB~CYd@W^WVSRWTa@N_@L_@Dc@@_@@_@E]Gi@OoFeBo@SWIsGoB]Gi@Ca@Bk@Fo@Tk@^[\\U`@Sf@EPEXAZ?d@Hb@b@jAf@~@h@t@V\\zCdDNPNRb@j@VVJNTXbBnBr@dAXd@T`@\\~@Ld@NlAJlA?dBGl@Kn@Ot@]dA??Sd@c@r@kBpCaDpEg@v@mA~AW\\u@hAW^eAxAm@~@m@dAkAzBGJCPShBCZCNEJYp@Uh@w@`Bu@hBWdAGVo@xD_BbJUdA[|AADCLCNuArG}@bESbAU~@i@fB_@`AUb@k@j@EF??S^KNUZUVURSPYTi@d@u@l@qC|B[T[VsAhAGDWPGHoAfAy@p@gEjDsEtDiA|@u@j@SNSNCDe@\\??QZWb@A@Yn@Mp@E~@?fAIhCI`@O^SZg@b@gAx@IPANFTfAtAPp@??v@bAxLdOjElFzApBvApBPXJXL\\J`@Jh@Fb@Df@@XBl@?`AErCIzA_@hNM~EKzDKpDKtDCpB?p@@vADhCHlBPhBNxAJ|@L|@Jp@Lv@TfAj@jCJ^Nl@Ph@Pf@hDpJVr@nDtJXt@lEvLZx@vA|D|AhErBtFzAzD|AdE\\t@`AvBfCvFv@~A^dAVz@Pn@Jl@l@~CPx@nCzMt@vDv@zF\\fCNlAL~@t@zGfArJpA`Ll@dFPdBhAtI|CtS\\jEHxAJ|ADhD?\\?D@F?\\@`AJpBBVBXNv@rAxEbAdDh@jBv@fClAxDj@pBNn@FZDTBVFl@Hn@HtAC`CCnCCf@Ip@Mr@CJIVO\\g@lAMVMTOXa@j@MNg@n@s@l@{HlFMLOLeIvFoCfBs@d@gBrAk@j@URe@d@UV_@d@QPi@t@oAdBkApBw@fBs@vBk@`Cq@bDEVGT_@hBkAzDADa@bAKTQ^MZEJWt@Ur@Qx@Id@I~@CNAbA?hAHv@Hj@Lh@H^T|@FVx@xCNh@DV`@dBx@tDXxAJr@Dt@?pAAVCt@C|@_@dLIpCC\\AV]hJQtDIfCAXIjBGzDAlCD|A@`@JzE@t@@XFhDFzBHbBLpANr@Pp@`@rAb@pAf@jArBfE~@hBp@`Bh@bBT|@Jd@|CjNDRR|@b@pBf@hCHh@Db@Bp@Ar@Gp@OlAUj@iBfDk@~BMdB??LZ@LBLHn@Lj@P`@N\\P\\^`@jAr@\\Pd@R\\LRLxB~@d@Pt@VNDb@RhAf@NFD@^PzAj@lAZ`@HD@NDb@J`@JLDv@Rh@PVBd@DR?b@Bf@???Mf@CNOj@_AbEOp@o@lCa@t@IN??g@dCCJWtAGVAFSnAEPGf@o@nDAFALCNCLG`@YrAI`A[~EIrAA\\C`@??YdGSjCShDIhAAN?@KzAC^QbCE~@CXGd@Ql@[r@a@v@_AxAYf@GHEHSZ??GJS\\OV{@tAUd@]j@]h@eAlBQZcC|Da@n@c@x@]n@g@p@INMPKReAfBuDpGYb@a@`AWx@i@tBOl@??PJlAj@n@Xf@XBBXPJFLHf@\\z@j@b@Xb@XRRPRR^R`@NZ@BDLXdADZ??D?F?rC?J?HCFIDiA??QGECCC?C????",
"node_type": "travel",
"navigation": [
{
"mode": "driving",
"name": "",
"distance": 889,
"duration": 151,
"maneuver": {
"type": "depart",
"location": [
103.999303,
1.321889
],
"modifier": "straight",
"bearing_after": 203,
"bearing_before": 0
}
},
{
"mode": "driving",
"name": "",
"distance": 1843.8,
"duration": 303.2,
"maneuver": {
"type": "end of road",
"location": [
103.996204,
1.314521
],
"modifier": "left",
"bearing_after": 112,
"bearing_before": 201
}
},
{
"mode": "driving",
"name": "",
"distance": 1678.6,
"duration": 265.6,
"maneuver": {
"type": "end of road",
"location": [
104.004168,
1.326635
],
"modifier": "right",
"bearing_after": 202,
"bearing_before": 106
}
},
{
"mode": "driving",
"name": "Tanah Merah Coast Road",
"distance": 4163.6,
"duration": 334,
"maneuver": {
"type": "end of road",
"location": [
103.998316,
1.312724
],
"modifier": "right",
"bearing_after": 203,
"bearing_before": 201
}
},
{
"mode": "driving",
"name": "Xilin Avenue",
"distance": 2042.6,
"duration": 154.3,
"maneuver": {
"type": "new name",
"location": [
103.971207,
1.324501
],
"modifier": "straight",
"bearing_after": 298,
"bearing_before": 291
}
},
{
"mode": "driving",
"name": "Simei Avenue",
"distance": 986.2,
"duration": 126.3,
"maneuver": {
"type": "new name",
"location": [
103.954868,
1.332301
],
"modifier": "straight",
"bearing_after": 300,
"bearing_before": 312
}
},
{
"mode": "driving",
"name": "",
"distance": 521.2,
"duration": 41.6,
"maneuver": {
"type": "on ramp",
"location": [
103.948997,
1.338926
],
"modifier": "slight left",
"bearing_after": 302,
"bearing_before": 317
}
},
{
"mode": "driving",
"name": "Pan-Island Expressway",
"distance": 13931.6,
"duration": 689.8,
"maneuver": {
"type": "merge",
"location": [
103.944907,
1.339788
],
"modifier": "slight right",
"bearing_after": 229,
"bearing_before": 250
}
},
{
"mode": "driving",
"name": "Whitley Road",
"distance": 876.2,
"duration": 93.6,
"maneuver": {
"type": "turn",
"location": [
103.829372,
1.326781
],
"modifier": "slight left",
"bearing_after": 243,
"bearing_before": 277
}
},
{
"mode": "driving",
"name": "Bukit Timah Road",
"distance": 332.2,
"duration": 28.1,
"maneuver": {
"type": "turn",
"location": [
103.82554,
1.32037
],
"modifier": "right",
"bearing_after": 288,
"bearing_before": 182
}
},
{
"mode": "driving",
"name": "Bukit Timah Road",
"distance": 676.2,
"duration": 57.6,
"maneuver": {
"type": "merge",
"location": [
103.822749,
1.321403
],
"modifier": "slight right",
"bearing_after": 285,
"bearing_before": 300
}
},
{
"mode": "driving",
"name": "Bukit Timah Underpass",
"distance": 805.9,
"duration": 44.6,
"maneuver": {
"type": "new name",
"location": [
103.816825,
1.322703
],
"modifier": "straight",
"bearing_after": 275,
"bearing_before": 275
}
},
{
"mode": "driving",
"name": "Bukit Timah Road",
"distance": 1028.5,
"duration": 62,
"maneuver": {
"type": "new name",
"location": [
103.809915,
1.324373
],
"modifier": "straight",
"bearing_after": 300,
"bearing_before": 303
}
},
{
"mode": "driving",
"name": "Namly Avenue",
"distance": 435.4,
"duration": 58.2,
"maneuver": {
"type": "turn",
"location": [
103.802095,
1.329249
],
"modifier": "left",
"bearing_after": 208,
"bearing_before": 288
}
},
{
"mode": "driving",
"name": "",
"distance": 151,
"duration": 39.5,
"maneuver": {
"type": "turn",
"location": [
103.799477,
1.326515
],
"modifier": "left",
"bearing_after": 177,
"bearing_before": 254
}
},
{
"mode": "driving",
"name": "",
"distance": 19.8,
"duration": 5.2,
"maneuver": {
"type": "turn",
"location": [
103.799923,
1.325534
],
"modifier": "left",
"bearing_after": 23,
"bearing_before": 94
}
},
{
"mode": "driving",
"name": "",
"distance": 0,
"duration": 0,
"maneuver": {
"type": "arrive",
"location": [
103.800022,
1.325673
],
"modifier": "left",
"bearing_after": 0,
"bearing_before": 73
}
}
],
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"to_node_uid": "f283bf96-36fc-4799-aab7-3d0db6e21386",
"from_node_uid": "705654c4-f51f-4416-99f4-b8e6d85669ee",
"to_booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"from_booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63"
},
{
"id": 16269807,
"lat": 1.3258751960077857,
"lon": 103.79996384867766,
"uid": "f283bf96-36fc-4799-aab7-3d0db6e21386",
"data": {
"product_kind": null
},
"slack": 0,
"demand": {
"fruit": -1
},
"status": "assigned",
"node_type": "dropoff",
"break_info": null,
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"display_name": "Grocery shop",
"geofence_ids": [],
"open_time_ts": "2030-12-31T17:00:00+00:00",
"scheduled_ts": "2030-12-31T17:47:44.400000+00:00",
"service_time": 300,
"time_windows": null,
"close_time_ts": "2031-01-01T16:00:00+00:00",
"location_name": "Grocery shop",
"vehicle_labels": null,
"vehicle_characteristics": {}
}
],
},
"rejected_bookings": [
{
"uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde64",
"pickup_location_lat": 1.3228751960077858,
"pickup_location_lon": 103.89996384867766,
"dropoff_location_lat": 1.3238751960077857,
"dropoff_location_lon": 103.78996384867766,
"pickup_location_name": "Hub",
"dropoff_location_name": "Grocery shop"
}
]
}

クリーンアップアクション (Cleanup Action)

結果が取得された後、必要に応じてシミュレーションと関連データを削除します。

エンドポイント: POST https://windmill.d.gcprod.swatrider.com/api/r/cleanup

リクエストボディ:

{
"simulation_id": "67890"
}

レスポンス:

HTTP OK