Fleet Engine 使用入门

借助 Fleet Engine On-Demand Rides and Deliveries API,您可以为“行程”和“订单进度”应用管理行程和车辆状态。它会处理驱动程序 SDK、Consumer SDK 和您的后端服务之间的事务;后端服务可通过进行 gRPCREST 调用与 Fleet Engine 通信。

前提条件

对于开发,请务必安装 Cloud SDK (gcloud) 并通过项目身份验证。

shell

gcloud auth login

您应该会看到如下所示的成功消息:

You are now logged in as [my-user@example.com].
Your current project is [project-id].  You ...

检查按需行程和送货解决方案 Fleet Engine API 是否配置正确。

shell

gcloud --project=project-id services enable fleetengine.googleapis.com

如果此命令导致错误,请与您的项目管理员和 Google 支持代表联系以获取访问权限。

日志记录

Fleet Engine 可以将有关其收到的 API 调用的日志消息写入 Google Cloud Platform 日志。如需简要了解如何读取和分析日志,请参阅 Cloud Logging 文档。

对于 2022 年 2 月 10 日之前创建的项目,日志记录功能可能默认处于停用状态。如需了解详情,请参阅日志记录文档

客户端库

我们使用几种常见的编程语言发布客户端库。与原始 REST 或 gRPC 相比,这些库有助于提供更好的开发者体验。如需了解如何获取服务器应用的客户端库,请参阅客户端库

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

身份验证与授权

您可以通过 Google Cloud 控制台配置“行程”和“订单进度”提供的功能。这些 API 和 SDK 需要使用通过从 Cloud Console 创建的服务帐号签名的 JSON Web 令牌。

Cloud 项目设置

如需设置云项目,请先创建项目,然后再创建服务帐号。

如需创建 Google Cloud 项目,请执行以下操作:

  1. 使用 Google Cloud 控制台创建 Google Cloud 项目。
  2. 使用 API 和服务信息中心,启用 Local Rides and Deliveries API。

服务帐号与一个或多个角色相关联。它们用于创建 JSON Web 令牌,以便根据角色授予一组不同的权限。通常,为了降低滥用的可能性,您可以创建多个服务帐号,并为每个服务帐号提供所需的最少角色集。

“行程和订单进度”页面使用以下角色:

角色说明
Fleet Engine Consumer SDK User

roles/fleetengine.consumerSdkUser
授予搜索车辆以及检索车辆和行程相关信息的权限。由具有此角色的服务帐号创建的令牌通常用于您的拼车或送餐消费者应用移动设备。
Fleet Engine Driver SDK 用户

roles/fleetengine.driverSdkUser
授予更新车辆位置和路线以及检索车辆和行程相关信息的权限。由具有此角色的服务帐号创建的令牌通常用于您的拼车或送餐司机应用移动设备。
Fleet Engine Service Super User

roles/fleetengine.serviceSuperUser
授予对所有车辆和行程 API 的权限。具有此角色的服务帐号创建的令牌通常使用您的后端服务器。

例如,为三个角色分别创建一个服务帐号,并为其分配各自的角色。

gcloud --project=project-id iam service-accounts create fleet-engine-consumer-sdk
gcloud projects add-iam-policy-binding project-id \
       --member=serviceAccount:fleet-engine-consumer-sdk@project-id.iam.gserviceaccount.com \
       --role=roles/fleetengine.consumerSdkUser

gcloud --project=project-id iam service-accounts create fleet-engine-driver-sdk
gcloud projects add-iam-policy-binding project-id \
       --member=serviceAccount:fleet-engine-driver-sdk@project-id.iam.gserviceaccount.com \
       --role=roles/fleetengine.driverSdkUser

gcloud --project=project-id iam service-accounts create fleet-engine-su
gcloud projects add-iam-policy-binding project-id \
       --member=serviceAccount:fleet-engine-su@project-id.iam.gserviceaccount.com \
       --role=roles/fleetengine.serviceSuperUser

Driver SDK 和 Consumer SDK 是围绕这些标准角色构建的。

或者,您也可以创建可将一组任意权限捆绑在一起的自定义角色。 每当缺少所需权限时,驱动程序 SDK 和消费者 SDK 就会显示错误消息。因此,我们强烈建议使用上述标准角色集,而不是使用自定义角色。

为方便起见,如果您需要为不受信任的客户端创建 JWT 令牌,可以将用户添加到 Service Account Token Creator 角色,这样他们就可以使用 gcloud 命令行工具创建令牌。

gcloud projects add-iam-policy-binding project-id \
       --member=user:my-user@example.com \
       --role=roles/iam.serviceAccountTokenCreator

其中,my-user@example.com 是用于使用 gcloud 进行身份验证的电子邮件地址 (gcloud auth list --format='value(account)')。

Fleet Engine 身份验证库

Fleet Engine 使用 JSON Web 令牌 (JWT) 限制对 Fleet Engine API 的访问。新的 Fleet Engine Auth 库(可在 GitHub 上提供)简化了 Fleet Engine JWT 的构建并安全地为其签名。

该库具有以下优势:

  • 简化创建 Fleet Engine 令牌的过程。
  • 提供除使用凭据文件以外的令牌签名机制(例如模拟服务帐号)。
  • 将已签名的令牌附加到从 gRPC 桩或 GAPIC 客户端发出的出站请求。

创建 JSON Web 令牌 (JWT) 以进行授权

如果不使用 Fleet Engine Auth 库,则需要直接在代码库中创建 JSON 网络令牌 (JWT)。您需要对 JWT 及其与 Fleet Engine 的关系有深入了解。因此,我们强烈建议使用 Fleet Engine Auth 库。

在 Fleet Engine 中,JSON Web 令牌 (JWT) 可提供短期身份验证,并确保设备只能修改其获得授权的车辆、行程或任务。JWT 包含标头和声明部分。标头部分包含要使用的私钥(从服务帐号获取)和加密算法等信息。声明部分包含令牌的创建时间、令牌存留时间、它声明访问权限的服务以及用于缩小访问权限范围的其他授权信息(例如车辆 ID)等信息。

