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

配達または集荷のETA

ノードには、3種類のETA(Estimated Time of Arrival:到着予定時刻)が利用可能です。

  • 最適化ステップで計算され、運用全体で保持される推定スケジュール時刻 scheduled_ts
  • estimated_scheduled_ts フィールドに現実的な推定時刻を表す推定スケジュール時刻。初期値はシミュレーションの最適化実行時です。
  • estimated_earliest_scheduled_ts フィールドに最も早い到着予定時刻を表す推定スケジュール時刻

estimated_scheduled_ts の値は、ルートに沿ったサービス時間、計算時に車両がサービスを受けている場合のノードの現在の残りサービス時間、および早着を考慮に入れます。したがって、利用アプリケーションでの精度向上のために推奨されます。 車両が移動中に遅延が発生し、調整された到着時刻を遅らせる必要がある場合、estimated_scheduled_tsestimated_earliest_scheduled_ts とともに新しい値に更新されます。

estimated_scheduled_ts フィールドの推定時刻は、次のように計算されます。

Estimated time calculation
  estimated_scheduled_ts = max(estimated_earliest_arrival_ts, node.scheduled_ts)

ETAの初期計算は、ノードが車両に割り当てられる際のルート最適化セッションの一部として行われます。

ヒント

ノードが有効な予約に属していない場合、Webhookおよびwebsocketメッセージはディスパッチされません。

ETA(到着予定時刻)は、シミュレーション内のすべての車両について5分ごとに更新されます。以下のルールが適用されます。

  • 更新はシミュレーションごとに行われます
  • 割り当てられたノードを持つ車両(vehicle.assigned_nodes フィールド内)のみが更新のために選択されます
  • 選択されたノードは、シミュレーション最適化実行からの既存の schedule_ts を持っている必要があります
  • 割り当てられたノードは、ゼロ以外の緯度と経度を持っている必要があります
  • ETAは、車両がすでに移動中でない限り、計算のために vehicle.start_timevehicle.should_wait_until を考慮します

ETA計算は、車両または車両が属するシミュレーションのルート設定を考慮します(車両設定がシミュレーションよりも優先されます)。ノードの行列タイムスタンプは、ルートが利用可能な場合、現在の時刻、または車両の開始時刻が優先順位に従って使用されます。具体的には:

Selecting timestamp for routing

start_time = assigned_nodes.get_matrix_timestamp()
if <vehicle has valid coordinates>:
start_time = timezone.now()
if vehicle.start_time is not None:
start_time = max(vehicle.start_time, start_time)
if vehicle.should_wait_until is not None:
start_time = max(vehicle.should_wait_until, start_time)

ノードで利用可能なタイムスタンプの種類

  • scheduled_ts: ルート最適化中にノードへの最初に計画された到着時刻を表します。これは、実際の到着時刻と計画された時刻を比較して、計画の品質を評価するために使用できます。
  • estimated_arrival_time: 非推奨。 使用すべきではなく、ノードデータでは null に設定されます。
  • estimated_earliest_arrival_ts: ノードへの到着時刻の、より不正確で楽観的な推定を表します。特定のノードについて定期的に(デフォルト:約5分)再計算されます。
  • estimated_scheduled_ts: ノードへの最も現実的な到着予定時刻を表します。特定のノードについて定期的に(デフォルト:約5分)再計算されます。以前のノードの service_time は考慮しますが、slack は考慮しません。
  • arrived_to_location_ts: ノードへの到着タイムスタンプを表します。ドライバーからの受信メッセージがあった場合に更新されます。この機能がドライバーアプリケーションで使用されていない場合は null になることがあります。
  • departed_at_ts: 非推奨。 使用すべきではなく、ノードデータでは null に設定されます。
  • failed_to_deliver_at_ts: ノードで配達失敗イベントが発生した場合、このタイムスタンプはイベントが発生した時刻を表します。予約の状態が fail_to_deliver の場合に更新され、それ以外の場合は null です。
  • started_service_at_ts: 非推奨。 使用すべきではなく、ノードデータでは null に設定されます。このフィールドは最適化APIでノードに使用され、完了したノードに対して設定する必要があります。
  • visited_at_ts: 非推奨。 使用すべきではなく、ノードデータでは null に設定されます。
  • completed_service_at: ノードが completed とマークされた時刻を表します。
  • slack: このノードで費やされた時間(サービス中ではなく、未使用の待機時間)を秒単位で表します。slackを参照してください。
  • service_time: 対応する予約から入力された時間を表します。この時間は、ノードで車両をサービスするために必要な時間として使用されます。
