Fahrzeug

Verwalten von zwei Thread-Verbindungen zwischen einem Qt gRPC Client und einem C++ gRPC Server.

Das Beispiel simuliert ein Fahrzeug-Dashboard, das Daten anzeigt, die von einem gRPC Server gesendet werden.

"Vehicle example dashboard screenshot"

Der Beispielcode besteht aus den folgenden Komponenten:

  • vehicle_client Qt gRPC Client-Anwendung, die die CMake-Funktionen qt_add_protobuf() und qt_add_grpc() für die Generierung von Nachrichten- und Service-Qt-Code verwendet.
  • vehicle_server Anwendung, die das C++ gRPC Plugin zur Erzeugung von Servercode und zur Implementierung einfacher Serverlogik aufruft.

Hinweis: Sie müssen das C++ gRPC Plugin installiert haben. Details finden Sie hier: Voraussetzungen für das Modul

Beide Komponenten verwenden generierte Nachrichten aus den Protobuf-Schemata, die in den Dateien vehicleservice.proto und navigationservice.proto beschrieben sind.

Fahrzeugdienst:

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) {}
}

Navigationsdienst:

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) {}
}

Das VehicleManager C++ Singleton verwendet zwei QThread Instanzen zur parallelen Kommunikation mit dem Server. Die Threads haben unterschiedliche gRPC Verbindungstypen. In diesem Beispiel gibt es zwei Typen:

  • Server-Streaming-RPCs Zum Beispiel der Geschwindigkeits-Stream des Fahrzeug-Threads. Er verwendet zwei Callback-Funktionen: QGrpcServerStream::messageReceived und 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-Fehler beim Abrufen der Geschwindigkeit %1 (%2)") .arg(status.message()) .arg(QVariant::fromValue(status.code()).toString()); emit connectionError(error);            qWarning() << error;
               return; }},  Qt::SingleShotConnection);
  • Unäre RPCs Der Vorgang RPC getAutonomy ist ein unärer RPC. Sie gibt eine einzige Antwort zurück. Sie ist nur mit dem Signal QGrpcOperation::finished verbunden.
    Empty 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("Aufruffehler beim Abrufen der Autonomie %1 (%2)") .arg(status.message()) .arg(QVariant::fromValue(status.code()).toString()); emit connectionError(error);            qWarning() << error;
               return; } if(const auto autonomyMsg =  autonomyReply->read<AutonomyMsg>()) { emit autonomyChanged(autonomyMsg->autonomy());} },  Qt::SingleShotConnection);

Die Schnittstelle des Client-Hauptfensters ist in der Datei Main.qml definiert. Sie verwendet den Typ QML Connections, um eine Verbindung zu den Signalen des VehicleManager C++-Singletons zu benutzerdefinierten Slots herzustellen:

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

Nach dem Empfang einer Antwort aktualisiert das Client-Fenster die Benutzeroberfläche mit den empfangenen Daten. Auf diese Weise können Nachrichten in verschiedenen Threads empfangen und dank der Signale auf eine thread-sichere Weise an die Client-Benutzeroberfläche gesendet werden.

Beispielprojekt @ code.qt.io

© 2025 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.