Fleet Engine 使用入门

借助 Fleet Engine On-demand Rides and Deliveries API,您可以管理行程 行程和订单进度应用的行程和车辆状态信息。 它会处理驱动程序 SDK、消费者 SDK 和您的 和 Fleet Engine 之间的通信, gRPCREST 调用。

前提条件

对于开发,请确保在 Cloud Build 中安装 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 需要使用 JSON Web 令牌, 已使用从以下来源创建的服务账号进行签名: Cloud 控制台。

Cloud 项目设置

要设置云项目,请先创建项目,然后 创建服务账号。

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

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

服务账号与一个或多个角色相关联。它们用于创建 JSON Web 令牌可根据 角色。通常,为了降低滥用风险,您可以创建 每个服务账号具有所需的最低角色集。

行程和订单进度使用以下角色:

角色说明
Fleet Engine 使用方 SDK 用户

roles/fleetengine.consumerSdkUser
授予搜索车辆和检索信息的权限 车辆和行程。由具有此令牌的服务账号创建的令牌 角色通常在拼车或外卖消费者应用移动设备上使用。
Fleet Engine Driver SDK 用户

roles/fleetengine.driverSdkUser
授予更新车辆位置和路线的权限 检索车辆和行程的相关信息。已创建的令牌数 通常由具有此角色的服务账号使用 拼车或送餐司机应用等移动设备。
Fleet Engine 按需管理员

roles/fleetengine.ondemandAdmin
授予对所有车辆和行程资源的读写权限。 拥有此角色的主账号不需要使用 JWT,而是应该使用 使用应用默认凭据。自定义 JWT 声明会被忽略。 此角色应仅限受信任的环境(客户后端)使用。
FleetEngine Service Super User **(已弃用)**

roles/fleetengine.serviceSuperUser
授予对所有车辆和行程 API 的权限。已铸造的代币 通常从您的后端使用。 服务器此角色已弃用。首选 roles/fleetengine.ondemandAdmin

例如,为这三个角色分别创建一个服务账号,并将 各自的角色

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

驱动程序 SDK 和使用方 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 Web 令牌 (JWT) 直接在代码库中编写代码这就需要您具备深厚的 了解 JWT 及其与 Fleet Engine 的关系。正因如此, 强烈建议充分利用 Fleet Engine Auth 库。

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

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

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

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

字段说明
iss 您的服务账号的电子邮件地址。
sub 您的服务账号的电子邮件地址。
aud 您的服务账号的 SERVICE_NAME,在本示例中为 https://fleetengine.googleapis.com/
iat 令牌创建时的时间戳(以秒为单位表示) 自 1970 年 1 月 1 日 00:00:00(世界协调时间)起。等待 10 分钟,使数据出现偏差。如果 时间戳过于久远或在未来,服务器可能会报告错误。
exp 令牌到期时的时间戳(以秒为单位表示) 自 1970 年 1 月 1 日 00:00:00(世界协调时间)起。如果时间戳为 1 个小时以后
授权 可能包含“vehicleid”或“tripid”,具体取决于用例。

创建 JWT 令牌意味着对其进行签名。如需查看相关说明和代码示例 有关创建和签署 JWT 的说明,请参阅 不使用 OAuth 的服务账号授权。 然后,您可以将已签名的令牌附加到 gRPC 调用或所用的其他方法 访问 Fleet Engine。

JWT 声明

创建 JWT 载荷时,请在授权中添加额外的声明 部分,其中键 vehicleidtripid 设置为 作为呼叫对象的车辆 ID 或行程 ID。

驱动程序 SDK 始终使用 vehicleid 声明,无论在运行 行程或车辆。Fleet Engine 后端可确保车辆 与请求的行程相关联,然后才会进行修改。

使用方 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"
   }
}

下面显示了驱动程序应用的示例令牌:

