开始使用 Fleet Engine 以提升舰队性能

使用 Fleet Engine Deliveries API 为配送第一和最后一公里的舰队活动建模。您可以通过使用适用于 Android 和 iOS 的驱动程序 SDK 来使用此 API,也可以直接使用 HTTP REST 或 gRPC 调用。

初始设置

您可以在 Google Cloud 控制台中配置 Fleet Engine Deliveries API。

验证设置

创建服务帐号后,请验证您的设置是否已完成,然后便可以创建送货车辆。立即验证您的设置可确保您已解决设置项目时可能会遇到的常见授权问题。您可以通过以下两种方式验证设置:

客户端库

为了与原始 gRPC 或 REST 相比,开发者获得更好的体验,请使用多种常见编程语言的客户端库。如需了解如何获取服务器应用的客户端库,请参阅客户端库

本文档中的 Java 示例假设您熟悉 gRPC。

数据结构

Fleet Engine Deliveries API 使用两种数据结构为货物的取货和交付建模:

  • 用于运输货物的送货车辆。
  • 货物自提和配送任务。

您还可以使用任务为驾驶员在一天中的休息时间和计划停靠站建模。

送货车辆

送货车辆用于将货物从仓库运送到送货地点,以及从取货地点运送到仓库。在某些情况下,他们还可以将货物直接从取货地点运送到送货地点。

使用 Driver SDK 在 Fleet Engine 中创建 DeliveryVehicle 对象并发送位置信息更新以进行货运和车队跟踪。

注意:您最多可以向 DeliveryVehicle 对象分配 500 个任务和 300 个剩余的车辆行程片段。

任务

对于车辆在白天执行的操作,您可以根据操作类型分配任务:

  • 对于自提和配送选项,请指定装运任务
  • 当驾驶员不可用时(例如需要的休息时间),分配不可用任务
  • 对于在保管箱或客户营业地点执行的非驾驶任务,请分配预定的停止任务

您分配的每个任务都必须具有唯一的任务 ID,但任务可以共用同一跟踪 ID。当 Fleet Engine 计算每项任务的预计到达时间时,它会使用所有任务以及对这些任务的计划顺序进行估算。如需详细了解任务 ID,请参阅任务 ID 准则

如需在 Fleet Engine 中创建任务,请使用驱动程序 SDK 任务管理器。

装运任务

为货物自提和配送创建运单任务,并提供以下信息:

  • 自提或送餐地点。
  • 跟踪编号或 ID。
  • 停留时间,用于说明为完成任务、寻找停车位或步行前往交接点所需要的额外时间。
  • 唯一的任务 ID。请参阅任务 ID 指南

如需了解详情,请参阅以下主题:

Android

iOS

不可用任务

不可用任务涵盖无法自提或送货的时间段,例如为车辆加油的休息时间或司机休息时间。

使用以下信息创建不可用任务:

  • 广告插播时长。
  • (可选)插播时间点的位置。您并非一定要提供具体位置,但这样做可以提供更准确的全天预计到达时间。

如需了解详情,请参阅以下主题:

Android

iOS

计划停止任务

创建计划停靠任务,以便为送货车辆需要进行的停靠工作建模。例如,为每日预定收集站在特定地点创建计划停止任务,而不考虑同一地点的其他送货或自提服务。您还可以为保管箱取件创建预定的停靠任务,或者为供给车辆换乘或停靠服务中心和服务点建模。

如需了解详情,请参阅以下主题:

Android

iOS

任务 ID 指南

创建任务 ID 时,请遵循以下内容和格式准则:

  • 创建唯一任务 ID
  • 请勿公开任何个人身份信息 (PII) 或明文数据。
  • 请使用有效的 Unicode 字符串。
  • 字符数不能超过 64 个。
  • 请勿包含以下任何 ASCII 字符:“/”“":"”“\”“?”或“#”。
  • 根据 Unicode 标准化形式 C 进行标准化。

以下是一些合适的任务 ID 示例:

  • 566c33d9-2a31-4b6a-9cd4-80ba1a0c643b
  • e4708eabcfa39bf2767c9546c9273f747b4626e8cc44e9630d50f6d129013d38
  • NTA1YTliYWNkYmViMTI0ZmMzMWFmOWY2NzNkM2Jk

下表显示了不受支持的任务 ID 示例:

不受支持的任务 ID 原因
8/31/2019-20:48-46.70746,-130.10807,-85.17909,61.33680 违反了个人身份信息和字符要求:逗号、句号、冒号和斜线。
JohnDoe-577b484da26f-Cupertino-SantaCruz 违反个人身份信息要求。
4R0oXLToF"112 Summer Dr. East Hartford, CT06118"577b484da26f8a 违反个人身份信息和字符要求:空格、逗号和引号。超过 64 个字符。

其他资源

如需查看每个数据结构中包含的特定字段,请参阅 DeliveryVehiclegRPCREST)和 TaskgRPCREST)的 API 参考文档。

车辆使用寿命

DeliveryVehicle 对象表示第一英里或最后一英里送货车辆。您可以使用以下命令创建一个 DeliveryVehicle 对象:

  • Google Cloud 项目的 ID,该项目包含用于调用 Fleet Engine API 的服务帐号。
  • 客户拥有的车辆 ID。

为每辆车使用唯一的车辆 ID。除非原始车辆没有任何活跃任务,否则请勿重复使用车辆 ID。

Fleet Engine 会在 7 天后自动删除未使用 UpdateDeliveryVehicle 更新的 DeliveryVehicle 对象。如需查看是否存在车辆,请执行以下操作:

  1. 调用 UpdateDeliveryVehicle
  2. 如果您收到 NOT_FOUND 错误,请调用 CreateDeliveryVehicle 以重新创建车辆。如果调用返回车辆,您仍然可以更新车辆。

车辆属性

DeliveryVehicle 实体包含 DeliveryVehicleAttribute 的重复字段。ListDeliveryVehicles API 包含一个 filter 字段,该字段可以将返回的 DeliveryVehicle 实体限制为具有指定属性的实体。DeliveryVehicleAttribute 不会影响 Fleet Engine 路由行为。

请勿在属性中添加个人身份信息 (PII) 或敏感信息,因为用户可能可以看到此字段。

任务的生命周期

您可以使用 Deliveries API gRPC 或 REST 接口在 Fleet Engine 中创建、更新和查询任务。

Task 对象有一个状态字段,可跟踪其在其生命周期中的进度。值会从“打开”变为“关闭”。新任务会创建为“打开”状态,这表示:

  • 任务尚未分配给送货车辆。
  • 送货车辆尚未经过任务的指定车辆停靠站。

任务指南

您只能将任务分配给处于打开状态的车辆。

您可以通过从车辆停靠站列表中移除任务来取消任务,这样做会自动将任务状态设置为“已关闭”。

当任务的车辆完成任务的车辆停止时:

  1. 将任务的结果字段更新为 SUCCEEDED 或 FAILED。

  2. 指定事件的时间戳。

    然后,JavaScript 舰队跟踪库会指示任务结果,并自动将任务状态设置为“已关闭”。如需了解详情,请参阅使用 JavaScript 舰队跟踪库跟踪舰队

与车辆一样,Fleet Engine 会删除七天后未更新的任务,如果您尝试使用已存在的 ID 创建任务,它将返回错误。

注意:Fleet Engine 不支持明确删除任务。该服务会在七天未更新的情况下自动删除任务。如果您希望任务数据的保留期限超过 7 天,则必须自行实现该功能。

任务属性

Task 实体包含 TaskAttribute 的重复字段,其值可以是字符串、数字和布尔值这 3 种类型之一。ListTasks API 包含一个 filter 字段,可以将返回的 Task 实体限制为具有指定属性的实体。任务属性不会影响 Fleet Engine 路由行为。

请勿在属性中添加个人身份信息 (PII) 或其他敏感信息,因为用户可能可以看到这些属性。

管理车辆和任务生命周期

提醒:您的内部系统是 Fleet Engine Deliveries API 代表您增强的可信数据源。

如需管理系统中的车辆和任务生命周期,请使用 Fleet Engine Deliveries API 来创建、更新和跟踪车辆及其相关任务。

同时,驾驶员应用直接与 Fleet Engine 通信,以更新设备位置和路线信息。借助此模型,Fleet Engine 可以高效地管理实时位置信息。它会将营业地点直接发送到跟踪库,然后您可以使用该库为消费者更新订单状态。

例如,假设您遇到以下情况:

  • 一名司机正在靠近一个配送站。驱动程序应用将其位置信息发送到 Fleet Engine。
  • Fleet Engine 将设备位置信息发送到跟踪库,您的消费者应用使用该库提醒消费者他们的包裹已接近。
  • 司机完成发货后,可以点击司机应用上的“发货已送达”按钮。
  • “已发货”操作会将信息发送到您的后端系统,该系统会执行必要的业务验证和验证步骤。
  • 您的系统确认任务已成功完成,并使用 Deliveries API 更新 Fleet Engine。

下图简要说明了这些流程。它还显示了您的系统、客户端和 Fleet Engine 之间的标准关系。

“集成 Deliveries API”示意图>

管理客户端令牌

来自驱动程序应用并直接发送到 Fleet Engine 的位置信息更新需要授权令牌。建议采用以下方法处理从客户端到 Fleet Engine 的更新:

  1. 请使用 Fleet Engine Delivery Untrusted Driver User 服务帐号角色生成令牌。

  2. 为驱动程序应用提供限定范围的令牌。此作用域仅允许更新 Fleet Engine 中的设备位置。

此方法可确保源自移动设备(被视为低信任环境)的调用遵循最小权限原则

其他服务账号角色

如果您想授权驱动程序应用直接进行 Fleet Engine 更新(超出不受信任的驱动程序角色所限的 Fleet Engine 更新),例如进行某些任务更新,您可以使用“可信驱动程序”角色。如需了解使用“可信驱动程序”角色的模型,请参阅可信驱动程序模型

如需详细了解不可信和可信驱动程序角色的用途,请参阅 Cloud 项目设置

模拟工作日

下表介绍了在送货和物流公司中,“第一公里”或“最后一公里”司机的工作日情况。您的公司可能在细节上有所不同,但您可以了解如何对工作日进行建模。

时间活动根据模型估算
当天开始后 24 小时内 调度员将货物分配给送货车辆或路线。 您可以提前在 Fleet Engine 中针对货物交付、取货、休息等创建任务。例如,您可以创建货运自提任务发货交付任务预定不可用预定停靠