JWT 标头部分包含以下字段:

字段说明
alg 要使用的算法。“RS256”。
typ 令牌的类型。“JWT”。
儿童 您的服务帐号的私钥 ID。您可以在服务帐号 JSON 文件的“private_key_id”字段中找到此值。请务必使用具有正确级别权限的服务账号中的密钥。

JWT 声明部分包含以下字段:

字段说明
iss 您的服务帐号的电子邮件地址。
sub 您的服务帐号的电子邮件地址。
aud 您的服务账号的 SERVICE_NAME,本例中为 https://fleetengine.googleapis.com/
iat 创建令牌时的时间戳,以自 1970 年 1 月 1 日 00:00:00 UTC(世界协调时间)起经过的秒数指定。允许 10 分钟偏差。如果时间戳过于久远或者是在将来,服务器可能会报告错误。
exp 令牌到期的时间戳,以自 1970 年 1 月 1 日 00:00:00 UTC(世界协调时间)起经过的秒数指定。如果时间戳在未来一小时以上,则请求失败。
授权 它可能包含“vehicleid”或“tripid”,具体取决于用例。

创建 JWT 令牌即为其签名。如需了解如何创建 JWT 并为其签名,请参阅不使用 OAuth 的服务帐号授权。 然后,您可以将已签名的令牌附加到 gRPC 调用或用于访问 Fleet Engine 的其他方法。

JWT 声明

创建 JWT 载荷时,请在授权部分添加一个额外的声明,并将键 vehicleidtripid 设置为执行调用的车辆 ID 或行程 ID 的值。

驾驶 SDK 始终使用 vehicleid 声明,无论是在行程还是车辆中操作。在进行修改之前,Fleet Engine 后端可确保车辆已与请求的行程相关联。

Consumer SDK 始终使用 tripid 声明。

拼车或送餐服务提供商应使用带“*”的 vehicleidtripid 来匹配所有车辆和行程。请注意,JWT 可以同时包含这两个令牌(即使不需要),这可能会简化令牌签名的实现。

JWT 使用场景

以下是提供商服务器的示例令牌:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "private_key_id_of_provider_service_account"
}
.
{
  "iss": "provider@yourgcpproject.iam.gserviceaccount.com",
  "sub": "provider@yourgcpproject.iam.gserviceaccount.com",
  "aud": "https://fleetengine.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600,
  "authorization": {
     "vehicleid": "*",
     "tripid": "*"
   }
}

下面显示了消费者应用的示例令牌:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "private_key_id_of_consumer_service_account"
}
.
{
  "iss": "consumer@yourgcpproject.iam.gserviceaccount.com",
  "sub": "consumer@yourgcpproject.iam.gserviceaccount.com",
  "aud": "https://fleetengine.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600,
  "authorization": {
     "tripid": "trip_54321"
   }
}

下面显示了 Driver app 的示例令牌:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "private_key_id_of_driver_service_account"
}
.
{
  "iss": "driver@yourgcpproject.iam.gserviceaccount.com",
  "sub": "driver@yourgcpproject.iam.gserviceaccount.com",
  "aud": "https://fleetengine.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600,
  "authorization": {
     "vehicleid": "driver_12345"
   }
}
  • 对于标头中的 kid 字段,请指定服务帐号的私钥 ID。您可以在服务帐号 JSON 文件的 private_key_id 字段中找到此值。
  • 对于 isssub 字段,请指定服务帐号的电子邮件地址。您可以在服务帐号 JSON 文件的 client_email 字段中找到此值。
  • 对于 aud 字段,请指定 https://SERVICE_NAME/
  • 对于 iat 字段,使用创建令牌时的时间戳,指定为自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的秒数。允许 10 分钟偏差。如果时间戳离现在太远,或者是未来的时间,服务器可能会报告错误。
  • 对于 exp 字段,使用令牌到期时的时间戳,以自 1970 年 1 月 1 日 00:00:00 UTC 以来的秒数指定。允许的最大值为 iat + 3600。

为要传递给移动设备的 JWT 签名时,请务必使用具有 Driver 或 Consumer SDK 角色的服务帐号。否则,移动设备将能够更改不应具有的状态。

同样,在为要用于特权调用的 JWT 签名时,请务必使用具有 Super User 角色的服务帐号。否则,操作将失败。

生成 JWT 以进行测试

在测试时,从终端生成令牌会很有帮助。

如需执行以下步骤,您的用户帐号必须具有 Service Account Token Creator 角色:

gcloud projects add-iam-policy-binding project-id \
       --member=user:my-user@example.com \
       --role=roles/iam.serviceAccountTokenCreator

创建一个名为 unsigned_token.json 且包含以下内容的新文件。iat 属性是当前时间(从纪元起经过的秒数),可通过在终端中运行 date +%s 来检索。exp 属性是纪元后的到期时间(以秒为单位),可通过在 iat 上添加 3600 计算得出。到期时间不能超过一个小时。

{
  "aud": "https://fleetengine.googleapis.com/",
  "iss": "super-user-service-account@project-id.iam.gserviceaccount.com",
  "sub": "super-user-service-account@project-id.iam.gserviceaccount.com",
  "iat": iat,
  "exp": exp,
  "authorization": {
     "vehicleid": "*",
     "tripid": "*"
   }
}

然后运行以下 gcloud 命令,以代表您超级用户服务帐号对令牌签名:

gcloud beta iam service-accounts sign-jwt --iam-account=super-user-service-account@project-id.iam.gserviceaccount.com unsigned_token.json signed_token.jwt

已签名的 Base64 编码的 JWT 现在应存储在文件 signed_token.jwt 中。该令牌在接下来的一小时内有效。

您现在可以通过对 List Vehicles REST 端点运行 curl 命令来测试令牌:

curl -X GET "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles" -H "Authorization: Bearer $(cat signed_token.jwt)"

车辆及其生命周期