{
  "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。您可以在服务的 private_key_id 字段中找到此值 账号 JSON 文件。
  • 对于 isssub 字段,请指定您的服务账号的电子邮件地址。 您可以在服务账号的 client_email 字段中找到此值 JSON 文件。
  • 对于 aud 字段,请指定 https://SERVICE_NAME/
  • 对于 iat 字段,请使用创建令牌时的时间戳, 自世界协调时间 (UTC) 1970 年 1 月 1 日 00:00:00 起经过的秒数。 等待 10 分钟,使数据出现偏差。如果时间戳过于久远, 或将来,服务器可能会报告错误。
  • 对于 exp 字段,请使用令牌到期时的时间戳。 自世界协调时间 (UTC) 1970 年 1 月 1 日 00:00:00 以来的秒数。最大 允许的值为 iat + 3600。

在对要传递到移动设备的 JWT 进行签名时,请务必使用 驱动程序或消费者 SDK 角色的服务账号。否则, 设备将能够更改不应具有的状态。

同样,在对用于特权调用的 JWT 进行签名时,请确保 使用具有超级用户角色的服务账号。否则, 操作会失败。

生成用于测试的 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 属性是 到期时间(以新纪元后经过的秒数表示),可通过 正在将 3600 添加到iat。到期时间在 。

{
  "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 运行 curl 命令来测试令牌 REST 端点:

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

交通工具及其生命周期

Vehicle 是表示驾驶员-车辆对的实体。目前, 无法单独跟踪驾驶员和车辆。拼车或配送服务提供商 使用提供商 ID(必须与 服务账号所属的 Google Cloud 项目的 ID 用于调用 Fleet Engine API)和拼车或配送服务提供商拥有的车辆 ID。

如果车辆在 7 天后仍未通过UpdateVehicle更新,则 其分配的行程(如果有)将标记为 未分配。使车辆保持可用状态的建议方法 定期更新其位置。更新到大多数 Vehicle 实体中的其他字段也会延长其生命周期,前提是 新字段值与现有字段值不同。

注意:Vehicle 实体上的某些字段(如 device_settings)是纯调试的 Fleet Engine 不会保留这些信息。更新它们不会 可延长 Vehicle 实体的生命周期。

使用CreateVehicle 提供方 ID/车辆 ID 对已存在。例如, 可以通过以下两种方式处理:频繁调用 CreateVehicle,包含预期的提供方 ID/车辆 ID 对并舍弃 如果相应车辆已存在,则会发生此错误;或者,在CreateVehicle UpdateVehicle 会返回 NOT_FOUND 错误。

车辆位置信息更新

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

  1. 使用驱动程序 SDK - AndroidiOS -- 最简单的方法。
  2. 使用自定义代码;如果需要 或者您使用的是 Android 或 iOS 版。

车辆类型

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

在以下情况下,车辆的所有路线都将使用对应的 RouteTravelMode: category 设置为 AUTOTWO_WHEELERBICYCLEPEDESTRIAN。 如果类别设置为 TAXITRUCK,则路由被视为与 AUTO 模式。

车辆属性

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

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

车辆剩余航点

Vehicle 实体包含 TripWaypoint 的重复字段 (RPC | REST), 名为 waypoints(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 必须是项目 ID (例如 my-on-demand-project)的 Google Cloud 项目,包含 将用于调用 Fleet Engine 的服务账号。请注意,虽然 多个服务账号可以针对同一拼车项目访问 Fleet Engine 或交付服务提供商,Fleet Engine 目前不支持 访问同一 Vehicles 的多个 Google Cloud 项目。

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

初始 last_location 可以包含在 CreateVehicle 调用中。 在获得允许的情况下,除非满足以下条件,否则不得在 ONLINE 状态下创建 Vehicle 一个 last_location

如需详细了解车辆,请参阅车辆类型 type 字段。

如需了解详情,请参阅车辆属性 指定属性

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 日志

在以下情况下,Fleet Engine API 会通过 Google Cloud Platform 日志写入日志条目: 对 CreateVehicle 端点的调用。日志条目包括 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 发布 车辆。要接收这些通知,请按照 此处的说明。

方法指南:更新车辆的位置

如果您不使用 Driver SDK 来更新车辆的位置,那么您可以 直接调用 Fleet Engine 并提供车辆的位置信息。对于任何有效车辆, Fleet Engine 预计每分钟至少更新一次位置信息,最多一次 每 5 秒一次。这些更新只需要 Fleet Engine Driver SDK User 权限。

示例

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 日志

在以下情况下,Fleet Engine API 会通过 Google Cloud Platform 日志写入日志条目: 对 UpdateVehicle 端点的调用。日志条目包括 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

首先更新我们在之前步骤中创建的车辆的位置, 有效。在实际使用中,这可通过运行 LLM 的 。

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 子句的形式运行 以及其他约束条件,例如 minimum_capacityvehicle_types 中的 SearchVehiclesRequest

方法指南:列出车辆

SearchVehicles 已经过优化,可查找已排名的少量车辆 订单处理速度很快,主要用于寻找附近最适合的司机 任务。不过,有时您想要查找满足特定要求的所有车辆, 也不必对结果进行分页。“ListVehicles”现为 针对该使用场景量身打造的应用。

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

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

示例

此示例对 vehicle_type 和使用 filter 字符串。

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 同时提供 RPC 服务、 TripService 以及 REST 资源 provider.trips ,了解所有最新动态。这些界面支持创建行程实体、信息请求、搜索 和更新功能

Trip 有一个状态字段,用于跟踪其在生命周期中的进度。 值从 NEW 变为 COMPLETE,再加上 CANCELEDUNKNOWN_TRIP_STATUS ,了解所有最新动态。请参阅 RPC 的 trip_statusTripStatus for REST

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

您的服务可将行程从这些状态更新为CANCELED。 当服务创建行程时,引擎会将状态设置为 NEW。答 vehicle_id 是可选的。与车辆一样,这些服务会自动删除未分配的行程 连续 7 天更新。如果您的服务尝试创建包含 ID 已存在,则返回错误。符合以下条件的行程会被视为“有效”行程: 它处于 COMPLETECANCELED 以外的状态。区别在于, active_trips 字段(车辆实体)和 SearchTripsRequest 字段中必须填写重要的字段。

只有在行程结束后,您的服务才能更改分配给行程的vehicle_id 有效。例如,如果司机在 100 天内取消行程, 行程路线将被重新分配给另一辆车。

在实施接连实施时,状态非常重要 行程支持。此支持使提供商能够为车辆分配新的行程 。代码 与单个行程相同,使用相同 车辆 ID。Fleet Engine 将新行程的出发地和目的地添加到 车辆的航点。如需详细了解连续行程,请参阅 创建多航点行程

行程剩余航点

Trip 实体包含重复的 TripWaypoint 字段 (RPC | REST), 名为 remainingWaypoints(RPC | REST)。 此字段包含车辆依序行驶的所有航点 。它通过 车辆的剩余航点。 在“Back-to-back”和“Carpool”用例中,此列表包含 在此行程之前要遍历的其他行程,但不包括任何航点 。列表中的航点可通过其 TripId 标识 和WaypointType

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

车辆的剩余航点 (RPC | REST) 将 在 Fleet Engine 收到行程状态更改请求时更新。通过 在以下时间后,之前的航点将从车辆的剩余航点列表中移除 tripStatus(RPC | REST) 已从其他状态更改为 ENROUTE_TO_XXX。也就是说, 行程状态从 ENROUTE_TO_PICKUP 更改为 ARRIVED_AT_PICKUP,则行程的 上车点仍在车辆的剩余航点列表中 状态已更改为 ENROUTE_TO_INTERMEDIATE_DESTINATION 或 ENROUTE_TO_DROPOFF, 那么该车辆的上车点便会从车辆的剩余航点中删除。

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

如果行程状态更改为“COMPLETED”,此行程将不会有任何航点 。

方法指南:创建行程

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

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

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

创建行程时,您可以提供number_of_passengersdropoff_pointvehicle_id。虽然这些字段不是必填字段,但如果提供, 它们也会被保留所有其他“行程”字段均会被忽略。例如,所有行程 即使您传入 trip_statustrip_status 也以 NEW 开头, CANCELED

示例

以下示例创建了前往印尼东购物中心的行程。旅程 为两名乘客,为专属优惠。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 日志

在以下情况下,Fleet Engine API 会使用 Google Cloud Platform 日志写入日志条目: 对 CreateTrip 端点的调用。日志条目包括 CreateTrip 请求中的值的相关信息。如果调用 成功后,它还会包含与返回的 Trip 相关的信息。

方法指南:更新行程

Trip 实体包含用于启用该服务跟踪的字段以及用于 通过驱动程序 SDK 报告行程进度, 使用方 SDK。如需更新属性,请使用 UpdateTripRequest 消息。这样会根据请求的 field_mask 更新“行程”字段。 请参阅 UpdateTripRequest

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

  • 行程状态。
  • 车辆 ID。可以在创建时创建,也可以在将车辆与 。
  • 更改上车点、下车点或航点。

使用 通过驱动程序 SDK 或消费者 SDK 实现行程分享功能:

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

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

用于记录行程更新的 Google Cloud Platform 日志

在以下情况下,Fleet Engine API 会使用 Google Cloud Platform 日志写入日志条目: 对 UpdateTrip 端点的调用。日志条目包括 UpdateTrip 请求中的值的相关信息。如果调用成功, 还包含所返回的 Trip 的相关信息。

方法指南:搜索行程

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

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

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

  • 协调提供商和舰队引擎状态 - 提供商可以使用 SearchTrips,以确保其行程状态与 Fleet Engine 的行程状态一致。 这对 TripStatus 而言尤为重要。如果行程的状态已分配 未正确设置为 COMPLETECANCELED,则该车辆 不包含在“SearchVehicles”中。

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

问题排查

如果发生 DEADLINE_EXCEEDED 错误,Fleet Engine 的状态为 未知。Provider 应再次调用 CreateTrip,这要么会返回 201(已创建)或 409(冲突)。在后一种情况下,上一个请求成功了 (截止日期:DEADLINE_EXCEEDED)。如需了解详情,请参阅 Consumer API 指南 如何处理行程错误:AndroidiOS

拼车行程支持

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

支持多个目的地

确定中间目的地

字段 intermediateDestinations 和字段 intermediateDestinationIndex in Trip (RPC | REST) 的组合用于表示目的地。

更新中间目的地

您可以通过 UpdateTrip 更新中间目的地。更新时 中间目的地,则必须提供中间目的地的完整列表。 包括已访问过的网页,而不仅仅是新访问的 添加或进行修改 当 intermediateDestinationIndex 指向 新添加/修改的中间目的地、新的/更新的中间目的地 目的地将不会添加到车辆的waypoints或行程的remainingWaypoints中。 原因是 intermediateDestinationIndex 之前的所有中间目的地 将被视为已访问。

行程状态变更

(RPC | REST) 中的字段 intermediateDestinationsVersion 必须填写在发送给 Fleet Engine 的行程状态更新请求中,以指明 中间目的地已过。目标中间目的地 通过 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。

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

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

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

要设置您的云项目以订阅通知, 请按照这些说明操作

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"
    }
  }
}