警告

値の可用性は、シミュレーションの操作モードとノードライフサイクルの現在の段階に依存します。常に存在するとは限りません。一部のタイムスタンプでデータが利用できない場合、それらは null に設定されます。たとえば、開始および終了場所などの手動で割り当てられたノードには、scheduled_ts が割り当てられない場合があります。

node.estimated_scheduled_ts は、ノードの適切な時間枠を考慮して計算されます。つまり、node.estimated_scheduled_ts はノードの開始時間枠の境界 node.open_time_ts より小さくなることはありません。したがって、車両が早く到着できる場合、node.estimated_scheduled_ts は少なくとも node.open_time_ts になるため、車両は待機する必要があります。結果として、slack はETAに暗黙的に含まれます。

ETA計算の例

与えられたノード:

  • 開始ノード(集荷)としての A
  • 開始時刻10AMの B(降車)
  • 開始時刻11AMの C(降車)
  • ノードAとBの両方のサービス時間は5分
  • ノード BC 間の移動時間は10分
// Node B ETA
pickup_B.open_time_ts = 10AM
pickup_B.estimated_scheduled_ts = 10AM
pickup_B.estimated_earliest_arrival_ts = 9:10AM
slack_B = 50 minutes

//Node C ETA
pickup_C.open_time_ts = 11AM
pickup_C.estimated_scheduled_ts = 11AM
pickup_C.estimated_earliest_arrival_ts = 9:25AM [9:10AM + 5min (service time B) + 10 minutes travel B->C]
slack_C = 45 minutes (10AM + 5min service time B + 10 minutes travel time = 10:15 - earliest arrival after first scheduled ts)

SWAT APIからの到着予定時刻の取得

SWATバックエンドは、特定のノードの集荷または降車予定時刻を取得する機能を提供します。SWAT APIからETAデータを取得するには、3つのオプションがあります。

ノードデータポーリング方式

ヒント

予約

には、集荷場所と降車場所を表す少なくとも2つのノードが含まれています。これが、ノードを使用してWebhookをトリガーする必要がある理由です(同じ予約内の降車または集荷のいずれか)。どのノードが予約に属しているかを調べるには、GET <base_url>/api/v2/node?booking=<booking_id> を使用できます。

ノードに関するデータを要求するには、以下のリクエストを使用できます。 GET /api/v2/node/{node_id}

これにより、集荷ノードの場合、次のような応答が返されます。

JSONペイロードを見る
Pickup node data