车辆是表示司机-车辆对的实体。目前,无法分别跟踪“驾驶员”和“车辆”。拼车或配送提供商使用提供商 ID(必须与用于调用 Fleet Engine API 的服务帐号的 Google Cloud 项目的项目 ID 相同)和拼车或配送提供商拥有的车辆 ID 创建车辆。

系统会自动删除七天后未通过 UpdateVehicle 更新的车辆。使用已存在的提供方 ID/车辆 ID 对调用 CreateVehicle 是错误的。车辆更新频率不高的情况可通过以下两种方式处理:频繁使用预期的提供方 ID/车辆 ID 对调用 CreateVehicle 并舍弃错误(如果车辆已存在);或者在 UpdateVehicle 返回并返回 NOT_FOUND 错误后调用 CreateVehicle

车辆位置信息更新

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

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

车辆类型

Vehicle 实体包含 VehicleType 的必填字段,其中包含 Category 枚举,可指定为 AUTOTAXITRUCKTWO_WHEELERBICYCLEPEDESTRIAN。车辆类型可用作 SearchVehiclesListVehicles 中的过滤条件。

如果类别设置为 AUTOTWO_WHEELERBICYCLEPEDESTRIAN,车辆的所有路线都将使用相应的 RouteTravelMode。如果该类别设置为 TAXITRUCK,则路由的处理方式与 AUTO 模式相同。

车辆属性

Vehicle 实体包含重复字段 VehicleAttribute。Fleet Engine 不会解读这些属性。SearchVehicles API 包含一个字段,要求匹配的 Vehicles 必须包含设置为指定值的所有所含属性。

请注意,属性字段是对 Vehicle 消息中几个其他受支持的字段(例如 vehicle_typesupported_trip_types)的补充。

车辆剩余的航点

车辆实体包含一个名为 waypoints (RPC | REST) 的重复字段 TripWaypoint(RPC | REST)。此字段包含行程中剩余的航点(按车辆到达这些航点的顺序排列)。Fleet Engine 在将行程分配给车辆时计算此字段,并在行程状态发生变化时更新此字段。这些航点可通过 TripId 字段和 WaypointType 字段进行标识。

放宽车辆对比赛的资格要求

通常,拼车或送餐服务提供商的服务负责将行程请求与车辆进行匹配。该服务可以使用车辆属性将车辆包含在大量搜索中。例如,提供商可以实现一组与车辆提供的福利或功能级别相对应的属性。例如,三个级别可以是一组具有布尔值的属性:is_bronze_levelis_silver_levelis_gold_level。一辆车符合所有这三种方式。当 Fleet Engine 收到需要白银级功能的行程请求时,搜索会包含该车辆。以这种方式使用属性包括提供各种功能的车辆。

您可以通过以下两种方式更新车辆属性。一个是 UpdateVehicle API。使用此 API 时,整套车辆属性会设为相应的值。无法只更新单个属性。 另一种方法是 UpdateVehicleAttributes API。此方法仅接受要更新的属性。请求中包含的属性将设置为新值或已添加;未指定的属性不会更改。

方法指南:制造车辆

必须为车队中要跟踪的每辆车创建一个 Vehicle 实体。

结合使用 CreateVehicle 端点和 CreateVehicleRequest 创建车辆。

Vehicleprovider_id 必须是 Google Cloud 项目的 ID(例如 my-on-demand-project),该项目包含将用于调用 Fleet Engine 的服务帐号。请注意,虽然多个服务帐号可以访问同一拼车或送餐提供方的 Fleet Engine,但 Fleet Engine 目前不支持来自访问同一 Vehicles 的多个 Google Cloud 项目中的服务帐号。

您可以创建处于 OFFLINEONLINE 状态的 Vehicle。如果已创建 ONLINE,系统可能会立即返回它以响应 SearchVehicles 查询。

初始 last_location 可能包含在 CreateVehicle 调用中。在允许的情况下,不应在没有 last_location 的情况下以 ONLINE 状态创建 Vehicle

如需详细了解车辆类型字段,请参阅交通工具类型

如需详细了解属性字段,请参阅车辆属性

CreateVehicle 返回的值是已创建的 Vehicle 实体。

示例

shell

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles?vehicleId=vid-8241890" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
    "vehicleState": "OFFLINE",
    "supportedTripTypes": ["EXCLUSIVE"],
    "maximumCapacity": 4,
    "vehicleType": {"category": "AUTO"},
    "attributes": [{"key": "on_trip", "value": "false"}]
}
EOM

请参阅 providers.vehicles.create 参考。

Java

static final String PROJECT_ID = "project-id";

VehicleServiceBlockingStub vehicleService =
    VehicleService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
Vehicle vehicle = Vehicle.newBuilder()
    .setVehicleState(VehicleState.OFFLINE)  // Initial state
    .addSupportedTripTypes(TripType.EXCLUSIVE)
    .setMaximumCapacity(4)
    .setVehicleType(VehicleType.newBuilder().setCategory(VehicleType.Category.AUTO))
    .addAttributes(VehicleAttribute.newBuilder()
        .setKey("on_trip").setValue("false"))  // Opaque to the Fleet Engine
    // Add .setBackToBackEnabled(true) to make this vehicle eligible for trip
    // matching while even if it is on a trip.  By default this is disabled.
    .build();

CreateVehicleRequest createVehicleRequest =
    CreateVehicleRequest.newBuilder()  // no need for the header
        .setParent(parent)
        .setVehicleId("vid-8241890")  // Vehicle ID assigned by Rideshare or Delivery Provider
        .setVehicle(vehicle)  // Initial state
        .build();

// In this case, the Vehicle is being created in the OFFLINE state and
// no initial position is being provided.  When the Driver App checks
// in with the Rideshare or Delivery Provider, the state can be set to ONLINE and
// the Driver App will update the Vehicle Location.