在确定了整套配送包裹及其交付顺序后,将任务分配给车辆。
当天开始时间 司机登录司机应用,在仓库开始一天的工作。 初始化 Delivery Driver API。 根据需要在 Fleet Engine 中创建送货车辆
驾驶员将货物装入送货车辆,扫描货物。 如果未提前创建运单交付任务,请在扫描时创建运单交付任务
驾驶员确认要执行的任务的顺序。 如果未提前创建这些任务,请创建运送自提任务预定的不可用时间预定的停靠站
驱动程序离开仓库,并提交下一个要完成的任务。 通过提交任务的完成顺序,将所有任务分配或部分任务分配给车辆。
司机配送货物。 到达停靠站后,执行与到达停靠站的车辆相关的操作。 交付货品后,关闭送货任务,并视需要存储运单状态和其他元信息。 在完成该经停点的所有任务后,在开始开车前往下一个经停点之前,执行与车辆完成一个停靠站前往下一个停靠点的车辆相关的操作。
司机遇到供给车辆,以便将额外的货物转移到送货车辆上。 供给车辆和送货车辆之间的换乘点应建模为预定停靠点

转移并扫描运单后,请创建配送任务(如果尚未创建任务)。然后,通过将任务分配给车辆更新任务顺序来更新任务完成顺序。
司机收到取车请求的通知。 接受自提请求后,创建货物自提任务。然后,通过将任务分配给车辆更新任务顺序来更新任务执行顺序。
中午 司机在午休。 如果某个位置与不可用任务相关联,请像对待任何其他任务一样对它进行处理。执行与以下车辆相关的操作:车辆到达一个停靠站完成一个停靠站以及前往下一个停靠站的车辆

否则,在广告插播结束之前无需执行进一步操作。 移除任务,方法是确认下一个任务和剩余任务,并更新任务排序
司机取货。 其建模方法与送货站类似。执行与车辆抵达停靠点关闭任务相关的操作,还可以选择存储运单状态和其他元信息。 在完成该经停点的所有任务后,在开始前往下一个经停点之前,执行与车辆完成一个停靠站前往下一个停靠点的车辆相关的操作。 注意:为确保结算正确,所有自提都必须有相应的传送任务。如果上车点将在当天驾驶员的同一路线上配送到其他地点,我们建议您将送货任务建模为路线上的任何其他送货任务。如果司机将上车点送回仓库,我们建议在仓库目的地创建配送任务。
司机会安排一个停靠站,从保管箱取货。 此建模方法与任何其他上车点一样。执行与车辆抵达停靠点关闭任务相关的操作。在完成该停靠站的所有任务并开始行驶到下一个经停点后,执行与车辆完成一个停靠站向下一个停靠站行驶相关的操作。
司机收到包裹正在改道到其他地点的通知。 将原始运单交付任务状态设置为“COMPLETED”(已完成),并为新的送货地点创建新的运单交付任务。如需了解详情,请参阅重新递送运单
司机尝试投递包裹,但未能成功。 其建模方式类似于成功停止传送,将传送任务标记为已完成。执行与车辆抵达停车点相关的操作。未能送达运单后,请关闭任务,并视需要存储运单状态和其他元信息。 在完成该经停点的所有任务后,在开始前往下一个经停点之前,执行与车辆完成一个停靠站前往下一个停靠点的车辆相关的操作。
通知司机保留包裹(不送货)。 收到通知并确认后,将任务状态设置为“已完成”。
系统通知驾驶员接下来将某件货物交付给客户,并更改了承诺的交付订单。 更新任务排序
司机选择按顺序交付货品。 更新任务排序,然后照常继续。
司机将多件货物送至一个地点。 其模型类似于单个配送站。 到达停车点后,执行与车辆抵达停车点相关的操作。 交付每笔运单后,关闭每项任务,并视需要存储运单状态和其他元信息。 在完成该经停点的所有任务后,在开始前往下一个经停点之前,执行与车辆完成一个停靠站前往下一个停靠点的车辆相关的操作。
当天结束时间 司机返回补给站。 如果司机在途中提货时回到仓库,您还必须创建并关闭每个包裹,这是为了确保结算正确。为此,您可以像对任何其他送货站一样,对仓库进行建模。 如果仓库未用作送货站点,您仍然可以选择将仓库建模为预定停靠站。通过对停靠站建模,您的司机可以查看返回补给站的路线,并了解他们的预计到达时间。

位置信息更新的运作方式

为了让 Fleet Engine 实现最佳性能,请为其提供一系列车辆位置信息更新。请使用以下任一方式提供这些更新:

  1. 使用驱动程序 SDK - AndroidiOS - 最简单的选项。
  2. 使用自定义代码 - 适用于通过后端中继位置信息,或使用 Android 或 iOS 以外的设备的情况。

无论您如何提供车辆位置信息更新,后端都负责在送货车辆前往停靠点(包括仓库)以及到达停靠点时更新 Fleet Engine。Fleet Engine 不会自动检测这些事件。

车辆停靠点和送货地点

车辆停靠是指送货车辆完成装运任务或其他任务的位置。它可以是一个接入点(如装运码头),也可以是道路贴靠位置。

交货地点是指货物的交货或提货位置。往返送货地点可能需要从停放点步行一段时间。

例如,当司机正在将货物送往购物中心的商店时,送货车辆会停靠在商场最近的入口附近的停车场。这是车辆停靠站。然后,司机从车辆停靠站步行前往商店所在的购物中心内的位置。这是送货地点。

为了向用户提供最佳的物流跟踪体验,请考虑如何将装运任务分配给车辆停靠站,并谨记,系统会向用户报告装运任务的剩余车辆停靠次数,以帮助用户查看装运进度。

例如,如果司机向单个办公大楼进行多次送货,请考虑将所有送货任务分配给一个车辆停靠站。如果每个送货任务都分配给其自己的车辆停靠站,那么您的货运跟踪体验对用户的实用性会大打折扣,因为只有在车辆到达目的地前有限数量的车辆停靠站内时,才能使用跟踪功能。如果在短时间内完成许多车辆停靠,用户就不会花很多时间来跟踪交付进度。

使用移动 SDK

在调用驱动程序 SDK 之前,请确保对其进行初始化。

初始化 Delivery Driver API

在初始化 Driver SDK 中的 Delivery Driver API 之前,请务必初始化 Navigation SDK。然后初始化 Delivery Driver API,如以下示例所示:

static final String PROVIDER_ID = "provider-1234";
static final String VEHICLE_ID = "vehicle-8241890";

NavigationApi.getNavigator(
   this, // Activity.
   new NavigatorListener() {
     @Override
     public void onNavigatorReady(Navigator navigator) {
       DeliveryDriverApi.createInstance(DriverContext.builder(getApplication())
         .setNavigator(navigator)
         .setProviderId(PROVIDER_ID)
         .setVehicleId(VEHICLE_ID)
         .setAuthTokenFactory((context) -> "JWT") // AuthTokenFactory returns JWT for call context.
         .setRoadSnappedLocationProvider(NavigationApi.getRoadSnappedLocationProvider(getApplication()))
         .setNavigationTransactionRecorder(NavigationApi.getNavigationTransactionRecorder(getApplication()))
         .setStatusListener((statusLevel,statusCode,statusMsg) -> // Optional, surfaces polling errors.
             Log.d("TAG", String.format("New status update. %s, %s, %s", statusLevel, statusCode, statusMsg)))
         .build));
     }
     @Override
     public void onError(int errorCode) {
       Log.e("TAG", String.format("Error loading Navigator instance: %s", errorCode));
     }
   });

用例

本部分介绍如何使用 Deliveries API 对常见用例进行建模。

唯一实体标识符

REST 调用中使用的唯一实体标识符的格式和值对 Fleet Engine 是不透明的。避免使用自动递增 ID,并确保标识符不包含任何个人身份信息 (PII),例如司机的电话号码。

创建车辆

您可以通过驱动程序 SDK 或者使用 gRPC 或 REST 的服务器环境创建车辆。

gRPC

如需创建新车辆,请对 Fleet Engine 进行 CreateDeliveryVehicle 调用。使用 CreateDeliveryVehicleRequest 对象定义新送货车辆的属性。请注意,根据用户指定 ID 的 API 指南,为 Name 字段指定的任何值都会被忽略。您应使用 DeliveryVehicleId 字段设置车辆的 ID。

创建 DeliveryVehicle 时,您可以选择指定以下字段:

  • 属性
  • LastLocation
  • 类型

不要设置任何其他字段。如果包含此字段,Fleet Engine 会返回错误,因为这些字段是只读的,或只能通过调用 UpdateDeliveryVehicle 进行更新。

若要在未设置任何可选字段的情况下创建车辆,您可以不设置 CreateDeliveryVehicleRequest 中的 DeliveryVehicle 字段。

以下示例展示了如何使用 Java gRPC 库创建车辆:

    static final String PROJECT_ID = "my-delivery-co-gcp-project";
    static final String VEHICLE_ID = "vehicle-8241890"; // Avoid auto-incrementing IDs.

    DeliveryServiceBlockingStub deliveryService =
      DeliveryServiceGrpc.newBlockingStub(channel);

    // Vehicle settings
    String parent = "providers/" + PROJECT_ID;
    DeliveryVehicle vehicle = DeliveryVehicle.newBuilder()
      .addAttributes(DeliveryVehicleAttribute.newBuilder()
        .setKey("route_number").setValue("1"))  // Opaque to the Fleet Engine
      .build();

    // Vehicle request
    CreateDeliveryVehicleRequest createVehicleRequest =
      CreateDeliveryVehicleRequest.newBuilder()  // No need for the header
          .setParent(parent)
          .setDeliveryVehicleId(VEHICLE_ID)     // Vehicle ID assigned by the Provider
          .setDeliveryVehicle(vehicle)
          .build();

    // Error handling
    // If Fleet Engine does not have vehicle with that ID and the credentials of the
    // requestor pass, the service creates the vehicle successfully.

    try {
      DeliveryVehicle createdVehicle =
        deliveryService.createDeliveryVehicle(createVehicleRequest);
    } catch (StatusRuntimeException e) {
      Status s = e.getStatus();
      switch (s.getCode()) {
         case ALREADY_EXISTS:
           break;
         case PERMISSION_DENIED:
           break;
      }
      return;
    }

REST

如需通过服务器环境创建车辆,请对 CreateDeliveryVehicle 进行 HTTP REST 调用:

POST https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles?deliveryVehicleId=<id>

<id> 是车队中送货车辆的唯一标识符

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

POST 正文表示要创建的 DeliveryVehicle 实体。您可以指定以下可选字段:

  • 属性
  • lastLocation
  • 类型

示例 curl 命令:

# Set $JWT, $PROJECT_ID, and $VEHICLE_ID in the local
# environment
curl -X POST "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles?deliveryVehicleId=${VEHICLE_ID}" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
--data-binary @- << EOM
{
  "attributes": [{"key": "model", "value": "sedan"}],
  "lastLocation": {"location": {"latitude": 12.1, "longitude": 14.5}}
}
EOM

根据 API 指南中有关用户指定 ID 的准则,Fleet Engine 会忽略 DeliveryVehicle 实体的 name 字段。不要设置任何其他字段。如果包含此字段,Fleet Engine 会返回错误,因为这些字段是只读的,或只能通过调用 UpdateDeliveryVehicle 进行更新。

如需在不设置任何字段的情况下创建车辆,请将 POST 请求的正文留空。然后,新创建的车辆会从 POST 网址的 deliveryVehicleId 参数中提取车辆 ID。

示例 curl 命令:

# Set $JWT, $PROJECT_ID, and $VEHICLE_ID in the local
# environment
curl -X POST "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles?deliveryVehicleId=${VEHICLE_ID}" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}"

创建货物自提任务

您可以通过驱动程序 SDK 或使用 gRPC 或 REST 从服务器环境创建货物自提任务。

gRPC

以下示例展示了如何使用 Java gRPC 库创建货物自提任务:

static final String PROJECT_ID = "my-delivery-co-gcp-project";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Task settings
String parent = "providers/" + PROJECT_ID;
Task task = Task.newBuilder()
  .setType(Task.Type.PICKUP)
  .setState(Task.State.OPEN)
  .setTrackingId("my-tracking-id")
  .setPlannedLocation(               // Grand Indonesia East Mall
    LocationInfo.newBuilder().setPoint(
      LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
  .setTaskDuration(
    Duration.newBuilder().setSeconds(2 * 60))
  .setTargetTimeWindow(
    TimeWindow.newBuilder()
      .setStartTime(Timestamp.newBuilder().setSeconds(1680123600))
      .setEndTime(Timestamp.newBuilder().setSeconds(1680130800)))
  .addAttributes(TaskAttribute.newBuilder().setKey("foo").setStringValue("value"))
  .addAttributes(TaskAttribute.newBuilder().setKey("bar").setNumberValue(10))
  .addAttributes(TaskAttribute.newBuilder().setKey("baz").setBoolValue(false))
  .build();

// Task request
CreateTaskRequest createTaskRequest =
  CreateTaskRequest.newBuilder()  // No need for the header
      .setParent(parent)          // Avoid using auto-incrementing IDs for the taskId
      .setTaskId("task-8241890")  // Task ID assigned by the Provider
      .setTask(task)              // Initial state
      .build();

// Error handling
// If Fleet Engine does not have a task with that ID and the credentials of the
// requestor pass, the service creates the task successfully.

try {
  Task createdTask = deliveryService.createTask(createTaskRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case ALREADY_EXISTS:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需从服务器环境中创建货物自提任务,请对 CreateTask 进行 HTTP REST 调用:

`POST https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks?taskId=<id>`

<id> 是任务的唯一标识符。此值不能是运单的跟踪编号。如果您的系统中没有任务 ID,您可以生成一个通用唯一标识符 (UUID)。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 Task 实体:

  • 必填字段:

    字段
    类型 Type.PICKUP
    state State.OPEN
    trackingId 您用于跟踪运单的编号或标识符。
    plannedLocation 完成任务的位置,在本例中为货物自提位置。
    taskDuration 在取货地点取货的预计时间(以秒为单位)。

  • 可选字段:

    字段
    targetTimeWindow 应完成任务的时间范围。这不会影响路由行为。
    属性 自定义 Task 属性列表。每个属性都必须具有唯一的键。

在创建时,实体中的所有其他字段都会被忽略。如果请求包含已分配的 deliveryVehicleId,Fleet Engine 会抛出异常。您可以使用 UpdateDeliveryVehicleRequest 分配任务。如需了解详情,请参阅将任务分配给车辆UpdateDeliveryVehicleRequest

示例 curl 命令:

# Set $JWT, $PROJECT_ID, $TRACKING_ID, and $TASK_ID in the local
# environment
curl -X POST "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks?taskId=${TASK_ID}" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "type": "PICKUP",
  "state": "OPEN",
  "trackingId": "${TRACKING_ID}",
  "plannedLocation": {
     "point": {
        "latitude": -6.195139,
        "longitude": 106.820826
     }
  },
  "taskDuration": "90s",
  "targetTimeWindow": {
    "startTime": "2023-03-29T21:00:00Z",
    "endTime": "2023-03-29T23:00:00Z"
  }
}
EOM

创建运单交付任务

使用 Driver SDK 或者使用 gRPC 或 REST 从服务器环境创建运单交付任务。

gRPC

以下示例展示了如何使用 Java gRPC 库创建货物交付任务:

static final String PROJECT_ID = "my-delivery-co-gcp-project";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Task settings
String parent = "providers/" + PROJECT_ID;
Task task = Task.newBuilder()
  .setType(Task.Type.DELIVERY)
  .setState(Task.State.OPEN)
  .setTrackingId("my-tracking-id")
  .setPlannedLocation(               // Grand Indonesia East Mall
    LocationInfo.newBuilder().setPoint(
      LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
  .setTaskDuration(
    Duration.newBuilder().setSeconds(2 * 60))
  .setTargetTimeWindow(
    TimeWindow.newBuilder()
      .setStartTime(Timestamp.newBuilder().setSeconds(1680123600))
      .setEndTime(Timestamp.newBuilder().setSeconds(1680130800)))
  .addAttributes(TaskAttribute.newBuilder().setKey("foo").setStringValue("value"))
  .addAttributes(TaskAttribute.newBuilder().setKey("bar").setNumberValue(10))
  .addAttributes(TaskAttribute.newBuilder().setKey("baz").setBoolValue(false))
  .build();

// Task request
CreateTaskRequest createTaskRequest =
  CreateTaskRequest.newBuilder()  // No need for the header
      .setParent(parent)          // Avoid using auto-incrementing IDs for the taskId
      .setTaskId("task-8241890")  // Task ID assigned by the Provider
      .setTask(task)              // Initial state
      .build();

// Error handling
// If Fleet Engine does not have task with that ID and the credentials of the
// requestor pass, the service creates the task successfully.

try {
  Task createdTask = deliveryService.createTask(createTaskRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case ALREADY_EXISTS:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需使用 gRPC 或 REST 从服务器环境创建货品交付任务,请对 CreateTask 发出 HTTP REST 调用:

`POST https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks?taskId=<id>`

<id> 是任务的唯一标识符。此值不能是运单的跟踪编号。如果您的系统中没有任务 ID,您可以生成一个通用唯一标识符 (UUID)。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 Task 实体:

  • 必填字段:

    字段
    类型 Type.DELIVERY
    state State.OPEN
    trackingId 您用于跟踪运单的编号或标识符。
    plannedLocation 完成任务的位置,在本例中为此次运单的投递地点。
    taskDuration 将货物送至送货地点的预计时间(以秒为单位)。

  • 可选字段:

    字段
    targetTimeWindow 应完成任务的时间范围。这不会影响路由行为。
    属性 自定义 Task 属性列表。每个属性都必须具有唯一的键。

在创建时,实体中的所有其他字段都会被忽略。如果请求包含已分配的 deliveryVehicleId,Fleet Engine 会抛出异常。您可以使用 UpdateDeliveryVehicleRequest 分配任务。如需了解详情,请参阅将任务分配给车辆UpdateDeliveryVehicleRequest

示例 curl 命令:

# Set $JWT, $PROJECT_ID, $TRACKING_ID, and $TASK_ID in the local
# environment
curl -X POST "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks?taskId=${TASK_ID}" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "type": "DELIVERY",
  "state": "OPEN",
  "trackingId": "${TRACKING_ID}",
  "plannedLocation": {
     "point": {
        "latitude": -6.195139,
        "longitude": 106.820826
     }
  },
  "taskDuration": "90s",
  "targetTimeWindow": {
    "startTime": "2023-03-29T21:00:00Z",
    "endTime": "2023-03-29T23:00:00Z"
  }
}
EOM

批量创建任务

您可以使用 gRPC 或 REST 从服务器环境创建批量任务。

gRPC

以下示例展示了如何使用 Java gRPC 库创建两项任务,一项用于配送,一项用于在同一地点取货:

static final String PROJECT_ID = "my-delivery-co-gcp-project";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Delivery Task settings
Task deliveryTask = Task.newBuilder()
  .setType(Task.Type.DELIVERY)
  .setState(Task.State.OPEN)
  .setTrackingId("delivery-tracking-id")
  .setPlannedLocation(               // Grand Indonesia East Mall
    LocationInfo.newBuilder().setPoint(
      LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
  .setTaskDuration(
    Duration.newBuilder().setSeconds(2 * 60))
  .build();

// Delivery Task request
CreateTaskRequest createDeliveryTaskRequest =
  CreateTaskRequest.newBuilder()  // No need for the header or parent fields
      .setTaskId("task-8312508")  // Task ID assigned by the Provider
      .setTask(deliveryTask)      // Initial state
      .build();

// Pickup Task settings
Task pickupTask = Task.newBuilder()
  .setType(Task.Type.PICKUP)
  .setState(Task.State.OPEN)
  .setTrackingId("pickup-tracking-id")
  .setPlannedLocation(               // Grand Indonesia East Mall
    LocationInfo.newBuilder().setPoint(
      LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
  .setTaskDuration(
    Duration.newBuilder().setSeconds(2 * 60))
  .build();

// Pickup Task request
CreateTaskRequest createPickupTaskRequest =
  CreateTaskRequest.newBuilder()  // No need for the header or parent fields
      .setTaskId("task-8241890")  // Task ID assigned by the Provider
      .setTask(pickupTask)        // Initial state
      .build();

// Batch Create Tasks settings
String parent = "providers/" + PROJECT_ID;

// Batch Create Tasks request
BatchCreateTasksRequest batchCreateTasksRequest =
  BatchCreateTasksRequest.newBuilder()
      .setParent(parent)
      .addRequests(createDeliveryTaskRequest)
      .addRequests(createPickupTaskRequest)
      .build();

// Error handling
// If Fleet Engine does not have any task(s) with these task ID(s) and the
// credentials of the requestor pass, the service creates the task(s)
// successfully.

try {
  BatchCreateTasksResponse createdTasks = deliveryService.batchCreateTasks(
    batchCreateTasksRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case ALREADY_EXISTS:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需从服务器环境中创建送货和自提任务,请对 BatchCreateTasks 进行 HTTP REST 调用:

POST https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks:batchCreate

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 BatchCreateTasksRequest 实体:

  • 必填字段:

    字段
    个请求 数组<CreateTasksRequest>

  • 可选字段:

    字段
    标头 “DeliveryRequestHeader”

requests 中的每个 CreateTasksRequest 元素都必须传递与 CreateTask 请求相同的验证规则,但 parentheader 字段是可选的。如果设置了此字段,则这些字段必须与顶级 BatchCreateTasksRequest 中各自对应的字段相同。如需了解每项运输任务的具体验证规则,请参阅创建货运自提任务创建运货交付任务

如需了解详情,请参阅 BatchCreateTasks 的 API 参考文档(gRPCREST)。

示例 curl 命令:

# Set $JWT, $PROJECT_ID, $DELIVERY_TRACKING_ID, $DELIVERY_TASK_ID,
# $PICKUP_TRACKING_ID, and $PICKUP_TASK_ID in the local environment
curl -X POST "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks:batchCreate" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "requests" : [
    {
      "taskId": "${DELIVERY_TASK_ID}",
      "task" : {
        "type": "DELIVERY",
        "state": "OPEN",
        "trackingId": "${DELIVERY_TRACKING_ID}",
        "plannedLocation": {
          "point": {
              "latitude": -6.195139,
              "longitude": 106.820826
          }
        },
        "taskDuration": "90s"
      }
    },
    {
      "taskId": "${PICKUP_TASK_ID}",
      "task" : {
        "type": "PICKUP",
        "state": "OPEN",
        "trackingId": "${PICKUP_TRACKING_ID}",
        "plannedLocation": {
          "point": {
              "latitude": -6.195139,
              "longitude": 106.820826
          }
        },
        "taskDuration": "90s"
      }
    }
  ]
}
EOM

预定不可用

您可以通过驾驶 SDK 或者使用 gRPC 或 REST 的服务器环境创建指示不可用状态的任务(例如,针对驾驶员中断或车辆加油)的任务。已排定的不可用任务不得包含跟踪 ID。您可以选择提供一个位置。

gRPC

以下示例展示了如何使用 Java gRPC 库创建不可用任务:

    static final String PROJECT_ID = "my-delivery-co-gcp-project";

    DeliveryServiceBlockingStub deliveryService =
      DeliveryServiceGrpc.newBlockingStub(channel);

    // Task settings
    String parent = "providers/" + PROJECT_ID;
    Task task = Task.newBuilder()
      .setType(Task.Type.UNAVAILABLE)
      .setState(Task.State.OPEN)
      .setTaskDuration(
        Duration.newBuilder().setSeconds(60 * 60))  // 1hr break
      .build();

    // Task request
    CreateTaskRequest createTaskRequest =
      CreateTaskRequest.newBuilder()  // No need for the header
          .setParent(parent)          // Avoid using auto-incrementing IDs for the taskId
          .setTaskId("task-8241890")  // Task ID assigned by the Provider
          .setTask(task)              // Initial state
          .build();

    // Error handling
    // If Fleet Engine does not have task with that ID and the credentials of the
    // requestor pass, the service creates the task successfully.

    try {
      Task createdTask = deliveryService.createTask(createTaskRequest);
    } catch (StatusRuntimeException e) {
      Status s = e.getStatus();
      switch (s.getCode()) {
         case ALREADY_EXISTS:
           break;
         case PERMISSION_DENIED:
           break;
      }
      return;
    }

REST

如需在服务器环境中创建不可用任务,请对 CreateTask 进行 HTTP REST 调用:

`POST https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks?taskId=<id>`

<id> 是任务的唯一标识符。如果您的系统中没有任务 ID,您可以生成一个通用唯一标识符 (UUID)。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 Task 实体:

  • 必填字段:

    字段
    类型 Type.UNAVAILABLE
    state State.OPEN
    taskDuration 广告插播时长(以秒为单位)。

  • 可选字段:

    字段
    plannedLocation 广告插播位置(如果必须在特定位置截取)。

在创建时,实体中的所有其他字段都会被忽略。如果请求包含已分配的 deliveryVehicleId,Fleet Engine 会抛出异常。您可以使用 UpdateDeliveryVehicleRequest 分配任务。如需了解详情,请参阅将任务分配给车辆UpdateDeliveryVehicleRequest

示例 curl 命令:

    # Set $JWT, $PROJECT_ID, and $TASK_ID in the local environment
    curl -X POST "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks?taskId=${TASK_ID}" \
      -H "Content-type: application/json" \
      -H "Authorization: Bearer ${JWT}" \
      --data-binary @- << EOM
    {
      "type": "UNAVAILABLE",
      "state": "OPEN",
      "plannedLocation": {
         "point": {
            "latitude": -6.195139,
            "longitude": 106.820826
         }
      },
      "taskDuration": "300s"
    }
    EOM

排定的经停点

您可以通过驱动程序 SDK,或者使用 gRPC 或 REST 的服务器环境创建计划停止任务。计划停止任务不能包含跟踪 ID。

gRPC

以下示例展示了如何使用 Java gRPC 库创建计划停止任务:

static final String PROJECT_ID = "my-delivery-co-gcp-project";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Task settings
String parent = "providers/" + PROJECT_ID;
Task task = Task.newBuilder()
  .setType(Task.Type.SCHEDULED_STOP)
  .setState(Task.State.OPEN)
  .setPlannedLocation(               // Grand Indonesia East Mall
    LocationInfo.newBuilder().setPoint(
      LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
  .setTaskDuration(
    Duration.newBuilder().setSeconds(2 * 60))
  .build();

// Task request
CreateTaskRequest createTaskRequest =
  CreateTaskRequest.newBuilder()  // No need for the header
      .setParent(parent)
      .setTaskId("task-8241890")  // Task ID assigned by the Provider
      .setTrip(task)              // Initial state
      .build();

// Error handling
// If Fleet Engine does not have task with that ID and the credentials of the
// requestor pass, the service creates the task successfully.

try {
  Task createdTask = deliveryService.createTask(createTaskRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case ALREADY_EXISTS:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需在服务器环境中创建计划停止任务,请对 CreateTask 进行 HTTP REST 调用:

`POST https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks?taskId=<id>`

<id> 是任务的唯一标识符。如果您的系统中没有任务 ID,您可以生成一个通用唯一标识符 (UUID)。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 Task 实体:

  • 必填字段:

    字段
    类型 Type.SCHEDULED_STOP
    state State.OPEN
    plannedLocation 经停点的位置。
    taskDuration 预计的停靠时长(以秒为单位)。

  • 可选字段:

在创建时,实体中的所有其他字段都会被忽略。如果请求包含已分配的 deliveryVehicleId,Fleet Engine 会抛出异常。您可以使用 UpdateDeliveryVehicleRequest 分配任务。如需了解详情,请参阅将任务分配给车辆UpdateDeliveryVehicleRequest

示例 curl 命令:

    # Set $JWT, $PROJECT_ID, and $TASK_ID in the local environment
    curl -X POST "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks?taskId=${TASK_ID}" \
      -H "Content-type: application/json" \
      -H "Authorization: Bearer ${JWT}" \
      --data-binary @- << EOM
    {
      "type": "SCHEDULED_STOP",
      "state": "OPEN",
      "plannedLocation": {
         "point": {
            "latitude": -6.195139,
            "longitude": 106.820826
         }
      },
      "taskDuration": "600s"
    }
    EOM

设置目标时间范围

目标时间范围是应该完成任务的 TimeWindow。例如,如果您向送达收件人传达了送货时间范围,则可以使用任务目标时间范围来捕获此时间范围,并使用该字段生成提醒或分析行程后的表现。

目标时间范围由开始时间和结束时间组成,可以针对任何任务类型进行设置。目标时间范围不会影响路由行为。

gRPC

以下示例展示了如何使用 Java gRPC 库设置任务时间范围:

    static final String PROJECT_ID = "my-delivery-co-gcp-project";
    static final String TASK_ID = "task-8241890";

    DeliveryServiceBlockingStub deliveryService =
      DeliveryServiceGrpc.newBlockingStub(channel);

    // Task settings
    String taskName = "providers/" + PROJECT_ID + "/tasks/" + TASK_ID;
    Task task = Task.newBuilder()
      .setName(taskName)
      .setTargetTimeWindow(
        TimeWindow.newBuilder()
          .setStartTime(Timestamp.newBuilder().setSeconds(1680123600))
          .setEndTime(Timestamp.newBuilder().setSeconds(1680130800)))
      .build();

    // Task request
    UpdateTaskRequest updateTaskRequest =
      UpdateTaskRequest.newBuilder()  // No need for the header
          .setTask(task)
          .setUpdateMask(FieldMask.newBuilder().addPaths("targetTimeWindow"))
          .build();

    try {
      Task updatedTask = deliveryService.updateTask(updateTaskRequest);
    } catch (StatusRuntimeException e) {
      Status s = e.getStatus();
      switch (s.getCode()) {
         case NOT_FOUND:
           break;
         case PERMISSION_DENIED:
           break;
      }
      return;
    }

REST

如需使用 HTTP 设置任务时间范围,请调用 UpdateTask

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks/<id>?updateMask=targetTimeWindow`

<id> 是任务的唯一标识符

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 Task 实体:

  • 必填字段:

    字段
    targetTimeWindow 应完成任务的时间范围。此设置不会影响路由行为

  • 可选字段:

在更新时,系统将忽略实体中的所有其他字段。

示例 curl 命令:

# Set JWT, PROJECT_ID, and TASK_ID in the local environment
curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks/${TASK_ID}?updateMask=targetTimeWindow" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "targetTimeWindow": {
    "startTime": "2023-03-29T21:00:00Z",
    "endTime": "2023-03-29T23:00:00Z"
  }
}
EOM

设置任务跟踪可见性配置

通过对任务设置 TaskTrackingViewConfig,可以按任务控制运送跟踪库中数据的公开范围以及从调用 GetTaskTrackingInfo 返回的数据。如需了解详情,请参阅活跃车辆任务。此操作可以在创建或更新任务时完成。以下是使用此配置更新任务的示例:

gRPC

以下示例展示了如何使用 Java gRPC 库设置任务跟踪视图配置:

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String TASK_ID = "task-8241890";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Task settings
String taskName = "providers/" + PROJECT_ID + "/tasks/" + TASK_ID;
Task task = Task.newBuilder()
  .setName(taskName)
  .setTaskTrackingViewConfig(
    TaskTrackingViewConfig.newBuilder()
      .setRoutePolylinePointsVisibility(
        VisibilityOption.newBuilder().setRemainingStopCountThreshold(3))
      .setEstimatedArrivalTimeVisibility(
        VisibilityOption.newBuilder().remainingDrivingDistanceMetersThreshold(5000))
      .setRemainingStopCountVisibility(
        VisibilityOption.newBuilder().setNever(true)))
  .build();

// Task request
UpdateTaskRequest updateTaskRequest =
  UpdateTaskRequest.newBuilder()  // No need for the header
      .setTask(task)
      .setUpdateMask(FieldMask.newBuilder().addPaths("taskTrackingViewConfig"))
      .build();

try {
  Task updatedTask = deliveryService.updateTask(updateTaskRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
      case NOT_FOUND:
        break;
      case PERMISSION_DENIED:
        break;
  }
  return;
}

REST

如需使用 HTTP 设置任务跟踪视图配置窗口,请调用 UpdateTask

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks/<id>?updateMask=taskTrackingViewConfig`

<id> 是任务的唯一标识符

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 Task 实体:

  • 必填字段:

    字段
    taskTrackingViewConfig 任务跟踪的配置,用于指定在什么情况下最终用户可以看到哪些数据元素。

  • 可选字段:

在更新时,系统将忽略实体中的所有其他字段。

示例 curl 命令:

# Set JWT, PROJECT_ID, and TASK_ID in the local environment
curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks/${TASK_ID}?updateMask=taskTrackingViewConfig" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "taskTrackingViewConfig": {
    "routePolylinePointsVisibility": {
      "remainingStopCountThreshold": 3
    },
    "estimatedArrivalTimeVisibility": {
      "remainingDrivingDistanceMetersThreshold": 5000
    },
    "remainingStopCountVisibility": {
      "never": true
    }
  }
}
EOM

将任务分配给车辆

您可以通过更新车辆的任务排序来将任务分配给配送车辆。车辆的任务排序由送货车辆的停靠站列表确定,您可以向每个停靠站分配一个或多个任务。如需了解详情,请参阅更新任务排序

如需将运单从一辆车更改为另一辆车,请关闭原始任务,然后在为其分配新车辆之前重新创建任务。如果为已分配给其他车辆的任务更新任务顺序,您会收到错误消息。

更新任务排序

您可以通过驾驶 SDK 或服务器环境更新分配给车辆的顺序任务。请勿同时使用这两种方法来避免竞态条件并保持单一可信来源。

更新车辆的任务排序时,还会执行以下操作:

  • 分配在车辆中首次执行的任务。
  • 关闭之前分配给车辆但并非更新后的顺序的所有任务。

如需将运单从一辆车更改为另一辆车,请关闭原始任务,然后在分配新车辆之前重新创建该任务。如果为已分配给其他车辆的任务更新任务顺序,您会收到错误消息。

您可以随时更新任务排序。

gRPC

以下示例展示了如何使用 Java gRPC 库更新车辆的任务排序:

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String VEHICLE_ID = "vehicle-8241890";
static final String TASK1_ID = "task-756390";
static final String TASK2_ID = "task-849263";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Vehicle settings
String vehicleName = "providers/" + PROJECT_ID + "/deliveryVehicles/" + VEHICLE_ID;
DeliveryVehicle deliveryVehicle = DeliveryVehicle.newBuilder()
    .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()  // 1st stop
       .setStop(VehicleStop.newBuilder()
           .setPlannedLocation(LocationInfo.newBuilder()
               .setPoint(LatLng.newBuilder()
                   .setLatitude(37.7749)
                   .setLongitude(122.4194)))
           .addTasks(TaskInfo.newBuilder().setTaskId(TASK1_ID))
           .setState(VehicleStop.State.NEW)))
    .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()  // 2nd stop
       .setStop(VehicleStop.newBuilder()
           .setPlannedLocation(LocationInfo.newBuilder()
               .setPoint(LatLng.newBuilder()
                   .setLatitude(37.3382)
                   .setLongitude(121.8863)))
           .addTasks(TaskInfo.newBuilder().setTaskId(TASK2_ID))
           .setState(VehicleStop.State.NEW)))
    .build();

// DeliveryVehicle request
UpdateDeliveryVehicleRequest updateDeliveryRequest =
  UpdateDeliveryVehicleRequest.newBuilder()  // No need for the header
      .setName(vehicleName)
      .setDeliveryVehicle(deliveryVehicle)
      .setUpdateMask(FieldMask.newBuilder().addPaths("remaining_vehicle_journey_segments"))
      .build();

try {
  DeliveryVehicle updatedDeliveryVehicle =
      deliveryService.updateDeliveryVehicle(updateDeliveryVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需在服务器环境中更新车辆的任务排序,请对 UpdateDeliveryVehicle 进行 HTTP REST 调用:

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles/<id>?updateMask=remainingVehicleJourneySegments`

<id> 是要更新任务排序的配送车辆的唯一标识符。它是您在创建车辆时指定的标识符。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 DeliveryVehicle 实体:

  • 必填字段:

    字段
    remainingVehicleJourneySegments 按应执行的顺序排列的任务历程段列表。列表中的第一个任务会首先执行。
    剩余车辆旅程细分 [i].stop 列表中任务 i 的停靠点。
    剩余车辆旅程细分 [i].stop.plannedLocation 规划的经停点位置。
    剩余车辆旅程细分 [i].stop.tasks 要在此车辆停靠站执行的任务列表。
    remainingVehicleJourneySegments[i].stop.state State.NEW

  • 可选字段:

在更新时,系统将忽略实体中的所有其他字段。

示例 curl 命令:

# Set JWT, PROJECT_ID, VEHICLE_ID, TASK1_ID, and TASK2_ID in the local
# environment
curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles/${VEHICLE_ID}?updateMask=remainingVehicleJourneySegments" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "remainingVehicleJourneySegments": [
    {
      "stop": {
        "state": "NEW",
        "plannedLocation": {
          "point": {
            "latitude": 37.7749,
            "longitude": -122.084061
          }
        },
        "tasks": [
          {
            "taskId": "${TASK1_ID}"
          }
        ]
      }
    },
    {
      "stop": {
        "state": "NEW",
        "plannedLocation": {
          "point": {
            "latitude": 37.3382,
            "longitude": 121.8863
          }
        },
        "tasks": [
          {
            "taskId": "${TASK2_ID}"
          }
        ]
      }
    }
  ]
}
EOM

车辆正在前往下一站

当车辆离开车站或开始导航时,Fleet Engine 必须收到通知。您可以通过驱动程序 SDK 或者使用 gRPC 或 REST 的服务器环境通知 Fleet Engine。请勿同时使用这两种方法来避免竞态条件和保持单一可信来源。

gRPC

以下示例展示了如何使用 Java gRPC 库通知 Fleet Engine 有车辆正在前往下一站。

    static final String PROJECT_ID = "my-delivery-co-gcp-project";
    static final String VEHICLE_ID = "vehicle-8241890";

    DeliveryServiceBlockingStub deliveryService =
      DeliveryServiceGrpc.newBlockingStub(channel);

    // Vehicle settings
    DeliveryVehicle deliveryVehicle = DeliveryVehicle.newBuilder()
        // Next stop marked as ENROUTE
        .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()  // 1st stop
           .setStop(VehicleStop.newBuilder()
               .setPlannedLocation(LocationInfo.newBuilder()
                   .setPoint(LatLng.newBuilder()
                       .setLatitude(37.7749)
                       .setLongitude(122.4194)))
               .addTasks(TaskInfo.newBuilder().setTaskId(TASK1_ID))
               .setState(VehicleStop.State.ENROUTE)))
        // All other stops marked as NEW
        .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()  // 2nd stop
           .setStop(VehicleStop.newBuilder()
               .setPlannedLocation(LocationInfo.newBuilder()
                   .setPoint(LatLng.newBuilder()
                       .setLatitude(37.3382)
                       .setLongitude(121.8863)))
               .addTasks(TaskInfo.newBuilder().setTaskId(TASK2_ID))
               .setState(VehicleStop.State.NEW)))
        .build();

    // DeliveryVehicle request
    UpdateDeliveryVehicleRequest updateDeliveryVehicleRequest =
      UpdateDeliveryVehicleRequest.newBuilder()  // No need for the header
          .setName(vehicleName)
          .setDeliveryVehicle(deliveryVehicle)
          .setUpdateMask(FieldMask.newBuilder().addPaths("remaining_vehicle_journey_segments"))
          .build();

    try {
      DeliveryVehicle updatedDeliveryVehicle =
          deliveryService.updateDeliveryVehicle(updateDeliveryVehicleRequest);
    } catch (StatusRuntimeException e) {
      Status s = e.getStatus();
      switch (s.getCode()) {
         case NOT_FOUND:
           break;
         case PERMISSION_DENIED:
           break;
      }
      return;
    }

REST

如需通知 Fleet Engine 车辆正在从服务器环境前往下一站,请对 UpdateDeliveryVehicle 进行 HTTP REST 调用:

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles/<id>?updateMask=remainingVehicleJourneySegments`

<id> 是要更新任务排序的配送车辆的唯一标识符。它是您在创建车辆时指定的标识符。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 DeliveryVehicle 实体:

  • 必填字段:

    字段
    remainingVehicleJourneySegments 其余车辆停靠站的列表,其状态被标记为 State.NEW。列表上的第一个停靠站的状态必须标记为 State.ENROUTE。

  • 可选字段:

对于通知,实体中的所有其他字段都会被忽略。

示例 curl 命令:

# Set JWT, PROJECT_ID, VEHICLE_ID, TASK1_ID, and TASK2_ID in the local
# environment
curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles/${VEHICLE_ID}?updateMask=remainingVehicleJourneySegments" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "remainingVehicleJourneySegments": [
    {
      "stop": {
        "state": "ENROUTE",
        "plannedLocation": {
          "point": {
            "latitude": 37.7749,
            "longitude": -122.084061
          }
        },
        "tasks": [
          {
            "taskId": "${TASK1_ID}"
          }
        ]
      }
    },
    {
      "stop": {
        "state": "NEW",
        "plannedLocation": {
          "point": {
            "latitude": 37.3382,
            "longitude": 121.8863
          }
        },
        "tasks": [
          {
            "taskId": "${TASK2_ID}"
          }
        ]
      }
    }
  ]
}
EOM

更新车辆位置信息

如果不使用 Driver SDK 更新车辆的位置信息,您可以使用车辆的位置信息直接调用 Fleet Engine。对于任何活跃车辆,Fleet Engine 预计每分钟至少更新一次,最多每 5 秒一次。

gRPC

以下示例展示了如何使用 Java gRPC 库在 Fleet Engine 中更新车辆的位置:

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String VEHICLE_ID = "vehicle-8241890";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Vehicle settings
String vehicleName = "providers/" + PROJECT_ID + "/deliveryVehicles/" + VEHICLE_ID;
DeliveryVehicle myDeliveryVehicle = DeliveryVehicle.newBuilder()
    .setLastLocation(DeliveryVehicleLocation.newBuilder()
        .setSupplementalLocation(LatLng.newBuilder()
            .setLatitude(37.3382)
            .setLongitude(121.8863))
        .setSupplementalLocationTime(now())
        .setSupplementalLocationSensor(DeliveryVehicleLocationSensor.CUSTOMER_SUPPLIED_LOCATION)
        .setSupplementalLocationAccuracy(DoubleValue.of(15.0)))  // Optional
    .build();

// DeliveryVehicle request
UpdateDeliveryVehicleRequest updateDeliveryVehicleRequest =
  UpdateDeliveryVehicleRequest.newBuilder()  // No need for the header
      .setName(vehicleName)
      .setDeliveryVehicle(myDeliveryVehicle)
      .setUpdateMask(FieldMask.newBuilder()
          .addPaths("last_location"))
      .build();

try {
  DeliveryVehicle updatedDeliveryVehicle =
      deliveryService.updateDeliveryVehicle(updateDeliveryVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需使用 HTTP REST 在 Fleet Engine 中更新车辆的位置,请调用 UpdateDeliveryVehicle

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles/<id>?updateMask=last_location`

<id> 是您车队中或您打算更新位置的送货车辆的唯一标识符。它是您在创建车辆时指定的标识符。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 DeliveryVehicle 实体:

  • 必填字段:

    字段
    lastLocation.supplementalLocation 车辆的位置。
    lastLocation.supplementalLocationTime 车辆在此位置最近一次的已知时间戳。
    lastLocation.supplementalLocationSensor 应填充 CUSTOMER_SUPPLIED_LOCATION。

  • 可选字段:

    字段
    lastLocation.supplementalLocationAccuracy 所提供位置的精确度(以米为单位)。

示例 curl 命令:

# Set JWT, PROJECT_ID, VEHICLE_ID, TASK1_ID, and TASK2_ID in the local
# environment
curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles/${VEHICLE_ID}?updateMask=remainingVehicleJourneySegments" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "lastLocation": {
    "supplementalLocation": {"latitude": 12.1, "longitude": 14.5},
    "supplementalLocationTime": "$(date -u --iso-8601=seconds)",
    "supplementalLocationSensor": "CUSTOMER_SUPPLIED_LOCATION",
    "supplementalLocationAccuracy": 15
  }
}
EOM

车辆抵达一个停车点

当车辆到达一个停车点时,Fleet Engine 必须收到通知。您可以通过驱动程序 SDK 或者使用 gRPC 或 REST 的服务器环境通知 Fleet Engine。请勿同时使用这两种方法来避免竞态条件和保持单一可信来源。

gRPC

以下示例展示了如何使用 Java gRPC 库通知 Fleet Engine 车辆已到达一个停靠点:

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String VEHICLE_ID = "vehicle-8241890";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Vehicle settings
String vehicleName = "providers/" + PROJECT_ID + "/deliveryVehicles/" + VEHICLE_ID;
DeliveryVehicle deliveryVehicle = DeliveryVehicle.newBuilder()
    // Marking the arrival at stop.
    .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()
       .setStop(VehicleStop.newBuilder()
           .setPlannedLocation(LocationInfo.newBuilder()
               .setPoint(LatLng.newBuilder()
                   .setLatitude(37.7749)
                   .setLongitude(122.4194)))
           .addTasks(TaskInfo.newBuilder().setTaskId(TASK1_ID))
           .setState(VehicleStop.State.ARRIVED)))
    // All other remaining stops marked as NEW.
    .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()  // 2nd stop
       .setStop(VehicleStop.newBuilder()
           .setPlannedLocation(LocationInfo.newBuilder()
               .setPoint(LatLng.newBuilder()
                   .setLatitude(37.3382)
                   .setLongitude(121.8863)))
           .addTasks(TaskInfo.newBuilder().setTaskId(TASK2_ID))
           .setState(VehicleStop.State.NEW))) // Remaining stops must be NEW.
    .build();

// DeliveryVehicle request
UpdateDeliveryVehicleRequest updateDeliveryVehicleRequest =
  UpdateDeliveryVehicleRequest.newBuilder()  // No need for the header
      .setName(vehicleName)
      .setDeliveryVehicle(deliveryVehicle)
      .setUpdateMask(FieldMask.newBuilder()
          .addPaths("remaining_vehicle_journey_segments"))
      .build();

try {
  DeliveryVehicle updatedDeliveryVehicle =
      deliveryService.updateDeliveryVehicle(updateDeliveryVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需在服务器环境中通知 Fleet Engine 车辆抵达经停点的情况,请对 UpdateDeliveryVehicle 进行 HTTP REST 调用:

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles/<id>?updateMask=remainingVehicleJourneySegments`

<id> 是要更新任务排序的配送车辆的唯一标识符。它是您在创建车辆时指定的标识符。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 DeliveryVehicle 实体:

  • 必填字段:

    字段
    remainingVehicleJourneySegments 您到达的经停点,状态设置为 State.ARRIVED,后跟状态标记为 State.NEW 的其余车辆停靠站的列表。

  • 可选字段:

在更新时,系统将忽略实体中的所有其他字段。

示例 curl 命令:

# Set JWT, PROJECT_ID, VEHICLE_ID, TASK1_ID, and TASK2_ID in the local
# environment
curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles/${VEHICLE_ID}?updateMask=remainingVehicleJourneySegments" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "remainingVehicleJourneySegments": [
    {
      "stop": {
        "state": "ARRIVED",
        "plannedLocation": {
          "point": {
            "latitude": 37.7749,
            "longitude": -122.084061
          }
        },
        "tasks": [
          {
            "taskId": "${TASK1_ID}"
          }
        ]
      }
    },
    {
      "stop": {
        "state": "NEW",
        "plannedLocation": {
          "point": {
            "latitude": 37.3382,
            "longitude": 121.8863
          }
        },
        "tasks": [
          {
            "taskId": "${TASK2_ID}"
          }
        ]
      }
    }
  ]
}
EOM

车辆完成一次停车

当车辆完成停车后,Fleet Engine 必须收到通知。这会导致与停止点关联的所有任务都设置为“已关闭”状态。您可以通过驱动程序 SDK 或者使用 gRPC 或 REST 的服务器环境通知 Fleet Engine。请勿同时使用这两种方法以避免竞态条件和保持单一可信来源。

gRPC

以下示例展示了如何使用 Java gRPC 库通知 Fleet Engine 车辆已完成停靠。

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String VEHICLE_ID = "vehicle-8241890";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Vehicle settings
String vehicleName = "providers/" + PROJECT_ID + "/deliveryVehicles/" + VEHICLE_ID;
DeliveryVehicle deliveryVehicle = DeliveryVehicle.newBuilder()
    // This stop has been completed and is commented out to indicate it
    // should be removed from the list of vehicle journey segments.
    // .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()
    //    .setStop(VehicleStop.newBuilder()
    //        .setPlannedLocation(LocationInfo.newBuilder()
    //            .setPoint(LatLng.newBuilder()
    //                .setLatitude(37.7749)
    //                .setLongitude(122.4194)))
    //        .addTasks(TaskInfo.newBuilder().setTaskId(TASK1_ID))
    //        .setState(VehicleStop.State.ARRIVED)))
    // All other remaining stops marked as NEW.
    // The next stop could be marked as ENROUTE if the vehicle has begun
    // its journey to the next stop.
    .addRemainingVehicleJourneySegments(VehicleJourneySegment.newBuilder()  // Next stop
       .setStop(VehicleStop.newBuilder()
           .setPlannedLocation(LocationInfo.newBuilder()
               .setPoint(LatLng.newBuilder()
                   .setLatitude(37.3382)
                   .setLongitude(121.8863)))
           .addTasks(TaskInfo.newBuilder().setTaskId(TASK2_ID))
           .setState(VehicleStop.State.NEW)))
    .build();

// DeliveryVehicle request
UpdateDeliveryVehicleRequest updateDeliveryVehicleRequest =
  UpdateDeliveryVehicleRequest.newBuilder()  // no need for the header
      .setName(vehicleName)
      .setDeliveryVehicle(deliveryVehicle)
      .setUpdateMask(FieldMask.newBuilder()
          .addPaths("remaining_vehicle_journey_segments"))
      .build();

try {
  DeliveryVehicle updatedDeliveryVehicle =
      deliveryService.updateDeliveryVehicle(updateDeliveryVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需通知 Fleet Engine 有关停止在服务器环境中完成的操作,请对 UpdateDeliveryVehicle 进行 HTTP REST 调用:

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles/<id>?updateMask=remaining_vehicle_journey_segments`

<id> 是要更新任务排序的配送车辆的唯一标识符。它是您在创建车辆时指定的标识符。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 DeliveryVehicle 实体:

  • 必填字段:

    字段
    remaining_vehicle_journey_segments 已完成的经停点应该不会再出现在剩余车辆停靠站列表中。

  • 可选字段:

在更新时,系统将忽略实体中的所有其他字段。

示例 curl 命令:

    # Set JWT, PROJECT_ID, VEHICLE_ID, TASK1_ID, and TASK2_ID in the local
    # environment
    curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles/${VEHICLE_ID}?updateMask=remainingVehicleJourneySegments" \
      -H "Content-type: application/json" \
      -H "Authorization: Bearer ${JWT}" \
      --data-binary @- << EOM
    {
      "remainingVehicleJourneySegments": [
        {
          "stop": {
            "state": "NEW",
            "plannedLocation": {
              "point": {
                "latitude": 37.3382,
                "longitude": 121.8863
              }
            },
            "tasks": [
              {
                "taskId": "${TASK2_ID}"
              }
            ]
          }
        }
      ]
    }
    EOM

更新任务

大多数任务字段是不可变的。不过,您可以通过直接更新任务实体来修改状态、任务结果、任务结果时间、任务结果位置和属性。例如,如果任务尚未分配给车辆,您可以通过直接更新状态来关闭任务。

gRPC

这是通过 gRPC 更新任务的示例。

REST

这是一个通过 REST 更新任务的示例。

关闭任务

如需关闭已分配给车辆的任务,请通知 Fleet Engine 车辆已完成任务发生地点的停靠站,也可以将其从车辆停靠站列表中移除。为此,您可以设置其余车辆停靠站的列表,就像更新车辆的任务排序一样。

如果任务尚未分配车辆并且需要关闭,请将任务更新为“已关闭”。不过,您无法重新打开“已关闭”任务。

任务关闭不表示成功还是失败。它表示任务不再被视为正在进行中。对于舰队跟踪,请务必指明任务的实际结果,以便显示交付结果。

gRPC

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String TASK_ID = "task-8241890";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Task settings
String taskName = "providers/" + PROJECT_ID + "/tasks/" + TASK_ID;
Task task = Task.newBuilder()
  .setName(taskName)
  .setState(Task.State.CLOSED) // You can only directly CLOSE a
  .build();                    // task that is NOT assigned to a vehicle.

// Task request
UpdateTaskRequest updateTaskRequest =
  UpdateTaskRequest.newBuilder()  // No need for the header
      .setTask(task)
      .setUpdateMask(FieldMask.newBuilder().addPaths("state"))
      .build();

try {
  Task updatedTask = deliveryService.updateTask(updateTaskRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需将任务标记为在服务器环境中关闭,请对 UpdateTask 进行 HTTP REST 调用:

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks/<id>?updateMask=state`

<id> 是任务的唯一标识符

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

您必须在请求正文中包含 Task 实体:

  • 必填字段:

    字段
    state State.CLOSED

  • 可选字段:

    字段
    taskOutcome results.SUCCEEDED 或 Outcome.FAILED
    taskOutcomeTime 任务完成的时间。
    taskOutcomeLocation 完成任务的位置。Fleet Engine 默认将此字段设为上次的车辆位置,除非提供程序手动替换。

在更新时,系统将忽略实体中的所有其他字段。

示例 curl 命令:

    # Set JWT, PROJECT_ID, and TASK_ID in the local environment
    curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks/${TASK_ID}?updateMask=state,taskOutcome,taskOutcomeTime" \
      -H "Content-type: application/json" \
      -H "Authorization: Bearer ${JWT}" \
      --data-binary @- << EOM
    {
      "state": "CLOSED",
      "taskOutcome": "SUCCEEDED",
      "taskOutcomeTime": "$(date -u --iso-8601=seconds)"
    }
    EOM

设置任务结果和结果位置

任务关闭并不表示成功或失败,而是表示不再将任务视为正在进行中。对于舰队跟踪,请务必指明任务的实际结果,以便显示交付结果并为服务进行适当的结算。设置后,您将无法更改任务结果。不过,您可以在设置任务结果时间和任务结果位置后对其进行修改。

处于“已关闭”状态的任务可将其结果设置为“成功”或“失败”。Fleet Engine 仅对状态为“成功”的传送任务收费,

标记任务结果时,Fleet Engine 会自动使用最近一次的已知车辆位置填充任务结果位置。您可以替换此行为。

gRPC

您可以选择在设置结果时设置任务结果位置。设置位置可防止 Fleet Engine 将其设置为上次车辆位置的默认值。您还可以覆盖 Fleet Engine 稍后设置的任务结果位置。Fleet Engine 绝不会覆盖您提供的任务结果位置。您无法为未设置任务结果的任务设置任务结果位置。您可以在同一个请求中同时设置任务结果和任务结果位置。

以下示例展示了如何使用 Java gRPC 库将任务结果设置为 SUCCEEDED,并设置任务的完成位置:

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String TASK_ID = "task-8241890";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Task settings
String taskName = "providers/" + PROJECT_ID + "/tasks/" + TASK_ID;
Task task = Task.newBuilder()
  .setName(taskName)
  .setTaskOutcome(TaskOutcome.SUCCEEDED)
  .setTaskOutcomeTime(now())
  .setTaskOutcomeLocation(               // Grand Indonesia East Mall
    LocationInfo.newBuilder().setPoint(
      LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
  .build();

// Task request
UpdateTaskRequest updateTaskRequest =
  UpdateTaskRequest.newBuilder()  // No need for the header
      .setTask(task)
      .setUpdateMask(FieldMask.newBuilder().addPaths("task_outcome", "task_outcome_time", "task_outcome_location"))
      .build();

try {
  Task updatedTask = deliveryService.updateTask(updateTaskRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需在服务器环境中将任务标记为已完成,请对 UpdateTask 进行 HTTP REST 调用:

`PATCH https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks/<id>?updateMask=taskOutcome,taskOutcomeTime,taskOutcomeLocation`

<id> 是任务的唯一标识符

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须包含 Task 实体:

  • 必填字段:

    字段
    taskOutcome results.SUCCEEDED 或 Outcome.FAILED
    taskOutcomeTime 设置任务结果时的时间戳(来自提供程序)。这是完成任务的时间。

  • 可选字段:

    字段
    taskOutcomeLocation 完成任务的位置。Fleet Engine 默认将此字段设为上次的车辆位置,除非提供程序手动替换。

在更新时,系统将忽略实体中的所有其他字段。

示例 curl 命令:

# Set JWT, PROJECT_ID, and TASK_ID in the local environment
curl -X PATCH "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks/${TASK_ID}?updateMask=taskOutcome,taskOutcomeTime,taskOutcomeLocation" \
  -H "Content-type: application/json" \
  -H "Authorization: Bearer ${JWT}" \
  --data-binary @- << EOM
{
  "taskOutcome": "SUCCEEDED",
  "taskOutcomeTime": "$(date -u --iso-8601=seconds)",
  "taskOutcomeLocation": {
    "point": {
      "latitude": -6.195139,
      "longitude": 106.820826
    }
  }
}
EOM

重新递送运单

配送任务一旦创建,便无法更改其计划位置。如需重新配送运单,请关闭运单任务而不设置结果,然后使用更新后的计划位置创建新任务。创建新任务后,将任务分配给同一车辆。如需了解详情,请参阅关闭配送任务分配任务

使用喂食器和送货车辆

如果您全天使用供给车辆将货物运送到送货车辆,请将货物转移建模为配送车辆的预定停止任务。为了确保准确跟踪位置,请仅在已转移的货物装入送货车辆后为其分配配送任务。如需了解详情,请参阅预定停止

商店配送状态和其他元信息

配送任务完成后,任务状态和结果会记录在任务中。不过,您可能需要更新特定于该运单的其他元信息。如需存储可在 Fleet Engine 服务之外引用的其他元信息,请将与任务关联的 tracking_id 用作外部表中的键。

如需了解详情,请参阅任务的生命周期

查找车辆

您可以通过驱动程序 SDK 或者使用 gRPC 或 REST 的服务器环境中查找车辆。

gRPC

以下示例展示了如何使用 Java gRPC 库查找车辆:

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String VEHICLE_ID = "vehicle-8241890";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Vehicle request
String name = "providers/" + PROJECT_ID + "/deliveryVehicles/" + VEHICLE_ID;
GetDeliveryVehicleRequest getVehicleRequest = GetDeliveryVehicleRequest.newBuilder()  // No need for the header
    .setName(name)
    .build();

try {
  DeliveryVehicle vehicle = deliveryService.getDeliveryVehicle(getVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;
     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需在服务器环境中查找车辆,请对 GetVehicle 进行 HTTP REST 调用:

`GET https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles/<vehicleId>`

<id> 是任务的唯一标识符

<vehicleId> 是要查找的车辆的 ID。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须为空。

如果查找成功,响应正文将包含一个车辆实体。

示例 curl 命令:

# Set JWT, PROJECT_ID, and VEHICLE_ID in the local environment
curl -H "Authorization: Bearer ${JWT}" \
  "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles/${VEHICLE_ID}"

查找任务

您可以使用 gRPC 或 REST 从服务器环境中查找任务。驱动程序 SDK 不支持查询任务。

gRPC

以下示例展示了如何使用 Java gRPC 库查找任务:

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String TASK_ID = "task-8597549";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Task request
String taskName = "providers/" + PROJECT_ID + "/tasks/" + TASK_ID;
GetTaskRequest getTaskRequest = GetTaskRequest.newBuilder()  // No need for the header
    .setName(taskName)
    .build();

try {
  Task task = deliveryService.getTask(getTaskRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;

     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需在服务器环境中查询任务,请对 GetTask 进行 HTTP REST 调用:

`GET https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks/<taskId>`

<id> 是任务的唯一标识符

<taskId> 是要查找的任务的 ID。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

请求正文必须为空。

如果查找成功,响应正文将包含一个任务实体。

示例 curl 命令:

    # Set JWT, PROJECT_ID, and TASK_ID in the local environment
    curl -H "Authorization: Bearer ${JWT}" \
      "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks/${TASK_ID}"

按跟踪 ID 查找配送任务信息

您可以通过以下方式查询舰队任务信息,每种方式都有单独的用途:

  • 按任务 ID:由有权访问任务数据完整视图的舰队操作员等用户使用。
  • 通过跟踪 ID 进行:您的客户端软件使用该 ID 向最终用户提供有限的信息,例如,他们家里有包裹。

本部分介绍了如何按跟踪 ID 查找任务信息。如果要按任务 ID 查找任务,请转到查找任务

要按跟踪 ID 查找信息,您可以使用以下任一选项:

查询要求

  • 跟踪 ID 提供的装运信息遵循控制所跟踪位置的可见性中所述的可见性规则。

  • 使用 Fleet Engine 按跟踪 ID 查询运单信息。驱动程序 SDK 不支持按跟踪 ID 查找信息。为了通过 Fleet Engine 执行此操作,您需要使用服务器或浏览器环境。

  • 使用尽可能窄的令牌以限制安全风险。例如,如果您使用配送消费者令牌,任何 Fleet Engine Deliveries API 调用都仅返回与该最终用户(例如货运的发货人或收货人)相关的信息。响应中的所有其他信息都会被隐去。 如需详细了解令牌,请参阅创建 JSON Web 令牌 (JWT) 以进行授权

通过 gRPC 使用 Java 进行查找

以下示例展示了如何使用 Java gRPC 库按跟踪 ID 查找有关配送任务的信息。

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String TRACKING_ID = "TID-7449w087464x5";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Tasks request
String parent = "providers/" + PROJECT_ID;
GetTaskTrackingInfoRequest getTaskTrackingInfoRequest = GetTaskTrackingInfoRequest.newBuilder()  // No need for the header
    .setParent(parent)
    .setTrackingId(TRACKING_ID)
    .build();

try {
  TaskTrackingInfo taskTrackingInfo = deliveryService.getTaskTrackingInfo(getTaskTrackingInfoRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;

     case PERMISSION_DENIED:
       break;
  }
  return;
}

使用 HTTP 进行查询

如需从浏览器查询运单任务,请对 GetTaskTrackingInfo 进行 HTTP REST 调用:

`GET https://fleetengine.googleapis.com/v1/providers/<project_id>/taskTrackingInfo/<tracking_id>`

<tracking_id> 是与任务关联的跟踪 ID。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

如果查找成功,响应正文将包含一个 taskTrackingInfo 实体。

示例 curl 命令:

# Set JWT, PROJECT_ID, and TRACKING_ID in the local environment
curl -H "Authorization: Bearer ${JWT}" \
  "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/taskTrackingInfo/${TRACKING_ID}"

列出任务

您可以列出服务器或浏览器环境中的任务。驱动程序 SDK 不支持列出任务。

列出任务会请求获得对任务的广泛访问权限。列出任务仅适用于可信用户。发出列表任务请求时,请使用传送舰队读取器或传送超级用户身份验证令牌。

所列任务中的以下字段被隐去:

  • VehicleStop.planned_location
  • VehicleStop.state
  • VehicleStop.TaskInfo.taskId

大多数任务属性都可以对列出的任务进行过滤。如需了解过滤条件查询语法,请参阅 AIP-160。以下列表显示了可用于过滤的有效任务属性:

  • 属性
  • delivery_vehicle_id
  • state
  • planned_location
  • task_duration
  • task_outcome
  • task_outcome_location
  • task_outcome_location_source
  • task_outcome_time
  • tracking_id
  • 类型

根据 Google API 改进建议,使用以下字段格式:

字段类型 形式 示例
时间戳 RFC-3339 task_outcome_time = 2022-03-01T11:30:00-08:00
时长 后跟 s 的秒数 task_duration = 120s
枚举 字符串 state = CLOSED AND type = PICKUP
位置 point.latitudepoint.longitude planned_location.point.latitude > 36.1 AND planned_location.point.longitude < -122.0

如需查看过滤器查询运算符的完整列表,请参阅 AIP-160

如果未指定过滤条件查询,系统会列出所有任务。

任务列表会分页。可以在列表任务请求中指定页面大小。 如果指定了页面大小,则返回的任务数不大于指定的页面大小。如果没有页面大小,则使用合理的默认值。如果所请求的页面大小超过内部最大值,则使用内部最大值。

任务列表可以包含用于读取下一页结果的令牌。将页面令牌与上一个请求完全相同的请求使用页面令牌,以检索下一页任务。如果返回的页面令牌为空,则没有更多任务可供检索。

gRPC

以下示例展示了如何使用 Java gRPC 库列出 deliveryVehicleId 和任务属性的任务。成功的响应仍然可以为空。空响应表示没有任何任务与所提供的 deliveryVehicleId 相关联。

static final String PROJECT_ID = "my-delivery-co-gcp-project";
static final String TRACKING_ID = "TID-7449w087464x5";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Tasks request
String parent = "providers/" + PROJECT_ID;
ListTasksRequest listTasksRequest = ListTasksRequest.newBuilder()  // No need for the header
    .setParent(parent)
    .setFilter("delivery_vehicle_id = 123 AND attributes.foo = true")
    .build();

try {
  ListTasksResponse listTasksResponse = deliveryService.listTasks(listTasksRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
     case NOT_FOUND:
       break;

     case PERMISSION_DENIED:
       break;
  }
  return;
}

REST

如需通过浏览器列出任务,请对 ListTasks 进行 HTTP REST 调用:

`GET https://fleetengine.googleapis.com/v1/providers/<project_id>/tasks`

若要对列出的任务应用过滤条件,请添加“filter”网址参数,并将网址转义过滤器查询作为其值。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

如果查找成功,响应正文将包含结构如下的数据:

    // JSON representation
    {
      "tasks": [
        {
          object (Task)
        }
      ],
      "nextPageToken": string,
      "totalSize": integer
    }

成功的响应仍然可以为空。如果响应为空,则表示未找到符合指定过滤条件的任务。

示例 curl 命令:

    # Set JWT, PROJECT_ID, and VEHICLE_ID in the local environment
    curl -H "Authorization: Bearer ${JWT}" \
      "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/tasks?filter=state%20%3D%20OPEN%20AND%20delivery_vehicle_id%20%3D%20${VEHICLE_ID}"

列出送货车辆

您可以在服务器或浏览器环境中列出送货车辆。Driver SDK 不支持列出送货车辆。

列出送货车辆会请求对送货车辆的广泛访问权限,并且仅适用于受信任的用户。发出列表传送车辆请求时,请使用传送舰队读取器或传送超级用户身份验证令牌。

所列送货车辆的以下字段因其对响应大小的影响而被隐去:

  • CurrentRouteSegment
  • RemainingVehicleJourneySegments

您可以按 attributes 属性过滤列表送货车辆。例如,如需查询键为 my_key 且值为 my_value 的属性,请使用 attributes.my_key = my_value。如需查询多个属性,请使用逻辑 ANDOR 运算符联接查询,如 attributes.key1 = value1 AND attributes.key2 = value2 中所示。如需查看过滤条件查询语法的完整说明,请参阅 AIP-160

您可以使用 viewport 请求参数按位置过滤列出的送货车辆。viewport 请求参数使用两个边界坐标来定义视口:纬度和经度坐标对 high(东北)和 low(西南)。如果请求包含的高纬度在地理位置上低于低纬度,则会被拒绝。

默认情况下,系统会使用合理的页面大小对配送车辆列表进行分页。如果您指定了页面大小,请求仅返回限制指定的车辆数量或更少数量。如果所请求的页面大小超过内部最大值,则使用内部最大值。默认和最大页面大小均为 100 辆车。

送货车辆列表可以包含用于读取下一页结果的令牌。只有当更多配送车辆页面可供检索时,响应中才会显示页面令牌。如需检索下一页任务,请将页面令牌与上一个请求完全相同的请求使用页面令牌。

gRPC

以下示例展示了如何使用 Java gRPC 库列出特定区域中具有特定属性的送货车辆。成功响应仍然可以为空。发生这种情况时,表示具有指定属性的车辆不在指定的视口中。

static final String PROJECT_ID = "my-delivery-co-gcp-project";

DeliveryServiceBlockingStub deliveryService =
  DeliveryServiceGrpc.newBlockingStub(channel);

// Tasks request
String parent = "providers/" + PROJECT_ID;
ListDeliveryVehiclesRequest listDeliveryVehiclesRequest =
  ListDeliveryVehiclesRequest.newBuilder()  // No need for the header
      .setParent(parent)
      .setViewport(
            Viewport.newBuilder()
              .setHigh(LatLng.newBuilder()
                  .setLatitude(37.45)
                  .setLongitude(-122.06)
                  .build())
              .setLow(LatLng.newBuilder()
                  .setLatitude(37.41)
                  .setLongitude(-122.11)
                  .build())
      .setFilter("attributes.my_key = my_value")
      .build();

try {
  ListDeliveryVehiclesResponse listDeliveryVehiclesResponse =
      deliveryService.listDeliveryVehicles(listDeliveryVehiclesRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
      case NOT_FOUND:
          break;

      case PERMISSION_DENIED:
          break;
  }
  return;
}

REST

如需通过浏览器列出任务,请对 ListDeliveryVehicles 进行 HTTP REST 调用:

`GET https://fleetengine.googleapis.com/v1/providers/<project_id>/deliveryVehicles`

如需对列出的任务应用过滤条件,请添加“filter”网址参数,并将网址转义过滤器查询作为其值。

请求标头必须包含值为 Bearer <token> 的字段 Authorization,其中 <token>由 Fleet Engine 令牌工厂颁发的令牌

如果查找成功,响应正文将包含结构如下的数据:

// JSON representation
{
  "deliveryVehicles": [
    {
      object (DeliveryVehicle)
    }
  ],
  "nextPageToken": string,
  "totalSize": integer
}

成功的响应仍然可以为空。如果发生这种情况,则意味着未找到符合指定过滤条件查询和视口的送货车辆。

示例 curl 命令:

# Set JWT, PROJECT_ID, and VEHICLE_ID in the local environment
curl -H "Authorization: Bearer ${JWT}" \
  "https://fleetengine.googleapis.com/v1/providers/${PROJECT_ID}/deliveryVehicles?filter=attributes.my_key%20%3D%20my_value%20&viewport.high.latitude=37.45&viewport.high.longitude=-122.06&viewport.low.latitude=37.41&viewport.low.longitude=-122.11"

车队跟踪

您可以通过以下两种方式使用 Fleet Engine Deliveries API 启用舰队跟踪:

  • 首选:使用 JavaScript 舰队跟踪库。借助该库,您可以直观呈现在 Fleet Engine 中跟踪到的车辆位置和感兴趣的位置。它包含一个 JavaScript 地图组件(可轻松替换标准 google.maps.Map 对象)以及用于与 Fleet Engine 连接的数据组件。借助此组件,您可以从 Web 应用或移动应用提供可自定义的动画舰队跟踪体验。

  • 在 Fleet Engine Deliveries API 上实现自己的车队跟踪。

关键是要按跟踪 ID 查询舰队任务

日志记录

您可以将 Fleet Engine 设置为将 RPC 日志发送到 Cloud Logging。如需了解详情,请参阅日志记录

授权角色和令牌

管理车辆和任务生命周期以及各个用例的授权说明中所述,调用 Fleet Engine 需要使用已使用服务帐号凭据签名的 JSON Web 令牌进行身份验证。用于颁发这些令牌的服务帐号可能具有一个或多个角色,其中每个角色授予一组不同的权限。

如需了解详情,请参阅身份验证和授权

排查常见问题

如果您遇到任何问题,请查看以下部分以获取帮助。

弹性

Fleet Engine 不属于可信来源。如有必要,您负责恢复系统的状态,但不依赖于 Fleet Engine。

Fleet Engine 中的丢失状态

使用 Fleet Engine 时,请实现客户端,以便系统在出现故障时进行自我修复。例如,当 Fleet Engine 尝试更新车辆时,它可能会响应一条错误,指明车辆不存在。然后,客户端应以新状态重新创建车辆。虽然此问题很少出现,但请确保您的系统的弹性足以处理该问题。

在极不可能发生 Fleet Engine 灾难性故障的场景中,您可能需要重新创建大多数或所有车辆和任务。如果创建速率过高,某些请求可能会因配额问题而再次失败,因为系统会执行配额检查以避免拒绝服务 (DOS) 攻击。在这种情况下,可以使用退避策略进行重试,以放慢重新创建速度。

驾驶员应用中的丢失状态

如果驱动程序应用崩溃,必须在驱动程序 SDK 中重新创建当前状态。应用应尝试重新创建任务,以确保这些任务存在并恢复其当前状态。应用还应为驱动程序 SDK 重新创建并明确设置停止点列表。

常见问题解答

如果司机不按顺序地停下来完成任务怎么办?

在这种情况下,请先更新任务的顺序,然后照常继续,标记到达目的地、任务完成和其他详细信息。否则,系统可能会变得不一致,加大型文字广告可能会变得不正确,并且系统可能会报告意外错误。