Tutorial - Post vehicle telemetry with Hermes using C++

In this tutorial we will create a C++ client that will use HermesV2 to publish telemetry data for a given vehicle.

HermesV2 is a protocol based on gRPC. All services and objects are described in proto files. gRPC provides tools to compile those proto files to C++ code, that will handle serialization, deserialization and transport mechanisms.

Get the HermesV2 protocol definitions

First, we need to get the proto files for HermesV2. They are available from the bestmile/hermes repo on github (https://github.com/bestmile/hermes) under the src. Copy them to your current directory:

git clone --depth 1 --branch hermes-v2.0 git@github.com:Bestmile/hermes.git

Compile the HermesV2 protocol definition to C++ code

Now we will generate code to handle serialization, deserialization, and the remote procedure call.

To do that you need Protocol Buffers v3 with the gRPC C++ plugin, use your distribution to install them or follow the instructions here:

Now we can compile the Hermes protocol: cd ./examples/cpp mkdir generated

protoc --proto_path ../../src/main/protobuf \
       --grpc_out=generated \
       --cpp_out=generated \
       --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` \
       $(find ../../src/main/protobuf -iname "*.proto")

The first command will generate the service call (RPC) code. The last two commands generate the serialization and deserialization code.

Create a C++ client that uses the generated code

Create a telemetry_client.cc file containing this code:

#include <iostream>
#include <memory>
#include <string>

#include <grpcpp/grpcpp.h>

#include "generated/com/bestmile/vehicle/service/v2/telemetry.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using com::bestmile::vehicle::model::v2::ConnectionStatus;
using com::bestmile::vehicle::model::v2::DrivingMode;
using com::bestmile::vehicle::service::v2::PostTelemetryRequest;
using com::bestmile::vehicle::service::v2::PostTelemetryResponse;
using com::bestmile::vehicle::model::v2::RequestIdentity;
using com::bestmile::vehicle::model::v2::ResponseStatus;
using com::bestmile::vehicle::model::v2::Telemetry;
using com::bestmile::vehicle::service::v2::TelemetryUpload;

using namespace std;

const string server = "vehicle-service.staging.aws4.bestmile.io:443";
const string api_key = "YOUR_API_KEY...";
const string vehicle_id = "YOUR_VEHICLE_ID...";
const float latitude = 46.51576;
const float longitude = 6.60821;
const float speed = 8.3;

class TelemetryClient {
 public:
  TelemetryClient(std::shared_ptr<Channel> channel): stub_(TelemetryUpload::NewStub(channel)) {}

  // Create the request message, populate it, send it and get the response status
  void PostTelemetry() {
      // Request to send to the server
      PostTelemetryRequest request;

      request.mutable_identity()->mutable_vehicle_id()->set_value(vehicle_id);
      request.mutable_telemetry()->mutable_location()->set_latitude(latitude);
      request.mutable_telemetry()->mutable_location()->set_longitude(longitude);
      request.mutable_telemetry()->mutable_speed()->set_value(speed);

      // Container for the response we expect from the server.
      PostTelemetryResponse response;

      // Context for the client.
      ClientContext context;

      //Passing the API key as an header
      context.AddMetadata("authorization", "BESTMILE-APIKEY-V4 "+ api_key);

      // Do the RPC
      Status status = stub_->PostTelemetry(&context, request, &response);

      // Act upon its status.
      if (status.ok() && response.status().type() == ResponseStatus::OK) {
        std::cout << "Telemetry has been published successfully" << std::endl;
      } else {
        std::cout << status.error_code() << ": " << status.error_message()
                  << std::endl;
        std::cout << "RPC failed: " << response.status().message() << std::endl;
      }
    }
private:
  std::unique_ptr<TelemetryUpload::Stub> stub_;
};

int main(int argc, char** argv) {
  // Instantiate the client. It requires a channel, out of which the actual RPCs
  // are created. This channel models a connection to an endpoint (the server).
  TelemetryClient client(grpc::CreateChannel(server, grpc::SslCredentials(grpc::SslCredentialsOptions())));
  client.PostTelemetry();

  return 0;
}

You will need to adapt the server, api_key and vehicle_id constants.

Compile and test the client

We can compile everything to an executable:

 g++ -std=c++11 -stdlib=libc++ -nostdinc++ telemetry_client.cc generated/*.cc generated/com/bestmile/vehicle/model/v2/*.cc generated/com/bestmile/vehicle/service/v2/*.cc -I./generated -L./generated `pkg-config --libs protobuf grpc++ grpc` -o client 

And run it:

./client

If everything is fine, we should get:

Telemetry has been published successfully