try {
  Vehicle createdVehicle =
      vehicleService.createVehicle(createVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case ALREADY_EXISTS:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}
// If no Exception, Vehicle created successfully.

用于车辆创建的 Google Cloud Platform 日志

收到对 CreateVehicle 端点的调用时,Fleet Engine API 会通过 Google Cloud Platform 日志写入日志条目。日志条目包含有关 CreateVehicle 请求中值的信息。如果调用成功,它还包含有关返回的 Vehicle 的信息。

shell

gcloud --project=project-id logging read --freshness=1h '
  jsonPayload.request.vehicleId="vid-8241890"
  jsonPayload.@type="type.googleapis.com/maps.fleetengine.v1.CreateVehicleLog"
'

应输出类似于以下内容的记录:

---
insertId: c2cf4d3a180251c1bdb892137c14f022
jsonPayload:
  '@type': type.googleapis.com/maps.fleetengine.v1.CreateVehicleLog
  request:
    vehicle:
      attributes:
      - key: on_trip
        value: 'false'
      maximumCapacity: 4
      state: VEHICLE_STATE_OFFLINE
      supportedTrips:
      - EXCLUSIVE_TRIP
      vehicleType:
        vehicleCategory: AUTO
    vehicleId: vid-8241890
  response:
    attributes:
    - key: on_trip
      value: 'false'
    availableCapacity: 4
    currentRouteSegmentHandle: AdSiwAwCO9gZ7Pw5UZZimOXOo41cJTjg/r3SuwVPQmuuaV0sU3+3UCY+z53Cl9i6mWHLoCKbBt9Vsj5PMRgOJ8zX
    maximumCapacity: 4
    name: providers/project-id/vehicles/vid-8241890
    state: VEHICLE_STATE_OFFLINE
    supportedTrips:
    - EXCLUSIVE_TRIP
    vehicleType:
      vehicleCategory: AUTO
labels:
  vehicle_id: vid-8241890
logName: projects/project-id/logs/fleetengine.googleapis.com%2Fcreate_vehicle
receiveTimestamp: '2021-09-22T03:25:16.361159871Z'
resource:
  labels:
    location: global
    resource_container: projects/project-id
  type: fleetengine.googleapis.com/Fleet
timestamp: '2021-09-22T03:25:15.724998Z'

用于车辆创建的 Cloud Pub/Sub 通知

创建新车辆时,Fleet Engine API 通过 Cloud Pub/Sub 发布通知。若要接收这些通知,请按照此处的说明进行操作。

方法:更新车辆的位置信息

如果不使用驾驶 SDK 更新车辆的位置信息,您可以直接使用车辆的位置信息来调用 Fleet Engine。对于任何活跃车辆,Fleet Engine 都预计每分钟至少更新一次,且最多每 5 秒更新一次。这些更新只需要 Fleet Engine Driver SDK 用户权限。

示例

shell

curl -X PUT \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles/vid-8241890?updateMask=last_location" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
    "supplementalLocation": {"latitude": 12.1, "longitude": 14.5},
    "supplementalLocationTime": "$(date -u --iso-8601=seconds)",
    "supplementalLocationSensor": "CUSTOMER_SUPPLIED_LOCATION",
    "supplementalLocationAccuracy": 15
}
EOM

请参阅 providers.vehicles.update 参考文档。

Java

static final String PROJECT_ID = "project-id";
static final String VEHICLE_ID = "vid-8241890";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String vehicleName = "providers/" + PROJECT_ID + "/vehicles/" + VEHICLE_ID;
Vehicle updatedVehicle = Vehicle.newBuilder()
    .setLastLocation(VehicleLocation.newBuilder()
        .setSupplementalLocation(LatLng.newBuilder()
            .setLatitude(37.3382)
            .setLongitude(121.8863))
        .setSupplementalLocationTime(now())
        .setSupplementalLocationSensor(LocationSensor.CUSTOMER_SUPPLIED_LOCATION)
        .setSupplementalLocationAccuracy(DoubleValue.of(15.0)))  // Optional)
    .build();

UpdateVehicleRequest updateVehicleRequest = UpdateVehicleRequest.newBuilder()
    .setName(vehicleName)
    .setVehicle(updatedVehicle)
    .setUpdateMask(FieldMask.newBuilder()
        .addPaths("last_location"))
    .build();

try {
  Vehicle updatedVehicle =
      vehicleService.updateVehicle(updateVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      // Most implementations will call CreateVehicle in this case
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}
// If no Exception, Vehicle updated successfully.

方法:更新其他车辆字段

对车辆状态其他属性的更新频率低于位置更新。更新 last_location 以外的特性需要 Fleet Engine 超级用户权限。

UpdateVehicleRequest 包含一个 update_mask,用于指示要更新的字段。该字段的行为与字段掩码的 Protobuf 文档中所述。

车辆属性中所述,更新 attributes 字段需要写入所有要保留的属性。在 UpdateVehicle 调用中,无法只更新一个键值对的值。如需更新特定属性的值,可以使用 UpdateVehicleAttributes API。

示例

此示例启用了 back_to_back

shell

curl -X PUT \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles/vid-8241890?updateMask=vehicle_state,attributes,back_to_back_enabled" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
    "vehicleState": "ONLINE",
    "attributes": [
      {"key": "on_trip", "value": "true"},
      {"key": "cash_only", "value": "false"}
    ],
    "backToBackEnabled": true
}
EOM

请参阅 providers.vehicles.update 参考文档。

Java

static final String PROJECT_ID = "project-id";
static final String VEHICLE_ID = "vid-8241890";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String vehicleName = "providers/" + PROJECT_ID + "/vehicles/" + VEHICLE_ID;
Vehicle updatedVehicle = Vehicle.newBuilder()
    .setVehicleState(VehicleState.ONLINE)
    .addAllAttributes(ImmutableList.of(
        VehicleAttribute.newBuilder().setKey("on_trip").setValue("true").build(),
        VehicleAttribute.newBuilder().setKey("cash_only").setValue("false").build()))
    .setBackToBackEnabled(true)
    .build();

UpdateVehicleRequest updateVehicleRequest = UpdateVehicleRequest.newBuilder()
    .setName(vehicleName)
    .setVehicle(updatedVehicle)
    .setUpdateMask(FieldMask.newBuilder()
        .addPaths("vehicle_state")
        .addPaths("attributes")
        .addPaths("back_to_back_enabled"))
    .build();

