Vehículo

Gestionar dos conexiones roscadas entre un cliente Qt gRPC y un servidor C++ gRPC.

El ejemplo simula el salpicadero de un vehículo que muestra datos enviados por un servidor gRPC.

Captura de pantalla del salpicadero de un vehículo de ejemplo

El código del ejemplo tiene los siguientes componentes:

  • vehicle_client Aplicación cliente Qt gRPC que utiliza las funciones CMake qt_add_protobuf() y qt_add_grpc() para la generación de código Qt de mensajes y servicios.
  • vehicle_server aplicación que llama al plugin C++ gRPC para generar código de servidor e implementar lógica de servidor simple.

Nota: necesitas tener instalado el plugin C++ gRPC. Encuentre los detalles aquí: Requisitos previos del módulo

Ambos componentes utilizan mensajes generados a partir de los esquemas protobuf descritos en los archivos vehicleservice.proto y navigationservice.proto.

Servicio de vehículos:

message SpeedMsg {
    int32 speed = 1;
}

message RpmMsg {
    int32 rpm = 1;
}

message AutonomyMsg {
    int32 autonomy = 1;
}

service VehicleService {
    rpc getSpeedStream(google.protobuf.Empty) returns (stream SpeedMsg) {}
    rpc getRpmStream(google.protobuf.Empty) returns (stream RpmMsg) {}
    rpc getAutonomy(google.protobuf.Empty) returns (AutonomyMsg) {}
}

Servicio de navegación:

enum DirectionEnum {
    RIGHT = 0;
    LEFT = 1;
    STRAIGHT = 2;
    BACKWARD = 3;
}

message NavigationMsg {
    int32 totalDistance = 1;
    int32 traveledDistance = 2;
    DirectionEnum direction = 3;
    string street = 4;
}

service NavigationService {
    rpc getNavigationStream(google.protobuf.Empty) returns (stream NavigationMsg) {}
}

El singleton C++ VehicleManager utiliza dos instancias QThread para comunicarse con el servidor en paralelo. Los hilos tienen diferentes tipos de conexión gRPC. En este ejemplo, hay dos tipos:

  • RPCs de flujo de servidor Por ejemplo, el flujo de velocidad del hilo de vehículo. Utiliza dos funciones de callback: QGrpcServerStream::messageReceived y QGrpcOperation::finished
    Empty speedRequest; m_streamSpeed =  m_client->getSpeedStream(speedRequest); connect(m_streamSpeed.get(), &QGrpcServerStream::messageReceived, this, [this]() { if(const auto speedResponse =  m_streamSpeed->read<SpeedMsg>()) { emit speedChanged(speedResponse->speed()); } }); connect( m_streamSpeed.get(), &QGrpcServerStream::finished, this,[this](const QGrpcStatus &status) { if (!status.isOk()) { auto error = QString("Stream error fetching speed %1 (%2)") .arg(status.message()) .arg(QVariant::fromValue(status.code()).toString()); emite connectionError(error);            qWarning() << error;
               return; }},  Qt::SingleShotConnection);
  • RPCs Unarios La operación RPC getAutonomy es una RPC unaria. Devuelve una única respuesta. Sólo está conectada a la señal QGrpcOperation::finished.
    Vacío autonomyRequest; std::unique_ptr<QGrpcCallReply> autonomyReply =  m_client->getAutonomy(autonomyRequest);const auto *autonomyReplyPtr = autonomyReply.get(); connect( autonomyReplyPtr, &QGrpcCallReply::finished, this,[this, autonomyReply = std::move(autonomyReply)](const QGrpcStatus &status) { if (!status.isOk()) { auto error = QString("Call error fetching autonomy %1 (%2)") .arg(status.message()) .arg(QVariant::fromValue(status.code()).toString()); emite connectionError(error);            qWarning() << error;
               return; } if(const auto autonomyMsg =  autonomyReply->read<AutonomyMsg>()) { emit autonomyChanged(autonomyMsg->autonomy());}},  Qt::SingleShotConnection);

La interfaz de la ventana principal del cliente se define en el archivo Main.qml. Utiliza el tipo QML Connections para conectarse a las señales del singleton C++ VehicleManager a ranuras personalizadas:

    Connections {
        target: VehicleManager

        // This slot will be executed when the VehicleManager::totalDistanceChanged
        // signal is emitted
        function onTotalDistanceChanged(distance: int): void {
            root.totalDistance = distance;
        }

        function onSpeedChanged(speed: int): void {
            root.speed = speed;
        }

        function onRpmChanged(rpm: int): void {
            root.rpm = rpm;
        }

        function onTraveledDistanceChanged(distance: int): void {
            root.traveledDistance = distance;
        }

        function onDirectionChanged(direction: int): void {
            if (direction == VehicleManager.RIGHT) {
                root.directionImageSource = "qrc:/direction_right.svg";
            } else if (direction == VehicleManager.LEFT) {
                root.directionImageSource =  "qrc:/direction_left.svg";
            } else if (direction == VehicleManager.STRAIGHT) {
                root.directionImageSource =  "qrc:/direction_straight.svg";
            } else {
                root.directionImageSource =  "";
            }
        }

Tras recibir una respuesta, la ventana cliente actualiza la interfaz de usuario con los datos recibidos. De esta forma, los mensajes pueden recibirse en diferentes hilos y enviarse a la interfaz de usuario del cliente de forma segura gracias a las señales.

Proyecto de ejemplo @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.