{
"allow_jump": false,
"arrived_to_location_ts": null,
"assigned_vehicle": "/api/v2/vehicle/17789910",
"assignment_order": 0,
"boarding_pass": "784",
"booking": "/api/v2/booking/23286258",
"booking_group_uid": "0274e962-2024-1008-0659-48d787b6ab5f",
"booking_uid": "0274e962-2024-1008-0659-48d787b6ab5f",
"cancelled_at_ts": null,
"close_time_ts": "2024-10-08T04:30:00+00:00",
"close_time_ts_dynamic": "2024-10-08T04:30:00+00:00",
"completed_service_at": "2024-10-08T05:33:47.409000+00:00",
"created_at": "2024-10-07T23:25:00.551915+00:00",
"customer_id": null,
"data": {
"dropoff_address": "P3749",
"dropoff_city": "Thailand",
"dropoff_country": "TH",
"dropoff_region": "Thailand",
"external_id": "123",
"is_dropoff_end_of_trip": true,
"is_pickup_end_of_trip": false,
"order_identity": {
"external_id": "123",
"simulation_id": 177821
},
"pickup_address": "Warehouse",
"pickup_city": "Thailand",
"pickup_country": "TH",
"pickup_customer_name": " Warehouse",
"pickup_location_lat": "11.69046292",
"pickup_location_lon": "101.516127",
"pickup_location_name": "Warehouse",
"pickup_region": "Thailand"
},
"demand": {
"cbcm": 1709600,
"undefined": 0,
"weight": 564
},
"departed_at_ts": null,
"display_name": "Warehouse",
"dynamic_break": null,
"end_of_trip": false,
"estimated_arrival_time": null,
"estimated_earliest_arrival_ts": "2024-10-08T05:35:46.221656+00:00",
"estimated_scheduled_ts": "2024-10-08T05:35:46.221656+00:00",
"external_id": null,
"failed_to_deliver_at_ts": null,
"finalization_type": "min",
"fixed_route_is_unreachable": false,
"fixed_route_journey_time": null,
"fixed_route_ride_time": null,
"fixed_route_wait_time": null,
"fixed_route_walk_time": null,
"geofence_id": 1613,
"geofence_ids": [
1613
],
"groups": [],
"h3": "0040062244546130126251",
"id": 50801404,
"initial_close_time_ts": null,
"initial_close_time_ts_dynamic": null,
"initial_max_trip_duration": null,
"initial_open_time_ts": null,
"lat": 11.69046292,
"lifo_order_check": false,
"lifo_order_penalty": null,
"location_code": "",
"location_name": "Warehouse",
"lon": 101.516127,
"matrix_timestamp": null,
"max_slack": 3600,
"max_trip_duration": 27000,
"min_trip_duration": null,
"modified_at": "2024-10-08T05:33:47.794259+00:00",
"node_type": "pickup",
"offer_should_be_auto_accepted": null,
"open_time_ts": "2024-10-08T03:30:00+00:00",
"open_time_ts_dynamic": null,
"partial_route_index": null,
"passenger_count": 0,
"penalty": 1000000,
"processing_order_timestamp": null,
"ready_to_board_at": null,
"resource_uri": "/api/v2/node/50801404",
"scheduled_ts": "2024-10-08T03:30:00.000734+00:00",
"service_time": 0,
"simulation": "/api/v2/simulation/177821",
"slack": 238.8,
"started_service_at_ts": null,
"status": "completed",
"time_windows": null,
"transfer_type": "depart_at",
"transit_stop": null,
"trip_cost": 0,
"uid": "cad4f25a-73c3-41b1-887c-682b93f3dd3f",
"user_accepted_offer_at": null,
"vehicle_characteristics": {
"ALL": 1,
"SVB1": 0,
"SVB2": 0,
"weight": {
"max": 4,
"min": 0
}
},
"vehicle_labels": {
"or": [
"10"
]
},
"visited_at_ts": null,
"walking_distance_to_node": 0,
"walking_time_to_node": 0,
"weight": null
}

または降車ノードの場合

