This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

gRPC service generation

Learn how to generate and fill your own gRPC services.

This tutorial shows how to generate a basic gRPC service like a seat service. For this example the proto file under https://raw.githubusercontent.com/eclipse-kuksa/kuksa-incubation/0.4.0/seat_service/proto/sdv/edge/comfort/seats/v1/seats.proto is taken.

All files included from services/seats are auto-generated and added to the app project as Conan dependency. For writing a complete gRPC service you need two velocitas apps/projects. One is implementing a client and the other one is for providing the server. To complete the server implementation you have to fill the generated *ServiceImpl.cpp. Have a look at the linked content beneath for a tutorial how it would be done for a SeatService leveraging https://raw.githubusercontent.com/eclipse-kuksa/kuksa-incubation/0.4.0/seat_service/proto/sdv/edge/comfort/seats/v1/seats.proto .

To run the example you need to start the velocitas app for the server first and then the second velocitas app for the client.

1 - Create a client

Learn how to create a client for a service definition.

App configuration

{
  "type": "grpc-interface",
  "config": {
      "src": "https://raw.githubusercontent.com/eclipse-kuksa/kuksa-incubation/0.4.0/seat_service/proto/sdv/edge/comfort/seats/v1/seats.proto",
      // "required" indicates you are trying to write a client for the service
      "required": {
        "methods": [
          "Move", "CurrentPosition"
        ]
      },
  }
}

Project configuration

You need to specify devenv-devcontainer-setup >= v2.4.2 in your project configuration. Therefore your .veloitas.json should look similair to this example:

{
  "packages": {
    "devenv-devcontainer-setup": "v2.4.2"
  },
  "components": [
    {
      "id": "grpc-interface-support", 
    }
  ],
}

To do that you can run velocitas component add grpc-interface-support when your package is above or equal to v2.4.2

Example code

To create a client we use the generated SeatsServiceClientFactory.h and seats.grpc.pb.h. These define request and response types and the operations that are available. An example implementation for the SeatService follows:

main.cpp

#include <sdk/middleware/Middleware.h>
#include <services/seats/SeatsServiceClientFactory.h>
#include <services/seats/seats.grpc.pb.h>

#include <iostream>

using namespace velocitas;

int main(int argc, char** argv) {
    auto serviceClient = SeatsServiceClientFactory::create(Middleware::getInstance());

    ::grpc::ClientContext                        context;
    ::sdv::edge::comfort::seats::v1::MoveRequest request;
    ::sdv::edge::comfort::seats::v1::MoveReply   response;

    ::sdv::edge::comfort::seats::v1::Seat seat;

    ::sdv::edge::comfort::seats::v1::SeatLocation seat_location;
    seat_location.set_row(1);
    seat_location.set_index(1);

    ::sdv::edge::comfort::seats::v1::Position seat_position;
    // we only set base here to keep the example simple
    // extend here if yu want to set lumbar etc.
    seat_position.set_base(1000);

    seat.set_allocated_location(&seat_location);
    seat.set_allocated_position(&seat_position);

    request.set_allocated_seat(&seat);

    auto status = serviceClient->Move(&context, request, &response);

    std::cout << "gRPC Server returned code: " << status.error_code() << std::endl;
    std::cout << "gRPC error message: " << status.error_message().c_str() << std::endl;

    if (status.error_code() == ::grpc::StatusCode::UNIMPLEMENTED) {
        return 1;
    } else {
        ::grpc::ClientContext                                   context;
        ::sdv::edge::comfort::seats::v1::CurrentPositionRequest request;
        ::sdv::edge::comfort::seats::v1::CurrentPositionReply   response;

        request.set_row(1);
        request.set_index(1);

        auto status_curr_pos = seatService->CurrentPosition(&context, request, &response);
        std::cout << "current Position:" << response.seat().position().base() << std::endl;
        std::cout << "gRPC Server returned code: " << status_curr_pos.error_code() << std::endl;
        std::cout << "gRPC error message: " << status_curr_pos.error_message().c_str() << std::endl;
        return 0;
    }
}

2 - Create a server

Learn how to create a server for a service definition.

App configuration