// Attributes and vehicle state are being updated, so both are
// included in the field mask.  Note that of on_trip were
// not being updated, but rather cash_only was being changed,
// the desired value of "on_trip" would still need to be written
// as the attributes are completely replaced in an update operation.

try {
  Vehicle updatedVehicle =
      vehicleService.updateVehicle(updateVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      // Most implementations will call CreateVehicle in this case
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}
// If no Exception, Vehicle updated successfully.

关于车辆更新的 Google Cloud Platform 日志

收到对 UpdateVehicle 端点的调用时,Fleet Engine API 会通过 Google Cloud Platform 日志写入日志条目。日志条目包含有关 UpdateVehicle 请求中值的信息。如果调用成功,它还包含有关返回的 Vehicle 的信息。

shell

gcloud --project=project-id logging read --freshness=1h '
  jsonPayload.request.vehicleId="vid-8241890"
  jsonPayload.@type="type.googleapis.com/maps.fleetengine.v1.UpdateVehicleLog"
'

针对车辆更新的 Cloud Pub/Sub 通知

Fleet Engine API 会在现有车辆更新时通过 Cloud Pub/Sub 发布通知。若要接收这些通知,请按照此处的说明进行操作。

方法:搜索车辆

Fleet Engine 支持搜索车辆。借助 SearchVehicles API,您可以查找附近最适合执行诸如约车或送餐等任务的空闲司机。SearchVehicles API 会返回一个按任务排序的司机列表,将任务属性与车队中的车辆属性相匹配。如需了解详情,请参阅查找附近的司机

示例

搜索可用车辆时,Fleet Engine 默认排除有效行程的车辆。拼车或送餐服务提供商的服务需要在搜索请求中明确包含相应服务。以下示例展示了如何在搜索与从印尼东购物中心到雅加达巴莱西当会展中心的行程匹配的车辆时包含这些车辆。

shell

首先,更新我们在之前步骤中创建的车辆的位置信息,使其符合条件。在现实世界中,这由在车辆内的 Android 或 iOS 设备上运行的 Driver SDK 来完成。

curl -X PUT \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles/vid-8241890?updateMask=last_location,attributes" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "lastLocation": {
    "updateTime": "$( date -u +"%Y-%m-%dT%H:%M:%SZ" )",
    "location": {
      "latitude": "-6.195139",
      "longitude": "106.820826"
    }
  },
  "attributes": [{"key": "on_trip", "value": "false"}]
}
EOM

执行搜索应该至少能找到那辆车。

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles:search" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "pickupPoint": {
    "point": {"latitude": "-6.195139", "longitude": "106.820826"}
  },
  "dropoffPoint": {
    "point": {"latitude": "-6.1275", "longitude": "106.6537"}
  },
  "pickupRadiusMeters": 2000,
  "count": 10,
  "minimumCapacity": 2,
  "tripTypes": ["EXCLUSIVE"],
  "vehicleTypes": [{"category": "AUTO"}],
  "filter": "attributes.on_trip=\"false\"",
  "orderBy": "PICKUP_POINT_ETA",
  "includeBackToBack": true
}
EOM

请参阅 providers.vehicles.search 参考文档。

Java

static final String PROJECT_ID = "project-id";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
SearchVehiclesRequest searchVehiclesRequest = SearchVehiclesRequest.newBuilder()
    .setParent(parent)
    .setPickupPoint( // Grand Indonesia East Mall
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
    .setDropoffPoint( // Balai Sidang Jakarta Convention Center
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.213796).setLongitude(106.807195)))
    .setPickupRadiusMeters(2000)
    .setCount(10)
    .setMinimumCapacity(2)
    .addTripTypes(TripType.EXCLUSIVE)
    .addVehicleTypes(VehicleType.newBuilder().setCategory(VehicleType.Category.AUTO))
    .setFilter("attributes.on_trip=\"false\"")
    .setOrderBy(VehicleMatchOrder.PICKUP_POINT_ETA)
    .setIncludeBackToBack(true) // Fleet Engine includes vehicles that are en route.
    .build();

// Error handling
// If matches are returned and the authentication passed, the request completed
// successfully

try {
  SearchVehiclesResponse searchVehiclesResponse =
      vehicleService.searchVehicles(searchVehiclesRequest);

  // Search results: Each vehicle match contains a vehicle entity and information
  // about the distance and ETA to the pickup point and dropoff point.
  List<VehicleMatch> vehicleMatches = searchVehiclesResponse.getMatchesList();
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

车辆过滤查询

SearchVehiclesListVehicles 支持使用过滤查询对车辆属性进行过滤。如需了解过滤条件查询语法,请参阅 AIP-160 中的示例。

请注意,过滤条件查询仅支持对车辆属性进行过滤,而不能用于其他字段。过滤条件查询充当具有其他约束条件的 AND 子句,例如 SearchVehiclesRequest 中的 minimum_capacityvehicle_types

方法:列出车辆

SearchVehicles 经过优化,可以非常快速地按排名顺序查找少量车辆,主要用于查找附近最适合执行任务的驾驶员。不过,有时,即使必须对结果进行分页,您也需要查找满足某些条件的所有车辆。ListVehicles 是专为该用例设计的。

借助 ListVehicles API,您可以查找满足某些特定请求选项的所有车辆。ListVehicles API 会返回项目中符合某些要求的车辆列表(已分页)。

如需按车辆属性进行过滤,请参阅车辆过滤查询

示例

以下示例使用 filter 字符串对 vehicle_type 和属性执行过滤。

shell

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles:list" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "vehicleTypes": [{"category": "AUTO"}],
  "filter": "attributes.on_trip=\"false\"",
}
EOM

请参阅 providers.vehicles.list 参考文档。

Java