JSONペイロードを見る
Drop off node data
{
"allow_jump": false,
"arrived_to_location_ts": null,
"assigned_vehicle": "/api/v2/vehicle/17789910",
"assignment_order": 6,
"boarding_pass": "047",
"booking": "/api/v2/booking/23286180",
"booking_group_uid": "47b2609f-2024-1008-0659-4d53afe4a5f3",
"booking_uid": "47b2609f-2024-1008-0659-4d53afe4a5f3",
"cancelled_at_ts": null,
"close_time_ts": "2024-10-08T09:00:00+00:00",
"close_time_ts_dynamic": "2024-10-08T09:00:00+00:00",
"completed_service_at": "2024-10-08T06:30:07.218000+00:00",
"created_at": "2024-10-07T23:25:00.557228+00:00",
"customer_id": null,
"data": {
"dropoff_address": "P3522",
"dropoff_city": "Thailand",
"dropoff_country": "TH",
"dropoff_region": "Thailand",
"external_id": "45",
"is_dropoff_end_of_trip": true,
"is_pickup_end_of_trip": false,
"order_identity": {
"external_id": "45",
"simulation_id": 177821
},
"pickup_address": "Warehouse",
"pickup_city": "Thailand",
"pickup_country": "TH",
"pickup_customer_name": "Warehouse",
"pickup_location_lat": "12.69046292",
"pickup_location_lon": "101.516127",
"pickup_location_name": "Warehouse",
"pickup_region": "Thailand"
},
"demand": {
"cbcm": -2828700,
"undefined": 0,
"weight": -962
},
"departed_at_ts": null,
"display_name": "P3522",
"dynamic_break": null,
"end_of_trip": true,
"estimated_arrival_time": null,
"estimated_earliest_arrival_ts": "2024-10-08T06:32:00.578048+00:00",
"estimated_scheduled_ts": "2024-10-08T06:36:39.900734+00:00",
"external_id": null,
"failed_to_deliver_at_ts": null,
"finalization_type": "min",
"fixed_route_is_unreachable": false,
"fixed_route_journey_time": null,
"fixed_route_ride_time": null,
"fixed_route_wait_time": null,
"fixed_route_walk_time": null,
"geofence_id": 1613,
"geofence_ids": [
1613
],
"groups": [],
"h3": "0040062244542150160631",
"id": 50801567,
"initial_close_time_ts": null,
"initial_close_time_ts_dynamic": null,
"initial_max_trip_duration": null,
"initial_open_time_ts": null,
"lat": 12.73208162,
"lifo_order_check": false,
"lifo_order_penalty": null,
"location_code": "",
"location_name": "P3522",
"lon": 101.4874158,
"matrix_timestamp": null,
"max_slack": 3600,
"max_trip_duration": 27000,
"min_trip_duration": null,
"modified_at": "2024-10-08T06:30:07.366347+00:00",
"node_type": "dropoff",
"offer_should_be_auto_accepted": null,
"open_time_ts": "2024-10-08T05:00:00+00:00",
"open_time_ts_dynamic": null,
"partial_route_index": null,
"passenger_count": 0,
"penalty": 1000000,
"processing_order_timestamp": null,
"ready_to_board_at": null,
"resource_uri": "/api/v2/node/50801567",
"scheduled_ts": "2024-10-08T06:36:39.900734+00:00",
"service_time": 1800,
"simulation": "/api/v2/simulation/177821",
"slack": 0,
"started_service_at_ts": null,
"status": "completed",
"time_windows": null,
"transfer_type": "depart_at",
"transit_stop": null,
"trip_cost": 0,
"uid": "f54331ec-9fa2-4167-aba0-29fe73d97ee0",
"user_accepted_offer_at": null,
"vehicle_characteristics": {
"ALL": 1,
"SVB1": 0,
"SVB2": 0,
"weight": {
"max": 4,
"min": 0
}
},
"vehicle_labels": {
"or": [
"10"
]
},
"visited_at_ts": null,
"walking_distance_to_node": 0,
"walking_time_to_node": 0,
"weight": null
}

WebhookまたはWebsockets方式

Webhookの設定については、Webhookの設定セクションを、Websocketsの設定については、Websocketsの設定セクションを参照してください。

ETAの目的では、vehicle_group エージェントタイプの下の message_type=vehicle_arrival を使用します。

クライアントアプリケーションがイベントストリームを購読すると、以下のスキーマに従ってイベントを受信し始めます。

