Fleet Engine 使用入门(货运跟踪)

使用 Fleet Engine Deliveries API 为车队活动建模,以便完成第一英里和最后一英里交付。您可以通过使用适用于 Android 和 iOS 的驱动程序 SDK 来使用此 API,也可以通过 HTTP REST 或 gRPC 调用直接使用该 API。

初始设置

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

验证设置

创建服务帐号后,请确认您的设置已完成,然后便可以创建送货车辆。立即验证您的设置,确保您已解决设置项目时可能出现的常见授权问题。验证设置的方法有两种:

客户端库

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

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

数据结构

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

  • 用于运输货物的送货车辆。
  • 商品取货和送货任务。

您还可以使用任务来模拟驾驶员全天的休息和计划停靠点。

送货车辆

货运车辆负责将货物从仓库运送到送货地点,再从取货地点运送到仓库。在某些情况下,他们还可能将货物直接从自提点运送到送货地点。

使用驱动程序 SDK 在 Fleet Engine 中创建 DeliveryVehicle 对象,并发送位置信息更新以跟踪运送和舰队。

任务

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

  • 对于自提和送货,请分配配送任务
  • 如果司机无法工作的时间(例如必要的休息时间),请分配空闲任务
  • 对于保管箱或客户所在位置的非驾车任务,请分配计划内停止任务

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

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

配送任务

创建同时用于自提和送货的配送任务,并提供以下信息:

  • 自提或送货地点。
  • 跟踪编号或 ID。
  • 停留时间,用于说明完成任务、寻找停车位或步行到换乘位置所需的额外时间。
  • 唯一的任务 ID。请参阅任务 ID 准则

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

Android

iOS

不可用任务

不可用状态任务涵盖车辆无法接驾或配送的时间段,例如给车辆加油的休息时间或驾驶员的休息休息时间。

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

  • 广告插播时长。
  • (可选)广告插播时间点的位置。您无需提供具体位置,但这样可提供更准确的预计到达时间窗口。

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

Android

iOS

计划停止任务

创建计划停止任务,以模拟送货车辆需要执行的停靠站。例如,为特定位置每天安排的收集站创建计划停止任务,与同一位置的其他配送或上车点无关。您还可以为来自 Dropbox 的集合创建计划停靠任务,或者为服务中心和服务点的供给车辆换乘或停靠点建模。

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

Android

iOS

任务 ID 准则

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

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

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

  • 566c33d9-2a31-4b6a-9cd4-80ba1a0c643b
  • eabcfa39bf2767c9546c9273f747b4626e8cc44e9630d50f6d129013d38
  • 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 对象:

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

为每辆车使用独一无二的车辆 ID。除非原始车辆没有有效任务,否则请勿重复使用车辆 ID。

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

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

车辆类型

VehicleType 实体包含 VehicleType 的可选字段,该字段包含可指定为 AUTOTWO_WHEELERBICYCLEPEDESTRIANCategory 枚举。如果未设置该字段,则默认为 AUTO

所有车辆路线均使用车辆类型对应的 RouteTravelMode

车辆属性

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 种类型之一:string、number 和 bool。ListTasks API 包含一个 filter 字段,该字段可以将返回的 Task 实体限制为具有指定属性的实体。任务属性不会影响 Fleet Engine 路由行为。

请勿在属性中包含个人身份信息 (PII) 或其他敏感信息,因为这些属性可能会对用户可见。

管理车辆和任务生命周期

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

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

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

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

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

下图在通用层面说明了这些过程。它还显示了您的系统、客户端和 Fleet Engine 之间的标准关系。

使用 Deliveries API

管理客户端令牌

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

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

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

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

其他服务账号角色

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

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

模拟工作日

下表说明了配送和物流公司需要“第一公里”或“最后一公里”司机的工作日情况。贵公司在细节上可能会有所不同,但您可以查看如何模拟工作日。

时间Activity建模
当天开始后 24 小时内 调度员负责向送货车辆或路线分配货物。 您可以提前在 Fleet Engine 中创建货物交付、取货、休息时间及其他任务。例如,您可以创建货物自提任务货物交付任务计划不可用性计划停止

最终确定一组交付包以及它们的交付顺序后,将任务分配给车辆。
当天开始时间 登录 Driver 应用,司机即可在补货站开启新的一天。 初始化 Delivery Driver API。根据需要在 Fleet Engine 中创建交付车辆
司机将货物加载到送货车辆上,扫描货物。 如果未提前创建运单递送任务,请在扫描时创建运单递送任务
驾驶员确认要执行的任务的顺序。 如果未提前创建运送任务,请创建发货自提任务预定的不可用性预定的经停点
驱动程序离开库,并提交到下一项待完成的任务。 通过提交完成顺序来将所有任务或部分任务分配给车辆。
司机在运送包裹。 到达送货站后,执行与车辆抵达经停点相关的操作。 配送商品后,请关闭配送任务,并视需要关闭商店配送状态和其他元信息。 在站点完成所有任务后,在开始驾车前往下一站之前,执行与车辆完成一个经停点前往下一站的车辆相关的操作。
司机遇到供给车辆,将其他货物转移到配送车辆上。 供给车辆和送货车辆之间的中转点应建模为“预定经停点”

