diff --git a/NOTICE-3RD-PARTY-CONTENT.md b/NOTICE-3RD-PARTY-CONTENT.md index f4d3277b..e87b6828 100644 --- a/NOTICE-3RD-PARTY-CONTENT.md +++ b/NOTICE-3RD-PARTY-CONTENT.md @@ -13,12 +13,12 @@ |distlib|0.3.8|Python Software Foundation License| |distro|1.8.0|Apache 2.0| |fasteners|0.19|Apache 2.0| -|filelock|3.13.1|The Unlicense (Unlicense)| +|filelock|3.13.3|The Unlicense (Unlicense)| |gcovr|5.2|BSD| |identify|2.5.35|MIT| |idna|3.6|BSD| |Jinja2|3.1.3|New BSD| -|lxml|5.1.0|New BSD| +|lxml|5.2.1|New BSD| |MarkupSafe|2.1.5|New BSD| |node-semver|0.6.1|MIT| |nodeenv|1.8.0|BSD| diff --git a/examples/seat-adjuster/AppManifest.json b/examples/seat-adjuster/AppManifest.json index 59e57578..04de300d 100644 --- a/examples/seat-adjuster/AppManifest.json +++ b/examples/seat-adjuster/AppManifest.json @@ -10,7 +10,7 @@ "required": [ { "path": "Vehicle.Cabin.Seat.Row1.Pos1.Position", - "access": "read" + "access": "write" }, { "path": "Vehicle.Speed", @@ -25,7 +25,7 @@ "config": { "reads": [ "seatadjuster/setPosition/request" ], "writes": [ - "seatadjuster/setPosition/response", + "seatadjuster/setPosition/response", "seatadjuster/currentPosition" ] } diff --git a/examples/seat-adjuster/src/SeatAdjusterApp.cpp b/examples/seat-adjuster/src/SeatAdjusterApp.cpp index 14a05130..bd3f4ac5 100644 --- a/examples/seat-adjuster/src/SeatAdjusterApp.cpp +++ b/examples/seat-adjuster/src/SeatAdjusterApp.cpp @@ -20,6 +20,7 @@ #include "sdk/QueryBuilder.h" #include "sdk/vdb/IVehicleDataBrokerClient.h" +#include #include #include @@ -126,8 +127,17 @@ void SeatAdjusterApp::onErrorTopic(const velocitas::Status& status) { } } // namespace example +std::unique_ptr myApp; + +void signal_handler(int sig) { + velocitas::logger().info("App terminating signal received: {}", sig); + myApp->stop(); +} + int main(int argc, char** argv) { - example::SeatAdjusterApp myApp; - myApp.run(); + signal(SIGINT, signal_handler); + + myApp = std::make_unique(); + myApp->run(); return 0; } diff --git a/examples/set-data-points/src/SetDataPointsApp.cpp b/examples/set-data-points/src/SetDataPointsApp.cpp index 72d237fa..4b3d3e7b 100644 --- a/examples/set-data-points/src/SetDataPointsApp.cpp +++ b/examples/set-data-points/src/SetDataPointsApp.cpp @@ -22,6 +22,7 @@ #include "sdk/vdb/IVehicleDataBrokerClient.h" #include "vehicle_model/Vehicle.h" +#include #include #include @@ -77,8 +78,17 @@ class SetDataPointsApp : public velocitas::VehicleApp { } // namespace example +std::unique_ptr myApp; + +void signal_handler(int sig) { + velocitas::logger().info("App terminating signal received: {}", sig); + myApp->stop(); +} + int main(int argc, char** argv) { - example::SetDataPointsApp myApp; - myApp.run(); + signal(SIGINT, signal_handler); + + myApp = std::make_unique(); + myApp->run(); return 0; } diff --git a/sdk/include/sdk/Model.h b/sdk/include/sdk/Model.h index 9ea3b2e7..43c99886 100644 --- a/sdk/include/sdk/Model.h +++ b/sdk/include/sdk/Model.h @@ -45,8 +45,8 @@ class Service : public Node { public: using Node::Node; - std::string getLocation() const; - Middleware::Metadata getMiddlewareMetadata() const; + [[nodiscard]] std::string getLocation() const; + [[nodiscard]] Middleware::Metadata getMiddlewareMetadata() const; }; } // namespace velocitas diff --git a/sdk/include/sdk/VehicleApp.h b/sdk/include/sdk/VehicleApp.h index f994e731..536fdea6 100644 --- a/sdk/include/sdk/VehicleApp.h +++ b/sdk/include/sdk/VehicleApp.h @@ -20,8 +20,10 @@ #include "sdk/AsyncResult.h" #include "sdk/DataPointReply.h" +#include #include #include +#include #include namespace velocitas { @@ -110,7 +112,7 @@ class VehicleApp { * the data point value of the requested data point. */ template - AsyncResultPtr_t + [[nodiscard]] AsyncResultPtr_t getDataPoint(const TDataPoint& dataPoint) const { return getDataPoint_internal(dataPoint)->template map( [&dataPoint](const DataPointReply& dataPointValues) { @@ -146,6 +148,9 @@ class VehicleApp { std::shared_ptr m_vdbClient; std::shared_ptr m_pubSubClient; + bool m_isRunning{false}; + std::mutex m_stopWaitMutex; + std::condition_variable m_stopWaitCV; }; } // namespace velocitas diff --git a/sdk/include/sdk/grpc/GrpcClient.h b/sdk/include/sdk/grpc/GrpcClient.h index 63f9a960..5271fcda 100644 --- a/sdk/include/sdk/grpc/GrpcClient.h +++ b/sdk/include/sdk/grpc/GrpcClient.h @@ -23,25 +23,24 @@ namespace velocitas { -class RecurringJob; class GrpcCall; class GrpcClient { public: - GrpcClient(); - virtual ~GrpcClient(); + GrpcClient() = default; + virtual ~GrpcClient() = default; GrpcClient(const GrpcClient&) = delete; GrpcClient(GrpcClient&&) = delete; GrpcClient& operator=(const GrpcClient&) = delete; GrpcClient& operator=(GrpcClient&&) = delete; - void addActiveCall(std::shared_ptr call); + void addActiveCall(std::shared_ptr call); + [[nodiscard]] size_t getNumActiveCalls() const { return m_activeCalls.size(); } +private: void pruneCompletedRequests(); -private: - std::shared_ptr m_recurringJob; std::vector> m_activeCalls; std::mutex m_mutex; }; diff --git a/sdk/src/sdk/VehicleApp.cpp b/sdk/src/sdk/VehicleApp.cpp index 1a53bf85..0492daf5 100644 --- a/sdk/src/sdk/VehicleApp.cpp +++ b/sdk/src/sdk/VehicleApp.cpp @@ -25,10 +25,7 @@ #include #include -#include #include -#include -#include namespace velocitas { @@ -40,26 +37,37 @@ VehicleApp::VehicleApp(std::shared_ptr vdbClient, } void VehicleApp::run() { - logger().info("Running App..."); + logger().info("Starting app ..."); Middleware::getInstance().start(); Middleware::getInstance().waitUntilReady(); m_pubSubClient->connect(); onStart(); + { + std::unique_lock lk(m_stopWaitMutex); + m_isRunning = true; + } + logger().info("App is running."); - // TODO: Fix busy waiting - while (true) { - std::this_thread::sleep_for(std::chrono::seconds(1)); + { + std::unique_lock lk(m_stopWaitMutex); + m_stopWaitCV.wait(lk, [this] { return !m_isRunning; }); } + logger().info("App stopped."); } void VehicleApp::stop() { - logger().info("Stopping App..."); + logger().info("Stopping app ..."); onStop(); m_pubSubClient->disconnect(); - Middleware::getInstance().stop(); + + { + std::unique_lock lk(m_stopWaitMutex); + m_isRunning = false; + m_stopWaitCV.notify_all(); + } } AsyncSubscriptionPtr_t VehicleApp::subscribeToTopic(const std::string& topic) { diff --git a/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.cpp b/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.cpp index bc14e122..936adb04 100644 --- a/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.cpp +++ b/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.cpp @@ -23,9 +23,7 @@ namespace velocitas { BrokerAsyncGrpcFacade::BrokerAsyncGrpcFacade(std::shared_ptr channel) - : m_channel{std::move(channel)} { - m_stub = std::make_shared(m_channel); -} + : m_stub{sdv::databroker::v1::Broker::NewStub(channel)} {} void BrokerAsyncGrpcFacade::GetDatapoints( const std::vector& datapoints, @@ -51,6 +49,7 @@ void BrokerAsyncGrpcFacade::GetDatapoints( } catch (std::exception& e) { logger().error("GRPC: Exception occurred during \"GetDatapoints\": {}", e.what()); } + callData->m_isComplete = true; }; addActiveCall(callData); @@ -83,6 +82,7 @@ void BrokerAsyncGrpcFacade::SetDatapoints( } catch (std::exception& e) { logger().error("GRPC: Exception occurred during \"SetDatapoints\": {}", e.what()); } + callData->m_isComplete = true; }; addActiveCall(callData); @@ -110,10 +110,11 @@ void BrokerAsyncGrpcFacade::Subscribe( callData->onData(itemHandler); - callData->onFinish([errorHandler](const auto& status) { + callData->onFinish([callData, errorHandler](const auto& status) { if (!status.ok()) { errorHandler(status); } + callData->m_isComplete = true; }); callData->startCall(); diff --git a/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.h b/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.h index 2509f116..b414cb4a 100644 --- a/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.h +++ b/sdk/src/sdk/grpc/BrokerAsyncGrpcFacade.h @@ -22,11 +22,16 @@ #include "sdv/databroker/v1/broker.grpc.pb.h" +#include +#include #include +#include +#include namespace grpc { class Channel; -} +class Status; +} // namespace grpc namespace velocitas { @@ -50,8 +55,7 @@ class BrokerAsyncGrpcFacade : public AsyncGrpcFacade, GrpcClient { std::function errorHandler); private: - std::shared_ptr m_stub; - std::shared_ptr m_channel; + std::unique_ptr m_stub; }; } // namespace velocitas diff --git a/sdk/src/sdk/grpc/GrpcClient.cpp b/sdk/src/sdk/grpc/GrpcClient.cpp index d7dc99d3..af20ac7f 100644 --- a/sdk/src/sdk/grpc/GrpcClient.cpp +++ b/sdk/src/sdk/grpc/GrpcClient.cpp @@ -15,19 +15,16 @@ */ #include "sdk/grpc/GrpcClient.h" -#include "sdk/ThreadPool.h" #include "sdk/grpc/GrpcCall.h" namespace velocitas { -GrpcClient::GrpcClient() { - m_recurringJob = std::make_shared([this]() { pruneCompletedRequests(); }); - ThreadPool::getInstance()->enqueue(m_recurringJob); -} - -GrpcClient::~GrpcClient() { - m_recurringJob->cancel(); - m_recurringJob->waitForTermination(); +void GrpcClient::addActiveCall(std::shared_ptr call) { + pruneCompletedRequests(); + { + std::scoped_lock lock(m_mutex); + m_activeCalls.emplace_back(call); + } } void GrpcClient::pruneCompletedRequests() { @@ -40,9 +37,4 @@ void GrpcClient::pruneCompletedRequests() { } } -void GrpcClient::addActiveCall(std::shared_ptr call) { - std::scoped_lock lock(m_mutex); - m_activeCalls.emplace_back(call); -} - } // namespace velocitas diff --git a/sdk/tests/unit/CMakeLists.txt b/sdk/tests/unit/CMakeLists.txt index 4b485f26..acb2520a 100644 --- a/sdk/tests/unit/CMakeLists.txt +++ b/sdk/tests/unit/CMakeLists.txt @@ -28,10 +28,11 @@ add_executable(${TARGET_NAME} ScopedBoolInverter_tests.cpp ThreadPool_tests.cpp Utils_tests.cpp - VehicleDataBrokerClient_tests.cpp QueryBuilder_tests.cpp #PubSub_tests.cpp TestBaseUsingEnvVars.cpp + grpc/GrpcClient_tests.cpp + grpc/VehicleDataBrokerClient_tests.cpp ) add_dependencies(${TARGET_NAME} diff --git a/sdk/tests/unit/grpc/GrpcClient_tests.cpp b/sdk/tests/unit/grpc/GrpcClient_tests.cpp new file mode 100644 index 00000000..96c1b453 --- /dev/null +++ b/sdk/tests/unit/grpc/GrpcClient_tests.cpp @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdk/grpc/GrpcCall.h" +#include "sdk/grpc/GrpcClient.h" + +#include + +using namespace velocitas; + +TEST(Test_GrpcClient, addActiveCall_newlyCreatedGrpcClient_oneActiveCall) { + // preparation + GrpcClient cut; + + // test + cut.addActiveCall(std::make_shared()); + EXPECT_EQ(1, cut.getNumActiveCalls()); +} + +TEST(Test_GrpcClient, addActiveCall_oneActiveCallPresent_twoActiveCalls) { + // preparation + GrpcClient cut; + cut.addActiveCall(std::make_shared()); + + // test + cut.addActiveCall(std::make_shared()); + EXPECT_EQ(2, cut.getNumActiveCalls()); +} + +TEST(Test_GrpcClient, addActiveCall_oneFinishedCallPresent_oneActiveCall) { + // preparation + GrpcClient cut; + auto finishedCall = std::make_shared(); + cut.addActiveCall(finishedCall); + finishedCall->m_isComplete = true; + + // test + cut.addActiveCall(std::make_shared()); + EXPECT_EQ(1, cut.getNumActiveCalls()); +} + +TEST(Test_GrpcClient, addActiveCall_oneFinishedAndOneActiveCallPresent_finishedCallRemoved) { + // preparation + GrpcClient cut; + auto finishedCall = std::make_shared(); + cut.addActiveCall(finishedCall); + auto activeCall = std::make_shared(); + cut.addActiveCall(activeCall); + finishedCall->m_isComplete = true; + + // test + auto anotherActiveCall = std::make_shared(); + cut.addActiveCall(anotherActiveCall); + EXPECT_EQ(2, cut.getNumActiveCalls()); + EXPECT_EQ(1, finishedCall.use_count()); // no other reference held to this pointer + EXPECT_EQ(2, activeCall.use_count()); + EXPECT_EQ(2, anotherActiveCall.use_count()); +} diff --git a/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp b/sdk/tests/unit/grpc/VehicleDataBrokerClient_tests.cpp similarity index 100% rename from sdk/tests/unit/VehicleDataBrokerClient_tests.cpp rename to sdk/tests/unit/grpc/VehicleDataBrokerClient_tests.cpp