Vehicle arrival schema
{
'simulation_id': id,
'agent_type': 'vehicle_group',
'message_type': 'vehicle_arrival',
'agent_id': String agent_id,
'data': {
'vehicle_id': integer vehicle id
'vehicle_agent_id': UUID Vehicle.agent_id
'booking_id': integer booking ID
'booking_uid': UUID booking UID
'node_id': integer node ID
'node_uid': node UID
'estimated_earliest_arrival_ts': timestamp of possible earliest arrival (not taking into account service times along the route and earliness)
'estimated_scheduled_ts': estimated time of starting servicing the node (taking into account service times along the route and earliness)
'scheduled_ts': scheduled timestamp, to compare scheduled and ETA
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
}

利用アプリケーションが他の追加のタイムスタンプを必要とする場合、メッセージ内の node_id の値を使用して、前述のノードデータからデータをポーリングできます。

ヒント

クライアントアプリケーションがHTTP 20Xステータスコードで応答しない場合、SWATサーバーは、メッセージを破棄する前に、指数関数的バックオフで5回再送信を試みます。

ルーティングAPI方式

任意の時点で到着予定時刻(ETA)を取得する別の方法は、車両の現在の位置から次のノードまでのルートを再計算することです。この方法は、SWATドライバーアプリが使用されておらず、SWATバックエンドシステムが車両の現在の位置を認識していない場合にも使用できます。

これを実現するには、クライアントアプリケーションは車両にスケジュールされている次のノードを取得し、そのノードの緯度と経度をキャプチャする必要があります。GET /api/v2/vehicle/<vehicle_id>/assigned_nodes エンドポイントを使用して、車両の今後の割り当て済みノードのリストを取得し、scheduled_ts でフィルタリングして次のスケジュール済みノードを取得できます。

応答にはノードの座標が含まれ、それを使用してそのノードへのルートを取得できます。

ヒント

ルートは車両の実際の現在位置に依存し、元の計画ルートとは異なる場合があります。ただし、最適化中に適用されたのと同じルーティングプロファイルが使用され、高いレベルのルート一貫性が保証されます。

車両のルートを取得するには、GET /vehicle_route_proxy/<vehicle_id>/<wkt_waypoints> エンドポイントを使用できます。ここで、WKT Waypointsは、最低限、車両の現在の位置と次のノードの座標を表します。リクエストは次のような結果を返します。

JSONペイロードを見る
Example route
{
"code": "Ok",
"waypoints": [
{
"name": "",
"hint": "b9V9gHHVfYAaAAAAFwAAADMAAAC2AAAAvJ0hQfeMC0EjbpxBCBqNQhoAAAAXAAAAMwAAALYAAAD5AgAAwsD9BZDm0AAfwf0FX-bQAAQADw1YmAJi",
"distance": 11.428674,
"location": [
100.516034,
13.690512
]
},
{
"name": "เจริญราษฎร์ 7 แยก 29-1-2",
"hint": "T9J9gFHSfYA5AAAAOwAAAAAAAAAAAAAAkRH_QaeEAEIAAAAAAAAAADkAAAA7AAAAAAAAAAAAAAD5AgAAIcj9BZ3u0ADvyP0FL-7QAAAADw1YmAJi",
"distance": 25.392264,
"location": [
100.517921,
13.692573
]
},
{
"name": "",
"hint": "TtJ9gKvSfYAzAAAAGAAAAAAAAACmAAAAL8LlQfXSTkEAAAAACoq3QjMAAAAYAAAAAAAAAKYAAAD5AgAAnsn9BSbv0ABTyf0Fk-7QAAAATwdYmAJi",
"distance": 18.17438,
"location": [
100.518302,
13.69271
]
}
],
"routes": [
{
"geometry": "_hrbYck_v~D|e@rUxa@kmAgr@c]bL{[krBaw@e`@z|@{N_HwN}GdF{M",
"weight": 149.6,
"distance": 832.1,
"duration": 149.6,
"weight_name": "routability",
"legs": [
{
"summary": "",
"distance": 771.4,
"duration": 133.7,
"weight": 133.7,
"steps": []
},
{
"summary": "",
"distance": 60.7,
"duration": 15.9,.
"weight": 15.9,
"steps": []
}
]
}
],
"uuid": "c2a247ad-b556-4abe-924d-bc56835b201f"
}

警告

このリクエストが発生するたびに、対応するイベントはwebsocketsまたはwebhooks経由で送信されます。別のマイクロサービスがこのイベントを処理し、車両ルーティングパスを要求および生成します。パスは、保存されている車両の現在位置と最終的に割り当てられたノードに基づいて計算されます。このパスは車両の path 属性に保存されます。

routes オブジェクトの duration フィールドには、ルートの合計所要時間(秒単位)が含まれており、クライアントはこれを現在の時刻に加算することでETAを計算できます。