static final String PROJECT_ID = "project-id";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
ListVehiclesRequest listVehiclesRequest = ListVehiclesRequest.newBuilder()
    .setParent(parent)
    .addTripTypes(TripType.EXCLUSIVE)
    .addVehicleTypes(VehicleType.newBuilder().setCategory(VehicleType.Category.AUTO))
    .setFilter("attributes.on_trip=\"false\"")
    .setIncludeBackToBack(true) // Fleet Engine includes vehicles that are en route.
    .build();

// Error handling
// If matches are returned and the authentication passed, the request completed
// successfully

try {
  ListVehiclesResponse listVehiclesResponse =
      vehicleService.listVehicles(listVehiclesRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

行程及其生命周期

Trip API 和生命周期与 Vehicle API 和生命周期类似。拼车提供商负责使用 Fleet Engine 界面创建行程。Fleet Engine 提供 RPC 服务、 TripService 和 REST 资源 provider.trips。这些接口支持行程实体创建、信息请求、搜索功能和更新功能。

Trip 有一个状态字段,用于跟踪其在整个生命周期中的进度。值从 NEW 移至 COMPLETE,再加 CANCELEDUNKNOWN_TRIP_STATUS。请参阅适用于 RPC 的 trip_status适用于 REST 的 TripStatus

  • NEW
  • ENROUTE_TO_PICKUP
  • ARRIVED_AT_PICKUP
  • ENROUTE_TO_INTERMEDIATE_DESTINATION
  • ARRIVED_AT_INTERMEDIATE_DESTINATION
  • ENROUTE_TO_DROPOFF
  • COMPLETE

您的服务可以从上述任一状态更新前往CANCELED的行程。 当您的服务创建行程时,引擎会将状态设置为 NEWvehicle_id 是可选的。与车辆一样,该服务会在不更新的情况下自动删除行程 7 天。如果您的服务尝试使用已存在的 ID 创建行程,系统将返回错误。如果某个行程处于 COMPLETECANCELED 以外的状态,则会被视为“活跃”。在 Vehicle 实体和 SearchTripsRequestactive_trips 字段中,这种区别非常重要。

仅当状态为 NEWCANCELED 时,服务才能更改分配给行程的 vehicle_id。如果驾驶员在途中取消行程,必须在更改或清除 vehicle_id 之前将行程状态设置为 NEWCANCELED

在实现背靠背行程支持时,状态非常重要。借助此支持,提供商能够在车辆处于活跃行程期间将新行程分配给车辆。用于创建背靠背行程的代码与单个行程相同,并使用相同的车辆 ID。Fleet Engine 会将新行程的出发地和目的地添加到车辆的航点。如需详细了解往返行程,请参阅创建多航点行程

行程剩余航点

Trip 实体包含一个名为 remainingWaypoints (RPC | REST) 的重复 TripWaypoint(RPC | REST) 字段。此字段包含车辆需要在此行程的最终下车点之前按顺序行驶的所有航点。它会根据车辆的剩余航点进行计算。在“背靠背”和“拼车”用例中,此列表包含将在此次行程之前遍历的其他行程中的航点,但排除此次行程之后的所有航点。列表中的航点可通过其 TripIdWaypointType 进行标识。

行程状态与车辆剩余航点之间的关系

当 Fleet Engine 收到行程状态更改请求时,系统会更新车辆的剩余航点 (RPC | REST)。当 tripStatus(RPC | REST) 从其他状态更改为 ENROUTE_TO_XXX 后,上一个航点将从车辆的剩余航点列表中移除。也就是说,当行程状态从 ENROUTE_TO_PICKUP 更改为 ARRIVED_AT_PICKUP 时,行程的上车点仍位于车辆的剩余航点列表中,但当行程状态更改为 ENROUTE_TO_INTERMEDIATE_DESTINATION 或 ENROUTE_TO_DROPOFF 时,该行程的上车点将从车辆的剩余航点中移除。

这对 ARRIVED_AT_INTERMEDIATE_DESTINATION 和 ENROUTE_TO_INTERMDEDIATE_DESTINATION 的效果相同。当 ARRIVED_AT_INTERMEDIATE_DESTINATION 时,当前的中间目的地不会从车辆的剩余航点列表中移除,直到车辆报告正在前往下一个航点。

行程状态更改为 COMPLETED 后,此行程中的航点将不会包含在车辆的剩余航点列表中。

方法:创建行程

必须创建 Trip 实体,才能跟踪每个行程请求并将其与车队中的车辆相匹配。将 CreateTrip 端点与 CreateTripRequest 搭配使用以创建行程。

以下属性是创建行程所必需的:

  • parent - 一个字符串,其中包含创建 Google Cloud 项目时创建的提供方 ID。
  • trip_id - 拼车提供商创建的字符串。
  • trip - 包含描述行程的基本元数据的容器。
    • trip_type - 表示行程中是否有其他乘客在同一车辆 (SHARED) 中来自同一出发地和目的地,或者只有单方乘客 (EXCLUSIVE)。
    • pickup_point - 表示行程出发地的 TerminalLocation。参阅 RPC 参考文档REST 参考文档

创建行程时,您可以提供 number_of_passengersdropoff_pointvehicle_id。虽然这些字段并非必填字段,但如果您提供了它们,它们就会被保留。所有其他行程字段均会被忽略。例如,所有行程都从 NEWtrip_status 开始,即使您在创建请求中传入 CANCELEDtrip_status 也是如此。

示例

以下示例创建了一个前往 Grand Indonesia East Mall 购物中心的行程。此趟行程仅面向两名乘客。Tripprovider_id 必须与项目 ID 相同。在此示例中,拼车提供商创建了 Google Cloud 项目 project-id。此项目必须具有用于调用 Fleet Engine 的服务帐号。行程的状态为 NEW

之后,在将行程与车辆进行匹配后,该服务可以调用 UpdateTrip 并在行程分配给车辆时更改 vehicle_id

shell

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/trips?tripId=tid-1f97" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "tripType": "EXCLUSIVE",
  "numberOfPassengers": 2,
  "pickupPoint": {
    "point": {"latitude": "-6.195139", "longitude": "106.820826"}
  },
  "dropoffPoint": {
    "point": {"latitude": "-6.1275", "longitude": "106.6537"}
  }
}
EOM

请参阅 providers.trips.create 参考。

Java

static final String PROJECT_ID = "project-id";

TripServiceBlockingStub tripService = TripService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
Trip trip = Trip.newBuilder()
    .setTripType(TripType.EXCLUSIVE) // Use TripType.SHARED for carpooling
    .setPickupPoint(                 // Grand Indonesia East Mall
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
    // Provide the number of passengers if available.
    .setNumberOfPassengers(2)
    // Provide the drop-off point if available.
    .setDropoffPoint(
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.1275).setLongitude(106.6537)))
    .build();