{
  "type": "grpc-interface",
  "config": {
      "src": "https://raw.githubusercontent.com/eclipse-kuksa/kuksa-incubation/0.4.0/seat_service/proto/sdv/edge/comfort/seats/v1/seats.proto",
      // "provided" indicates you want to implement the server business logic for the service
      "provided": { }
  }
}

Project configuration

You need to specify devenv-devcontainer-setup >= v2.4.2 in your project configuration. Therefore your .veloitas.json should look similair to this example:

{
  "packages": {
    "devenv-devcontainer-setup": "v2.4.2"
  },
  "components": [
    {
      "id": "grpc-interface-support", 
    }
  ],
}

To do that you can run velocitas component add grpc-interface-support when your package is above or equal to v2.4.2

To create a server that is providing the gRPC service we are leveraging the generated SeatsServiceImpl.h and SeatsServiceServerFactory.h. The SeatsServiceImpl.cpp needs to be filled with the actual implementation of the service. A quick example for a SeatService is described in the following:

main.cpp

#include <sdk/middleware/Middleware.h>
#include <services/seats/SeatsServiceServerFactory.h>
#include "SeatsServiceImpl.h"

#include <memory>

using namespace velocitas;

int main(int argc, char** argv) {
    auto seatsImpl = std::make_shared<SeatsService>();

    velocitas::VehicleModelContext::getInstance().setVdbc(
        velocitas::IVehicleDataBrokerClient::createInstance("vehicledatabroker"));
    auto seatServer =
        SeatsServiceServerFactory::create(Middleware::getInstance(), seatsImpl);

    seatServer->Wait();
    return 0;
}

SeatsServiceImpl.cpp

#include "SeatsServiceImpl.h"
#include <sdk/VehicleApp.h>
#include <sdk/VehicleModelContext.h>
#include <sdk/vdb/IVehicleDataBrokerClient.h>
#include <vehicle/Vehicle.hpp>
#include <grpc/grpc.h>
#include <services/seats/seats.grpc.pb.h>

namespace velocitas {

::grpc::Status SeatsService::Move(::grpc::ServerContext*                              context,
                                  const ::sdv::edge::comfort::seats::v1::MoveRequest* request,
                                  ::sdv::edge::comfort::seats::v1::MoveReply*         response) {
    (void)context;
    (void)response;
    vehicle::Vehicle Vehicle;

    auto seat     = request->seat();
    auto location = seat.location();
    auto row      = location.row();
    auto pos      = location.index();

    // you would need to extend this to add support for lumbar etc.
    // Vehicle.Cabin.Seat.Row(row).Pos(pos).Position.set(seat->position()->xxxxxx())->await();
    auto status = Vehicle.Cabin.Seat.Row1.DriverSide.Position.set(seat.position().base())->await();
    if (status.ok()) {
        return ::grpc::Status(::grpc::StatusCode::OK, "");
    } else {
        return ::grpc::Status(::grpc::StatusCode::CANCELLED, status.errorMessage());
    }
}

::grpc::Status
SeatsService::MoveComponent(::grpc::ServerContext*                                       context,
                            const ::sdv::edge::comfort::seats::v1::MoveComponentRequest* request,
                            ::sdv::edge::comfort::seats::v1::MoveComponentReply*         response) {
    (void)context;
    (void)request;
    (void)response;
    return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}

::grpc::Status SeatsService::CurrentPosition(
    ::grpc::ServerContext*                                         context,
    const ::sdv::edge::comfort::seats::v1::CurrentPositionRequest* request,
    ::sdv::edge::comfort::seats::v1::CurrentPositionReply*         response) {
    (void)context;
    (void)request;
    vehicle::Vehicle Vehicle;

    auto             seat          = response->mutable_seat();
    auto             seat_position = seat->mutable_position();

    auto seatPos = Vehicle.Cabin.Seat.Row1.DriverSide.Position.get()->await().value();

    // we only set base here to keep the example simple
    // extend here if yu want to set lumbar etc.
    seat_position->set_base(seatPos);
    return ::grpc::Status(::grpc::StatusCode::OK, "");
}

} // namespace velocitas