From 82c612c62fa58b1c28142228a872c4e35164655c Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:04:26 +0000 Subject: [PATCH 01/26] Implement KUKSAReader class in header file --- qt_app/include/kuksareader.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 qt_app/include/kuksareader.hpp diff --git a/qt_app/include/kuksareader.hpp b/qt_app/include/kuksareader.hpp new file mode 100644 index 000000000..9f27dc4a9 --- /dev/null +++ b/qt_app/include/kuksareader.hpp @@ -0,0 +1,14 @@ +#ifndef KUKSAREADER_HPP +#define KUKSAREADER_HPP + +#include + +class KUKSAReader : public QObject +{ + Q_OBJECT +public: + +private: +}; + +#endif // KUKSAREADER_HPP \ No newline at end of file From 54fa7630e1f969c086675723a4f1a147a22ce302 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:06:00 +0000 Subject: [PATCH 02/26] Add gRPC includes and namespace for KUKSAReader class --- qt_app/include/kuksareader.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qt_app/include/kuksareader.hpp b/qt_app/include/kuksareader.hpp index 9f27dc4a9..af013fe24 100644 --- a/qt_app/include/kuksareader.hpp +++ b/qt_app/include/kuksareader.hpp @@ -2,6 +2,11 @@ #define KUKSAREADER_HPP #include +#include +#include "val.grpc.pb.h" + +// KUKSA Val gRPC namespace to avoid long names +using namespace kuksa::val::v1; class KUKSAReader : public QObject { From 483bc2f18fdf674ef9247787bd857a90bca68676 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:07:12 +0000 Subject: [PATCH 03/26] Implement constructor and destructor for KUKSAReader class --- qt_app/include/kuksareader.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qt_app/include/kuksareader.hpp b/qt_app/include/kuksareader.hpp index af013fe24..6393dcd27 100644 --- a/qt_app/include/kuksareader.hpp +++ b/qt_app/include/kuksareader.hpp @@ -12,6 +12,8 @@ class KUKSAReader : public QObject { Q_OBJECT public: + explicit KUKSAReader(QObject *parent = nullptr); + ~KUKSAReader() override; private: }; From 5b7b11be2d5ec80065047911ec3c661f1c92252c Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:10:29 +0000 Subject: [PATCH 04/26] Refactor KUKSAReader class to add start slot and speedReceived signal --- qt_app/include/kuksareader.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qt_app/include/kuksareader.hpp b/qt_app/include/kuksareader.hpp index 6393dcd27..bc4881438 100644 --- a/qt_app/include/kuksareader.hpp +++ b/qt_app/include/kuksareader.hpp @@ -14,8 +14,17 @@ class KUKSAReader : public QObject public: explicit KUKSAReader(QObject *parent = nullptr); ~KUKSAReader() override; +public slots: + // Starts the data fetching process + void start(); + +signals: + // Emitted when new speed data is received + void speedReceived(float speed); private: + // gRPC client stub -> used to communicate with KUKSA Val server + std::unique_ptr m_stub_; }; #endif // KUKSAREADER_HPP \ No newline at end of file From c94baef1c8c80668cfd20c15e21d07949e99e8ef Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:14:09 +0000 Subject: [PATCH 05/26] Implement KUKSAReader class with constructor, destructor, and start method --- qt_app/src/kuksareader.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 qt_app/src/kuksareader.cpp diff --git a/qt_app/src/kuksareader.cpp b/qt_app/src/kuksareader.cpp new file mode 100644 index 000000000..3d08a99a8 --- /dev/null +++ b/qt_app/src/kuksareader.cpp @@ -0,0 +1,18 @@ +#include "kuksareader.hpp" + +void KUKSAReader::KUKSAReader + : QObject(parent) +{ + +} + +KUKSAReader::~KUKSAReader() +{ + +} + +void KUKSAReader::start() +{ + +} + From 8fc88dcc915d1c456d466f48e9b715ae203856ee Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:30:48 +0000 Subject: [PATCH 06/26] Refactor KUKSAReader constructor and destructor for clarity; enhance start method with connection and subscription logic --- qt_app/src/kuksareader.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/qt_app/src/kuksareader.cpp b/qt_app/src/kuksareader.cpp index 3d08a99a8..4e83cde1f 100644 --- a/qt_app/src/kuksareader.cpp +++ b/qt_app/src/kuksareader.cpp @@ -2,17 +2,29 @@ void KUKSAReader::KUKSAReader : QObject(parent) -{ - -} +{} KUKSAReader::~KUKSAReader() -{ - -} +{} void KUKSAReader::start() { + //1. Connect to Broker auto channel + auto channel = grpc::CreateChannel("localhost:55555", grpc::InsecureChannelCredentials()); + m_stub_ = VAL::NewStub(channel); + + //2. Subscribe to speed data + grpc::ClientContext context; + SubscribeRequest request; + + auto *entry = request.add_entries(); + entry->set_path("Vehicle.Speed"); + entry->add_fields(Field::FIELD_VALUE); + + std::unique_ptr> reader( + m_stub_->Subscribe(&context, request)); + SubscribeResponse response; + qDebug() << "KuksaReader: Connected and Subscribed to Vehicle.Speed"; } From e78abf86bc8f2f897f47afea27cd4111600e504e Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:33:32 +0000 Subject: [PATCH 07/26] Implement loop for reading incoming speed data in KUKSAReader::start method --- qt_app/src/kuksareader.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/qt_app/src/kuksareader.cpp b/qt_app/src/kuksareader.cpp index 4e83cde1f..2daa0a6fe 100644 --- a/qt_app/src/kuksareader.cpp +++ b/qt_app/src/kuksareader.cpp @@ -26,5 +26,18 @@ void KUKSAReader::start() SubscribeResponse response; qDebug() << "KuksaReader: Connected and Subscribed to Vehicle.Speed"; + + //3. Loop to read incoming speed data (blocking call so run in separate thread (QThread)) + while(reader->Read(&response)) + { + for (const auto &update : response.updates()) + { + if (update.entry().path() == "Vehicle.Speed") + { + float speed = update.entry().value().float_(); + emit speedReceived(speed); + } + } + } } From e7fe99e9fca33d3adcce7fc2460f3313415c0dfb Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:28:54 +0000 Subject: [PATCH 08/26] Add Kuksa Middleware dependencies to CMakeLists.txt --- qt_app/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qt_app/CMakeLists.txt b/qt_app/CMakeLists.txt index c71826cfe..8de12fea1 100644 --- a/qt_app/CMakeLists.txt +++ b/qt_app/CMakeLists.txt @@ -9,6 +9,12 @@ set(CMAKE_AUTOUIC ON) # Use Qt6 find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Qml QuickControls2 SerialBus) +# --- Find Kuksa Middleware Dependencies --- +find_package(Protobuf REQUIRED) +find_package(gRPC REQUIRED) +find_program(gRPC_PLUGIN_EXECUTABLE grpc_cpp_plugin) # Tool to generate network code +find_package(absl REQUIRED) # Google common libraries (Critical dependency) + file(GLOB_RECURSE APP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" ) From 758fc3878a76ba2838e38d1a0225b24620aee173 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:29:44 +0000 Subject: [PATCH 09/26] Add Protocol Buffers code generation paths in CMakeLists.txt --- qt_app/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qt_app/CMakeLists.txt b/qt_app/CMakeLists.txt index 8de12fea1..9d1c8a0e0 100644 --- a/qt_app/CMakeLists.txt +++ b/qt_app/CMakeLists.txt @@ -15,6 +15,13 @@ find_package(gRPC REQUIRED) find_program(gRPC_PLUGIN_EXECUTABLE grpc_cpp_plugin) # Tool to generate network code find_package(absl REQUIRED) # Google common libraries (Critical dependency) +# --- Protocol Buffers Code Generation --- +# Define paths for input (.proto) and output (generated C++) +set(PROTO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/proto") +set(PROTO_SRCS "${PROTO_DIR}/val.proto" "${PROTO_DIR}/types.proto") +set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +file(MAKE_DIRECTORY ${GEN_DIR}) + file(GLOB_RECURSE APP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" ) From 50d4c85455aa0b877b882917b252e4bab542491b Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:32:04 +0000 Subject: [PATCH 10/26] Add initial Protocol Buffers definitions for VSS entries and RPC services --- qt_app/proto/types.proto | 288 +++++++++++++++++++++++++++++++++++++++ qt_app/proto/val.proto | 126 +++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100644 qt_app/proto/types.proto create mode 100644 qt_app/proto/val.proto diff --git a/qt_app/proto/types.proto b/qt_app/proto/types.proto new file mode 100644 index 000000000..8914e7aff --- /dev/null +++ b/qt_app/proto/types.proto @@ -0,0 +1,288 @@ +/******************************************************************************** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License 2.0 which is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +syntax = "proto3"; + +// I added V1 as in databroker. Is this good practice? +package kuksa.val.v1; +import "google/protobuf/timestamp.proto"; + +option go_package = "kuksa/val/v1"; + +// Describes a VSS entry +// When requesting an entry, the amount of information returned can +// be controlled by specifying either a `View` or a set of `Field`s. +message DataEntry { + // Defines the full VSS path of the entry. + string path = 1; // [field: FIELD_PATH] + + // The value (datapoint) + Datapoint value = 2; // [field: FIELD_VALUE] + + // Actuator target (only used if the entry is an actuator) + Datapoint actuator_target = 3; // [field: FIELD_ACTUATOR_TARGET] + + // Metadata for this entry + Metadata metadata = 10; // [field: FIELD_METADATA] +} + +message Datapoint { + google.protobuf.Timestamp timestamp = 1; + + oneof value { + string string = 11; + bool bool = 12; + sint32 int32 = 13; + sint64 int64 = 14; + uint32 uint32 = 15; + uint64 uint64 = 16; + float float = 17; + double double = 18; + StringArray string_array = 21; + BoolArray bool_array = 22; + Int32Array int32_array = 23; + Int64Array int64_array = 24; + Uint32Array uint32_array = 25; + Uint64Array uint64_array = 26; + FloatArray float_array = 27; + DoubleArray double_array = 28; + } +} + +message Metadata { + // Data type + // The VSS data type of the entry (i.e. the value, min, max etc). + // + // NOTE: protobuf doesn't have int8, int16, uint8 or uint16 which means + // that these values must be serialized as int32 and uint32 respectively. + DataType data_type = 11; // [field: FIELD_METADATA_DATA_TYPE] + + // Entry type + EntryType entry_type = 12; // [field: FIELD_METADATA_ENTRY_TYPE] + + // Description + // Describes the meaning and content of the entry. + optional string description = 13; // [field: FIELD_METADATA_DESCRIPTION] + + // Comment [optional] + // A comment can be used to provide additional informal information + // on a entry. + optional string comment = 14; // [field: FIELD_METADATA_COMMENT] + + // Deprecation [optional] + // Whether this entry is deprecated. Can contain recommendations of what + // to use instead. + optional string deprecation = 15; // [field: FIELD_METADATA_DEPRECATION] + + // Unit [optional] + // The unit of measurement + optional string unit = 16; // [field: FIELD_METADATA_UNIT] + + // Value restrictions [optional] + // Restrict which values are allowed. + // Only restrictions matching the DataType {datatype} above are valid. + ValueRestriction value_restriction = 17; // [field: FIELD_METADATA_VALUE_RESTRICTION] + + // Entry type specific metadata + oneof entry_specific { + Actuator actuator = 20; // [field: FIELD_METADATA_ACTUATOR] + Sensor sensor = 30; // [field: FIELD_METADATA_SENSOR] + Attribute attribute = 40; // [field: FIELD_METADATA_ATTRIBUTE] + } +} + +/////////////////////// +// Actuator specific fields +message Actuator { + // Nothing for now +} + +//////////////////////// +// Sensor specific +message Sensor { + // Nothing for now +} + +//////////////////////// +// Attribute specific +message Attribute { + // Nothing for now. +} + +// Value restriction +// +// One ValueRestriction{type} for each type, since +// they don't make sense unless the types match +// +message ValueRestriction { + oneof type { + ValueRestrictionString string = 21; + // For signed VSS integers + ValueRestrictionInt signed = 22; + // For unsigned VSS integers + ValueRestrictionUint unsigned = 23; + // For floating point VSS values (float and double) + ValueRestrictionFloat floating_point = 24; + } +} + +message ValueRestrictionInt { + optional sint64 min = 1; + optional sint64 max = 2; + repeated sint64 allowed_values = 3; +} + +message ValueRestrictionUint { + optional uint64 min = 1; + optional uint64 max = 2; + repeated uint64 allowed_values = 3; +} + +message ValueRestrictionFloat { + optional double min = 1; + optional double max = 2; + + // allowed for doubles/floats not recommended + repeated double allowed_values = 3; +} + +// min, max doesn't make much sense for a string +message ValueRestrictionString { + repeated string allowed_values = 3; +} + +// VSS Data type of a signal +// +// Protobuf doesn't support int8, int16, uint8 or uint16. +// These are mapped to int32 and uint32 respectively. +// +enum DataType { + DATA_TYPE_UNSPECIFIED = 0; + DATA_TYPE_STRING = 1; + DATA_TYPE_BOOLEAN = 2; + DATA_TYPE_INT8 = 3; + DATA_TYPE_INT16 = 4; + DATA_TYPE_INT32 = 5; + DATA_TYPE_INT64 = 6; + DATA_TYPE_UINT8 = 7; + DATA_TYPE_UINT16 = 8; + DATA_TYPE_UINT32 = 9; + DATA_TYPE_UINT64 = 10; + DATA_TYPE_FLOAT = 11; + DATA_TYPE_DOUBLE = 12; + DATA_TYPE_TIMESTAMP = 13; + DATA_TYPE_STRING_ARRAY = 20; + DATA_TYPE_BOOLEAN_ARRAY = 21; + DATA_TYPE_INT8_ARRAY = 22; + DATA_TYPE_INT16_ARRAY = 23; + DATA_TYPE_INT32_ARRAY = 24; + DATA_TYPE_INT64_ARRAY = 25; + DATA_TYPE_UINT8_ARRAY = 26; + DATA_TYPE_UINT16_ARRAY = 27; + DATA_TYPE_UINT32_ARRAY = 28; + DATA_TYPE_UINT64_ARRAY = 29; + DATA_TYPE_FLOAT_ARRAY = 30; + DATA_TYPE_DOUBLE_ARRAY = 31; + DATA_TYPE_TIMESTAMP_ARRAY = 32; +} + +// Entry type +enum EntryType { + ENTRY_TYPE_UNSPECIFIED = 0; + ENTRY_TYPE_ATTRIBUTE = 1; + ENTRY_TYPE_SENSOR = 2; + ENTRY_TYPE_ACTUATOR = 3; +} + +// A `View` specifies a set of fields which should +// be populated in a `DataEntry` (in a response message) +enum View { + VIEW_UNSPECIFIED = 0; // Unspecified. Equivalent to VIEW_CURRENT_VALUE unless `fields` are explicitly set. + VIEW_CURRENT_VALUE = 1; // Populate DataEntry with value. + VIEW_TARGET_VALUE = 2; // Populate DataEntry with actuator target. + VIEW_METADATA = 3; // Populate DataEntry with metadata. + VIEW_FIELDS = 10; // Populate DataEntry only with requested fields. + VIEW_ALL = 20; // Populate DataEntry with everything. +} + +// A `Field` corresponds to a specific field of a `DataEntry`. +// +// It can be used to: +// * populate only specific fields of a `DataEntry` response. +// * specify which fields of a `DataEntry` should be set as +// part of a `Set` request. +// * subscribe to only specific fields of a data entry. +// * convey which fields of an updated `DataEntry` have changed. +enum Field { + FIELD_UNSPECIFIED = 0; // "*" i.e. everything + FIELD_PATH = 1; // path + FIELD_VALUE = 2; // value + FIELD_ACTUATOR_TARGET = 3; // actuator_target + FIELD_METADATA = 10; // metadata.* + FIELD_METADATA_DATA_TYPE = 11; // metadata.data_type + FIELD_METADATA_DESCRIPTION = 12; // metadata.description + FIELD_METADATA_ENTRY_TYPE = 13; // metadata.entry_type + FIELD_METADATA_COMMENT = 14; // metadata.comment + FIELD_METADATA_DEPRECATION = 15; // metadata.deprecation + FIELD_METADATA_UNIT = 16; // metadata.unit + FIELD_METADATA_VALUE_RESTRICTION = 17; // metadata.value_restriction.* + FIELD_METADATA_ACTUATOR = 20; // metadata.actuator.* + FIELD_METADATA_SENSOR = 30; // metadata.sensor.* + FIELD_METADATA_ATTRIBUTE = 40; // metadata.attribute.* +} + +// Error response shall be an HTTP-like code. +// Should follow https://www.w3.org/TR/viss2-transport/#status-codes. +message Error { + uint32 code = 1; + string reason = 2; + string message = 3; +} + +// Used in get/set requests to report errors for specific entries +message DataEntryError { + string path = 1; // vss path + Error error = 2; +} + +message StringArray { + repeated string values = 1; +} + +message BoolArray { + repeated bool values = 1; +} + +message Int32Array { + repeated sint32 values = 1; +} + +message Int64Array { + repeated sint64 values = 1; +} + +message Uint32Array { + repeated uint32 values = 1; +} + +message Uint64Array { + repeated uint64 values = 1; +} + +message FloatArray { + repeated float values = 1; +} + +message DoubleArray { + repeated double values = 1; +} diff --git a/qt_app/proto/val.proto b/qt_app/proto/val.proto new file mode 100644 index 000000000..dde49b5e9 --- /dev/null +++ b/qt_app/proto/val.proto @@ -0,0 +1,126 @@ +/******************************************************************************** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License 2.0 which is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +syntax = "proto3"; + +package kuksa.val.v1; + +option go_package = "kuksa/val/v1"; + +import "kuksa/val/v1/types.proto"; + +// Note on authorization: +// Tokens (auth-token or auth-uuid) are sent as (GRPC / http2) metadata. +// +// The auth-token is a JWT compliant token as the examples found here: +// https://github.com/eclipse-kuksa/kuksa-databroker/tree/main/certificates/jwt +// +// See also https://github.com/eclipse-kuksa/kuksa-databroker/blob/main/doc/authorization.md#jwt-access-token +// +// Upon reception of auth-token, server shall generate an auth-uuid in metadata +// that the client can use instead of auth-token in subsequent calls. + +service VAL { + // Get entries + rpc Get(GetRequest) returns (GetResponse); + + // Set entries + rpc Set(SetRequest) returns (SetResponse); + + rpc StreamedUpdate(stream StreamedUpdateRequest) returns (stream StreamedUpdateResponse); + + // Subscribe to a set of entries + // + // Returns a stream of notifications. + // + // InvalidArgument is returned if the request is malformed. + rpc Subscribe(SubscribeRequest) returns (stream SubscribeResponse); + + // Shall return information that allows the client to determine + // what server/server implementation/version it is talking to + // eg. kuksa-databroker 0.5.1 + rpc GetServerInfo(GetServerInfoRequest) returns (GetServerInfoResponse); +} + +// Define which data we want +message EntryRequest { + string path = 1; + View view = 2; + repeated Field fields = 3; +} + +// Request a set of entries. +message GetRequest { + repeated EntryRequest entries = 1; +} + +// Global errors are specified in `error`. +// Errors for individual entries are specified in `errors`. +message GetResponse { + repeated DataEntry entries = 1; + repeated DataEntryError errors = 2; + Error error = 3; +} + +// Define the data we want to set +message EntryUpdate { + DataEntry entry = 1; + repeated Field fields = 2; +} + +// A list of entries to be updated +message SetRequest { + repeated EntryUpdate updates = 1; +} + +// Global errors are specified in `error`. +// Errors for individual entries are specified in `errors`. +message SetResponse { + Error error = 1; + repeated DataEntryError errors = 2; +} + +message StreamedUpdateRequest { + repeated EntryUpdate updates = 1; +} + +message StreamedUpdateResponse { + Error error = 1; + repeated DataEntryError errors = 2; +} + +// Define what to subscribe to +message SubscribeEntry { + string path = 1; + View view = 2; + repeated Field fields = 3; +} + +// Subscribe to changes in datapoints. +message SubscribeRequest { + repeated SubscribeEntry entries = 1; +} + +// A subscription response +message SubscribeResponse { + repeated EntryUpdate updates = 1; +} + +message GetServerInfoRequest { + // Nothing yet +} + +message GetServerInfoResponse { + string name = 1; + string version = 2; +} From 6d3901fb7c0ac576957804e6f138529504755cfa Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:33:46 +0000 Subject: [PATCH 11/26] Add gRPC generated files to CMake build process --- qt_app/CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/qt_app/CMakeLists.txt b/qt_app/CMakeLists.txt index 9d1c8a0e0..12231bc96 100644 --- a/qt_app/CMakeLists.txt +++ b/qt_app/CMakeLists.txt @@ -22,6 +22,20 @@ set(PROTO_SRCS "${PROTO_DIR}/val.proto" "${PROTO_DIR}/types.proto") set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") file(MAKE_DIRECTORY ${GEN_DIR}) +# Command to auto-generate C++ sources from .proto files before compilation +add_custom_command( + OUTPUT "${GEN_DIR}/val.pb.cc" "${GEN_DIR}/val.pb.h" + "${GEN_DIR}/val.grpc.pb.cc" "${GEN_DIR}/val.grpc.pb.h" + "${GEN_DIR}/types.pb.cc" "${GEN_DIR}/types.pb.h" + # Step 1: Generate gRPC Network Stubs + COMMAND ${Protobuf_PROTOC_EXECUTABLE} + ARGS --grpc_out=${GEN_DIR} --plugin=protoc-gen-grpc=${gRPC_PLUGIN_EXECUTABLE} -I${PROTO_DIR} ${PROTO_SRCS} + # Step 2: Generate Data Serialization Classes + COMMAND ${Protobuf_PROTOC_EXECUTABLE} + ARGS --cpp_out=${GEN_DIR} -I${PROTO_DIR} ${PROTO_SRCS} + DEPENDS ${PROTO_SRCS} +) + file(GLOB_RECURSE APP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" ) From 9058fe833d56895ac420eacc1beae0dbaa7e33eb Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:35:14 +0000 Subject: [PATCH 12/26] Add generated gRPC and Protocol Buffers source files to myqtapp target --- qt_app/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qt_app/CMakeLists.txt b/qt_app/CMakeLists.txt index 12231bc96..7be77501b 100644 --- a/qt_app/CMakeLists.txt +++ b/qt_app/CMakeLists.txt @@ -44,6 +44,7 @@ add_executable(myqtapp ${APP_SOURCES}) target_include_directories(myqtapp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${GEN_DIR} ) target_sources(myqtapp PRIVATE @@ -51,6 +52,10 @@ target_sources(myqtapp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/resources/resources.qrc" "${CMAKE_CURRENT_SOURCE_DIR}/include/vehicledata.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/canreader.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/include/kuksareader.hpp" + "${GEN_DIR}/val.pb.cc" + "${GEN_DIR}/val.grpc.pb.cc" + "${GEN_DIR}/types.pb.cc" ) target_link_libraries(myqtapp PRIVATE Qt6::Core Qt6::Gui Qt6::Quick Qt6::Qml Qt6::QuickControls2 Qt6::SerialBus) From df1479478125c0d7fa937589e59078358f851f75 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:36:42 +0000 Subject: [PATCH 13/26] Add gRPC and Abseil dependencies to target_link_libraries for myqtapp --- qt_app/CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qt_app/CMakeLists.txt b/qt_app/CMakeLists.txt index 7be77501b..dad761bd5 100644 --- a/qt_app/CMakeLists.txt +++ b/qt_app/CMakeLists.txt @@ -58,7 +58,14 @@ target_sources(myqtapp PRIVATE "${GEN_DIR}/types.pb.cc" ) -target_link_libraries(myqtapp PRIVATE Qt6::Core Qt6::Gui Qt6::Quick Qt6::Qml Qt6::QuickControls2 Qt6::SerialBus) +target_link_libraries(myqtapp PRIVATE + Qt6::Core Qt6::Gui Qt6::Quick Qt6::Qml Qt6::QuickControls2 Qt6::SerialBus + gRPC::grpc++ # The gRPC Transport Layer + protobuf::libprotobuf # The Serialization Library + absl::base # Abseil Base (Required by gRPC) + absl::strings # Abseil Strings + absl::log_internal_check_op # <--- Critical fix for "Undefined Symbol" linker errors +) install(TARGETS myqtapp RUNTIME DESTINATION bin) From b5c976a12e504d40f6a1091892390b41cbcbcb05 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:46:51 +0000 Subject: [PATCH 14/26] Set application name for DrivaPi Dashboard in main.cpp --- qt_app/src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index 4f1b37ea4..76a75e3d9 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -14,6 +14,7 @@ int main(int argc, char *argv[]) // Create QML engine QQmlApplicationEngine engine; + app.setApplicationName("DrivaPi Dashboard"); // Create VehicleData using QScopedPointer for automatic cleanup QScopedPointer vehicleData(new VehicleData()); From 196446f8cb43a6f2a2f15b6151c9db2ca63cc072 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:47:43 +0000 Subject: [PATCH 15/26] Add argument parsing for Kuksa mode in main.cpp --- qt_app/src/main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index 76a75e3d9..be1798a0a 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -7,6 +7,7 @@ #include #include "vehicledata.hpp" #include "canreader.hpp" +#include "kuksareader.hpp" int main(int argc, char *argv[]) { @@ -16,6 +17,21 @@ int main(int argc, char *argv[]) QQmlApplicationEngine engine; app.setApplicationName("DrivaPi Dashboard"); + // --- 1. ARGUMENT PARSING --- + QCommandLineParser parser; + parser.setApplicationDescription("Hybrid Dashboard (CAN / Kuksa)"); + parser.addHelpOption(); + + // Define the "--kuksa" or "-k" option + QCommandLineOption kuksaOption(QStringList() << "k" << "kuksa", + "Enable Kuksa mode (gRPC). Defaults to CAN if omitted."); + parser.addOption(kuksaOption); + + parser.process(app); + + // Check if the user passed the argument + bool useKuksa = parser.isSet(kuksaOption); + // Create VehicleData using QScopedPointer for automatic cleanup QScopedPointer vehicleData(new VehicleData()); From 12d5a06f0cb4cbc3f940d0c91ad1362f0c542d19 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:49:07 +0000 Subject: [PATCH 16/26] Refactor main.cpp to improve QML engine initialization and maintain code clarity --- qt_app/src/main.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index be1798a0a..b5fc7a535 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -11,10 +11,7 @@ int main(int argc, char *argv[]) { - QGuiApplication app(argc, argv); - - // Create QML engine - QQmlApplicationEngine engine; + QGuiApplication app(argc, argv) app.setApplicationName("DrivaPi Dashboard"); // --- 1. ARGUMENT PARSING --- @@ -32,9 +29,9 @@ int main(int argc, char *argv[]) // Check if the user passed the argument bool useKuksa = parser.isSet(kuksaOption); - // Create VehicleData using QScopedPointer for automatic cleanup + // UI and Engine setup + QQmlApplicationEngine engine; QScopedPointer vehicleData(new VehicleData()); - // Expose VehicleData to QML (keep ownership in C++) engine.rootContext()->setContextProperty("vehicleData", vehicleData.data()); From 31a8dc140a0342461f4eb4923674a6b7f3c67fec Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:52:40 +0000 Subject: [PATCH 17/26] Implement KUKSAReader setup in main.cpp for Kuksa mode support --- qt_app/src/main.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index b5fc7a535..84f6ee00e 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -35,17 +35,32 @@ int main(int argc, char *argv[]) // Expose VehicleData to QML (keep ownership in C++) engine.rootContext()->setContextProperty("vehicleData", vehicleData.data()); - // Create CANReader and move it to its own thread if needed - QThread *canThread = new QThread(&app); + // Worker thread setup for data reading + QThread *workerThread = new QThread(&app); + + if (useKuksa) + { + qInfo() << "Starting in KUKSA mode"; + // KUKSA Reader setup + KUKSAReader *kuksareader = new KUKSAReader(); + kuksareader->moveToThread(workerThread); + // Start KUKSAReader when thread starts + QObject::connect(workerThread, &QThread::started, kuksareader, &KUKSAReader::start); + // Ensure worker object is deleted when thread finishes (safe because it's a QObject) + QObject::connect(workerThread, &QThread::finished, kuksareader, &KUKSAReader::deleteLater); + // Forward speed data from KUKSAReader to VehicleData (thread-safe) + QObject::connect(kuksareader, &KUKSAReader::speedReceived, + vehicleData.data(), &VehicleData::handleSpeedUpdate); + } // Use a raw pointer for the worker object (we call deleteLater on it) CANReader *canReader = new CANReader(QStringLiteral("can1")); - canReader->moveToThread(canThread); + canReader->moveToThread(workerThread); // Start CANReader when thread starts - QObject::connect(canThread, &QThread::started, canReader, &CANReader::start); + QObject::connect(workerThread, &QThread::started, canReader, &CANReader::start); // Ensure worker object is deleted when thread finishes (safe because it's a QObject) - QObject::connect(canThread, &QThread::finished, canReader, &CANReader::deleteLater); + QObject::connect(workerThread, &QThread::finished, canReader, &CANReader::deleteLater); // Forward CAN messages from thread worker to UI (thread-safe) QObject::connect(canReader, &CANReader::canMessageReceived, From 27b1207195a788ec356cdc70058d99ba970aba8b Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:00:06 +0000 Subject: [PATCH 18/26] Refactor CAN and KUKSA reader setup in main.cpp for improved thread management and cleanup --- qt_app/src/main.cpp | 70 +++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index 84f6ee00e..d5037fa9f 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -51,47 +51,43 @@ int main(int argc, char *argv[]) // Forward speed data from KUKSAReader to VehicleData (thread-safe) QObject::connect(kuksareader, &KUKSAReader::speedReceived, vehicleData.data(), &VehicleData::handleSpeedUpdate); - } - - // Use a raw pointer for the worker object (we call deleteLater on it) - CANReader *canReader = new CANReader(QStringLiteral("can1")); - canReader->moveToThread(workerThread); - - // Start CANReader when thread starts - QObject::connect(workerThread, &QThread::started, canReader, &CANReader::start); - // Ensure worker object is deleted when thread finishes (safe because it's a QObject) - QObject::connect(workerThread, &QThread::finished, canReader, &CANReader::deleteLater); - - // Forward CAN messages from thread worker to UI (thread-safe) - QObject::connect(canReader, &CANReader::canMessageReceived, - vehicleData.data(), &VehicleData::handleCanMessage, - Qt::QueuedConnection); - - // Handle CANReader errors (lambda - no capture needed here) - QObject::connect(canReader, &CANReader::errorOccurred, [](const QString &msg) - { - qWarning() << "CANReader error:" << msg; - }); - - // Start the CAN thread - canThread->start(); + } else { + qInfo() << "Starting in CAN mode"; + // CAN Reader setup + CANReader *canReader = new CANReader(QStringLiteral("can1")); + canReader->moveToThread(workerThread); + // Start CANReader when thread starts + QObject::connect(workerThread, &QThread::started, canReader, &CANReader::start); + // Ensure worker object is deleted when thread finishes (safe because it's a QObject) + QObject::connect(workerThread, &QThread::finished, canReader, &CANReader::deleteLater); - // Clean up thread and worker on app exit. Capture raw pointers by value. - QObject::connect(&app, &QCoreApplication::aboutToQuit, [canThread, canReader]() { - // ask the thread worker to stop reading CAN messages (queued to worker thread) - QMetaObject::invokeMethod(canReader, "stop", Qt::QueuedConnection); + // Forward CAN messages from thread worker to UI (thread-safe) + QObject::connect(canReader, &CANReader::canMessageReceived, + vehicleData.data(), &VehicleData::handleCanMessage, + Qt::QueuedConnection); + } - // request thread to quit its event loop - canThread->quit(); + workerThread->start(); - // wait for thread to finish - if (!canThread->wait(2000)) { - qWarning() << "CAN thread did not quit in 2 seconds, terminating"; - canThread->terminate(); - canThread->wait(); + // CLEANUP HANDLING + QObject::connect(&app, &QCoreApplication::aboutToQuit, [workerThread, canReader, kuksaReader]() { + if (canReader) + { + // ask CAN reader to stop + QMetaObject::invokeMethod(canReader, "stop", Qt::QueuedConnection); + } + if (workerThread) + { + workerThread->quit(); + // wait for thread to finish + if (!workerThread->wait(2000)) { + qWarning() << "Worker thread did not quit in 2 seconds, terminating"; + workerThread->terminate(); + workerThread->wait(); + } + // delete the thread (worker will be deleted via deleteLater) + delete workerThread; } - // delete the thread (worker will be deleted via deleteLater) - delete canThread; }); // Load main QML file From 59fa00d616e113bdf6abdcc9c857ab6bf2b9e100 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:45:35 +0000 Subject: [PATCH 19/26] Add thread-safe handler for KUKSA speed updates in VehicleData --- qt_app/include/vehicledata.hpp | 3 +++ qt_app/src/vehicledata.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/qt_app/include/vehicledata.hpp b/qt_app/include/vehicledata.hpp index da278c96c..45beb5b56 100644 --- a/qt_app/include/vehicledata.hpp +++ b/qt_app/include/vehicledata.hpp @@ -50,6 +50,9 @@ class VehicleData : public QObject Q_INVOKABLE void changeGearUp(); Q_INVOKABLE void changeGearDown(); + // Thread-safe handlers for incoming data + void handleSpeedUpdate(float speed); + public slots: // Called by CANReader (queued connection) with raw payload + canId void handleCanMessage(const QByteArray &payload, uint32_t canId); diff --git a/qt_app/src/vehicledata.cpp b/qt_app/src/vehicledata.cpp index 1bcf0b932..2834149e1 100644 --- a/qt_app/src/vehicledata.cpp +++ b/qt_app/src/vehicledata.cpp @@ -245,3 +245,11 @@ void VehicleData::checkStaleProperties() } // Repeat for other keys if you want special handling } + +// KUKSA speed update handler +void VehicleData::handleSpeedUpdate(float speed) +{ + setSpeed(speed); // updates timestamp inside + // debug + // qDebug() << "Updated speed from KUKSA (m/s):" << speed; +} From 8baf2b9457f152d5e49ce6a16bd46549cdc6e32f Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:56:13 +0000 Subject: [PATCH 20/26] Update gRPC import path and change CANReader interface to use vcan0 for test --- qt_app/proto/val.proto | 2 +- qt_app/src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qt_app/proto/val.proto b/qt_app/proto/val.proto index dde49b5e9..26f72d59a 100644 --- a/qt_app/proto/val.proto +++ b/qt_app/proto/val.proto @@ -17,7 +17,7 @@ package kuksa.val.v1; option go_package = "kuksa/val/v1"; -import "kuksa/val/v1/types.proto"; +import "types.proto"; // Note on authorization: // Tokens (auth-token or auth-uuid) are sent as (GRPC / http2) metadata. diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index d5037fa9f..7d324f22a 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) } else { qInfo() << "Starting in CAN mode"; // CAN Reader setup - CANReader *canReader = new CANReader(QStringLiteral("can1")); + CANReader *canReader = new CANReader(QStringLiteral("vcan0")); canReader->moveToThread(workerThread); // Start CANReader when thread starts QObject::connect(workerThread, &QThread::started, canReader, &CANReader::start); From 409a6bd6c10d8ed5bed26d6e3341b03f979e7745 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:58:31 +0000 Subject: [PATCH 21/26] Fix constructor definition in KUKSAReader class and include QDebug header --- qt_app/include/kuksareader.hpp | 1 + qt_app/src/kuksareader.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/qt_app/include/kuksareader.hpp b/qt_app/include/kuksareader.hpp index bc4881438..f06dd3f72 100644 --- a/qt_app/include/kuksareader.hpp +++ b/qt_app/include/kuksareader.hpp @@ -2,6 +2,7 @@ #define KUKSAREADER_HPP #include +#include #include #include "val.grpc.pb.h" diff --git a/qt_app/src/kuksareader.cpp b/qt_app/src/kuksareader.cpp index 2daa0a6fe..a4ffcae8b 100644 --- a/qt_app/src/kuksareader.cpp +++ b/qt_app/src/kuksareader.cpp @@ -1,6 +1,6 @@ #include "kuksareader.hpp" -void KUKSAReader::KUKSAReader +KUKSAReader::KUKSAReader(QObject *parent) : QObject(parent) {} From c7a2575d5b62e6a77aae89e1916072c6af7944c7 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 22:04:14 +0000 Subject: [PATCH 22/26] Implement KUKSAReader and CANReader setup in main.cpp for improved thread management --- qt_app/src/main.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index 7d324f22a..affd609b3 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -5,13 +5,15 @@ #include #include #include +#include +#include #include "vehicledata.hpp" #include "canreader.hpp" #include "kuksareader.hpp" int main(int argc, char *argv[]) { - QGuiApplication app(argc, argv) + QGuiApplication app(argc, argv); app.setApplicationName("DrivaPi Dashboard"); // --- 1. ARGUMENT PARSING --- @@ -38,23 +40,27 @@ int main(int argc, char *argv[]) // Worker thread setup for data reading QThread *workerThread = new QThread(&app); + // Pointers to readers for cleanup + CANReader *canReader = nullptr; + KUKSAReader *kuksaReader = nullptr; + if (useKuksa) { qInfo() << "Starting in KUKSA mode"; // KUKSA Reader setup - KUKSAReader *kuksareader = new KUKSAReader(); - kuksareader->moveToThread(workerThread); + kuksaReader = new KUKSAReader(); + kuksaReader->moveToThread(workerThread); // Start KUKSAReader when thread starts - QObject::connect(workerThread, &QThread::started, kuksareader, &KUKSAReader::start); + QObject::connect(workerThread, &QThread::started, kuksaReader, &KUKSAReader::start); // Ensure worker object is deleted when thread finishes (safe because it's a QObject) - QObject::connect(workerThread, &QThread::finished, kuksareader, &KUKSAReader::deleteLater); + QObject::connect(workerThread, &QThread::finished, kuksaReader, &KUKSAReader::deleteLater); // Forward speed data from KUKSAReader to VehicleData (thread-safe) - QObject::connect(kuksareader, &KUKSAReader::speedReceived, + QObject::connect(kuksaReader, &KUKSAReader::speedReceived, vehicleData.data(), &VehicleData::handleSpeedUpdate); } else { qInfo() << "Starting in CAN mode"; // CAN Reader setup - CANReader *canReader = new CANReader(QStringLiteral("vcan0")); + canReader = new CANReader(QStringLiteral("vcan0")); canReader->moveToThread(workerThread); // Start CANReader when thread starts QObject::connect(workerThread, &QThread::started, canReader, &CANReader::start); From e86b2e8ce06b597e36b653eed992afebd37aab75 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Thu, 11 Dec 2025 22:17:59 +0000 Subject: [PATCH 23/26] Change CANReader interface to use 'can1' instead of 'vcan0' --- qt_app/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index affd609b3..ba8d646d4 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) } else { qInfo() << "Starting in CAN mode"; // CAN Reader setup - canReader = new CANReader(QStringLiteral("vcan0")); + canReader = new CANReader(QStringLiteral("can1")); canReader->moveToThread(workerThread); // Start CANReader when thread starts QObject::connect(workerThread, &QThread::started, canReader, &CANReader::start); From 1e67b8c0447014d94848c89337c4c2d89b643820 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Fri, 12 Dec 2025 12:40:16 +0000 Subject: [PATCH 24/26] add code for latency testing of KUKSAreader --- qt_app/src/kuksareader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qt_app/src/kuksareader.cpp b/qt_app/src/kuksareader.cpp index a4ffcae8b..270625ddd 100644 --- a/qt_app/src/kuksareader.cpp +++ b/qt_app/src/kuksareader.cpp @@ -35,6 +35,12 @@ void KUKSAReader::start() if (update.entry().path() == "Vehicle.Speed") { float speed = update.entry().value().float_(); + + //LATENCY TESTING CODE - REMOVE LATER + qint64 t1 = QDateTime::currentMSecsSinceEpoch(); + qDebug() << "KuksaReader: Received speed:" << speed << " at " << t1; + //END LATENCY TESTING CODE + emit speedReceived(speed); } } From 3adc95b59bd9eb41cbf0289f59e0c9614c03ab47 Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Fri, 12 Dec 2025 12:43:08 +0000 Subject: [PATCH 25/26] Add QDateTime for latency testing in KUKSAReader and update CANReader to use vcan0 --- kuksa-can-provider | 1 + qt_app/src/kuksareader.cpp | 4 +++- qt_app/src/main.cpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 160000 kuksa-can-provider diff --git a/kuksa-can-provider b/kuksa-can-provider new file mode 160000 index 000000000..34b71ced4 --- /dev/null +++ b/kuksa-can-provider @@ -0,0 +1 @@ +Subproject commit 34b71ced42175ac68b8eecfd513501cb4cab4cf2 diff --git a/qt_app/src/kuksareader.cpp b/qt_app/src/kuksareader.cpp index 270625ddd..57fffaf89 100644 --- a/qt_app/src/kuksareader.cpp +++ b/qt_app/src/kuksareader.cpp @@ -1,4 +1,6 @@ #include "kuksareader.hpp" +//QDateTime for latency testing - remove later +#include KUKSAReader::KUKSAReader(QObject *parent) : QObject(parent) @@ -40,7 +42,7 @@ void KUKSAReader::start() qint64 t1 = QDateTime::currentMSecsSinceEpoch(); qDebug() << "KuksaReader: Received speed:" << speed << " at " << t1; //END LATENCY TESTING CODE - + emit speedReceived(speed); } } diff --git a/qt_app/src/main.cpp b/qt_app/src/main.cpp index ba8d646d4..affd609b3 100644 --- a/qt_app/src/main.cpp +++ b/qt_app/src/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) } else { qInfo() << "Starting in CAN mode"; // CAN Reader setup - canReader = new CANReader(QStringLiteral("can1")); + canReader = new CANReader(QStringLiteral("vcan0")); canReader->moveToThread(workerThread); // Start CANReader when thread starts QObject::connect(workerThread, &QThread::started, canReader, &CANReader::start); From 9bbf777571faddfde64a58be29ab2c5cdf60cf0f Mon Sep 17 00:00:00 2001 From: melaniereis <166030520+melaniereis@users.noreply.github.com> Date: Fri, 12 Dec 2025 12:49:39 +0000 Subject: [PATCH 26/26] add latency testing code to canreader.cpp --- qt_app/src/canreader.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qt_app/src/canreader.cpp b/qt_app/src/canreader.cpp index 07e664fa2..83134a836 100644 --- a/qt_app/src/canreader.cpp +++ b/qt_app/src/canreader.cpp @@ -1,4 +1,6 @@ #include "canreader.hpp" +//QDateTime for latency testing - remove later +#include CANReader *reader0 = new CANReader(QStringLiteral("can1")); @@ -81,6 +83,12 @@ void CANReader::handleFramesReceived() QCanBusFrame frame = m_device->readFrame(); QByteArray payload = frame.payload(); uint32_t canId = static_cast(frame.frameId()); + + //LATENCY TESTING CODE - REMOVE LATER + qint64 t1 = QDateTime::currentMSecsSinceEpoch(); + qDebug() << "CANReader: Received CAN frame ID=0x" << QString::number(canId, 16) << " at " << t1; + //END LATENCY TESTING CODE + emit canMessageReceived(payload, canId); qDebug() << "Received CAN frame: ID=0x" << QString::number(canId, 16) << " Payload=" << payload.toHex(); }