CreateTripRequest createTripRequest =
    CreateTripRequest.newBuilder()  // no need for the header
        .setParent(parent)
        .setTripId("tid-1f97")  // Trip ID assigned by the Provider
        .setTrip(trip)              // Initial state
        .build();

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

try {
  Trip createdTrip =
      tripService.createTrip(createTripRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case ALREADY_EXISTS:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

创建行程的 Google Cloud Platform 日志

收到对 CreateTrip 端点的调用时,Fleet Engine API 会使用 Google Cloud 平台日志写入日志条目。日志条目包含有关 CreateTrip 请求中值的信息。如果调用成功,它还包含有关返回的 Trip 的信息。

方法:更新行程

行程实体包含一些字段,这些字段支持通过服务进行跟踪,以及由 Driver SDK 和 Consumer SDK 报告行程进度。如需更新属性,请使用 UpdateTripRequest 消息。这将根据请求的 field_mask 更新行程字段。请参阅 UpdateTripRequest

拼车提供商负责更新以下属性:

  • 行程状态。
  • 车辆 ID。可在创建时或将车辆与行程匹配之后创建。
  • 更改上车点、下车点或航点。

通过 Driver SDK 或 Consumer SDK 使用行程分享功能时,Fleet Engine 会自动更新以下字段:

  • 路由
  • 预计到达时间
  • 剩余距离
  • 车辆位置
  • 其余航点

请参阅 RPC 中的 TripREST 中的 Resource.Trip

行程最新动态的 Google Cloud Platform 日志

收到对 UpdateTrip 端点的调用时,Fleet Engine API 会使用 Google Cloud 平台日志写入日志条目。日志条目包含有关 UpdateTrip 请求中值的信息。如果调用成功,它还包含有关返回的 Trip 的信息。

方法:搜索行程

Fleet Engine 支持搜索行程。如前所述,行程会在 7 天后自动删除,因此 SearchTrips 不会显示所有行程的完整历史记录。

虽然 SearchTrips 是一种灵活的 API,但以下列表考虑了两种用例。

  • 确定车辆的有效行程 - 提供商可以确定车辆当前的有效行程。在 SearchTripsRequest 中,vehicle_id 应设置为考虑使用的车辆,active_trips_only 应设置为 true

  • 协调提供方和 Fleet Engine 状态 - 提供方可以使用 SearchTrips 确保其行程状态与 Fleet Engine 状态保持一致。这对于 TripStatus 尤为重要。如果分配给车辆的行程状态未正确设置为 COMPLETECANCELED,则 SearchVehicles 不会包含该车辆。

如需以这种方式使用 SearchTrips,请将 vehicle_id 留空,将 active_trips_only 设置为 true,并将 minimum_staleness 设置为大于大多数行程时长的时间。例如,您可以使用 1 小时。结果包括未完成和已取消且在一个小时内未更新的行程。提供商应检查这些行程,以确保其在 Fleet Engine 中的状态得到正确更新。

问题排查

如果发生 DEADLINE_EXCEEDED 错误,Fleet Engine 的状态将未知。提供程序应再次调用 CreateTrip,这样会返回 201 (CREATED) 或 409 (CONFLICT)。在后一种情况下,上一个请求在 DEADLINE_EXCEEDED 之前成功。如需详细了解如何处理行程错误,请参阅 Consumer API 指南(AndroidiOS)。

拼车行程支持

您可以为支持 TripType.SHARED 的车辆分配多个 SHARED 行程。(在 CreateTripUpdateTrip 请求中)为共享行程分配 vehicle_id 时,您需要通过 Trip.vehicle_waypoints 为分配给此共享行程中的车辆的所有行程指定所有未传递航点的顺序。对于 RPC,请参阅 vehicle_waypoints;对于 REST,请参阅 vehicleWaypoints

支持多个目的地

确定中间目的地

Trip 中的 intermediateDestinations 字段和 intermediateDestinationIndex 字段 (RPC | REST) 会合并用于指示目的地。

更新中间目的地

您可以通过 UpdateTrip 更新中间目的地。更新中间目的地时,您必须提供中间目的地的完整列表,其中包括访问过的目的地,而不仅仅是新添加或修改的目的地。当 intermediateDestinationIndex 指向新添加/修改后的中间目的地位置之后的索引时,新/更新后的中间目的地不会添加到车辆的 waypoints 或行程的 remainingWaypoints 中。其原因在于,intermediateDestinationIndex 之前的任何中间目的地都会被视为已访问过。

行程状态更改

在发送到 Fleet Engine 的行程状态更新请求中,(RPC | REST) 中的字段 intermediateDestinationsVersion 是必填项,用于指示中间目的地已过。目标中间目的地通过字段 intermediateDestinationIndex 指定。当 tripStatus (RPC | REST) 为 ENROUTE_TO_INTERMEDIATE_DESTINATION 时,[0..N-1] 之间的数字表示车辆接下来将经过哪个中间目的地。当 tripStatus 为 ARRIVED_AT_INTERMEDIATE_DESTINATION 时,[0..N-1] 之间的数字表示车辆所在的中间目的地。

示例

以下代码示例演示了如何更新行程状态以前往其第一个中间目的地(假设您已创建多目的地行程且行程已过其上车点)。

Java

static final String PROJECT_ID = "project-id";
static final String TRIP_ID = "multi-destination-trip-A";

String tripName = "providers/" + PROJECT_ID + "/trips/" + TRIP_ID;
Trip trip = …; // Fetch trip object from FleetEngine or your storage.

TripServiceBlockingStub tripService = TripService.newBlockingStub(channel);

// Trip settings to update.
Trip trip = Trip.newBuilder()
    // Trip status cannot go back to a previous status once it is passed
    .setTripStatus(TripStatus.ENROUTE_TO_INTERMEDIATE_DESTINATION)
    // Enrouting to the first intermediate destination.
    .setIntermediateDestinationIndex(0)
    // intermediate_destinations_version MUST be provided to ensure you
    // have the same picture on intermediate destinations list as FleetEngine has.
    .setIntermediateDestinationsVersion(
        trip.getIntermediateDestinationsVersion())
    .build();

// Trip update request
UpdateTripRequest updateTripRequest =
    UpdateTripRequest.newBuilder()
        .setName(tripName)
        .setTrip(trip)
        .setUpdateMask(
            FieldMask.newBuilder()
                .addPaths("trip_status")
                .addPaths("intermediate_destination_index")
                // intermediate_destinations_version must not be in the
                // update mask.
                .build())
        .build();

// Error handling
try {
  Trip updatedTrip = tripService.updateTrip(updateTripRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:  // Trip does not exist.
      break;
    case FAILED_PRECONDITION:  // The given trip status is invalid, or the
                                // intermediate_destinations_version
                                // doesn’t match FleetEngine’s.
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

方法:订阅来自 Fleet Engine API 的通知消息

Fleet Engine API 使用 Google Cloud Pub/Sub 针对使用方 Google Cloud 项目创建的主题发布通知。默认情况下,Google Cloud 项目上的 Fleet Engine 不会启用 Pub/Sub。请提交支持请求或与您的客户工程师联系,以启用 Pub/Sub。

如需为您的 Cloud 项目创建主题,请按照这些说明操作。主题 ID 必须是“fleet_engine_notifications”。

该主题必须在调用 Fleet Engine API 的同一 Cloud 项目中创建。

创建主题后,您需要授予 Fleet Engine API 在该主题上进行发布的权限。为此,请点击刚刚创建的主题并添加新权限。您可能需要点击显示信息面板才能打开权限编辑器。 主帐号应为 geo-fleet-engine@system.gserviceaccount.com,角色应为 Pub/Sub publisher

如需设置 Cloud 项目以订阅通知,请按照这些说明操作

Fleet Engine API 以两种不同的数据格式(protobufjson)发布每条通知。每个通知的数据格式在 PubsubMessage 属性中表示,键为 data_format,值为 protobufjson

通知架构:

Protobuf

// A batch of notifications that is published by the Fleet Engine service using
// Cloud Pub/Sub in a single PubsubMessage.
message BatchNotification {
  // Required. At least one notification must exist.
  // List of notifications containing information related to changes in
  // Fleet Engine data.
  repeated Notification notifications = 1;
}

// A notification related to changes in Fleet Engine data.
// The data provides additional information specific to the type of the
// notification.
message Notification {
  // Required. At least one type must exist.
  // Type of notification.
  oneof type {
    // Notification related to changes in vehicle data.
    VehicleNotification vehicle_notification = 1;
  }
}

// Notification sent when a new vehicle was created.
message CreateVehicleNotification {
  // Required.
  // Vehicle must contain all fields that were set when it was created.
  Vehicle vehicle = 1;
}

// Notification sent when an existing vehicle is updated.
message UpdateVehicleNotification {
  // Required.
  // Vehicle must only contain name and fields that are present in the
  // field_mask field below.
  Vehicle vehicle = 1;

  // Required.
  // Contains vehicle field paths that were specifically requested
  // by the Provider.
  google.protobuf.FieldMask field_mask = 2;
}

// Notification related to changes in vehicle data.
message VehicleNotification {
  // Required. At least one type must be set.
  // Type of notification.
  oneof type {
    // Notification sent when a new vehicle was created.
    CreateVehicleNotification create_notification = 1;
    // Notification sent when an existing vehicle is updated.
    UpdateVehicleNotification update_notification = 2;
  }
}

JSON

BatchNotification: {
  "description": "A batch of notifications that is published by the Fleet Engine service using Cloud Pub/Sub in a single PubsubMessage.",
  "type": "object",
  "required": ["notifications"],
  "properties": {
    "notifications": {
      "description": "At least one notification must exist. List of notifications containing information related to changes in Fleet Engine data.",
      "type": "Notification[]"
    }
  }
}

Notification: {
  "description": "A notification related to changes in Fleet Engine data. The data provides additional information specific to the type of the notification.",
  "type": "object",
  "properties": {
    "vehicleNotification": {
      "description": "Notification related to changes in vehicle data.",
      "type": "VehicleNotification"
    }
  }
}

VehicleNotification: {
  "description": "Notification related to changes in vehicle data.",
  "type": "object",
  "properties": {
    "createNotification": {
      "description": "Notification sent when a new vehicle was created.",
      "type": "CreateVehicleNotification"
    },
    "updateNotification": {
      "description": "Notification sent when an existing vehicle is updated.",
      "type": "UpdateVehicleNotification"
    }
  }
}

CreateVehicleNotification: {
  "description": "Notification sent when a new vehicle was created.",
  "type": "object",
  "required": ["vehicle"],
  "properties": {
    "vehicle": {
      "description": "Vehicle must contain all fields that were set when it was created.",
      "type": "Vehicle"
    }
  }
}

UpdateVehicleNotification: {
  "description": "Notification sent when an existing vehicle is updated.",
  "type": "object",
  "required": ["vehicle", "fieldMask"],
  "properties": {
    "vehicle": {
      "description": "Vehicle must only contain name and fields that are present in the fieldMask field below.",
      "type": "Vehicle"
    },
    "fieldMask": {
      "description": "Contains vehicle field paths that were specifically requested by the Provider.",
      "type": "FieldMask"
    }
  }
}