在转移和扫描货物后,创建配送任务(如果尚未创建)。然后,通过将任务分配给车辆更新任务排序来更新任务完成顺序。
司机收到自提请求通知。 接受自提请求后,请创建商品自提任务。然后,通过将任务分配给车辆更新任务排序来更新任务执行顺序。
中午 司机在午休。 如果某个营业地点与不可用任务相关联,请将其视为任何其他任务。执行与车辆已抵达一个经停点车辆完成经停点以及前往下一个经停点的车辆相关的操作。

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

位置信息更新的运作方式

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

  1. 使用驱动程序 SDK - 最简单的方法:Android 版iOS
  2. 使用自定义代码 - 如果位置信息通过后端进行中继,或者您使用 Android 或 iOS 以外的其他设备,则此类代码会很有用。

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

车辆停靠点和送货地点

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

送货地点是指商品配送或取货地点。往返送货地点可能需要从车辆经停点步行一段时间。

例如,当司机在向购物中心的商店配送货物时,送货车辆会停在购物中心的最近入口附近的停车场。这是车辆停靠站。然后,司机从停靠站走到该商店所在的购物中心内。这是送货地点。

为了让用户获得最佳运送跟踪体验,请考虑如何将运送任务分配给车辆停靠站,并注意,系统会向用户报告用于运输任务的剩余车辆停靠点数量,以帮助他们查看装运进度。

例如,如果司机需要向一栋办公楼进行多次配送,请考虑将所有配送任务分配给一个车辆停靠站。如果将每个配送任务分配给它自己的车辆停靠站,您的货物跟踪体验对用户的帮助会比较大,因为只有在车辆在到达目的地之前的有限数量的车辆停靠后,才能进行跟踪。如果在短时间内完成许多车辆停靠,用户没有足够的时间来跟踪交付进度。

使用移动 SDK

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

初始化 Delivery Driver API

在驱动程序 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

Fleet Engine 会根据用户指定的 ID 的 API 指南,忽略 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 应完成任务的时间范围。这不会影响路由行为。
    属性 自定义任务属性列表。每个属性必须具有唯一的键。

在创建时,实体中的所有其他字段都将被忽略。如果请求包含分配的 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

创建货运配送任务

通过驱动程序 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 应完成任务的时间范围。这不会影响路由行为。
    属性 自定义任务属性列表。每个属性必须具有唯一的键。

在创建时,实体中的所有其他字段都将被忽略。如果请求包含已分配的 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 中的字段相同。请参阅创建运单自提任务创建运单配送任务,了解各项任务的具体验证规则。

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

示例 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 按执行顺序排列任务的过程细分列表。列表中的第一个任务最先执行。
    remainingVehicleJourneySegments[i].stop 列表中任务 i 的停止点。
    remainingVehicleJourneySegments[i].stop.plannedLocation 计划的经停点位置。
    remainingVehicleJourneySegments[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 Output.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

设置任务结果和结果位置

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

处于 CLOSED 状态的任务可以将其结果设置为 SUCCEEDED 或 FAILED。Fleet Engine 仅对状态为 SUCCEEDED 的交付任务收费。

在标记任务结果时,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 Output.SUCCEEDED 或 Outcome.FAILED

  • 选填字段:

    字段价值
    taskOutcomeLocation 完成任务的位置。如果未设置,Fleet Engine 会默认将其设置为最近一次车辆位置。
    taskOutcomeTime 任务完成时的时间戳。

对于更新,将忽略实体中的所有其他字段。

示例 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 网络令牌 (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 不支持列出任务。

列出任务请求获得对任务的广泛访问权限。列出任务仅适用于可信用户。在发出列表任务请求时,使用 Delivery Fleet Reader 或 Delivery Super User 身份验证令牌。

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

  • 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.latitude”和“point.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 Deliveries API 调用仅返回与发货方或接收方相关的信息。响应中的所有其他信息都会被隐去。您负责对最终用户进行身份验证。此外,系统会根据已在完成的任务来过滤位置信息。在执行不可用任务期间,系统不会与最终用户共享任何位置信息。

日志记录

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

授权角色和令牌

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

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

排查常见问题

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

弹性

Fleet Engine 不属于可信来源。您负责在必要时恢复系统状态,而无需依赖 Fleet Engine。

Fleet Engine 中的丢失状态

使用 Fleet Engine 时,请实现客户端,以便系统在发生故障时自我修复。例如,当 Fleet Engine 尝试更新车辆时,可能会返回一条错误消息,表明车辆不存在。然后,客户端应重新创建处于新状态的车辆。虽然此问题很少发生,但请确保您的系统能够灵活处理。

在极其罕见的 Fleet Engine 发生灾难性故障的情况下,您可能需要重新创建大部分或所有车辆和任务。如果创建速率过高,某些请求可能会因为配额问题而再次失败,因为系统会实施配额检查,以避免拒绝服务 (DOS) 攻击。在这种情况下,请使用重试的退避策略来降低重新创建速率。

司机应用中的丢失状态

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

常见问题解答

如果司机为了不按顺序而停下车,该怎么办?

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