From 032d8f1316bbc63656a76be1a34cf2d32e312b13 Mon Sep 17 00:00:00 2001 From: Tyler Payne Date: Thu, 16 Jul 2026 17:54:02 +0000 Subject: [PATCH 1/3] feat(core): add heartbeat watchdog (deadman timer) (#2895) While a watchdog timeout is configured, MAVSDK's periodic heartbeats are only sent as long as the watchdog keeps being fed at least once per timeout period, so heartbeats reflect the liveness of the client application. - Configure via Configuration::set_heartbeat_watchdog_timeout_s() or at runtime via Mavsdk::set_heartbeat_watchdog_timeout_s(); must be 0 (disabled) or at least 1 second. - Feed via Mavsdk::feed_heartbeat_watchdog() (C++/C API) or the FeedHeartbeatWatchdog gRPC RPC. Also exposed as the SetHeartbeatWatchdogTimeout RPC (proto submodule bump) and the mavsdk_server --heartbeat-watchdog-timeout CLI option. - Heartbeats latch off until fed: on enable, timeout change, expiry, and any other stop (e.g. disconnect), so a dead client cannot emit heartbeats across reconnects or discovery. A feed only restarts heartbeats that are supposed to be sent (always_send_heartbeats or a connected system). - Watchdog state is guarded by _heartbeat_mutex with a generation counter against in-flight expiries; configuration writers are serialized on a dedicated mutex, and heartbeat starts/stops self-heal against stale policy snapshots. - Unit tests (expiry, latching, policy, feed restart, reconfiguration, stress) and gRPC service tests. --- c/src/cmavsdk/mavsdk.cpp | 15 + c/src/cmavsdk/mavsdk.h | 4 + cpp/src/mavsdk/core/CMakeLists.txt | 1 + .../mavsdk/core/heartbeat_watchdog_test.cpp | 517 +++++++++++++ cpp/src/mavsdk/core/include/mavsdk/mavsdk.hpp | 97 +++ cpp/src/mavsdk/core/mavsdk.cpp | 38 + cpp/src/mavsdk/core/mavsdk_impl.cpp | 360 +++++++-- cpp/src/mavsdk/core/mavsdk_impl.hpp | 80 +- cpp/src/mavsdk/core/mavsdk_time.cpp | 36 +- cpp/src/mavsdk/core/mavsdk_time.hpp | 8 +- cpp/src/mavsdk/core/mocks/mavsdk_mock.hpp | 2 + cpp/src/mavsdk/core/timeout_handler.cpp | 10 +- cpp/src/mavsdk/core/timeout_handler.hpp | 4 +- .../src/core/core_service_impl.hpp | 27 + .../src/generated/core/core.grpc.pb.cc | 84 +++ .../src/generated/core/core.grpc.pb.h | 409 ++++++++++- .../src/generated/core/core.pb.cc | 670 ++++++++++++++++- .../src/generated/core/core.pb.h | 681 +++++++++++++++++- .../mission_raw_server.grpc.pb.h | 2 +- cpp/src/mavsdk_server/src/mavsdk_server.cpp | 13 +- cpp/src/mavsdk_server/src/mavsdk_server.hpp | 4 + .../mavsdk_server/src/mavsdk_server_api.cpp | 14 +- cpp/src/mavsdk_server/src/mavsdk_server_api.h | 15 + .../mavsdk_server/src/mavsdk_server_bin.cpp | 34 +- .../test/core_service_impl_test.cpp | 59 ++ proto | 2 +- 26 files changed, 3081 insertions(+), 105 deletions(-) create mode 100644 cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp diff --git a/c/src/cmavsdk/mavsdk.cpp b/c/src/cmavsdk/mavsdk.cpp index 8ffc858562..87cfa099f3 100644 --- a/c/src/cmavsdk/mavsdk.cpp +++ b/c/src/cmavsdk/mavsdk.cpp @@ -121,6 +121,16 @@ void mavsdk_configuration_set_always_send_heartbeats(mavsdk_configuration_t conf cpp_config->set_always_send_heartbeats(always_send_heartbeats != 0); } +double mavsdk_configuration_get_heartbeat_watchdog_timeout_s(mavsdk_configuration_t config) { + auto* cpp_config = reinterpret_cast(config); + return cpp_config->get_heartbeat_watchdog_timeout_s(); +} + +int mavsdk_configuration_set_heartbeat_watchdog_timeout_s(mavsdk_configuration_t config, double timeout_s) { + auto* cpp_config = reinterpret_cast(config); + return cpp_config->set_heartbeat_watchdog_timeout_s(timeout_s) ? 1 : 0; +} + mavsdk_component_type_t mavsdk_configuration_get_component_type(mavsdk_configuration_t config) { auto* cpp_config = reinterpret_cast(config); return c_component_type_from_cpp(cpp_config->get_component_type()); @@ -556,6 +566,11 @@ void mavsdk_set_timeout_s(mavsdk_t mavsdk, double timeout_s) { cpp_mavsdk->set_timeout_s(timeout_s); } +void mavsdk_feed_heartbeat_watchdog(mavsdk_t mavsdk) { + auto* cpp_mavsdk = reinterpret_cast(mavsdk); + cpp_mavsdk->feed_heartbeat_watchdog(); +} + // --- Memory Management Helpers --- void mavsdk_free_connection_error(mavsdk_connection_error_t* error) { if (error) { diff --git a/c/src/cmavsdk/mavsdk.h b/c/src/cmavsdk/mavsdk.h index bd4be132ec..bbace3e493 100644 --- a/c/src/cmavsdk/mavsdk.h +++ b/c/src/cmavsdk/mavsdk.h @@ -86,6 +86,9 @@ CMAVSDK_EXPORT uint8_t mavsdk_configuration_get_component_id(mavsdk_configuratio CMAVSDK_EXPORT void mavsdk_configuration_set_component_id(mavsdk_configuration_t config, uint8_t component_id); CMAVSDK_EXPORT int mavsdk_configuration_get_always_send_heartbeats(mavsdk_configuration_t config); CMAVSDK_EXPORT void mavsdk_configuration_set_always_send_heartbeats(mavsdk_configuration_t config, int always_send_heartbeats); +CMAVSDK_EXPORT double mavsdk_configuration_get_heartbeat_watchdog_timeout_s(mavsdk_configuration_t config); +// Returns 1 if the value was accepted, 0 if it was rejected (must be 0 or >= 1 second). +CMAVSDK_EXPORT int mavsdk_configuration_set_heartbeat_watchdog_timeout_s(mavsdk_configuration_t config, double timeout_s); CMAVSDK_EXPORT mavsdk_component_type_t mavsdk_configuration_get_component_type(mavsdk_configuration_t config); CMAVSDK_EXPORT void mavsdk_configuration_set_component_type(mavsdk_configuration_t config, mavsdk_component_type_t component_type); CMAVSDK_EXPORT uint8_t mavsdk_configuration_get_mav_type(mavsdk_configuration_t config); @@ -163,6 +166,7 @@ CMAVSDK_EXPORT void mavsdk_set_timeout_s( mavsdk_t mavsdk, double timeout_s ); +CMAVSDK_EXPORT void mavsdk_feed_heartbeat_watchdog(mavsdk_t mavsdk); // ===== Server Components ===== CMAVSDK_EXPORT mavsdk_server_component_t mavsdk_server_component( diff --git a/cpp/src/mavsdk/core/CMakeLists.txt b/cpp/src/mavsdk/core/CMakeLists.txt index 98048d488f..8f6bbbfbf9 100644 --- a/cpp/src/mavsdk/core/CMakeLists.txt +++ b/cpp/src/mavsdk/core/CMakeLists.txt @@ -270,6 +270,7 @@ list(APPEND UNIT_TEST_SOURCES ${PROJECT_SOURCE_DIR}/mavsdk/core/file_cache_test.cpp ${PROJECT_SOURCE_DIR}/mavsdk/core/locked_queue_test.cpp ${PROJECT_SOURCE_DIR}/mavsdk/core/geometry_test.cpp + ${PROJECT_SOURCE_DIR}/mavsdk/core/heartbeat_watchdog_test.cpp ${PROJECT_SOURCE_DIR}/mavsdk/core/math_utils_test.cpp ${PROJECT_SOURCE_DIR}/mavsdk/core/mavsdk_test.cpp ${PROJECT_SOURCE_DIR}/mavsdk/core/mavsdk_time_test.cpp diff --git a/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp b/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp new file mode 100644 index 0000000000..1297fa5472 --- /dev/null +++ b/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp @@ -0,0 +1,517 @@ +#include "mavsdk_impl.hpp" +#include "mavsdk_time.hpp" +#include "mavlink_include.hpp" + +#include + +#include +#include +#include +#include + +using namespace mavsdk; + +namespace { + +// These tests run against MavsdkImpl with an injected FakeTime, so all +// heartbeat and watchdog periods are simulated: fake_time.sleep_for() +// advances the clock instantly. The io thread polls the timer handlers every +// 5 ms of real time, so after advancing the fake time, effects appear within +// a few milliseconds. Real-time waits below only bound that scheduling +// latency, never a simulated duration. + +// Real-time wait for the io thread to act on an advanced fake clock. +template +bool wait_for( + Predicate predicate, std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)) +{ + const auto deadline = std::chrono::steady_clock::now() + timeout; + while (std::chrono::steady_clock::now() < deadline) { + if (predicate()) { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + return predicate(); +} + +// Let the io thread run its timer poll several times, so that negative +// assertions ("nothing happened after advancing time") are meaningful. +void settle() +{ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +} + +class HeartbeatCounter { +public: + explicit HeartbeatCounter(MavsdkImpl& mavsdk) + { + mavsdk.intercept_outgoing_messages_async([this](mavlink_message_t& message) { + if (message.msgid == MAVLINK_MSG_ID_HEARTBEAT) { + ++_count; + } + return true; + }); + } + + int count() const { return _count.load(); } + +private: + std::atomic _count{0}; +}; + +} // namespace + +TEST(Heartbeat, DisablingAlwaysSendStopsHeartbeatsWhenNoSystemConnected) +{ + // Heartbeats are forced on via always_send_heartbeats, with no watchdog and + // no connected system. + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // Confirm heartbeats are actually being sent (first one is immediate, the + // second needs a simulated second to pass). + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 1; })); + fake_time.sleep_for(std::chrono::milliseconds(1100)); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 2; })); + + // Disabling always_send_heartbeats with no connected system must stop them. + auto disabled_configuration = configuration; + disabled_configuration.set_always_send_heartbeats(false); + mavsdk.set_configuration(disabled_configuration); + + settle(); + const int count_after_disable = heartbeats.count(); + fake_time.sleep_for(std::chrono::seconds(5)); + settle(); + EXPECT_EQ(heartbeats.count(), count_after_disable); +} + +TEST(Heartbeat, DisablingAlwaysSendDuringDiscoveryKeepsHeartbeatsRunning) +{ + // Regression test for a stale-snapshot race: set_configuration() turning + // always_send_heartbeats off evaluates is_any_system_connected() before + // stopping. A system finishing its connection in that window (its + // heartbeat start is posted to the io thread) could have its + // just-started heartbeats stopped, leaving them off although a system is + // connected. The interleaving cannot be forced deterministically, so + // each iteration opens the window once (a system only posts that start + // on the heartbeat that connects it) and then asserts the invariant: + // with a system connected, heartbeats keep ticking even with always_send + // off. + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + // Note: adding the raw connection force-enables always_send_heartbeats. + ASSERT_EQ( + mavsdk.add_any_connection("raw://", ForwardingOption::ForwardingOff).first, + ConnectionResult::Success); + + auto inject_heartbeat = [&mavsdk](uint8_t sysid) { + mavlink_message_t message; + mavlink_msg_heartbeat_pack( + sysid, + MAV_COMP_ID_AUTOPILOT1, + &message, + MAV_TYPE_QUADROTOR, + MAV_AUTOPILOT_PX4, + 0, + 0, + MAV_STATE_ACTIVE); + uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; + const uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message); + mavsdk.pass_received_raw_bytes(reinterpret_cast(buffer), buffer_len); + }; + + auto on_configuration = configuration; + on_configuration.set_always_send_heartbeats(true); + auto off_configuration = configuration; + off_configuration.set_always_send_heartbeats(false); + + for (uint8_t sysid = 1; sysid <= 30; ++sysid) { + mavsdk.set_configuration(on_configuration); + + // Keep the already-connected systems alive: each iteration advances + // the fake clock by 1.1 s, so without a fresh heartbeat they would + // eventually hit the heartbeat timeout and disconnect, which stops + // heartbeats through an unrelated code path. + for (uint8_t alive = 1; alive < sysid; ++alive) { + inject_heartbeat(alive); + } + + // The first heartbeat of a new system only creates it: the system's + // HEARTBEAT handler registration is posted to the io thread, so this + // message is not delivered to the system itself and does not connect + // it yet. Wait for the system to exist, then let the posted handler + // registration run. + inject_heartbeat(sysid); + ASSERT_TRUE(wait_for([&]() { return mavsdk.systems().size() >= sysid; })); + settle(); + + // The second heartbeat connects the system: set_connected() posts a + // start_sending_heartbeats() to the io thread, racing the policy-off + // update below. + inject_heartbeat(sysid); + mavsdk.set_configuration(off_configuration); + + // At least one system is connected (or about to finish connecting), + // so the policy still requires heartbeats: they must keep ticking. + const int count_before = heartbeats.count(); + fake_time.sleep_for(std::chrono::milliseconds(1100)); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_before; })) + << "heartbeats stopped although a system is connected (iteration " + << static_cast(sysid) << ")"; + } +} + +TEST(HeartbeatWatchdog, ConfigurationStoresAndValidatesTimeout) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + EXPECT_DOUBLE_EQ(configuration.get_heartbeat_watchdog_timeout_s(), 0.0); + + EXPECT_TRUE(configuration.set_heartbeat_watchdog_timeout_s(2.5)); + EXPECT_DOUBLE_EQ(configuration.get_heartbeat_watchdog_timeout_s(), 2.5); + + // Sub-second, negative and non-finite values are rejected and leave the + // stored value unchanged. + EXPECT_FALSE(configuration.set_heartbeat_watchdog_timeout_s(0.5)); + EXPECT_FALSE(configuration.set_heartbeat_watchdog_timeout_s(-1.0)); + EXPECT_FALSE(configuration.set_heartbeat_watchdog_timeout_s( + std::numeric_limits::infinity())); + EXPECT_FALSE( + configuration.set_heartbeat_watchdog_timeout_s(std::numeric_limits::quiet_NaN())); + EXPECT_DOUBLE_EQ(configuration.get_heartbeat_watchdog_timeout_s(), 2.5); + + // 0 (disabled) is valid. + EXPECT_TRUE(configuration.set_heartbeat_watchdog_timeout_s(0.0)); + EXPECT_DOUBLE_EQ(configuration.get_heartbeat_watchdog_timeout_s(), 0.0); +} + +TEST(HeartbeatWatchdog, RejectsSubSecondTimeout) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + configuration.set_heartbeat_watchdog_timeout_s(2.0); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 1; })); + + // Rejected: must not shorten the active 2 s watchdog to 0.5 s. + EXPECT_FALSE(mavsdk.set_heartbeat_watchdog_timeout_s(0.5)); + + // 1.2 s after the feed: had the 0.5 s timeout been accepted, the watchdog + // would have expired before the 1 s heartbeat tick. With the 2 s timeout + // still active, the tick goes through. + const int count_before_tick = heartbeats.count(); + fake_time.sleep_for(std::chrono::milliseconds(1200)); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_before_tick; })); + + // After the 2 s watchdog expires, heartbeats stop. + fake_time.sleep_for(std::chrono::milliseconds(1000)); + settle(); + const int count_after_expiry = heartbeats.count(); + fake_time.sleep_for(std::chrono::seconds(5)); + settle(); + EXPECT_EQ(heartbeats.count(), count_after_expiry); +} + +TEST(HeartbeatWatchdog, FeedWithoutWatchdogConfiguredIsNoOp) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // Without a watchdog configured, feeding is a no-op and heartbeats just + // keep being sent periodically. + mavsdk.feed_heartbeat_watchdog(); + + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 1; })); + fake_time.sleep_for(std::chrono::milliseconds(1100)); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 2; })); +} + +TEST(HeartbeatWatchdog, NoHeartbeatsOnStartupUntilFed) +{ + // With the watchdog configured, heartbeats must not start on their own at + // startup (e.g. when a system is discovered or always_send_heartbeats is + // set) until the watchdog has been fed at least once. + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + configuration.set_heartbeat_watchdog_timeout_s(1); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + fake_time.sleep_for(std::chrono::seconds(5)); + settle(); + EXPECT_EQ(heartbeats.count(), 0); + + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 1; })); +} + +TEST(HeartbeatWatchdog, FeedDoesNotStartHeartbeatsThatNeverRan) +{ + // Watchdog configured, but heartbeats never started: no connection, no + // discovered system, and always_send_heartbeats off. + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_heartbeat_watchdog_timeout_s(1); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // Feeding only resets the watchdog, it must not act as a start trigger. + for (int i = 0; i < 5; ++i) { + mavsdk.feed_heartbeat_watchdog(); + fake_time.sleep_for(std::chrono::milliseconds(500)); + settle(); + } + + EXPECT_EQ(heartbeats.count(), 0); +} + +TEST(HeartbeatWatchdog, FeedRespectsHeartbeatPolicy) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + configuration.set_heartbeat_watchdog_timeout_s(1); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // Never feed: the watchdog expires and latches heartbeats off. + fake_time.sleep_for(std::chrono::milliseconds(1200)); + settle(); + + // Turn the policy off (no always_send_heartbeats, no connected system). + auto policy_off_configuration = configuration; + policy_off_configuration.set_always_send_heartbeats(false); + mavsdk.set_configuration(policy_off_configuration); + const int count_with_policy_off = heartbeats.count(); + + // Feeding now must not restart heartbeats: they are not supposed to be + // sent while the policy is off. + mavsdk.feed_heartbeat_watchdog(); + fake_time.sleep_for(std::chrono::seconds(5)); + settle(); + EXPECT_EQ(heartbeats.count(), count_with_policy_off); + + // The feed cleared the latch though: once the policy allows heartbeats + // again, they start without another feed. + mavsdk.set_configuration(configuration); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_with_policy_off; })); +} + +TEST(HeartbeatWatchdog, RuntimeTimeoutReconfiguration) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + configuration.set_heartbeat_watchdog_timeout_s(1); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // Never feed: the watchdog expires and latches heartbeats off. + fake_time.sleep_for(std::chrono::milliseconds(1200)); + settle(); + const int count_latched = heartbeats.count(); + + // Disabling the watchdog (timeout 0) clears the latch: heartbeats resume + // without a feed and run unconditionally. + EXPECT_TRUE(mavsdk.set_heartbeat_watchdog_timeout_s(0)); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_latched; })); + + // Re-enabling the watchdog must stop heartbeats immediately and keep them + // off until a feed - never grant a free timeout period without a feed. + settle(); + const int count_before_reenable = heartbeats.count(); + EXPECT_TRUE(mavsdk.set_heartbeat_watchdog_timeout_s(1)); + fake_time.sleep_for(std::chrono::seconds(5)); + settle(); + EXPECT_EQ(heartbeats.count(), count_before_reenable); + + // The re-enabled watchdog still reacts to a feed. + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_before_reenable; })); +} + +TEST(HeartbeatWatchdog, EnableViaGrpcStyleApiOnStartup) +{ + // Watchdog disabled at startup; enable at runtime via set_heartbeat_watchdog_timeout_s(). + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // Autonomous heartbeats while the watchdog is off. + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 1; })); + + // Enable the watchdog at runtime: heartbeats stop immediately and stay + // off until a feed. + settle(); + const int count_before_enable = heartbeats.count(); + EXPECT_TRUE(mavsdk.set_heartbeat_watchdog_timeout_s(1)); + fake_time.sleep_for(std::chrono::seconds(5)); + settle(); + EXPECT_EQ(heartbeats.count(), count_before_enable); + + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_before_enable; })); +} + +TEST(HeartbeatWatchdog, ConcurrentReconfigurationStress) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + ASSERT_EQ( + mavsdk.add_any_connection("raw://", ForwardingOption::ForwardingOff).first, + ConnectionResult::Success); + + // Churn the configuration and the watchdog from a separate thread. These + // calls serialize on the configuration writer lock and take the systems, + // server components and heartbeat locks individually. + std::atomic done{false}; + std::thread configuration_thread([&mavsdk, &configuration, &done]() { + auto toggled_configuration = configuration; + bool always_send_heartbeats = true; + int iteration = 0; + while (!done) { + always_send_heartbeats = !always_send_heartbeats; + toggled_configuration.set_always_send_heartbeats(always_send_heartbeats); + mavsdk.set_configuration(toggled_configuration); + mavsdk.set_heartbeat_watchdog_timeout_s((iteration++ % 2 == 0) ? 0.0 : 1.0); + mavsdk.feed_heartbeat_watchdog(); + } + }); + + // Meanwhile keep injecting heartbeats from new system IDs, so the io + // thread keeps discovering systems and calling start_sending_heartbeats() + // while holding the systems lock. This combination used to be an ABBA + // deadlock with configuration updates, which held the server components + // lock while taking the systems lock. + for (int sysid = 1; sysid <= 150; ++sysid) { + mavlink_message_t message; + mavlink_msg_heartbeat_pack( + static_cast(sysid), + MAV_COMP_ID_AUTOPILOT1, + &message, + MAV_TYPE_QUADROTOR, + MAV_AUTOPILOT_PX4, + 0, + 0, + MAV_STATE_ACTIVE); + uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; + const uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message); + mavsdk.pass_received_raw_bytes(reinterpret_cast(buffer), buffer_len); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + done = true; + configuration_thread.join(); +} + +TEST(HeartbeatWatchdog, StopLatchesHeartbeatsOffUntilFed) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + configuration.set_heartbeat_watchdog_timeout_s(2); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // Feed so heartbeats start. + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 1; })); + + // Feed again so the watchdog is not expired, then stop heartbeats by + // turning the policy off (no system is connected). + mavsdk.feed_heartbeat_watchdog(); + auto policy_off_configuration = configuration; + policy_off_configuration.set_always_send_heartbeats(false); + mavsdk.set_configuration(policy_off_configuration); + + // Give a possibly in-flight heartbeat tick time to drain, then snapshot. + settle(); + const int count_latched = heartbeats.count(); + + // Turning the policy back on must not restart heartbeats: the stop + // latched them off until the next feed, even though the watchdog had + // not expired. Otherwise a dead client would get a fresh watchdog + // period of heartbeats after a reconnect. The check window (1.5 s) is + // shorter than the watchdog timeout (2 s), so without the latch the + // restarted heartbeats would be observed here. + mavsdk.set_configuration(configuration); + + fake_time.sleep_for(std::chrono::milliseconds(1500)); + settle(); + EXPECT_EQ(heartbeats.count(), count_latched); + + // Feeding clears the latch and restarts heartbeats. + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_latched; })); +} + +TEST(HeartbeatWatchdog, ExpiryStopsHeartbeatsAndFeedRestartsThem) +{ + Mavsdk::Configuration configuration{ComponentType::GroundStation}; + configuration.set_always_send_heartbeats(true); + configuration.set_heartbeat_watchdog_timeout_s(1); + + FakeTime fake_time; + MavsdkImpl mavsdk{configuration, fake_time}; + HeartbeatCounter heartbeats{mavsdk}; + + // While the watchdog keeps being fed, periodic heartbeats keep coming + // (they are sent every simulated second). + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() >= 1; })); + for (int i = 0; i < 4; ++i) { + mavsdk.feed_heartbeat_watchdog(); + fake_time.sleep_for(std::chrono::milliseconds(600)); + settle(); + } + EXPECT_GE(heartbeats.count(), 2); + + // Once we stop feeding, the watchdog expires and heartbeats stop. + fake_time.sleep_for(std::chrono::milliseconds(1200)); + settle(); + const int count_after_expiry = heartbeats.count(); + + // Expiry latches heartbeats off: even toggling always_send_heartbeats + // (which normally starts them) must not revive them. + auto toggled_configuration = configuration; + toggled_configuration.set_always_send_heartbeats(false); + mavsdk.set_configuration(toggled_configuration); + mavsdk.set_configuration(configuration); + + fake_time.sleep_for(std::chrono::seconds(5)); + settle(); + EXPECT_EQ(heartbeats.count(), count_after_expiry); + + // Feeding the watchdog clears the latch and restarts heartbeats. + mavsdk.feed_heartbeat_watchdog(); + EXPECT_TRUE(wait_for([&]() { return heartbeats.count() > count_after_expiry; })); +} diff --git a/cpp/src/mavsdk/core/include/mavsdk/mavsdk.hpp b/cpp/src/mavsdk/core/include/mavsdk/mavsdk.hpp index 2088d1ae8a..6217da7a2e 100644 --- a/cpp/src/mavsdk/core/include/mavsdk/mavsdk.hpp +++ b/cpp/src/mavsdk/core/include/mavsdk/mavsdk.hpp @@ -21,6 +21,17 @@ namespace mavsdk { class ServerPluginImplBase; +/** @brief Minimum heartbeat watchdog timeout when enabled, in seconds. */ +constexpr double heartbeat_watchdog_min_timeout_s = 1.0; + +/** + * @brief Check whether a heartbeat watchdog timeout is valid. + * + * @return true if timeout_s is 0 (disabled), or finite and at least + * heartbeat_watchdog_min_timeout_s. + */ +MAVSDK_PUBLIC bool is_valid_heartbeat_watchdog_timeout_s(double timeout_s); + /** * @brief ForwardingOption for Connection, used to set message forwarding option. */ @@ -241,9 +252,57 @@ class MAVSDK_PUBLIC Mavsdk { /** * @brief Set whether to send heartbeats by default. + * + * Note: when a heartbeat watchdog is configured + * (set_heartbeat_watchdog_timeout_s()) and has expired, the watchdog + * latch takes precedence: heartbeats stay off until + * Mavsdk::feed_heartbeat_watchdog() is called again, even if + * always_send_heartbeats is set. */ void set_always_send_heartbeats(bool always_send_heartbeats); + /** + * @brief Get the heartbeat watchdog timeout. + * @return Timeout in seconds, 0 if the watchdog is disabled. + */ + double get_heartbeat_watchdog_timeout_s() const; + + /** + * @brief Set the heartbeat watchdog (deadman timer) timeout. + * + * When set to a value greater than 0, the periodic heartbeats sent + * by MAVSDK are only sent as long as Mavsdk::feed_heartbeat_watchdog() + * keeps being called at least once per timeout period. Heartbeats + * never start (and any already-running heartbeats are stopped) until + * the watchdog has been fed - including when the watchdog is first + * enabled or its timeout is changed. If the watchdog times out, + * heartbeats are latched off - including across reconnects and new + * system discovery - until the watchdog is fed again. + * + * Heartbeats are also latched off whenever they stop for any other + * reason while the watchdog is configured (e.g. when the connected + * system disconnects): after a reconnect, heartbeats only resume + * once the watchdog has been fed again. + * + * This is useful when MAVSDK's heartbeats should reflect the liveness + * of the application: if the application hangs or dies, heartbeats + * stop. + * + * While the watchdog is expired, the latch takes precedence over + * set_always_send_heartbeats(): heartbeats stay off until the watchdog + * is fed again. + * + * When set to 0, the watchdog is disabled and heartbeats follow the + * usual policy (always_send_heartbeats or a connected system). + * + * Default: 0 (disabled) + * + * @param timeout_s Timeout in seconds: 0 (disabled) or at least 1. + * @return true if the value was accepted, false if it was rejected + * (invalid values are ignored and the previous value kept). + */ + bool set_heartbeat_watchdog_timeout_s(double timeout_s); + /** @brief Component type of this configuration, used for automatic ID set */ ComponentType get_component_type() const; @@ -302,6 +361,7 @@ class MAVSDK_PUBLIC Mavsdk { uint8_t _system_id; uint8_t _component_id; bool _always_send_heartbeats; + double _heartbeat_watchdog_timeout_s{0.0}; ComponentType _component_type; MAV_TYPE _mav_type; Autopilot _autopilot{Autopilot::Unknown}; @@ -347,6 +407,26 @@ class MAVSDK_PUBLIC Mavsdk { */ void set_configuration(Configuration configuration); + /** + * @brief Set the heartbeat watchdog timeout at runtime. + * + * When set to a value greater than 0, the periodic heartbeats sent by + * MAVSDK are only sent as long as feed_heartbeat_watchdog() keeps being + * called at least once per timeout period. Enabling or changing the + * timeout stops any running heartbeats until the watchdog is fed. + * + * When set to 0, the watchdog is disabled and heartbeats follow the + * usual policy (always_send_heartbeats or a connected system). + * + * This is an alternative to configuring the watchdog via + * Configuration::set_heartbeat_watchdog_timeout_s() at startup. + * + * @param timeout_s Timeout in seconds: 0 (disabled) or at least 1. + * @return true if the value was accepted, false if it was rejected + * (invalid values are ignored and the previous value kept). + */ + bool set_heartbeat_watchdog_timeout_s(double timeout_s); + /** * @brief Set timeout of MAVLink transfers. * @@ -374,6 +454,23 @@ class MAVSDK_PUBLIC Mavsdk { */ double get_heartbeat_timeout_s() const; + /** + * @brief Feed the heartbeat watchdog. + * + * Resets the watchdog timer configured with + * Configuration::set_heartbeat_watchdog_timeout_s(), keeping the periodic + * heartbeats alive for another timeout period. If the watchdog had + * already expired, this restarts the heartbeats. + * + * Has no effect if no watchdog is configured. A feed only clears the + * watchdog latch (set on expiry or whenever heartbeats stop while the + * watchdog is configured), and only restarts heartbeats if they are + * supposed to be sent in the first place (always_send_heartbeats is set + * or a system is connected). It never starts heartbeats that are off for + * any other reason. + */ + void feed_heartbeat_watchdog(); + /** * @brief Set a custom callback executor. * diff --git a/cpp/src/mavsdk/core/mavsdk.cpp b/cpp/src/mavsdk/core/mavsdk.cpp index 00c74c255c..25090a98a2 100644 --- a/cpp/src/mavsdk/core/mavsdk.cpp +++ b/cpp/src/mavsdk/core/mavsdk.cpp @@ -1,9 +1,18 @@ #include "mavsdk.hpp" +#include "log.hpp" #include "mavsdk_impl.hpp" +#include + namespace mavsdk { +bool is_valid_heartbeat_watchdog_timeout_s(double timeout_s) +{ + return timeout_s == 0.0 || + (std::isfinite(timeout_s) && timeout_s >= heartbeat_watchdog_min_timeout_s); +} + Mavsdk::Mavsdk(Configuration configuration) { _impl = std::make_shared(configuration); @@ -48,6 +57,11 @@ void Mavsdk::set_configuration(Configuration configuration) _impl->set_configuration(configuration); } +bool Mavsdk::set_heartbeat_watchdog_timeout_s(double timeout_s) +{ + return _impl->set_heartbeat_watchdog_timeout_s(timeout_s); +} + void Mavsdk::set_timeout_s(double timeout_s) { _impl->set_timeout_s(timeout_s); @@ -63,6 +77,11 @@ double Mavsdk::get_heartbeat_timeout_s() const return _impl->heartbeat_timeout_s(); } +void Mavsdk::feed_heartbeat_watchdog() +{ + _impl->feed_heartbeat_watchdog(); +} + void Mavsdk::set_callback_executor(std::function)> executor) { _impl->set_callback_executor(std::move(executor)); @@ -198,6 +217,25 @@ void Mavsdk::Configuration::set_always_send_heartbeats(bool always_send_heartbea _always_send_heartbeats = always_send_heartbeats; } +double Mavsdk::Configuration::get_heartbeat_watchdog_timeout_s() const +{ + return _heartbeat_watchdog_timeout_s; +} + +bool Mavsdk::Configuration::set_heartbeat_watchdog_timeout_s(double timeout_s) +{ + if (!is_valid_heartbeat_watchdog_timeout_s(timeout_s)) { + LogWarn( + "Invalid heartbeat watchdog timeout: {} s (must be 0 or >= {} s)", + timeout_s, + heartbeat_watchdog_min_timeout_s); + return false; + } + + _heartbeat_watchdog_timeout_s = timeout_s; + return true; +} + ComponentType Mavsdk::Configuration::get_component_type() const { return _component_type; diff --git a/cpp/src/mavsdk/core/mavsdk_impl.cpp b/cpp/src/mavsdk/core/mavsdk_impl.cpp index 92e9844d28..4cdcc8673b 100644 --- a/cpp/src/mavsdk/core/mavsdk_impl.cpp +++ b/cpp/src/mavsdk/core/mavsdk_impl.cpp @@ -48,6 +48,11 @@ struct TlogFile { template class MAVSDK_TEMPL_INST CallbackList<>; MavsdkImpl::MavsdkImpl(const Mavsdk::Configuration& configuration) : + MavsdkImpl(configuration, _own_time) +{} + +MavsdkImpl::MavsdkImpl(const Mavsdk::Configuration& configuration, Time& time_override) : + time(time_override), timeout_handler(time), call_every_handler(time) { @@ -108,13 +113,17 @@ MavsdkImpl::MavsdkImpl(const Mavsdk::Configuration& configuration) : MavsdkImpl::~MavsdkImpl() { + _should_exit = true; + { std::lock_guard lock(_heartbeat_mutex); call_every_handler.remove(_heartbeat_send_cookie); + timeout_handler.remove(_heartbeat_watchdog_cookie); + // Invalidate any watchdog expiry callback already in flight on the io + // thread, so it doesn't log a spurious expiry during shutdown. + ++_heartbeat_watchdog_generation; } - _should_exit = true; - // Stop the Asio io_context so _io_thread exits io_context::run(). // This also cancels any pending async_receive_from operations on UdpConnection sockets. _io_work_guard.reset(); @@ -996,12 +1005,11 @@ MavsdkImpl::add_udp_connection(const CliArg::Udp& udp, ForwardingOption forwardi } new_conn->add_remote_to_keep(remote_ip.value(), udp.port); - std::lock_guard lock(_mutex); // With a UDP remote, we need to initiate the connection by sending heartbeats. - auto new_configuration = get_configuration(); - new_configuration.set_always_send_heartbeats(true); - set_configuration(new_configuration); + update_configuration([](Mavsdk::Configuration& configuration) { + configuration.set_always_send_heartbeats(true); + }); } auto handle = add_connection(std::move(new_conn)); @@ -1085,13 +1093,12 @@ std::pair MavsdkImpl::add_serial_con if (ret == ConnectionResult::Success) { auto handle = add_connection(std::move(new_conn)); - auto new_configuration = get_configuration(); - // PX4 starting with v1.13 does not send heartbeats by default, so we need // to initiate the MAVLink connection by sending heartbeats. // Therefore, we override the default here and enable sending heartbeats. - new_configuration.set_always_send_heartbeats(true); - set_configuration(new_configuration); + update_configuration([](Mavsdk::Configuration& configuration) { + configuration.set_always_send_heartbeats(true); + }); return {ret, handle}; @@ -1132,9 +1139,9 @@ MavsdkImpl::add_raw_connection(ForwardingOption forwarding_option) auto handle = add_connection(std::move(new_conn)); // Enable heartbeats for raw connection - auto new_configuration = get_configuration(); - new_configuration.set_always_send_heartbeats(true); - set_configuration(new_configuration); + update_configuration([](Mavsdk::Configuration& configuration) { + configuration.set_always_send_heartbeats(true); + }); return {ConnectionResult::Success, handle}; } @@ -1183,28 +1190,57 @@ ComponentType MavsdkImpl::get_component_type() const return _configuration.get_component_type(); } -void MavsdkImpl::set_configuration(Mavsdk::Configuration new_configuration) +bool MavsdkImpl::set_heartbeat_watchdog_timeout_s(double timeout_s) { - std::lock_guard server_components_lock(_server_components_mutex); - // We just point the default to the newly created component. This means - // that the previous default component will be deleted if it is not - // used/referenced anywhere. - _default_server_component = server_component_by_id_with_lock( - new_configuration.get_component_id(), new_configuration.get_mav_type()); + // Configuration::set_heartbeat_watchdog_timeout_s() validates the value + // (and warns) itself; an invalid value leaves the configuration unchanged. + bool accepted = false; + update_configuration([timeout_s, &accepted](Mavsdk::Configuration& configuration) { + accepted = configuration.set_heartbeat_watchdog_timeout_s(timeout_s); + }); + return accepted; +} - const bool was_always_sending_heartbeats = [this] { - std::lock_guard configuration_lock(_configuration_mutex); - return _configuration.get_always_send_heartbeats(); - }(); +void MavsdkImpl::update_configuration(const std::function& modify) +{ + // _configuration_update_mutex serializes all configuration writers. It is + // recursive, so holding it across the read-modify-write is fine even + // though set_configuration() locks it again. + std::lock_guard configuration_update_lock(_configuration_update_mutex); - if (new_configuration.get_always_send_heartbeats() && !was_always_sending_heartbeats) { - start_sending_heartbeats(); - } else if ( - !new_configuration.get_always_send_heartbeats() && was_always_sending_heartbeats && - !is_any_system_connected()) { - stop_sending_heartbeats(); + auto new_configuration = get_configuration(); + modify(new_configuration); + set_configuration(new_configuration); +} + +void MavsdkImpl::set_configuration(Mavsdk::Configuration new_configuration) +{ + // Serializes configuration writers. Deliberately not _server_components_mutex: + // this function takes _mutex (via is_any_system_connected()) and the io + // thread takes the locks in the opposite order (process_message() holds + // _mutex and takes _server_components_mutex via + // start_sending_heartbeats()), so holding _server_components_mutex across + // this body would be an ABBA deadlock. _configuration_update_mutex is + // never taken by the io thread. + std::lock_guard configuration_update_lock(_configuration_update_mutex); + + { + std::lock_guard server_components_lock(_server_components_mutex); + // We just point the default to the newly created component. This means + // that the previous default component will be deleted if it is not + // used/referenced anywhere. + _default_server_component = server_component_by_id_with_lock( + new_configuration.get_component_id(), new_configuration.get_mav_type()); } + const Mavsdk::Configuration old_configuration = get_configuration(); + + // No watchdog timeout validation needed here: + // Configuration::set_heartbeat_watchdog_timeout_s() rejects invalid + // values, so a Configuration always carries a valid timeout. + + // Assign the new configuration first, so that start/stop_sending_heartbeats + // below act on the new values in _configuration. { std::lock_guard configuration_lock(_configuration_mutex); _configuration = new_configuration; @@ -1212,6 +1248,71 @@ void MavsdkImpl::set_configuration(Mavsdk::Configuration new_configuration) // We cache these values as atomic to avoid having to lock any mutex for them. _our_system_id = new_configuration.get_system_id(); _our_component_id = new_configuration.get_component_id(); + _our_mav_type = new_configuration.get_mav_type(); + _our_autopilot = new_configuration.get_autopilot(); + _our_compatibility_mode = new_configuration.get_compatibility_mode(); + { + // Written under _heartbeat_mutex so that stop_sending_heartbeats() + // (which reads _always_send_heartbeats under the same mutex) cannot + // base its stop decision on a policy value that is concurrently + // being flipped: any stop then either runs before this write (and the + // start below wins) or observes the new value. + std::lock_guard heartbeat_lock(_heartbeat_mutex); + _always_send_heartbeats = new_configuration.get_always_send_heartbeats(); + _heartbeat_watchdog_timeout_s = new_configuration.get_heartbeat_watchdog_timeout_s(); + } + + // Process watchdog timeout changes before the always_send_heartbeats toggle + // so that enabling the watchdog latches heartbeats off before anything tries + // to start them. + const double new_watchdog_timeout_s = new_configuration.get_heartbeat_watchdog_timeout_s(); + if (new_watchdog_timeout_s != old_configuration.get_heartbeat_watchdog_timeout_s()) { + // Heartbeats may only be restarted if they are supposed to be sent + // according to the usual policy (see feed_heartbeat_watchdog()). + const bool policy_allows_heartbeats = + new_configuration.get_always_send_heartbeats() || is_any_system_connected(); + + bool needs_start = false; + { + std::lock_guard lock(_heartbeat_mutex); + timeout_handler.remove(_heartbeat_watchdog_cookie); + _heartbeat_watchdog_cookie = 0; + // Invalidate any expiry callback already in flight for the + // removed watchdog timeout. + ++_heartbeat_watchdog_generation; + + if (new_watchdog_timeout_s > 0.0) { + // Whenever the watchdog is enabled or its timeout changes, + // heartbeats must not run until the watchdog has been fed. + // Stop any in-flight heartbeats so enabling the watchdog + // never grants a free timeout period without a feed. + call_every_handler.remove(_heartbeat_send_cookie); + _heartbeat_send_cookie = 0; + _heartbeat_watchdog_expired = true; + } else { + // Disabling the watchdog clears the latch: heartbeats follow + // the usual policy again, so start them if they are off but + // supposed to be running (see feed_heartbeat_watchdog() for + // why this is keyed off "not running" rather than the latch). + needs_start = policy_allows_heartbeats && _heartbeat_send_cookie == 0; + _heartbeat_watchdog_expired = false; + } + } + if (needs_start) { + start_sending_heartbeats(); + stop_heartbeats_if_policy_disallows(); + } + } + + if (new_configuration.get_always_send_heartbeats() && + !old_configuration.get_always_send_heartbeats()) { + start_sending_heartbeats(); + } else if ( + !new_configuration.get_always_send_heartbeats() && + old_configuration.get_always_send_heartbeats() && !is_any_system_connected()) { + stop_sending_heartbeats(); + start_heartbeats_if_policy_requires(); + } } uint8_t MavsdkImpl::get_own_system_id() const @@ -1226,20 +1327,17 @@ uint8_t MavsdkImpl::get_own_component_id() const uint8_t MavsdkImpl::get_mav_type() const { - std::lock_guard configuration_lock(_configuration_mutex); - return _configuration.get_mav_type(); + return _our_mav_type; } Autopilot MavsdkImpl::get_autopilot() const { - std::lock_guard configuration_lock(_configuration_mutex); - return _configuration.get_autopilot(); + return _our_autopilot; } uint8_t MavsdkImpl::get_mav_autopilot() const { - std::lock_guard configuration_lock(_configuration_mutex); - switch (_configuration.get_autopilot()) { + switch (_our_autopilot.load()) { case Autopilot::Px4: return MAV_AUTOPILOT_PX4; case Autopilot::ArduPilot: @@ -1252,18 +1350,12 @@ uint8_t MavsdkImpl::get_mav_autopilot() const CompatibilityMode MavsdkImpl::get_compatibility_mode() const { - std::lock_guard configuration_lock(_configuration_mutex); - return _configuration.get_compatibility_mode(); + return _our_compatibility_mode; } Autopilot MavsdkImpl::effective_autopilot(Autopilot detected) const { - CompatibilityMode compatibility_mode; - { - std::lock_guard configuration_lock(_configuration_mutex); - compatibility_mode = _configuration.get_compatibility_mode(); - } - switch (compatibility_mode) { + switch (_our_compatibility_mode.load()) { case CompatibilityMode::Auto: return detected; case CompatibilityMode::Pure: @@ -1550,24 +1642,192 @@ void MavsdkImpl::start_sending_heartbeats() { std::lock_guard lock(_heartbeat_mutex); + + // With a watchdog configured, heartbeats stay latched off until + // feed_heartbeat_watchdog() runs (on first enable, after expiry, and + // after any stop). Without a feed, start is a no-op. + if (_heartbeat_watchdog_expired) { + return; + } + call_every_handler.remove(_heartbeat_send_cookie); _heartbeat_send_cookie = call_every_handler.add([this]() { send_heartbeats(); }, HEARTBEAT_SEND_INTERVAL_S); + + // If the watchdog is configured, arm it when heartbeats start. Only + // arm if it isn't already running, so that restarting heartbeats (e.g. + // on new system/component discovery) does not reset an in-flight + // deadman countdown - only feed_heartbeat_watchdog() extends it. When + // heartbeats are not running, _heartbeat_watchdog_cookie is always 0 + // (cleared on stop/expiry), so a genuine start always arms it. + const double watchdog_timeout_s = _heartbeat_watchdog_timeout_s; + if (watchdog_timeout_s > 0.0 && _heartbeat_watchdog_cookie == 0) { + arm_heartbeat_watchdog_with_lock(watchdog_timeout_s); + } } } void MavsdkImpl::stop_sending_heartbeats() { - const bool always_send_heartbeats = [this] { - std::lock_guard configuration_lock(_configuration_mutex); - return _configuration.get_always_send_heartbeats(); - }(); - if (!always_send_heartbeats) { + std::lock_guard lock(_heartbeat_mutex); + + // Checked under _heartbeat_mutex: set_configuration() writes + // _always_send_heartbeats under the same mutex before it starts or stops + // heartbeats, so this check cannot race a concurrent policy change. + if (_always_send_heartbeats) { + return; + } + + call_every_handler.remove(_heartbeat_send_cookie); + _heartbeat_send_cookie = 0; + timeout_handler.remove(_heartbeat_watchdog_cookie); + _heartbeat_watchdog_cookie = 0; + // Invalidate any expiry callback already in flight for the removed + // watchdog timeout: the latch below already records that heartbeats are + // off, and a subsequent feed's latch-clear must not be overridden by a + // stale expiry. + ++_heartbeat_watchdog_generation; + // With a watchdog configured, any stop latches heartbeats off until + // the watchdog is fed again. Otherwise a dead client would get a + // fresh watchdog period of heartbeats after a reconnect (the watchdog + // is re-armed on start), weakening the deadman guarantee across + // disconnects. This mirrors startup, where heartbeats also only start + // once the watchdog has been fed. + if (_heartbeat_watchdog_timeout_s > 0.0) { + _heartbeat_watchdog_expired = true; + } +} + +void MavsdkImpl::feed_heartbeat_watchdog() +{ + if (_should_exit) { + return; + } + + const double watchdog_timeout_s = _heartbeat_watchdog_timeout_s; + if (watchdog_timeout_s <= 0.0) { + // No watchdog configured: nothing to feed. + return; + } + + // Heartbeats may only be restarted if they are supposed to be sent + // according to the usual policy. Evaluated before taking _heartbeat_mutex + // because is_any_system_connected() locks _mutex (lock order: _mutex + // before _heartbeat_mutex). This snapshot can go stale if a system + // connects or disconnects concurrently; both directions are handled + // below. + const bool policy_allows_heartbeats = _always_send_heartbeats || is_any_system_connected(); + + bool needs_start = false; + { std::lock_guard lock(_heartbeat_mutex); - call_every_handler.remove(_heartbeat_send_cookie); + + // Re-read under _heartbeat_mutex (it is written under the same mutex + // in set_configuration()): the snapshot from above could have gone + // stale against a concurrent reconfiguration, and a re-arm below must + // use the current timeout. + const double current_watchdog_timeout_s = _heartbeat_watchdog_timeout_s; + if (current_watchdog_timeout_s <= 0.0) { + // The watchdog was disabled concurrently: nothing to feed. + return; + } + + if (_heartbeat_watchdog_cookie != 0 && + !timeout_handler.refresh(_heartbeat_watchdog_cookie)) { + // The watchdog just expired: the timeout handler already removed + // the timeout, but on_heartbeat_watchdog_expired() has not run + // yet. This feed still arrived in time, so re-arm with a new + // generation, which turns the in-flight expiry into a no-op. + arm_heartbeat_watchdog_with_lock(current_watchdog_timeout_s); + } + + // Restart heartbeats whenever they are off although they are supposed + // to be running. Keying this off "not running" rather than the expiry + // latch makes feeding self-healing: if a system connected just after + // the policy snapshot above was taken (so its start was blocked by + // the still-set latch while this feed saw the policy as off), the + // next feed catches up and restarts heartbeats. With the latch as + // condition, that state would persist because the latch is already + // cleared below. + needs_start = policy_allows_heartbeats && _heartbeat_send_cookie == 0; + // A feed always clears the latch, even if heartbeats may not be + // restarted right now: the client proved it is alive, so heartbeats + // may start again whenever the policy allows it (e.g. on discovery). + _heartbeat_watchdog_expired = false; + } + + // Only restart heartbeats if they are supposed to be running. If they are + // off for another reason (never started, disconnected, policy off), a + // feed must not start them. start_sending_heartbeats() re-arms the + // watchdog. + if (needs_start) { + start_sending_heartbeats(); + stop_heartbeats_if_policy_disallows(); } } +void MavsdkImpl::stop_heartbeats_if_policy_disallows() +{ + // Callers start heartbeats based on a policy snapshot taken before + // _heartbeat_mutex was acquired (lock order: _mutex before + // _heartbeat_mutex). If the last connected system disconnected in + // between, its stop_sending_heartbeats() ran before the start and the + // snapshot is stale: heartbeats are now running although the policy no + // longer allows it. Re-evaluate and stop them again if so. + // stop_sending_heartbeats() keeps heartbeats running while + // _always_send_heartbeats is set, so this can never stop heartbeats that + // are supposed to be on, and it re-latches while the watchdog is + // configured, so a later feed can cleanly restart them. + if (!_always_send_heartbeats && !is_any_system_connected()) { + stop_sending_heartbeats(); + } +} + +void MavsdkImpl::start_heartbeats_if_policy_requires() +{ + // Mirror image of stop_heartbeats_if_policy_disallows(): a stop based on + // a policy snapshot taken before _heartbeat_mutex was acquired can go + // stale if a system finished connecting in between - the start it posted + // to the io thread ran before the stop, and heartbeats are now off + // although the policy requires them. Re-evaluate and start them again if + // so. This converges in all interleavings: a system flips its connected + // flag before it triggers its start, so if this re-check still sees no + // connected system, that system's own start runs after the stop and wins. + // With a watchdog configured, start_sending_heartbeats() respects the + // latch set by the stop, so heartbeats still stay off until the next + // feed. + if (_always_send_heartbeats || is_any_system_connected()) { + start_sending_heartbeats(); + } +} + +void MavsdkImpl::arm_heartbeat_watchdog_with_lock(double timeout_s) +{ + const uint64_t generation = ++_heartbeat_watchdog_generation; + _heartbeat_watchdog_cookie = timeout_handler.add( + [this, generation]() { on_heartbeat_watchdog_expired(generation); }, timeout_s); +} + +void MavsdkImpl::on_heartbeat_watchdog_expired(uint64_t generation) +{ + std::lock_guard lock(_heartbeat_mutex); + + if (generation != _heartbeat_watchdog_generation) { + // Stale expiry: the watchdog was fed or reconfigured between this + // timeout firing and this callback running. + return; + } + + LogWarn("Heartbeat watchdog expired, stopping heartbeats"); + + call_every_handler.remove(_heartbeat_send_cookie); + _heartbeat_send_cookie = 0; + // The expired timeout has already been removed by the timeout handler. + _heartbeat_watchdog_cookie = 0; + // Latch heartbeats off until the watchdog is fed again. + _heartbeat_watchdog_expired = true; +} + ServerComponentImpl& MavsdkImpl::default_server_component_impl() { std::lock_guard lock(_server_components_mutex); diff --git a/cpp/src/mavsdk/core/mavsdk_impl.hpp b/cpp/src/mavsdk/core/mavsdk_impl.hpp index 5d8222d677..c00e1ac394 100644 --- a/cpp/src/mavsdk/core/mavsdk_impl.hpp +++ b/cpp/src/mavsdk/core/mavsdk_impl.hpp @@ -43,7 +43,7 @@ class RawConnection; struct TlogFile; -class MavsdkImpl { +class MAVSDK_TEST_EXPORT MavsdkImpl { // The Asio io_context must outlive every member that posts onto it during teardown // (the message handler, parameter subscriptions, connections, systems, and server // components). Declaring it first means it is destroyed last, so those members' @@ -55,7 +55,11 @@ class MavsdkImpl { _io_context.get_executor()}; public: - MavsdkImpl(const Mavsdk::Configuration& configuration); + explicit MavsdkImpl(const Mavsdk::Configuration& configuration); + // Constructor for tests: inject a Time implementation (e.g. FakeTime) so + // that tests can advance time without real sleeps. The Time object must + // outlive this MavsdkImpl. + MavsdkImpl(const Mavsdk::Configuration& configuration, Time& time_override); ~MavsdkImpl(); MavsdkImpl(const MavsdkImpl&) = delete; void operator=(const MavsdkImpl&) = delete; @@ -77,6 +81,7 @@ class MavsdkImpl { std::optional> first_autopilot(double timeout_s); void set_configuration(Mavsdk::Configuration new_configuration); + bool set_heartbeat_watchdog_timeout_s(double timeout_s); Mavsdk::Configuration get_configuration() const; ComponentType get_component_type() const; @@ -110,6 +115,7 @@ class MavsdkImpl { void start_sending_heartbeats(); void stop_sending_heartbeats(); + void feed_heartbeat_watchdog(); void intercept_incoming_messages_async(std::function callback); void intercept_outgoing_messages_async(std::function callback); @@ -141,7 +147,14 @@ class MavsdkImpl { server_component_by_type(ComponentType server_component_type, unsigned instance = 0); std::shared_ptr server_component_by_id(uint8_t component_id, uint8_t mav_type); - Time time{}; +private: + // Declared before `time` so it is initialized first. + Time _own_time{}; + +public: + // References _own_time by default, or the Time passed into the + // test-only constructor. + Time& time; TimeoutHandler timeout_handler; CallEveryHandler call_every_handler; @@ -199,7 +212,21 @@ class MavsdkImpl { Mavsdk::ConnectionHandle add_connection(std::unique_ptr&& connection); void make_system_with_component(uint8_t system_id, uint8_t component_id); + // Apply a change to the configuration as one atomic read-modify-write, so + // that concurrent configuration updates cannot be lost between reading + // the current configuration and writing back the modified copy. + void update_configuration(const std::function& modify); + void send_heartbeats(); + // Requires _heartbeat_mutex to be held. + void arm_heartbeat_watchdog_with_lock(double timeout_s); + void on_heartbeat_watchdog_expired(uint64_t generation); + // Must be called without _heartbeat_mutex held. Stops heartbeats again + // if the policy snapshot that justified a start has gone stale. + void stop_heartbeats_if_policy_disallows(); + // Must be called without _heartbeat_mutex held. Starts heartbeats again + // if the policy snapshot that justified a stop has gone stale. + void start_heartbeats_if_policy_requires(); void process_user_callbacks_thread(); @@ -251,13 +278,38 @@ class MavsdkImpl { CallbackList<> _new_system_callbacks{_io_context}; + // Serializes configuration writers (set_configuration() and + // update_configuration()) so that read-modify-write updates cannot lose + // each other's changes. Always the outermost lock: it is never taken by + // the io thread, and set_configuration() takes _mutex, + // _server_components_mutex and _heartbeat_mutex only individually while + // holding it, so it cannot participate in a lock-order cycle. Recursive + // because update_configuration() calls set_configuration(). + std::recursive_mutex _configuration_update_mutex; + // Leaf mutex guarding only _configuration. It is never held while acquiring // another mutex, so it cannot participate in a lock-order inversion with - // _mutex / _server_components_mutex. + // _mutex / _server_components_mutex. Writers must additionally hold + // _configuration_update_mutex (via set_configuration() or + // update_configuration()), which serializes read-modify-write updates. mutable std::mutex _configuration_mutex{}; Mavsdk::Configuration _configuration{ComponentType::GroundStation}; std::atomic _our_system_id{0}; std::atomic _our_component_id{0}; + // Cached as atomics so they can be read on lock-free paths (e.g. + // feed_heartbeat_watchdog()) without racing against set_configuration() + // writing _configuration. This also keeps the getters free of _mutex: + // some of them are called while _server_components_mutex is held (e.g. + // send_heartbeats() -> ServerComponentImpl::send_heartbeat() -> + // get_mav_autopilot()), and taking _mutex there would create a + // _server_components_mutex -> _mutex ordering that conflicts with the io + // thread's _mutex -> _server_components_mutex order (process_message() + // -> start_sending_heartbeats()). + std::atomic _always_send_heartbeats{false}; + std::atomic _heartbeat_watchdog_timeout_s{0.0}; + std::atomic _our_mav_type{0}; + std::atomic _our_autopilot{Autopilot::Unknown}; + std::atomic _our_compatibility_mode{CompatibilityMode::Auto}; struct UserCallback { UserCallback() = default; @@ -307,7 +359,25 @@ class MavsdkImpl { static constexpr double HEARTBEAT_SEND_INTERVAL_S = 1.0; std::mutex _heartbeat_mutex{}; - CallEveryHandler::Cookie _heartbeat_send_cookie{}; + CallEveryHandler::Cookie _heartbeat_send_cookie{0}; + // Heartbeat watchdog (deadman timer): while configured (timeout > 0 in + // the configuration), periodic heartbeats are stopped unless + // feed_heartbeat_watchdog() is called at least once per timeout period. + // When enabled or when the timeout changes, heartbeats are latched off + // until the watchdog is fed - never left running for a free timeout + // period. Whenever heartbeats stop - watchdog expiry, disconnect, or the + // policy turning off - they are latched off until the watchdog is fed + // again. + TimeoutHandler::Cookie _heartbeat_watchdog_cookie{0}; + bool _heartbeat_watchdog_expired{false}; + // Incremented (under _heartbeat_mutex) whenever the watchdog timeout is + // armed, re-armed, reconfigured or stopped. The expiry callback captures + // the generation it was armed with and ignores the expiry if it no longer + // matches: TimeoutHandler removes an expired timeout before invoking its + // callback, so a feed_heartbeat_watchdog() or stop racing with the expiry + // could otherwise be lost (refresh misses, then the stale expiry latches + // heartbeats off although the feed arrived in time). + uint64_t _heartbeat_watchdog_generation{0}; std::mutex _callback_executor_mutex{}; std::function)> _callback_executor{}; diff --git a/cpp/src/mavsdk/core/mavsdk_time.cpp b/cpp/src/mavsdk/core/mavsdk_time.cpp index 42fbda67e9..d89e341dc1 100644 --- a/cpp/src/mavsdk/core/mavsdk_time.cpp +++ b/cpp/src/mavsdk/core/mavsdk_time.cpp @@ -51,12 +51,14 @@ double Time::elapsed_since_s(const SteadyTimePoint& since) SteadyTimePoint Time::steady_time_in_future(double duration_s) { auto now = steady_time(); - return now + std::chrono::milliseconds(int64_t(duration_s * 1e3)); + // llround instead of a truncating cast: e.g. 1.001 * 1e3 is + // 1000.999... in double and would truncate to 1000. + return now + std::chrono::milliseconds(std::llround(duration_s * 1e3)); } void Time::shift_steady_time_by(SteadyTimePoint& time, double offset_s) { - time += std::chrono::milliseconds(int64_t(offset_s * 1e3)); + time += std::chrono::milliseconds(std::llround(offset_s * 1e3)); } void Time::sleep_for(std::chrono::hours h) @@ -92,53 +94,51 @@ void Time::sleep_for(std::chrono::nanoseconds ns) FakeTime::FakeTime() : Time() { // Start with current time so we don't start from 0. - _current = steady_clock::now(); + _current_ns = std::chrono::duration_cast( + steady_clock::now().time_since_epoch()) + .count(); } SteadyTimePoint FakeTime::steady_time() { - return _current; + return SteadyTimePoint(std::chrono::duration_cast( + std::chrono::nanoseconds(_current_ns.load()))); } void FakeTime::sleep_for(std::chrono::hours h) { - _current += h; - add_overhead(); + add_time(h); } void FakeTime::sleep_for(std::chrono::minutes m) { - _current += m; - add_overhead(); + add_time(m); } void FakeTime::sleep_for(std::chrono::seconds s) { - _current += s; - add_overhead(); + add_time(s); } void FakeTime::sleep_for(std::chrono::milliseconds ms) { - _current += ms; - add_overhead(); + add_time(ms); } void FakeTime::sleep_for(std::chrono::microseconds us) { - _current += us; - add_overhead(); + add_time(us); } void FakeTime::sleep_for(std::chrono::nanoseconds ns) { - _current += ns; - add_overhead(); + add_time(ns); } -void FakeTime::add_overhead() +void FakeTime::add_time(std::chrono::nanoseconds ns) { - _current += std::chrono::microseconds(50); + // The 50 us of overhead simulate the imperfection of a real sleep. + _current_ns += (ns + std::chrono::microseconds(50)).count(); } SystemTimePoint AutopilotTime::system_time() diff --git a/cpp/src/mavsdk/core/mavsdk_time.hpp b/cpp/src/mavsdk/core/mavsdk_time.hpp index e0c638d47f..6aceb3be79 100644 --- a/cpp/src/mavsdk/core/mavsdk_time.hpp +++ b/cpp/src/mavsdk/core/mavsdk_time.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include "mavsdk_export.h" @@ -45,8 +46,11 @@ class MAVSDK_TEST_EXPORT FakeTime : public Time { void sleep_for(std::chrono::nanoseconds ns) override; private: - std::chrono::time_point _current{}; - void add_overhead(); + // Atomic so that one thread can advance the fake time (sleep_for) while + // other threads (e.g. the io thread polling TimeoutHandler and + // CallEveryHandler) concurrently read it via steady_time(). + std::atomic _current_ns{0}; + void add_time(std::chrono::nanoseconds ns); }; class AutopilotTime { diff --git a/cpp/src/mavsdk/core/mocks/mavsdk_mock.hpp b/cpp/src/mavsdk/core/mocks/mavsdk_mock.hpp index 503c87eb2c..753a1d9599 100644 --- a/cpp/src/mavsdk/core/mocks/mavsdk_mock.hpp +++ b/cpp/src/mavsdk/core/mocks/mavsdk_mock.hpp @@ -18,6 +18,8 @@ class MockMavsdk { MOCK_CONST_METHOD1(subscribe_on_new_system, void(NewSystemCallback)) {}; MOCK_CONST_METHOD0(systems, std::vector>()) {}; MOCK_CONST_METHOD1(set_timeout_s, void(double)) {}; + MOCK_CONST_METHOD1(set_heartbeat_watchdog_timeout_s, bool(double)) {}; + MOCK_CONST_METHOD0(feed_heartbeat_watchdog, void()) {}; }; } // namespace testing diff --git a/cpp/src/mavsdk/core/timeout_handler.cpp b/cpp/src/mavsdk/core/timeout_handler.cpp index aafa2b1df8..143c646aed 100644 --- a/cpp/src/mavsdk/core/timeout_handler.cpp +++ b/cpp/src/mavsdk/core/timeout_handler.cpp @@ -19,17 +19,19 @@ TimeoutHandler::Cookie TimeoutHandler::add(std::function callback, doubl return new_timeout.cookie; } -void TimeoutHandler::refresh(Cookie cookie) +bool TimeoutHandler::refresh(Cookie cookie) { std::lock_guard lock(_timeouts_mutex); auto it = std::find_if(_timeouts.begin(), _timeouts.end(), [&](const Timeout& timeout) { return timeout.cookie == cookie; }); - if (it != _timeouts.end()) { - auto future_time = _time.steady_time_in_future(it->duration_s); - it->time = future_time; + if (it == _timeouts.end()) { + return false; } + + it->time = _time.steady_time_in_future(it->duration_s); + return true; } void TimeoutHandler::remove(Cookie cookie) diff --git a/cpp/src/mavsdk/core/timeout_handler.hpp b/cpp/src/mavsdk/core/timeout_handler.hpp index 51c8205281..0a317a3f69 100644 --- a/cpp/src/mavsdk/core/timeout_handler.hpp +++ b/cpp/src/mavsdk/core/timeout_handler.hpp @@ -25,7 +25,9 @@ class MAVSDK_TEST_EXPORT TimeoutHandler { using Cookie = uint64_t; [[nodiscard]] Cookie add(std::function callback, double duration_s); - void refresh(Cookie cookie); + // Returns false if no timeout with this cookie exists (e.g. it already + // expired and was removed). + bool refresh(Cookie cookie); void remove(Cookie cookie); void run_once(); diff --git a/cpp/src/mavsdk_server/src/core/core_service_impl.hpp b/cpp/src/mavsdk_server/src/core/core_service_impl.hpp index e65905bf4b..f3b89c4fe1 100644 --- a/cpp/src/mavsdk_server/src/core/core_service_impl.hpp +++ b/cpp/src/mavsdk_server/src/core/core_service_impl.hpp @@ -43,6 +43,33 @@ class CoreServiceImpl final : public mavsdk::rpc::core::CoreService::Service { return grpc::Status::OK; } + grpc::Status FeedHeartbeatWatchdog( + grpc::ServerContext* /* context */, + const rpc::core::FeedHeartbeatWatchdogRequest* /* request */, + rpc::core::FeedHeartbeatWatchdogResponse* /* response */) override + { + _mavsdk.feed_heartbeat_watchdog(); + + return grpc::Status::OK; + } + + grpc::Status SetHeartbeatWatchdogTimeout( + grpc::ServerContext* /* context */, + const rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, + rpc::core::SetHeartbeatWatchdogTimeoutResponse* /* response */) override + { + const double timeout_s = request->timeout_s(); + if (!is_valid_heartbeat_watchdog_timeout_s(timeout_s)) { + return grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + "heartbeat watchdog timeout must be 0 or at least 1 second"); + } + + _mavsdk.set_heartbeat_watchdog_timeout_s(timeout_s); + + return grpc::Status::OK; + } + void stop() { _stop_promise.set_value(); } private: diff --git a/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.cc b/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.cc index ac4bd3fdb6..a78542577b 100644 --- a/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.cc +++ b/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.cc @@ -26,6 +26,8 @@ namespace core { static const char* CoreService_method_names[] = { "/mavsdk.rpc.core.CoreService/SubscribeConnectionState", "/mavsdk.rpc.core.CoreService/SetMavlinkTimeout", + "/mavsdk.rpc.core.CoreService/FeedHeartbeatWatchdog", + "/mavsdk.rpc.core.CoreService/SetHeartbeatWatchdogTimeout", }; std::unique_ptr< CoreService::Stub> CoreService::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { @@ -37,6 +39,8 @@ std::unique_ptr< CoreService::Stub> CoreService::NewStub(const std::shared_ptr< CoreService::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) : channel_(channel), rpcmethod_SubscribeConnectionState_(CoreService_method_names[0], options.suffix_for_stats(),::grpc::internal::RpcMethod::SERVER_STREAMING, channel) , rpcmethod_SetMavlinkTimeout_(CoreService_method_names[1], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel) + , rpcmethod_FeedHeartbeatWatchdog_(CoreService_method_names[2], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel) + , rpcmethod_SetHeartbeatWatchdogTimeout_(CoreService_method_names[3], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel) {} ::grpc::ClientReader< ::mavsdk::rpc::core::ConnectionStateResponse>* CoreService::Stub::SubscribeConnectionStateRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SubscribeConnectionStateRequest& request) { @@ -78,6 +82,52 @@ ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetMavlinkTimeoutRespons return result; } +::grpc::Status CoreService::Stub::FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response) { + return ::grpc::internal::BlockingUnaryCall< ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_FeedHeartbeatWatchdog_, context, request, response); +} + +void CoreService::Stub::async::FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response, std::function f) { + ::grpc::internal::CallbackUnaryCall< ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_FeedHeartbeatWatchdog_, context, request, response, std::move(f)); +} + +void CoreService::Stub::async::FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response, ::grpc::ClientUnaryReactor* reactor) { + ::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_FeedHeartbeatWatchdog_, context, request, response, reactor); +} + +::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* CoreService::Stub::PrepareAsyncFeedHeartbeatWatchdogRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) { + return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse, ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_FeedHeartbeatWatchdog_, context, request); +} + +::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* CoreService::Stub::AsyncFeedHeartbeatWatchdogRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) { + auto* result = + this->PrepareAsyncFeedHeartbeatWatchdogRaw(context, request, cq); + result->StartCall(); + return result; +} + +::grpc::Status CoreService::Stub::SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response) { + return ::grpc::internal::BlockingUnaryCall< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_SetHeartbeatWatchdogTimeout_, context, request, response); +} + +void CoreService::Stub::async::SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response, std::function f) { + ::grpc::internal::CallbackUnaryCall< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SetHeartbeatWatchdogTimeout_, context, request, response, std::move(f)); +} + +void CoreService::Stub::async::SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response, ::grpc::ClientUnaryReactor* reactor) { + ::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SetHeartbeatWatchdogTimeout_, context, request, response, reactor); +} + +::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* CoreService::Stub::PrepareAsyncSetHeartbeatWatchdogTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) { + return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_SetHeartbeatWatchdogTimeout_, context, request); +} + +::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* CoreService::Stub::AsyncSetHeartbeatWatchdogTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) { + auto* result = + this->PrepareAsyncSetHeartbeatWatchdogTimeoutRaw(context, request, cq); + result->StartCall(); + return result; +} + CoreService::Service::Service() { AddMethod(new ::grpc::internal::RpcServiceMethod( CoreService_method_names[0], @@ -99,6 +149,26 @@ CoreService::Service::Service() { ::mavsdk::rpc::core::SetMavlinkTimeoutResponse* resp) { return service->SetMavlinkTimeout(ctx, req, resp); }, this))); + AddMethod(new ::grpc::internal::RpcServiceMethod( + CoreService_method_names[2], + ::grpc::internal::RpcMethod::NORMAL_RPC, + new ::grpc::internal::RpcMethodHandler< CoreService::Service, ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>( + [](CoreService::Service* service, + ::grpc::ServerContext* ctx, + const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* req, + ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* resp) { + return service->FeedHeartbeatWatchdog(ctx, req, resp); + }, this))); + AddMethod(new ::grpc::internal::RpcServiceMethod( + CoreService_method_names[3], + ::grpc::internal::RpcMethod::NORMAL_RPC, + new ::grpc::internal::RpcMethodHandler< CoreService::Service, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>( + [](CoreService::Service* service, + ::grpc::ServerContext* ctx, + const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* req, + ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* resp) { + return service->SetHeartbeatWatchdogTimeout(ctx, req, resp); + }, this))); } CoreService::Service::~Service() { @@ -118,6 +188,20 @@ ::grpc::Status CoreService::Service::SetMavlinkTimeout(::grpc::ServerContext* co return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } +::grpc::Status CoreService::Service::FeedHeartbeatWatchdog(::grpc::ServerContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response) { + (void) context; + (void) request; + (void) response; + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); +} + +::grpc::Status CoreService::Service::SetHeartbeatWatchdogTimeout(::grpc::ServerContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response) { + (void) context; + (void) request; + (void) response; + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); +} + } // namespace mavsdk } // namespace rpc diff --git a/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.h b/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.h index 97e73ce357..05451013a9 100644 --- a/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.h +++ b/cpp/src/mavsdk_server/src/generated/core/core.grpc.pb.h @@ -63,6 +63,49 @@ class CoreService final { std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>> PrepareAsyncSetMavlinkTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest& request, ::grpc::CompletionQueue* cq) { return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>>(PrepareAsyncSetMavlinkTimeoutRaw(context, request, cq)); } + // + // Feed the heartbeat watchdog. + // + // MAVSDK can be configured with a heartbeat watchdog (deadman timer). + // While configured, the periodic heartbeats sent by MAVSDK are only sent + // as long as this is called at least once per timeout period. If the + // watchdog times out, heartbeats stop until it is fed again. + // + // This allows MAVSDK's heartbeats to reflect the liveness of the client: + // if the client hangs or dies, heartbeats stop. + // + // Has no effect if no watchdog is configured (e.g. with the + // --heartbeat-watchdog-timeout option of mavsdk_server, or + // SetHeartbeatWatchdogTimeout). + virtual ::grpc::Status FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response) = 0; + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>> AsyncFeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>>(AsyncFeedHeartbeatWatchdogRaw(context, request, cq)); + } + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>> PrepareAsyncFeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>>(PrepareAsyncFeedHeartbeatWatchdogRaw(context, request, cq)); + } + // + // Set the heartbeat watchdog timeout. + // + // When timeout_s is greater than 0, the periodic heartbeats sent by MAVSDK + // are only sent as long as FeedHeartbeatWatchdog is called at least once + // per timeout period. If the watchdog times out, heartbeats stop until it + // is fed again. + // + // When timeout_s is 0, the watchdog is disabled and heartbeats follow the + // usual policy (always_send_heartbeats or a connected system). + // + // Values greater than 0 and less than 1 are rejected. + // + // This is an alternative to configuring the watchdog at mavsdk_server + // startup with the --heartbeat-watchdog-timeout option. + virtual ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response) = 0; + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>> AsyncSetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>>(AsyncSetHeartbeatWatchdogTimeoutRaw(context, request, cq)); + } + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>> PrepareAsyncSetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>>(PrepareAsyncSetHeartbeatWatchdogTimeoutRaw(context, request, cq)); + } class async_interface { public: virtual ~async_interface() {} @@ -78,6 +121,39 @@ class CoreService final { // need to be increased to prevent timeouts. virtual void SetMavlinkTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest* request, ::mavsdk::rpc::core::SetMavlinkTimeoutResponse* response, std::function) = 0; virtual void SetMavlinkTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest* request, ::mavsdk::rpc::core::SetMavlinkTimeoutResponse* response, ::grpc::ClientUnaryReactor* reactor) = 0; + // + // Feed the heartbeat watchdog. + // + // MAVSDK can be configured with a heartbeat watchdog (deadman timer). + // While configured, the periodic heartbeats sent by MAVSDK are only sent + // as long as this is called at least once per timeout period. If the + // watchdog times out, heartbeats stop until it is fed again. + // + // This allows MAVSDK's heartbeats to reflect the liveness of the client: + // if the client hangs or dies, heartbeats stop. + // + // Has no effect if no watchdog is configured (e.g. with the + // --heartbeat-watchdog-timeout option of mavsdk_server, or + // SetHeartbeatWatchdogTimeout). + virtual void FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response, std::function) = 0; + virtual void FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response, ::grpc::ClientUnaryReactor* reactor) = 0; + // + // Set the heartbeat watchdog timeout. + // + // When timeout_s is greater than 0, the periodic heartbeats sent by MAVSDK + // are only sent as long as FeedHeartbeatWatchdog is called at least once + // per timeout period. If the watchdog times out, heartbeats stop until it + // is fed again. + // + // When timeout_s is 0, the watchdog is disabled and heartbeats follow the + // usual policy (always_send_heartbeats or a connected system). + // + // Values greater than 0 and less than 1 are rejected. + // + // This is an alternative to configuring the watchdog at mavsdk_server + // startup with the --heartbeat-watchdog-timeout option. + virtual void SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response, std::function) = 0; + virtual void SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response, ::grpc::ClientUnaryReactor* reactor) = 0; }; typedef class async_interface experimental_async_interface; virtual class async_interface* async() { return nullptr; } @@ -88,6 +164,10 @@ class CoreService final { virtual ::grpc::ClientAsyncReaderInterface< ::mavsdk::rpc::core::ConnectionStateResponse>* PrepareAsyncSubscribeConnectionStateRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SubscribeConnectionStateRequest& request, ::grpc::CompletionQueue* cq) = 0; virtual ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>* AsyncSetMavlinkTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest& request, ::grpc::CompletionQueue* cq) = 0; virtual ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>* PrepareAsyncSetMavlinkTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest& request, ::grpc::CompletionQueue* cq) = 0; + virtual ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* AsyncFeedHeartbeatWatchdogRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) = 0; + virtual ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* PrepareAsyncFeedHeartbeatWatchdogRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) = 0; + virtual ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* AsyncSetHeartbeatWatchdogTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) = 0; + virtual ::grpc::ClientAsyncResponseReaderInterface< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* PrepareAsyncSetHeartbeatWatchdogTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) = 0; }; class Stub final : public StubInterface { public: @@ -108,12 +188,30 @@ class CoreService final { std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>> PrepareAsyncSetMavlinkTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest& request, ::grpc::CompletionQueue* cq) { return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>>(PrepareAsyncSetMavlinkTimeoutRaw(context, request, cq)); } + ::grpc::Status FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response) override; + std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>> AsyncFeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>>(AsyncFeedHeartbeatWatchdogRaw(context, request, cq)); + } + std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>> PrepareAsyncFeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>>(PrepareAsyncFeedHeartbeatWatchdogRaw(context, request, cq)); + } + ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response) override; + std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>> AsyncSetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>>(AsyncSetHeartbeatWatchdogTimeoutRaw(context, request, cq)); + } + std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>> PrepareAsyncSetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>>(PrepareAsyncSetHeartbeatWatchdogTimeoutRaw(context, request, cq)); + } class async final : public StubInterface::async_interface { public: void SubscribeConnectionState(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SubscribeConnectionStateRequest* request, ::grpc::ClientReadReactor< ::mavsdk::rpc::core::ConnectionStateResponse>* reactor) override; void SetMavlinkTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest* request, ::mavsdk::rpc::core::SetMavlinkTimeoutResponse* response, std::function) override; void SetMavlinkTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest* request, ::mavsdk::rpc::core::SetMavlinkTimeoutResponse* response, ::grpc::ClientUnaryReactor* reactor) override; + void FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response, std::function) override; + void FeedHeartbeatWatchdog(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response, ::grpc::ClientUnaryReactor* reactor) override; + void SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response, std::function) override; + void SetHeartbeatWatchdogTimeout(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response, ::grpc::ClientUnaryReactor* reactor) override; private: friend class Stub; explicit async(Stub* stub): stub_(stub) { } @@ -130,8 +228,14 @@ class CoreService final { ::grpc::ClientAsyncReader< ::mavsdk::rpc::core::ConnectionStateResponse>* PrepareAsyncSubscribeConnectionStateRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SubscribeConnectionStateRequest& request, ::grpc::CompletionQueue* cq) override; ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>* AsyncSetMavlinkTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest& request, ::grpc::CompletionQueue* cq) override; ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetMavlinkTimeoutResponse>* PrepareAsyncSetMavlinkTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest& request, ::grpc::CompletionQueue* cq) override; + ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* AsyncFeedHeartbeatWatchdogRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) override; + ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* PrepareAsyncFeedHeartbeatWatchdogRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest& request, ::grpc::CompletionQueue* cq) override; + ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* AsyncSetHeartbeatWatchdogTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) override; + ::grpc::ClientAsyncResponseReader< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* PrepareAsyncSetHeartbeatWatchdogTimeoutRaw(::grpc::ClientContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest& request, ::grpc::CompletionQueue* cq) override; const ::grpc::internal::RpcMethod rpcmethod_SubscribeConnectionState_; const ::grpc::internal::RpcMethod rpcmethod_SetMavlinkTimeout_; + const ::grpc::internal::RpcMethod rpcmethod_FeedHeartbeatWatchdog_; + const ::grpc::internal::RpcMethod rpcmethod_SetHeartbeatWatchdogTimeout_; }; static std::unique_ptr NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions()); @@ -150,6 +254,37 @@ class CoreService final { // if MAVSDK has to communicate over links with high latency it might // need to be increased to prevent timeouts. virtual ::grpc::Status SetMavlinkTimeout(::grpc::ServerContext* context, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest* request, ::mavsdk::rpc::core::SetMavlinkTimeoutResponse* response); + // + // Feed the heartbeat watchdog. + // + // MAVSDK can be configured with a heartbeat watchdog (deadman timer). + // While configured, the periodic heartbeats sent by MAVSDK are only sent + // as long as this is called at least once per timeout period. If the + // watchdog times out, heartbeats stop until it is fed again. + // + // This allows MAVSDK's heartbeats to reflect the liveness of the client: + // if the client hangs or dies, heartbeats stop. + // + // Has no effect if no watchdog is configured (e.g. with the + // --heartbeat-watchdog-timeout option of mavsdk_server, or + // SetHeartbeatWatchdogTimeout). + virtual ::grpc::Status FeedHeartbeatWatchdog(::grpc::ServerContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response); + // + // Set the heartbeat watchdog timeout. + // + // When timeout_s is greater than 0, the periodic heartbeats sent by MAVSDK + // are only sent as long as FeedHeartbeatWatchdog is called at least once + // per timeout period. If the watchdog times out, heartbeats stop until it + // is fed again. + // + // When timeout_s is 0, the watchdog is disabled and heartbeats follow the + // usual policy (always_send_heartbeats or a connected system). + // + // Values greater than 0 and less than 1 are rejected. + // + // This is an alternative to configuring the watchdog at mavsdk_server + // startup with the --heartbeat-watchdog-timeout option. + virtual ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ServerContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response); }; template class WithAsyncMethod_SubscribeConnectionState : public BaseClass { @@ -191,7 +326,47 @@ class CoreService final { ::grpc::Service::RequestAsyncUnary(1, context, request, response, new_call_cq, notification_cq, tag); } }; - typedef WithAsyncMethod_SubscribeConnectionState > AsyncService; + template + class WithAsyncMethod_FeedHeartbeatWatchdog : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithAsyncMethod_FeedHeartbeatWatchdog() { + ::grpc::Service::MarkMethodAsync(2); + } + ~WithAsyncMethod_FeedHeartbeatWatchdog() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status FeedHeartbeatWatchdog(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* /*request*/, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestFeedHeartbeatWatchdog(::grpc::ServerContext* context, ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::grpc::ServerAsyncResponseWriter< ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(2, context, request, response, new_call_cq, notification_cq, tag); + } + }; + template + class WithAsyncMethod_SetHeartbeatWatchdogTimeout : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithAsyncMethod_SetHeartbeatWatchdogTimeout() { + ::grpc::Service::MarkMethodAsync(3); + } + ~WithAsyncMethod_SetHeartbeatWatchdogTimeout() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestSetHeartbeatWatchdogTimeout(::grpc::ServerContext* context, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::grpc::ServerAsyncResponseWriter< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(3, context, request, response, new_call_cq, notification_cq, tag); + } + }; + typedef WithAsyncMethod_SubscribeConnectionState > > > AsyncService; template class WithCallbackMethod_SubscribeConnectionState : public BaseClass { private: @@ -241,7 +416,61 @@ class CoreService final { virtual ::grpc::ServerUnaryReactor* SetMavlinkTimeout( ::grpc::CallbackServerContext* /*context*/, const ::mavsdk::rpc::core::SetMavlinkTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetMavlinkTimeoutResponse* /*response*/) { return nullptr; } }; - typedef WithCallbackMethod_SubscribeConnectionState > CallbackService; + template + class WithCallbackMethod_FeedHeartbeatWatchdog : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithCallbackMethod_FeedHeartbeatWatchdog() { + ::grpc::Service::MarkMethodCallback(2, + new ::grpc::internal::CallbackUnaryHandler< ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>( + [this]( + ::grpc::CallbackServerContext* context, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* request, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* response) { return this->FeedHeartbeatWatchdog(context, request, response); }));} + void SetMessageAllocatorFor_FeedHeartbeatWatchdog( + ::grpc::MessageAllocator< ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* allocator) { + ::grpc::internal::MethodHandler* const handler = ::grpc::Service::GetHandler(2); + static_cast<::grpc::internal::CallbackUnaryHandler< ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>*>(handler) + ->SetMessageAllocator(allocator); + } + ~WithCallbackMethod_FeedHeartbeatWatchdog() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status FeedHeartbeatWatchdog(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* /*request*/, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + virtual ::grpc::ServerUnaryReactor* FeedHeartbeatWatchdog( + ::grpc::CallbackServerContext* /*context*/, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* /*request*/, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* /*response*/) { return nullptr; } + }; + template + class WithCallbackMethod_SetHeartbeatWatchdogTimeout : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithCallbackMethod_SetHeartbeatWatchdogTimeout() { + ::grpc::Service::MarkMethodCallback(3, + new ::grpc::internal::CallbackUnaryHandler< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>( + [this]( + ::grpc::CallbackServerContext* context, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* request, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* response) { return this->SetHeartbeatWatchdogTimeout(context, request, response); }));} + void SetMessageAllocatorFor_SetHeartbeatWatchdogTimeout( + ::grpc::MessageAllocator< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* allocator) { + ::grpc::internal::MethodHandler* const handler = ::grpc::Service::GetHandler(3); + static_cast<::grpc::internal::CallbackUnaryHandler< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>*>(handler) + ->SetMessageAllocator(allocator); + } + ~WithCallbackMethod_SetHeartbeatWatchdogTimeout() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + virtual ::grpc::ServerUnaryReactor* SetHeartbeatWatchdogTimeout( + ::grpc::CallbackServerContext* /*context*/, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* /*response*/) { return nullptr; } + }; + typedef WithCallbackMethod_SubscribeConnectionState > > > CallbackService; typedef CallbackService ExperimentalCallbackService; template class WithGenericMethod_SubscribeConnectionState : public BaseClass { @@ -278,6 +507,40 @@ class CoreService final { } }; template + class WithGenericMethod_FeedHeartbeatWatchdog : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithGenericMethod_FeedHeartbeatWatchdog() { + ::grpc::Service::MarkMethodGeneric(2); + } + ~WithGenericMethod_FeedHeartbeatWatchdog() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status FeedHeartbeatWatchdog(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* /*request*/, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + }; + template + class WithGenericMethod_SetHeartbeatWatchdogTimeout : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithGenericMethod_SetHeartbeatWatchdogTimeout() { + ::grpc::Service::MarkMethodGeneric(3); + } + ~WithGenericMethod_SetHeartbeatWatchdogTimeout() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + }; + template class WithRawMethod_SubscribeConnectionState : public BaseClass { private: void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} @@ -318,6 +581,46 @@ class CoreService final { } }; template + class WithRawMethod_FeedHeartbeatWatchdog : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithRawMethod_FeedHeartbeatWatchdog() { + ::grpc::Service::MarkMethodRaw(2); + } + ~WithRawMethod_FeedHeartbeatWatchdog() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status FeedHeartbeatWatchdog(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* /*request*/, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestFeedHeartbeatWatchdog(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(2, context, request, response, new_call_cq, notification_cq, tag); + } + }; + template + class WithRawMethod_SetHeartbeatWatchdogTimeout : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithRawMethod_SetHeartbeatWatchdogTimeout() { + ::grpc::Service::MarkMethodRaw(3); + } + ~WithRawMethod_SetHeartbeatWatchdogTimeout() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestSetHeartbeatWatchdogTimeout(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(3, context, request, response, new_call_cq, notification_cq, tag); + } + }; + template class WithRawCallbackMethod_SubscribeConnectionState : public BaseClass { private: void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} @@ -362,6 +665,50 @@ class CoreService final { ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; } }; template + class WithRawCallbackMethod_FeedHeartbeatWatchdog : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithRawCallbackMethod_FeedHeartbeatWatchdog() { + ::grpc::Service::MarkMethodRawCallback(2, + new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( + [this]( + ::grpc::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->FeedHeartbeatWatchdog(context, request, response); })); + } + ~WithRawCallbackMethod_FeedHeartbeatWatchdog() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status FeedHeartbeatWatchdog(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* /*request*/, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + virtual ::grpc::ServerUnaryReactor* FeedHeartbeatWatchdog( + ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; } + }; + template + class WithRawCallbackMethod_SetHeartbeatWatchdogTimeout : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithRawCallbackMethod_SetHeartbeatWatchdogTimeout() { + ::grpc::Service::MarkMethodRawCallback(3, + new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( + [this]( + ::grpc::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->SetHeartbeatWatchdogTimeout(context, request, response); })); + } + ~WithRawCallbackMethod_SetHeartbeatWatchdogTimeout() override { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + virtual ::grpc::ServerUnaryReactor* SetHeartbeatWatchdogTimeout( + ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; } + }; + template class WithStreamedUnaryMethod_SetMavlinkTimeout : public BaseClass { private: void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} @@ -388,7 +735,61 @@ class CoreService final { // replace default version of method with streamed unary virtual ::grpc::Status StreamedSetMavlinkTimeout(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::mavsdk::rpc::core::SetMavlinkTimeoutRequest,::mavsdk::rpc::core::SetMavlinkTimeoutResponse>* server_unary_streamer) = 0; }; - typedef WithStreamedUnaryMethod_SetMavlinkTimeout StreamedUnaryService; + template + class WithStreamedUnaryMethod_FeedHeartbeatWatchdog : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithStreamedUnaryMethod_FeedHeartbeatWatchdog() { + ::grpc::Service::MarkMethodStreamed(2, + new ::grpc::internal::StreamedUnaryHandler< + ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>( + [this](::grpc::ServerContext* context, + ::grpc::ServerUnaryStreamer< + ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* streamer) { + return this->StreamedFeedHeartbeatWatchdog(context, + streamer); + })); + } + ~WithStreamedUnaryMethod_FeedHeartbeatWatchdog() override { + BaseClassMustBeDerivedFromService(this); + } + // disable regular version of this method + ::grpc::Status FeedHeartbeatWatchdog(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest* /*request*/, ::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + // replace default version of method with streamed unary + virtual ::grpc::Status StreamedFeedHeartbeatWatchdog(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest,::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>* server_unary_streamer) = 0; + }; + template + class WithStreamedUnaryMethod_SetHeartbeatWatchdogTimeout : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} + public: + WithStreamedUnaryMethod_SetHeartbeatWatchdogTimeout() { + ::grpc::Service::MarkMethodStreamed(3, + new ::grpc::internal::StreamedUnaryHandler< + ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>( + [this](::grpc::ServerContext* context, + ::grpc::ServerUnaryStreamer< + ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* streamer) { + return this->StreamedSetHeartbeatWatchdogTimeout(context, + streamer); + })); + } + ~WithStreamedUnaryMethod_SetHeartbeatWatchdogTimeout() override { + BaseClassMustBeDerivedFromService(this); + } + // disable regular version of this method + ::grpc::Status SetHeartbeatWatchdogTimeout(::grpc::ServerContext* /*context*/, const ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest* /*request*/, ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse* /*response*/) override { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + // replace default version of method with streamed unary + virtual ::grpc::Status StreamedSetHeartbeatWatchdogTimeout(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest,::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>* server_unary_streamer) = 0; + }; + typedef WithStreamedUnaryMethod_SetMavlinkTimeout > > StreamedUnaryService; template class WithSplitStreamingMethod_SubscribeConnectionState : public BaseClass { private: @@ -417,7 +818,7 @@ class CoreService final { virtual ::grpc::Status StreamedSubscribeConnectionState(::grpc::ServerContext* context, ::grpc::ServerSplitStreamer< ::mavsdk::rpc::core::SubscribeConnectionStateRequest,::mavsdk::rpc::core::ConnectionStateResponse>* server_split_streamer) = 0; }; typedef WithSplitStreamingMethod_SubscribeConnectionState SplitStreamedService; - typedef WithSplitStreamingMethod_SubscribeConnectionState > StreamedService; + typedef WithSplitStreamingMethod_SubscribeConnectionState > > > StreamedService; }; } // namespace core diff --git a/cpp/src/mavsdk_server/src/generated/core/core.pb.cc b/cpp/src/mavsdk_server/src/generated/core/core.pb.cc index 07706d72a2..af19de1192 100644 --- a/cpp/src/mavsdk_server/src/generated/core/core.pb.cc +++ b/cpp/src/mavsdk_server/src/generated/core/core.pb.cc @@ -88,6 +88,85 @@ struct SetMavlinkTimeoutRequestDefaultTypeInternal { PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SetMavlinkTimeoutRequestDefaultTypeInternal _SetMavlinkTimeoutRequest_default_instance_; + template +PROTOBUF_CONSTEXPR SetHeartbeatWatchdogTimeoutResponse::SetHeartbeatWatchdogTimeoutResponse(::_pbi::ConstantInitialized) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(_class_data_.base()){} +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase() { +} +#endif // PROTOBUF_CUSTOM_VTABLE +struct SetHeartbeatWatchdogTimeoutResponseDefaultTypeInternal { + PROTOBUF_CONSTEXPR SetHeartbeatWatchdogTimeoutResponseDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {} + ~SetHeartbeatWatchdogTimeoutResponseDefaultTypeInternal() {} + union { + SetHeartbeatWatchdogTimeoutResponse _instance; + }; +}; + +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT + PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SetHeartbeatWatchdogTimeoutResponseDefaultTypeInternal _SetHeartbeatWatchdogTimeoutResponse_default_instance_; + +inline constexpr SetHeartbeatWatchdogTimeoutRequest::Impl_::Impl_( + ::_pbi::ConstantInitialized) noexcept + : timeout_s_{0}, + _cached_size_{0} {} + +template +PROTOBUF_CONSTEXPR SetHeartbeatWatchdogTimeoutRequest::SetHeartbeatWatchdogTimeoutRequest(::_pbi::ConstantInitialized) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::Message(_class_data_.base()), +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::Message(), +#endif // PROTOBUF_CUSTOM_VTABLE + _impl_(::_pbi::ConstantInitialized()) { +} +struct SetHeartbeatWatchdogTimeoutRequestDefaultTypeInternal { + PROTOBUF_CONSTEXPR SetHeartbeatWatchdogTimeoutRequestDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {} + ~SetHeartbeatWatchdogTimeoutRequestDefaultTypeInternal() {} + union { + SetHeartbeatWatchdogTimeoutRequest _instance; + }; +}; + +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT + PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SetHeartbeatWatchdogTimeoutRequestDefaultTypeInternal _SetHeartbeatWatchdogTimeoutRequest_default_instance_; + template +PROTOBUF_CONSTEXPR FeedHeartbeatWatchdogResponse::FeedHeartbeatWatchdogResponse(::_pbi::ConstantInitialized) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(_class_data_.base()){} +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase() { +} +#endif // PROTOBUF_CUSTOM_VTABLE +struct FeedHeartbeatWatchdogResponseDefaultTypeInternal { + PROTOBUF_CONSTEXPR FeedHeartbeatWatchdogResponseDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {} + ~FeedHeartbeatWatchdogResponseDefaultTypeInternal() {} + union { + FeedHeartbeatWatchdogResponse _instance; + }; +}; + +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT + PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FeedHeartbeatWatchdogResponseDefaultTypeInternal _FeedHeartbeatWatchdogResponse_default_instance_; + template +PROTOBUF_CONSTEXPR FeedHeartbeatWatchdogRequest::FeedHeartbeatWatchdogRequest(::_pbi::ConstantInitialized) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(_class_data_.base()){} +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase() { +} +#endif // PROTOBUF_CUSTOM_VTABLE +struct FeedHeartbeatWatchdogRequestDefaultTypeInternal { + PROTOBUF_CONSTEXPR FeedHeartbeatWatchdogRequestDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {} + ~FeedHeartbeatWatchdogRequestDefaultTypeInternal() {} + union { + FeedHeartbeatWatchdogRequest _instance; + }; +}; + +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT + PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FeedHeartbeatWatchdogRequestDefaultTypeInternal _FeedHeartbeatWatchdogRequest_default_instance_; inline constexpr ConnectionState::Impl_::Impl_( ::_pbi::ConstantInitialized) noexcept @@ -184,6 +263,39 @@ const ::uint32_t ~0u, // no _split_ ~0u, // no sizeof(Split) ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _split_ + ~0u, // no sizeof(Split) + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _split_ + ~0u, // no sizeof(Split) + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _split_ + ~0u, // no sizeof(Split) + PROTOBUF_FIELD_OFFSET(::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest, _impl_.timeout_s_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _split_ + ~0u, // no sizeof(Split) + ~0u, // no _has_bits_ PROTOBUF_FIELD_OFFSET(::mavsdk::rpc::core::ConnectionState, _internal_metadata_), ~0u, // no _extensions_ ~0u, // no _oneof_case_ @@ -200,13 +312,21 @@ static const ::_pbi::MigrationSchema {8, 17, -1, sizeof(::mavsdk::rpc::core::ConnectionStateResponse)}, {18, -1, -1, sizeof(::mavsdk::rpc::core::SetMavlinkTimeoutRequest)}, {27, -1, -1, sizeof(::mavsdk::rpc::core::SetMavlinkTimeoutResponse)}, - {35, -1, -1, sizeof(::mavsdk::rpc::core::ConnectionState)}, + {35, -1, -1, sizeof(::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest)}, + {43, -1, -1, sizeof(::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse)}, + {51, -1, -1, sizeof(::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest)}, + {60, -1, -1, sizeof(::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse)}, + {68, -1, -1, sizeof(::mavsdk::rpc::core::ConnectionState)}, }; static const ::_pb::Message* const file_default_instances[] = { &::mavsdk::rpc::core::_SubscribeConnectionStateRequest_default_instance_._instance, &::mavsdk::rpc::core::_ConnectionStateResponse_default_instance_._instance, &::mavsdk::rpc::core::_SetMavlinkTimeoutRequest_default_instance_._instance, &::mavsdk::rpc::core::_SetMavlinkTimeoutResponse_default_instance_._instance, + &::mavsdk::rpc::core::_FeedHeartbeatWatchdogRequest_default_instance_._instance, + &::mavsdk::rpc::core::_FeedHeartbeatWatchdogResponse_default_instance_._instance, + &::mavsdk::rpc::core::_SetHeartbeatWatchdogTimeoutRequest_default_instance_._instance, + &::mavsdk::rpc::core::_SetHeartbeatWatchdogTimeoutResponse_default_instance_._instance, &::mavsdk::rpc::core::_ConnectionState_default_instance_._instance, }; const char descriptor_table_protodef_core_2fcore_2eproto[] ABSL_ATTRIBUTE_SECTION_VARIABLE( @@ -216,27 +336,38 @@ const char descriptor_table_protodef_core_2fcore_2eproto[] ABSL_ATTRIBUTE_SECTIO "ionStateResponse\022:\n\020connection_state\030\001 \001" "(\0132 .mavsdk.rpc.core.ConnectionState\"-\n\030" "SetMavlinkTimeoutRequest\022\021\n\ttimeout_s\030\001 " - "\001(\001\"\033\n\031SetMavlinkTimeoutResponse\"\'\n\017Conn" - "ectionState\022\024\n\014is_connected\030\002 \001(\0102\367\001\n\013Co" - "reService\022z\n\030SubscribeConnectionState\0220." - "mavsdk.rpc.core.SubscribeConnectionState" - "Request\032(.mavsdk.rpc.core.ConnectionStat" - "eResponse\"\0000\001\022l\n\021SetMavlinkTimeout\022).mav" - "sdk.rpc.core.SetMavlinkTimeoutRequest\032*." - "mavsdk.rpc.core.SetMavlinkTimeoutRespons" - "e\"\000B\033\n\016io.mavsdk.coreB\tCoreProtob\006proto3" + "\001(\001\"\033\n\031SetMavlinkTimeoutResponse\"\036\n\034Feed" + "HeartbeatWatchdogRequest\"\037\n\035FeedHeartbea" + "tWatchdogResponse\"7\n\"SetHeartbeatWatchdo" + "gTimeoutRequest\022\021\n\ttimeout_s\030\001 \001(\001\"%\n#Se" + "tHeartbeatWatchdogTimeoutResponse\"\'\n\017Con" + "nectionState\022\024\n\014is_connected\030\002 \001(\0102\376\003\n\013C" + "oreService\022z\n\030SubscribeConnectionState\0220" + ".mavsdk.rpc.core.SubscribeConnectionStat" + "eRequest\032(.mavsdk.rpc.core.ConnectionSta" + "teResponse\"\0000\001\022l\n\021SetMavlinkTimeout\022).ma" + "vsdk.rpc.core.SetMavlinkTimeoutRequest\032*" + ".mavsdk.rpc.core.SetMavlinkTimeoutRespon" + "se\"\000\022x\n\025FeedHeartbeatWatchdog\022-.mavsdk.r" + "pc.core.FeedHeartbeatWatchdogRequest\032..m" + "avsdk.rpc.core.FeedHeartbeatWatchdogResp" + "onse\"\000\022\212\001\n\033SetHeartbeatWatchdogTimeout\0223" + ".mavsdk.rpc.core.SetHeartbeatWatchdogTim" + "eoutRequest\0324.mavsdk.rpc.core.SetHeartbe" + "atWatchdogTimeoutResponse\"\000B\033\n\016io.mavsdk" + ".coreB\tCoreProtob\006proto3" }; static ::absl::once_flag descriptor_table_core_2fcore_2eproto_once; PROTOBUF_CONSTINIT const ::_pbi::DescriptorTable descriptor_table_core_2fcore_2eproto = { false, false, - 560, + 984, descriptor_table_protodef_core_2fcore_2eproto, "core/core.proto", &descriptor_table_core_2fcore_2eproto_once, nullptr, 0, - 5, + 9, schemas, file_default_instances, TableStruct_core_2fcore_2eproto::offsets, @@ -910,6 +1041,521 @@ ::google::protobuf::Metadata SetMavlinkTimeoutResponse::GetMetadata() const { } // =================================================================== +class FeedHeartbeatWatchdogRequest::_Internal { + public: +}; + +FeedHeartbeatWatchdogRequest::FeedHeartbeatWatchdogRequest(::google::protobuf::Arena* arena) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(arena, _class_data_.base()) { +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase(arena) { +#endif // PROTOBUF_CUSTOM_VTABLE + // @@protoc_insertion_point(arena_constructor:mavsdk.rpc.core.FeedHeartbeatWatchdogRequest) +} +FeedHeartbeatWatchdogRequest::FeedHeartbeatWatchdogRequest( + ::google::protobuf::Arena* arena, + const FeedHeartbeatWatchdogRequest& from) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(arena, _class_data_.base()) { +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase(arena) { +#endif // PROTOBUF_CUSTOM_VTABLE + FeedHeartbeatWatchdogRequest* const _this = this; + (void)_this; + _internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>( + from._internal_metadata_); + + // @@protoc_insertion_point(copy_constructor:mavsdk.rpc.core.FeedHeartbeatWatchdogRequest) +} + +inline void* FeedHeartbeatWatchdogRequest::PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena) { + return ::new (mem) FeedHeartbeatWatchdogRequest(arena); +} +constexpr auto FeedHeartbeatWatchdogRequest::InternalNewImpl_() { + return ::google::protobuf::internal::MessageCreator::ZeroInit(sizeof(FeedHeartbeatWatchdogRequest), + alignof(FeedHeartbeatWatchdogRequest)); +} +PROTOBUF_CONSTINIT +PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::google::protobuf::internal::ClassDataFull FeedHeartbeatWatchdogRequest::_class_data_ = { + ::google::protobuf::internal::ClassData{ + &_FeedHeartbeatWatchdogRequest_default_instance_._instance, + &_table_.header, + nullptr, // OnDemandRegisterArenaDtor + nullptr, // IsInitialized + &FeedHeartbeatWatchdogRequest::MergeImpl, + ::google::protobuf::internal::ZeroFieldsBase::GetNewImpl(), +#if defined(PROTOBUF_CUSTOM_VTABLE) + &FeedHeartbeatWatchdogRequest::SharedDtor, + ::google::protobuf::internal::ZeroFieldsBase::GetClearImpl(), &FeedHeartbeatWatchdogRequest::ByteSizeLong, + &FeedHeartbeatWatchdogRequest::_InternalSerialize, +#endif // PROTOBUF_CUSTOM_VTABLE + PROTOBUF_FIELD_OFFSET(FeedHeartbeatWatchdogRequest, _impl_._cached_size_), + false, + }, + &FeedHeartbeatWatchdogRequest::kDescriptorMethods, + &descriptor_table_core_2fcore_2eproto, + nullptr, // tracker +}; +const ::google::protobuf::internal::ClassData* FeedHeartbeatWatchdogRequest::GetClassData() const { + ::google::protobuf::internal::PrefetchToLocalCache(&_class_data_); + ::google::protobuf::internal::PrefetchToLocalCache(_class_data_.tc_table); + return _class_data_.base(); +} +PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::_pbi::TcParseTable<0, 0, 0, 0, 2> FeedHeartbeatWatchdogRequest::_table_ = { + { + 0, // no _has_bits_ + 0, // no _extensions_ + 0, 0, // max_field_number, fast_idx_mask + offsetof(decltype(_table_), field_lookup_table), + 4294967295, // skipmap + offsetof(decltype(_table_), field_names), // no field_entries + 0, // num_field_entries + 0, // num_aux_entries + offsetof(decltype(_table_), field_names), // no aux_entries + _class_data_.base(), + nullptr, // post_loop_handler + ::_pbi::TcParser::GenericFallback, // fallback + #ifdef PROTOBUF_PREFETCH_PARSE_TABLE + ::_pbi::TcParser::GetTable<::mavsdk::rpc::core::FeedHeartbeatWatchdogRequest>(), // to_prefetch + #endif // PROTOBUF_PREFETCH_PARSE_TABLE + }, {{ + {::_pbi::TcParser::MiniParse, {}}, + }}, {{ + 65535, 65535 + }}, + // no field_entries, or aux_entries + {{ + }}, +}; + + + + + + + + +::google::protobuf::Metadata FeedHeartbeatWatchdogRequest::GetMetadata() const { + return ::google::protobuf::internal::ZeroFieldsBase::GetMetadataImpl(GetClassData()->full()); +} +// =================================================================== + +class FeedHeartbeatWatchdogResponse::_Internal { + public: +}; + +FeedHeartbeatWatchdogResponse::FeedHeartbeatWatchdogResponse(::google::protobuf::Arena* arena) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(arena, _class_data_.base()) { +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase(arena) { +#endif // PROTOBUF_CUSTOM_VTABLE + // @@protoc_insertion_point(arena_constructor:mavsdk.rpc.core.FeedHeartbeatWatchdogResponse) +} +FeedHeartbeatWatchdogResponse::FeedHeartbeatWatchdogResponse( + ::google::protobuf::Arena* arena, + const FeedHeartbeatWatchdogResponse& from) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(arena, _class_data_.base()) { +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase(arena) { +#endif // PROTOBUF_CUSTOM_VTABLE + FeedHeartbeatWatchdogResponse* const _this = this; + (void)_this; + _internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>( + from._internal_metadata_); + + // @@protoc_insertion_point(copy_constructor:mavsdk.rpc.core.FeedHeartbeatWatchdogResponse) +} + +inline void* FeedHeartbeatWatchdogResponse::PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena) { + return ::new (mem) FeedHeartbeatWatchdogResponse(arena); +} +constexpr auto FeedHeartbeatWatchdogResponse::InternalNewImpl_() { + return ::google::protobuf::internal::MessageCreator::ZeroInit(sizeof(FeedHeartbeatWatchdogResponse), + alignof(FeedHeartbeatWatchdogResponse)); +} +PROTOBUF_CONSTINIT +PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::google::protobuf::internal::ClassDataFull FeedHeartbeatWatchdogResponse::_class_data_ = { + ::google::protobuf::internal::ClassData{ + &_FeedHeartbeatWatchdogResponse_default_instance_._instance, + &_table_.header, + nullptr, // OnDemandRegisterArenaDtor + nullptr, // IsInitialized + &FeedHeartbeatWatchdogResponse::MergeImpl, + ::google::protobuf::internal::ZeroFieldsBase::GetNewImpl(), +#if defined(PROTOBUF_CUSTOM_VTABLE) + &FeedHeartbeatWatchdogResponse::SharedDtor, + ::google::protobuf::internal::ZeroFieldsBase::GetClearImpl(), &FeedHeartbeatWatchdogResponse::ByteSizeLong, + &FeedHeartbeatWatchdogResponse::_InternalSerialize, +#endif // PROTOBUF_CUSTOM_VTABLE + PROTOBUF_FIELD_OFFSET(FeedHeartbeatWatchdogResponse, _impl_._cached_size_), + false, + }, + &FeedHeartbeatWatchdogResponse::kDescriptorMethods, + &descriptor_table_core_2fcore_2eproto, + nullptr, // tracker +}; +const ::google::protobuf::internal::ClassData* FeedHeartbeatWatchdogResponse::GetClassData() const { + ::google::protobuf::internal::PrefetchToLocalCache(&_class_data_); + ::google::protobuf::internal::PrefetchToLocalCache(_class_data_.tc_table); + return _class_data_.base(); +} +PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::_pbi::TcParseTable<0, 0, 0, 0, 2> FeedHeartbeatWatchdogResponse::_table_ = { + { + 0, // no _has_bits_ + 0, // no _extensions_ + 0, 0, // max_field_number, fast_idx_mask + offsetof(decltype(_table_), field_lookup_table), + 4294967295, // skipmap + offsetof(decltype(_table_), field_names), // no field_entries + 0, // num_field_entries + 0, // num_aux_entries + offsetof(decltype(_table_), field_names), // no aux_entries + _class_data_.base(), + nullptr, // post_loop_handler + ::_pbi::TcParser::GenericFallback, // fallback + #ifdef PROTOBUF_PREFETCH_PARSE_TABLE + ::_pbi::TcParser::GetTable<::mavsdk::rpc::core::FeedHeartbeatWatchdogResponse>(), // to_prefetch + #endif // PROTOBUF_PREFETCH_PARSE_TABLE + }, {{ + {::_pbi::TcParser::MiniParse, {}}, + }}, {{ + 65535, 65535 + }}, + // no field_entries, or aux_entries + {{ + }}, +}; + + + + + + + + +::google::protobuf::Metadata FeedHeartbeatWatchdogResponse::GetMetadata() const { + return ::google::protobuf::internal::ZeroFieldsBase::GetMetadataImpl(GetClassData()->full()); +} +// =================================================================== + +class SetHeartbeatWatchdogTimeoutRequest::_Internal { + public: +}; + +SetHeartbeatWatchdogTimeoutRequest::SetHeartbeatWatchdogTimeoutRequest(::google::protobuf::Arena* arena) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::Message(arena, _class_data_.base()) { +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::Message(arena) { +#endif // PROTOBUF_CUSTOM_VTABLE + SharedCtor(arena); + // @@protoc_insertion_point(arena_constructor:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) +} +SetHeartbeatWatchdogTimeoutRequest::SetHeartbeatWatchdogTimeoutRequest( + ::google::protobuf::Arena* arena, const SetHeartbeatWatchdogTimeoutRequest& from) + : SetHeartbeatWatchdogTimeoutRequest(arena) { + MergeFrom(from); +} +inline PROTOBUF_NDEBUG_INLINE SetHeartbeatWatchdogTimeoutRequest::Impl_::Impl_( + ::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena) + : _cached_size_{0} {} + +inline void SetHeartbeatWatchdogTimeoutRequest::SharedCtor(::_pb::Arena* arena) { + new (&_impl_) Impl_(internal_visibility(), arena); + _impl_.timeout_s_ = {}; +} +SetHeartbeatWatchdogTimeoutRequest::~SetHeartbeatWatchdogTimeoutRequest() { + // @@protoc_insertion_point(destructor:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + SharedDtor(*this); +} +inline void SetHeartbeatWatchdogTimeoutRequest::SharedDtor(MessageLite& self) { + SetHeartbeatWatchdogTimeoutRequest& this_ = static_cast(self); + this_._internal_metadata_.Delete<::google::protobuf::UnknownFieldSet>(); + ABSL_DCHECK(this_.GetArena() == nullptr); + this_._impl_.~Impl_(); +} + +inline void* SetHeartbeatWatchdogTimeoutRequest::PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena) { + return ::new (mem) SetHeartbeatWatchdogTimeoutRequest(arena); +} +constexpr auto SetHeartbeatWatchdogTimeoutRequest::InternalNewImpl_() { + return ::google::protobuf::internal::MessageCreator::ZeroInit(sizeof(SetHeartbeatWatchdogTimeoutRequest), + alignof(SetHeartbeatWatchdogTimeoutRequest)); +} +PROTOBUF_CONSTINIT +PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::google::protobuf::internal::ClassDataFull SetHeartbeatWatchdogTimeoutRequest::_class_data_ = { + ::google::protobuf::internal::ClassData{ + &_SetHeartbeatWatchdogTimeoutRequest_default_instance_._instance, + &_table_.header, + nullptr, // OnDemandRegisterArenaDtor + nullptr, // IsInitialized + &SetHeartbeatWatchdogTimeoutRequest::MergeImpl, + ::google::protobuf::Message::GetNewImpl(), +#if defined(PROTOBUF_CUSTOM_VTABLE) + &SetHeartbeatWatchdogTimeoutRequest::SharedDtor, + ::google::protobuf::Message::GetClearImpl(), &SetHeartbeatWatchdogTimeoutRequest::ByteSizeLong, + &SetHeartbeatWatchdogTimeoutRequest::_InternalSerialize, +#endif // PROTOBUF_CUSTOM_VTABLE + PROTOBUF_FIELD_OFFSET(SetHeartbeatWatchdogTimeoutRequest, _impl_._cached_size_), + false, + }, + &SetHeartbeatWatchdogTimeoutRequest::kDescriptorMethods, + &descriptor_table_core_2fcore_2eproto, + nullptr, // tracker +}; +const ::google::protobuf::internal::ClassData* SetHeartbeatWatchdogTimeoutRequest::GetClassData() const { + ::google::protobuf::internal::PrefetchToLocalCache(&_class_data_); + ::google::protobuf::internal::PrefetchToLocalCache(_class_data_.tc_table); + return _class_data_.base(); +} +PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::_pbi::TcParseTable<0, 1, 0, 0, 2> SetHeartbeatWatchdogTimeoutRequest::_table_ = { + { + 0, // no _has_bits_ + 0, // no _extensions_ + 1, 0, // max_field_number, fast_idx_mask + offsetof(decltype(_table_), field_lookup_table), + 4294967294, // skipmap + offsetof(decltype(_table_), field_entries), + 1, // num_field_entries + 0, // num_aux_entries + offsetof(decltype(_table_), field_names), // no aux_entries + _class_data_.base(), + nullptr, // post_loop_handler + ::_pbi::TcParser::GenericFallback, // fallback + #ifdef PROTOBUF_PREFETCH_PARSE_TABLE + ::_pbi::TcParser::GetTable<::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest>(), // to_prefetch + #endif // PROTOBUF_PREFETCH_PARSE_TABLE + }, {{ + // double timeout_s = 1; + {::_pbi::TcParser::FastF64S1, + {9, 63, 0, PROTOBUF_FIELD_OFFSET(SetHeartbeatWatchdogTimeoutRequest, _impl_.timeout_s_)}}, + }}, {{ + 65535, 65535 + }}, {{ + // double timeout_s = 1; + {PROTOBUF_FIELD_OFFSET(SetHeartbeatWatchdogTimeoutRequest, _impl_.timeout_s_), 0, 0, + (0 | ::_fl::kFcSingular | ::_fl::kDouble)}, + }}, + // no aux_entries + {{ + }}, +}; + +PROTOBUF_NOINLINE void SetHeartbeatWatchdogTimeoutRequest::Clear() { +// @@protoc_insertion_point(message_clear_start:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + ::google::protobuf::internal::TSanWrite(&_impl_); + ::uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.timeout_s_ = 0; + _internal_metadata_.Clear<::google::protobuf::UnknownFieldSet>(); +} + +#if defined(PROTOBUF_CUSTOM_VTABLE) + ::uint8_t* SetHeartbeatWatchdogTimeoutRequest::_InternalSerialize( + const MessageLite& base, ::uint8_t* target, + ::google::protobuf::io::EpsCopyOutputStream* stream) { + const SetHeartbeatWatchdogTimeoutRequest& this_ = static_cast(base); +#else // PROTOBUF_CUSTOM_VTABLE + ::uint8_t* SetHeartbeatWatchdogTimeoutRequest::_InternalSerialize( + ::uint8_t* target, + ::google::protobuf::io::EpsCopyOutputStream* stream) const { + const SetHeartbeatWatchdogTimeoutRequest& this_ = *this; +#endif // PROTOBUF_CUSTOM_VTABLE + // @@protoc_insertion_point(serialize_to_array_start:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + ::uint32_t cached_has_bits = 0; + (void)cached_has_bits; + + // double timeout_s = 1; + if (::absl::bit_cast<::uint64_t>(this_._internal_timeout_s()) != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteDoubleToArray( + 1, this_._internal_timeout_s(), target); + } + + if (PROTOBUF_PREDICT_FALSE(this_._internal_metadata_.have_unknown_fields())) { + target = + ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + this_._internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + return target; + } + +#if defined(PROTOBUF_CUSTOM_VTABLE) + ::size_t SetHeartbeatWatchdogTimeoutRequest::ByteSizeLong(const MessageLite& base) { + const SetHeartbeatWatchdogTimeoutRequest& this_ = static_cast(base); +#else // PROTOBUF_CUSTOM_VTABLE + ::size_t SetHeartbeatWatchdogTimeoutRequest::ByteSizeLong() const { + const SetHeartbeatWatchdogTimeoutRequest& this_ = *this; +#endif // PROTOBUF_CUSTOM_VTABLE + // @@protoc_insertion_point(message_byte_size_start:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + ::size_t total_size = 0; + + ::uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void)cached_has_bits; + + { + // double timeout_s = 1; + if (::absl::bit_cast<::uint64_t>(this_._internal_timeout_s()) != 0) { + total_size += 9; + } + } + return this_.MaybeComputeUnknownFieldsSize(total_size, + &this_._impl_._cached_size_); + } + +void SetHeartbeatWatchdogTimeoutRequest::MergeImpl(::google::protobuf::MessageLite& to_msg, const ::google::protobuf::MessageLite& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + ABSL_DCHECK_NE(&from, _this); + ::uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (::absl::bit_cast<::uint64_t>(from._internal_timeout_s()) != 0) { + _this->_impl_.timeout_s_ = from._impl_.timeout_s_; + } + _this->_internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(from._internal_metadata_); +} + +void SetHeartbeatWatchdogTimeoutRequest::CopyFrom(const SetHeartbeatWatchdogTimeoutRequest& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + + +void SetHeartbeatWatchdogTimeoutRequest::InternalSwap(SetHeartbeatWatchdogTimeoutRequest* PROTOBUF_RESTRICT other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_impl_.timeout_s_, other->_impl_.timeout_s_); +} + +::google::protobuf::Metadata SetHeartbeatWatchdogTimeoutRequest::GetMetadata() const { + return ::google::protobuf::Message::GetMetadataImpl(GetClassData()->full()); +} +// =================================================================== + +class SetHeartbeatWatchdogTimeoutResponse::_Internal { + public: +}; + +SetHeartbeatWatchdogTimeoutResponse::SetHeartbeatWatchdogTimeoutResponse(::google::protobuf::Arena* arena) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(arena, _class_data_.base()) { +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase(arena) { +#endif // PROTOBUF_CUSTOM_VTABLE + // @@protoc_insertion_point(arena_constructor:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutResponse) +} +SetHeartbeatWatchdogTimeoutResponse::SetHeartbeatWatchdogTimeoutResponse( + ::google::protobuf::Arena* arena, + const SetHeartbeatWatchdogTimeoutResponse& from) +#if defined(PROTOBUF_CUSTOM_VTABLE) + : ::google::protobuf::internal::ZeroFieldsBase(arena, _class_data_.base()) { +#else // PROTOBUF_CUSTOM_VTABLE + : ::google::protobuf::internal::ZeroFieldsBase(arena) { +#endif // PROTOBUF_CUSTOM_VTABLE + SetHeartbeatWatchdogTimeoutResponse* const _this = this; + (void)_this; + _internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>( + from._internal_metadata_); + + // @@protoc_insertion_point(copy_constructor:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutResponse) +} + +inline void* SetHeartbeatWatchdogTimeoutResponse::PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena) { + return ::new (mem) SetHeartbeatWatchdogTimeoutResponse(arena); +} +constexpr auto SetHeartbeatWatchdogTimeoutResponse::InternalNewImpl_() { + return ::google::protobuf::internal::MessageCreator::ZeroInit(sizeof(SetHeartbeatWatchdogTimeoutResponse), + alignof(SetHeartbeatWatchdogTimeoutResponse)); +} +PROTOBUF_CONSTINIT +PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::google::protobuf::internal::ClassDataFull SetHeartbeatWatchdogTimeoutResponse::_class_data_ = { + ::google::protobuf::internal::ClassData{ + &_SetHeartbeatWatchdogTimeoutResponse_default_instance_._instance, + &_table_.header, + nullptr, // OnDemandRegisterArenaDtor + nullptr, // IsInitialized + &SetHeartbeatWatchdogTimeoutResponse::MergeImpl, + ::google::protobuf::internal::ZeroFieldsBase::GetNewImpl(), +#if defined(PROTOBUF_CUSTOM_VTABLE) + &SetHeartbeatWatchdogTimeoutResponse::SharedDtor, + ::google::protobuf::internal::ZeroFieldsBase::GetClearImpl(), &SetHeartbeatWatchdogTimeoutResponse::ByteSizeLong, + &SetHeartbeatWatchdogTimeoutResponse::_InternalSerialize, +#endif // PROTOBUF_CUSTOM_VTABLE + PROTOBUF_FIELD_OFFSET(SetHeartbeatWatchdogTimeoutResponse, _impl_._cached_size_), + false, + }, + &SetHeartbeatWatchdogTimeoutResponse::kDescriptorMethods, + &descriptor_table_core_2fcore_2eproto, + nullptr, // tracker +}; +const ::google::protobuf::internal::ClassData* SetHeartbeatWatchdogTimeoutResponse::GetClassData() const { + ::google::protobuf::internal::PrefetchToLocalCache(&_class_data_); + ::google::protobuf::internal::PrefetchToLocalCache(_class_data_.tc_table); + return _class_data_.base(); +} +PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 +const ::_pbi::TcParseTable<0, 0, 0, 0, 2> SetHeartbeatWatchdogTimeoutResponse::_table_ = { + { + 0, // no _has_bits_ + 0, // no _extensions_ + 0, 0, // max_field_number, fast_idx_mask + offsetof(decltype(_table_), field_lookup_table), + 4294967295, // skipmap + offsetof(decltype(_table_), field_names), // no field_entries + 0, // num_field_entries + 0, // num_aux_entries + offsetof(decltype(_table_), field_names), // no aux_entries + _class_data_.base(), + nullptr, // post_loop_handler + ::_pbi::TcParser::GenericFallback, // fallback + #ifdef PROTOBUF_PREFETCH_PARSE_TABLE + ::_pbi::TcParser::GetTable<::mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse>(), // to_prefetch + #endif // PROTOBUF_PREFETCH_PARSE_TABLE + }, {{ + {::_pbi::TcParser::MiniParse, {}}, + }}, {{ + 65535, 65535 + }}, + // no field_entries, or aux_entries + {{ + }}, +}; + + + + + + + + +::google::protobuf::Metadata SetHeartbeatWatchdogTimeoutResponse::GetMetadata() const { + return ::google::protobuf::internal::ZeroFieldsBase::GetMetadataImpl(GetClassData()->full()); +} +// =================================================================== + class ConnectionState::_Internal { public: }; diff --git a/cpp/src/mavsdk_server/src/generated/core/core.pb.h b/cpp/src/mavsdk_server/src/generated/core/core.pb.h index 8fc552e10d..37ee5c5e5c 100644 --- a/cpp/src/mavsdk_server/src/generated/core/core.pb.h +++ b/cpp/src/mavsdk_server/src/generated/core/core.pb.h @@ -61,6 +61,18 @@ extern ConnectionStateDefaultTypeInternal _ConnectionState_default_instance_; class ConnectionStateResponse; struct ConnectionStateResponseDefaultTypeInternal; extern ConnectionStateResponseDefaultTypeInternal _ConnectionStateResponse_default_instance_; +class FeedHeartbeatWatchdogRequest; +struct FeedHeartbeatWatchdogRequestDefaultTypeInternal; +extern FeedHeartbeatWatchdogRequestDefaultTypeInternal _FeedHeartbeatWatchdogRequest_default_instance_; +class FeedHeartbeatWatchdogResponse; +struct FeedHeartbeatWatchdogResponseDefaultTypeInternal; +extern FeedHeartbeatWatchdogResponseDefaultTypeInternal _FeedHeartbeatWatchdogResponse_default_instance_; +class SetHeartbeatWatchdogTimeoutRequest; +struct SetHeartbeatWatchdogTimeoutRequestDefaultTypeInternal; +extern SetHeartbeatWatchdogTimeoutRequestDefaultTypeInternal _SetHeartbeatWatchdogTimeoutRequest_default_instance_; +class SetHeartbeatWatchdogTimeoutResponse; +struct SetHeartbeatWatchdogTimeoutResponseDefaultTypeInternal; +extern SetHeartbeatWatchdogTimeoutResponseDefaultTypeInternal _SetHeartbeatWatchdogTimeoutResponse_default_instance_; class SetMavlinkTimeoutRequest; struct SetMavlinkTimeoutRequestDefaultTypeInternal; extern SetMavlinkTimeoutRequestDefaultTypeInternal _SetMavlinkTimeoutRequest_default_instance_; @@ -570,6 +582,635 @@ class SetMavlinkTimeoutRequest final }; // ------------------------------------------------------------------- +class SetHeartbeatWatchdogTimeoutResponse final + : public ::google::protobuf::internal::ZeroFieldsBase +/* @@protoc_insertion_point(class_definition:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutResponse) */ { + public: + inline SetHeartbeatWatchdogTimeoutResponse() : SetHeartbeatWatchdogTimeoutResponse(nullptr) {} + +#if defined(PROTOBUF_CUSTOM_VTABLE) + void operator delete(SetHeartbeatWatchdogTimeoutResponse* msg, std::destroying_delete_t) { + SharedDtor(*msg); + ::google::protobuf::internal::SizedDelete(msg, sizeof(SetHeartbeatWatchdogTimeoutResponse)); + } +#endif + + template + explicit PROTOBUF_CONSTEXPR SetHeartbeatWatchdogTimeoutResponse( + ::google::protobuf::internal::ConstantInitialized); + + inline SetHeartbeatWatchdogTimeoutResponse(const SetHeartbeatWatchdogTimeoutResponse& from) : SetHeartbeatWatchdogTimeoutResponse(nullptr, from) {} + inline SetHeartbeatWatchdogTimeoutResponse(SetHeartbeatWatchdogTimeoutResponse&& from) noexcept + : SetHeartbeatWatchdogTimeoutResponse(nullptr, std::move(from)) {} + inline SetHeartbeatWatchdogTimeoutResponse& operator=(const SetHeartbeatWatchdogTimeoutResponse& from) { + CopyFrom(from); + return *this; + } + inline SetHeartbeatWatchdogTimeoutResponse& operator=(SetHeartbeatWatchdogTimeoutResponse&& from) noexcept { + if (this == &from) return *this; + if (::google::protobuf::internal::CanMoveWithInternalSwap(GetArena(), from.GetArena())) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance); + } + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>(); + } + + static const ::google::protobuf::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::google::protobuf::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::google::protobuf::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const SetHeartbeatWatchdogTimeoutResponse& default_instance() { + return *internal_default_instance(); + } + static inline const SetHeartbeatWatchdogTimeoutResponse* internal_default_instance() { + return reinterpret_cast( + &_SetHeartbeatWatchdogTimeoutResponse_default_instance_); + } + static constexpr int kIndexInFileMessages = 7; + friend void swap(SetHeartbeatWatchdogTimeoutResponse& a, SetHeartbeatWatchdogTimeoutResponse& b) { a.Swap(&b); } + inline void Swap(SetHeartbeatWatchdogTimeoutResponse* other) { + if (other == this) return; + if (::google::protobuf::internal::CanUseInternalSwap(GetArena(), other->GetArena())) { + InternalSwap(other); + } else { + ::google::protobuf::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(SetHeartbeatWatchdogTimeoutResponse* other) { + if (other == this) return; + ABSL_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + SetHeartbeatWatchdogTimeoutResponse* New(::google::protobuf::Arena* arena = nullptr) const { + return ::google::protobuf::internal::ZeroFieldsBase::DefaultConstruct(arena); + } + using ::google::protobuf::internal::ZeroFieldsBase::CopyFrom; + inline void CopyFrom(const SetHeartbeatWatchdogTimeoutResponse& from) { + ::google::protobuf::internal::ZeroFieldsBase::CopyImpl(*this, from); + } + using ::google::protobuf::internal::ZeroFieldsBase::MergeFrom; + void MergeFrom(const SetHeartbeatWatchdogTimeoutResponse& from) { + ::google::protobuf::internal::ZeroFieldsBase::MergeImpl(*this, from); + } + + public: + bool IsInitialized() const { + return true; + } + private: + template + friend ::absl::string_view( + ::google::protobuf::internal::GetAnyMessageName)(); + static ::absl::string_view FullMessageName() { return "mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutResponse"; } + + protected: + explicit SetHeartbeatWatchdogTimeoutResponse(::google::protobuf::Arena* arena); + SetHeartbeatWatchdogTimeoutResponse(::google::protobuf::Arena* arena, const SetHeartbeatWatchdogTimeoutResponse& from); + SetHeartbeatWatchdogTimeoutResponse(::google::protobuf::Arena* arena, SetHeartbeatWatchdogTimeoutResponse&& from) noexcept + : SetHeartbeatWatchdogTimeoutResponse(arena) { + *this = ::std::move(from); + } + const ::google::protobuf::internal::ClassData* GetClassData() const PROTOBUF_FINAL; + static void* PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena); + static constexpr auto InternalNewImpl_(); + static const ::google::protobuf::internal::ClassDataFull _class_data_; + + public: + ::google::protobuf::Metadata GetMetadata() const; + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + // @@protoc_insertion_point(class_scope:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutResponse) + private: + class _Internal; + friend class ::google::protobuf::internal::TcParser; + static const ::google::protobuf::internal::TcParseTable< + 0, 0, 0, + 0, 2> + _table_; + + friend class ::google::protobuf::MessageLite; + friend class ::google::protobuf::Arena; + template + friend class ::google::protobuf::Arena::InternalHelper; + using InternalArenaConstructable_ = void; + using DestructorSkippable_ = void; + struct Impl_ { + inline explicit constexpr Impl_( + ::google::protobuf::internal::ConstantInitialized) noexcept; + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena); + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena, const Impl_& from, + const SetHeartbeatWatchdogTimeoutResponse& from_msg); + PROTOBUF_TSAN_DECLARE_MEMBER + }; + friend struct ::TableStruct_core_2fcore_2eproto; +}; +// ------------------------------------------------------------------- + +class SetHeartbeatWatchdogTimeoutRequest final + : public ::google::protobuf::Message +/* @@protoc_insertion_point(class_definition:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) */ { + public: + inline SetHeartbeatWatchdogTimeoutRequest() : SetHeartbeatWatchdogTimeoutRequest(nullptr) {} + ~SetHeartbeatWatchdogTimeoutRequest() PROTOBUF_FINAL; + +#if defined(PROTOBUF_CUSTOM_VTABLE) + void operator delete(SetHeartbeatWatchdogTimeoutRequest* msg, std::destroying_delete_t) { + SharedDtor(*msg); + ::google::protobuf::internal::SizedDelete(msg, sizeof(SetHeartbeatWatchdogTimeoutRequest)); + } +#endif + + template + explicit PROTOBUF_CONSTEXPR SetHeartbeatWatchdogTimeoutRequest( + ::google::protobuf::internal::ConstantInitialized); + + inline SetHeartbeatWatchdogTimeoutRequest(const SetHeartbeatWatchdogTimeoutRequest& from) : SetHeartbeatWatchdogTimeoutRequest(nullptr, from) {} + inline SetHeartbeatWatchdogTimeoutRequest(SetHeartbeatWatchdogTimeoutRequest&& from) noexcept + : SetHeartbeatWatchdogTimeoutRequest(nullptr, std::move(from)) {} + inline SetHeartbeatWatchdogTimeoutRequest& operator=(const SetHeartbeatWatchdogTimeoutRequest& from) { + CopyFrom(from); + return *this; + } + inline SetHeartbeatWatchdogTimeoutRequest& operator=(SetHeartbeatWatchdogTimeoutRequest&& from) noexcept { + if (this == &from) return *this; + if (::google::protobuf::internal::CanMoveWithInternalSwap(GetArena(), from.GetArena())) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance); + } + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>(); + } + + static const ::google::protobuf::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::google::protobuf::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::google::protobuf::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const SetHeartbeatWatchdogTimeoutRequest& default_instance() { + return *internal_default_instance(); + } + static inline const SetHeartbeatWatchdogTimeoutRequest* internal_default_instance() { + return reinterpret_cast( + &_SetHeartbeatWatchdogTimeoutRequest_default_instance_); + } + static constexpr int kIndexInFileMessages = 6; + friend void swap(SetHeartbeatWatchdogTimeoutRequest& a, SetHeartbeatWatchdogTimeoutRequest& b) { a.Swap(&b); } + inline void Swap(SetHeartbeatWatchdogTimeoutRequest* other) { + if (other == this) return; + if (::google::protobuf::internal::CanUseInternalSwap(GetArena(), other->GetArena())) { + InternalSwap(other); + } else { + ::google::protobuf::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(SetHeartbeatWatchdogTimeoutRequest* other) { + if (other == this) return; + ABSL_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + SetHeartbeatWatchdogTimeoutRequest* New(::google::protobuf::Arena* arena = nullptr) const { + return ::google::protobuf::Message::DefaultConstruct(arena); + } + using ::google::protobuf::Message::CopyFrom; + void CopyFrom(const SetHeartbeatWatchdogTimeoutRequest& from); + using ::google::protobuf::Message::MergeFrom; + void MergeFrom(const SetHeartbeatWatchdogTimeoutRequest& from) { SetHeartbeatWatchdogTimeoutRequest::MergeImpl(*this, from); } + + private: + static void MergeImpl( + ::google::protobuf::MessageLite& to_msg, + const ::google::protobuf::MessageLite& from_msg); + + public: + bool IsInitialized() const { + return true; + } + ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL; + #if defined(PROTOBUF_CUSTOM_VTABLE) + private: + static ::size_t ByteSizeLong(const ::google::protobuf::MessageLite& msg); + static ::uint8_t* _InternalSerialize( + const MessageLite& msg, ::uint8_t* target, + ::google::protobuf::io::EpsCopyOutputStream* stream); + + public: + ::size_t ByteSizeLong() const { return ByteSizeLong(*this); } + ::uint8_t* _InternalSerialize( + ::uint8_t* target, + ::google::protobuf::io::EpsCopyOutputStream* stream) const { + return _InternalSerialize(*this, target, stream); + } + #else // PROTOBUF_CUSTOM_VTABLE + ::size_t ByteSizeLong() const final; + ::uint8_t* _InternalSerialize( + ::uint8_t* target, + ::google::protobuf::io::EpsCopyOutputStream* stream) const final; + #endif // PROTOBUF_CUSTOM_VTABLE + int GetCachedSize() const { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::google::protobuf::Arena* arena); + static void SharedDtor(MessageLite& self); + void InternalSwap(SetHeartbeatWatchdogTimeoutRequest* other); + private: + template + friend ::absl::string_view( + ::google::protobuf::internal::GetAnyMessageName)(); + static ::absl::string_view FullMessageName() { return "mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest"; } + + protected: + explicit SetHeartbeatWatchdogTimeoutRequest(::google::protobuf::Arena* arena); + SetHeartbeatWatchdogTimeoutRequest(::google::protobuf::Arena* arena, const SetHeartbeatWatchdogTimeoutRequest& from); + SetHeartbeatWatchdogTimeoutRequest(::google::protobuf::Arena* arena, SetHeartbeatWatchdogTimeoutRequest&& from) noexcept + : SetHeartbeatWatchdogTimeoutRequest(arena) { + *this = ::std::move(from); + } + const ::google::protobuf::internal::ClassData* GetClassData() const PROTOBUF_FINAL; + static void* PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena); + static constexpr auto InternalNewImpl_(); + static const ::google::protobuf::internal::ClassDataFull _class_data_; + + public: + ::google::protobuf::Metadata GetMetadata() const; + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + enum : int { + kTimeoutSFieldNumber = 1, + }; + // double timeout_s = 1; + void clear_timeout_s() ; + double timeout_s() const; + void set_timeout_s(double value); + + private: + double _internal_timeout_s() const; + void _internal_set_timeout_s(double value); + + public: + // @@protoc_insertion_point(class_scope:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest) + private: + class _Internal; + friend class ::google::protobuf::internal::TcParser; + static const ::google::protobuf::internal::TcParseTable< + 0, 1, 0, + 0, 2> + _table_; + + friend class ::google::protobuf::MessageLite; + friend class ::google::protobuf::Arena; + template + friend class ::google::protobuf::Arena::InternalHelper; + using InternalArenaConstructable_ = void; + using DestructorSkippable_ = void; + struct Impl_ { + inline explicit constexpr Impl_( + ::google::protobuf::internal::ConstantInitialized) noexcept; + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena); + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena, const Impl_& from, + const SetHeartbeatWatchdogTimeoutRequest& from_msg); + double timeout_s_; + ::google::protobuf::internal::CachedSize _cached_size_; + PROTOBUF_TSAN_DECLARE_MEMBER + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_core_2fcore_2eproto; +}; +// ------------------------------------------------------------------- + +class FeedHeartbeatWatchdogResponse final + : public ::google::protobuf::internal::ZeroFieldsBase +/* @@protoc_insertion_point(class_definition:mavsdk.rpc.core.FeedHeartbeatWatchdogResponse) */ { + public: + inline FeedHeartbeatWatchdogResponse() : FeedHeartbeatWatchdogResponse(nullptr) {} + +#if defined(PROTOBUF_CUSTOM_VTABLE) + void operator delete(FeedHeartbeatWatchdogResponse* msg, std::destroying_delete_t) { + SharedDtor(*msg); + ::google::protobuf::internal::SizedDelete(msg, sizeof(FeedHeartbeatWatchdogResponse)); + } +#endif + + template + explicit PROTOBUF_CONSTEXPR FeedHeartbeatWatchdogResponse( + ::google::protobuf::internal::ConstantInitialized); + + inline FeedHeartbeatWatchdogResponse(const FeedHeartbeatWatchdogResponse& from) : FeedHeartbeatWatchdogResponse(nullptr, from) {} + inline FeedHeartbeatWatchdogResponse(FeedHeartbeatWatchdogResponse&& from) noexcept + : FeedHeartbeatWatchdogResponse(nullptr, std::move(from)) {} + inline FeedHeartbeatWatchdogResponse& operator=(const FeedHeartbeatWatchdogResponse& from) { + CopyFrom(from); + return *this; + } + inline FeedHeartbeatWatchdogResponse& operator=(FeedHeartbeatWatchdogResponse&& from) noexcept { + if (this == &from) return *this; + if (::google::protobuf::internal::CanMoveWithInternalSwap(GetArena(), from.GetArena())) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance); + } + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>(); + } + + static const ::google::protobuf::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::google::protobuf::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::google::protobuf::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const FeedHeartbeatWatchdogResponse& default_instance() { + return *internal_default_instance(); + } + static inline const FeedHeartbeatWatchdogResponse* internal_default_instance() { + return reinterpret_cast( + &_FeedHeartbeatWatchdogResponse_default_instance_); + } + static constexpr int kIndexInFileMessages = 5; + friend void swap(FeedHeartbeatWatchdogResponse& a, FeedHeartbeatWatchdogResponse& b) { a.Swap(&b); } + inline void Swap(FeedHeartbeatWatchdogResponse* other) { + if (other == this) return; + if (::google::protobuf::internal::CanUseInternalSwap(GetArena(), other->GetArena())) { + InternalSwap(other); + } else { + ::google::protobuf::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(FeedHeartbeatWatchdogResponse* other) { + if (other == this) return; + ABSL_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + FeedHeartbeatWatchdogResponse* New(::google::protobuf::Arena* arena = nullptr) const { + return ::google::protobuf::internal::ZeroFieldsBase::DefaultConstruct(arena); + } + using ::google::protobuf::internal::ZeroFieldsBase::CopyFrom; + inline void CopyFrom(const FeedHeartbeatWatchdogResponse& from) { + ::google::protobuf::internal::ZeroFieldsBase::CopyImpl(*this, from); + } + using ::google::protobuf::internal::ZeroFieldsBase::MergeFrom; + void MergeFrom(const FeedHeartbeatWatchdogResponse& from) { + ::google::protobuf::internal::ZeroFieldsBase::MergeImpl(*this, from); + } + + public: + bool IsInitialized() const { + return true; + } + private: + template + friend ::absl::string_view( + ::google::protobuf::internal::GetAnyMessageName)(); + static ::absl::string_view FullMessageName() { return "mavsdk.rpc.core.FeedHeartbeatWatchdogResponse"; } + + protected: + explicit FeedHeartbeatWatchdogResponse(::google::protobuf::Arena* arena); + FeedHeartbeatWatchdogResponse(::google::protobuf::Arena* arena, const FeedHeartbeatWatchdogResponse& from); + FeedHeartbeatWatchdogResponse(::google::protobuf::Arena* arena, FeedHeartbeatWatchdogResponse&& from) noexcept + : FeedHeartbeatWatchdogResponse(arena) { + *this = ::std::move(from); + } + const ::google::protobuf::internal::ClassData* GetClassData() const PROTOBUF_FINAL; + static void* PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena); + static constexpr auto InternalNewImpl_(); + static const ::google::protobuf::internal::ClassDataFull _class_data_; + + public: + ::google::protobuf::Metadata GetMetadata() const; + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + // @@protoc_insertion_point(class_scope:mavsdk.rpc.core.FeedHeartbeatWatchdogResponse) + private: + class _Internal; + friend class ::google::protobuf::internal::TcParser; + static const ::google::protobuf::internal::TcParseTable< + 0, 0, 0, + 0, 2> + _table_; + + friend class ::google::protobuf::MessageLite; + friend class ::google::protobuf::Arena; + template + friend class ::google::protobuf::Arena::InternalHelper; + using InternalArenaConstructable_ = void; + using DestructorSkippable_ = void; + struct Impl_ { + inline explicit constexpr Impl_( + ::google::protobuf::internal::ConstantInitialized) noexcept; + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena); + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena, const Impl_& from, + const FeedHeartbeatWatchdogResponse& from_msg); + PROTOBUF_TSAN_DECLARE_MEMBER + }; + friend struct ::TableStruct_core_2fcore_2eproto; +}; +// ------------------------------------------------------------------- + +class FeedHeartbeatWatchdogRequest final + : public ::google::protobuf::internal::ZeroFieldsBase +/* @@protoc_insertion_point(class_definition:mavsdk.rpc.core.FeedHeartbeatWatchdogRequest) */ { + public: + inline FeedHeartbeatWatchdogRequest() : FeedHeartbeatWatchdogRequest(nullptr) {} + +#if defined(PROTOBUF_CUSTOM_VTABLE) + void operator delete(FeedHeartbeatWatchdogRequest* msg, std::destroying_delete_t) { + SharedDtor(*msg); + ::google::protobuf::internal::SizedDelete(msg, sizeof(FeedHeartbeatWatchdogRequest)); + } +#endif + + template + explicit PROTOBUF_CONSTEXPR FeedHeartbeatWatchdogRequest( + ::google::protobuf::internal::ConstantInitialized); + + inline FeedHeartbeatWatchdogRequest(const FeedHeartbeatWatchdogRequest& from) : FeedHeartbeatWatchdogRequest(nullptr, from) {} + inline FeedHeartbeatWatchdogRequest(FeedHeartbeatWatchdogRequest&& from) noexcept + : FeedHeartbeatWatchdogRequest(nullptr, std::move(from)) {} + inline FeedHeartbeatWatchdogRequest& operator=(const FeedHeartbeatWatchdogRequest& from) { + CopyFrom(from); + return *this; + } + inline FeedHeartbeatWatchdogRequest& operator=(FeedHeartbeatWatchdogRequest&& from) noexcept { + if (this == &from) return *this; + if (::google::protobuf::internal::CanMoveWithInternalSwap(GetArena(), from.GetArena())) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.unknown_fields<::google::protobuf::UnknownFieldSet>(::google::protobuf::UnknownFieldSet::default_instance); + } + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return _internal_metadata_.mutable_unknown_fields<::google::protobuf::UnknownFieldSet>(); + } + + static const ::google::protobuf::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::google::protobuf::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::google::protobuf::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const FeedHeartbeatWatchdogRequest& default_instance() { + return *internal_default_instance(); + } + static inline const FeedHeartbeatWatchdogRequest* internal_default_instance() { + return reinterpret_cast( + &_FeedHeartbeatWatchdogRequest_default_instance_); + } + static constexpr int kIndexInFileMessages = 4; + friend void swap(FeedHeartbeatWatchdogRequest& a, FeedHeartbeatWatchdogRequest& b) { a.Swap(&b); } + inline void Swap(FeedHeartbeatWatchdogRequest* other) { + if (other == this) return; + if (::google::protobuf::internal::CanUseInternalSwap(GetArena(), other->GetArena())) { + InternalSwap(other); + } else { + ::google::protobuf::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(FeedHeartbeatWatchdogRequest* other) { + if (other == this) return; + ABSL_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + FeedHeartbeatWatchdogRequest* New(::google::protobuf::Arena* arena = nullptr) const { + return ::google::protobuf::internal::ZeroFieldsBase::DefaultConstruct(arena); + } + using ::google::protobuf::internal::ZeroFieldsBase::CopyFrom; + inline void CopyFrom(const FeedHeartbeatWatchdogRequest& from) { + ::google::protobuf::internal::ZeroFieldsBase::CopyImpl(*this, from); + } + using ::google::protobuf::internal::ZeroFieldsBase::MergeFrom; + void MergeFrom(const FeedHeartbeatWatchdogRequest& from) { + ::google::protobuf::internal::ZeroFieldsBase::MergeImpl(*this, from); + } + + public: + bool IsInitialized() const { + return true; + } + private: + template + friend ::absl::string_view( + ::google::protobuf::internal::GetAnyMessageName)(); + static ::absl::string_view FullMessageName() { return "mavsdk.rpc.core.FeedHeartbeatWatchdogRequest"; } + + protected: + explicit FeedHeartbeatWatchdogRequest(::google::protobuf::Arena* arena); + FeedHeartbeatWatchdogRequest(::google::protobuf::Arena* arena, const FeedHeartbeatWatchdogRequest& from); + FeedHeartbeatWatchdogRequest(::google::protobuf::Arena* arena, FeedHeartbeatWatchdogRequest&& from) noexcept + : FeedHeartbeatWatchdogRequest(arena) { + *this = ::std::move(from); + } + const ::google::protobuf::internal::ClassData* GetClassData() const PROTOBUF_FINAL; + static void* PlacementNew_(const void*, void* mem, + ::google::protobuf::Arena* arena); + static constexpr auto InternalNewImpl_(); + static const ::google::protobuf::internal::ClassDataFull _class_data_; + + public: + ::google::protobuf::Metadata GetMetadata() const; + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + // @@protoc_insertion_point(class_scope:mavsdk.rpc.core.FeedHeartbeatWatchdogRequest) + private: + class _Internal; + friend class ::google::protobuf::internal::TcParser; + static const ::google::protobuf::internal::TcParseTable< + 0, 0, 0, + 0, 2> + _table_; + + friend class ::google::protobuf::MessageLite; + friend class ::google::protobuf::Arena; + template + friend class ::google::protobuf::Arena::InternalHelper; + using InternalArenaConstructable_ = void; + using DestructorSkippable_ = void; + struct Impl_ { + inline explicit constexpr Impl_( + ::google::protobuf::internal::ConstantInitialized) noexcept; + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena); + inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, + ::google::protobuf::Arena* arena, const Impl_& from, + const FeedHeartbeatWatchdogRequest& from_msg); + PROTOBUF_TSAN_DECLARE_MEMBER + }; + friend struct ::TableStruct_core_2fcore_2eproto; +}; +// ------------------------------------------------------------------- + class ConnectionState final : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:mavsdk.rpc.core.ConnectionState) */ { @@ -630,7 +1271,7 @@ class ConnectionState final return reinterpret_cast( &_ConnectionState_default_instance_); } - static constexpr int kIndexInFileMessages = 4; + static constexpr int kIndexInFileMessages = 8; friend void swap(ConnectionState& a, ConnectionState& b) { a.Swap(&b); } inline void Swap(ConnectionState* other) { if (other == this) return; @@ -1105,6 +1746,44 @@ inline void SetMavlinkTimeoutRequest::_internal_set_timeout_s(double value) { // ------------------------------------------------------------------- +// FeedHeartbeatWatchdogRequest + +// ------------------------------------------------------------------- + +// FeedHeartbeatWatchdogResponse + +// ------------------------------------------------------------------- + +// SetHeartbeatWatchdogTimeoutRequest + +// double timeout_s = 1; +inline void SetHeartbeatWatchdogTimeoutRequest::clear_timeout_s() { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.timeout_s_ = 0; +} +inline double SetHeartbeatWatchdogTimeoutRequest::timeout_s() const { + // @@protoc_insertion_point(field_get:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest.timeout_s) + return _internal_timeout_s(); +} +inline void SetHeartbeatWatchdogTimeoutRequest::set_timeout_s(double value) { + _internal_set_timeout_s(value); + // @@protoc_insertion_point(field_set:mavsdk.rpc.core.SetHeartbeatWatchdogTimeoutRequest.timeout_s) +} +inline double SetHeartbeatWatchdogTimeoutRequest::_internal_timeout_s() const { + ::google::protobuf::internal::TSanRead(&_impl_); + return _impl_.timeout_s_; +} +inline void SetHeartbeatWatchdogTimeoutRequest::_internal_set_timeout_s(double value) { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.timeout_s_ = value; +} + +// ------------------------------------------------------------------- + +// SetHeartbeatWatchdogTimeoutResponse + +// ------------------------------------------------------------------- + // ConnectionState // bool is_connected = 2; diff --git a/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h b/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h index d8ed59edb5..8474f04e09 100644 --- a/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h +++ b/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h @@ -29,7 +29,7 @@ namespace mavsdk { namespace rpc { namespace mission_raw_server { -// Acts as a vehicle and receives incoming missions from GCS (in raw MAVLINK format). +// Acts as a vehicle and receives incoming missions from GCS (in raw MAVLINK format). // Provides current mission item state, so the server can progress through missions. class MissionRawServerService final { public: diff --git a/cpp/src/mavsdk_server/src/mavsdk_server.cpp b/cpp/src/mavsdk_server/src/mavsdk_server.cpp index b22dbbb0d3..6d76bae38e 100644 --- a/cpp/src/mavsdk_server/src/mavsdk_server.cpp +++ b/cpp/src/mavsdk_server/src/mavsdk_server.cpp @@ -41,9 +41,12 @@ class MavsdkServer::Impl { int getPort() { return _grpc_port; } - void setMavlinkIds(uint8_t system_id, uint8_t component_id) + void setMavlinkIds( + uint8_t system_id, uint8_t component_id, double heartbeat_watchdog_timeout_s = 0.0) { - _mavsdk.set_configuration(mavsdk::Mavsdk::Configuration{system_id, component_id, false}); + auto config = mavsdk::Mavsdk::Configuration{system_id, component_id, false}; + config.set_heartbeat_watchdog_timeout_s(heartbeat_watchdog_timeout_s); + _mavsdk.set_configuration(config); } private: @@ -85,3 +88,9 @@ void MavsdkServer::setMavlinkIds(uint8_t system_id, uint8_t component_id) { _impl->setMavlinkIds(system_id, component_id); } + +void MavsdkServer::setMavlinkIds( + uint8_t system_id, uint8_t component_id, double heartbeat_watchdog_timeout_s) +{ + _impl->setMavlinkIds(system_id, component_id, heartbeat_watchdog_timeout_s); +} diff --git a/cpp/src/mavsdk_server/src/mavsdk_server.hpp b/cpp/src/mavsdk_server/src/mavsdk_server.hpp index c6f7d5f135..0f5ed28254 100644 --- a/cpp/src/mavsdk_server/src/mavsdk_server.hpp +++ b/cpp/src/mavsdk_server/src/mavsdk_server.hpp @@ -17,6 +17,10 @@ struct MavsdkServer { void stop(); int getPort(); void setMavlinkIds(uint8_t system_id, uint8_t component_id); + // Overload (rather than a default argument) so that the two-parameter + // symbol above stays unchanged for existing binaries. + void setMavlinkIds( + uint8_t system_id, uint8_t component_id, double heartbeat_watchdog_timeout_s); private: class Impl; diff --git a/cpp/src/mavsdk_server/src/mavsdk_server_api.cpp b/cpp/src/mavsdk_server/src/mavsdk_server_api.cpp index 2a6f037359..d6db9643ef 100644 --- a/cpp/src/mavsdk_server/src/mavsdk_server_api.cpp +++ b/cpp/src/mavsdk_server/src/mavsdk_server_api.cpp @@ -31,7 +31,19 @@ int mavsdk_server_run_with_mavlink_ids( const uint8_t system_id, const uint8_t component_id) { - mavsdk_server->setMavlinkIds(system_id, component_id); + return mavsdk_server_run_with_mavlink_ids_and_options( + mavsdk_server, system_address, mavsdk_server_port, system_id, component_id, 0.0); +} + +int mavsdk_server_run_with_mavlink_ids_and_options( + MavsdkServer* mavsdk_server, + const char* system_address, + const int mavsdk_server_port, + const uint8_t system_id, + const uint8_t component_id, + const double heartbeat_watchdog_timeout_s) +{ + mavsdk_server->setMavlinkIds(system_id, component_id, heartbeat_watchdog_timeout_s); return mavsdk_server_run(mavsdk_server, system_address, mavsdk_server_port); } diff --git a/cpp/src/mavsdk_server/src/mavsdk_server_api.h b/cpp/src/mavsdk_server/src/mavsdk_server_api.h index 17cc77f738..4f834e98d5 100644 --- a/cpp/src/mavsdk_server/src/mavsdk_server_api.h +++ b/cpp/src/mavsdk_server/src/mavsdk_server_api.h @@ -52,6 +52,21 @@ MAVSDK_PUBLIC int mavsdk_server_run_with_mavlink_ids( const uint8_t system_id, const uint8_t component_id); +/* + * Run MavsdkServer with additional options. + * + * @param heartbeat_watchdog_timeout_s When greater than 0, MAVSDK's periodic + * heartbeats are only sent as long as the FeedHeartbeatWatchdog RPC + * keeps being called at least once per timeout period. + */ +MAVSDK_PUBLIC int mavsdk_server_run_with_mavlink_ids_and_options( + struct MavsdkServer* mavsdk_server, + const char* system_address, + const int mavsdk_server_port, + const uint8_t system_id, + const uint8_t component_id, + const double heartbeat_watchdog_timeout_s); + /* * Get gRPC port. * diff --git a/cpp/src/mavsdk_server/src/mavsdk_server_bin.cpp b/cpp/src/mavsdk_server/src/mavsdk_server_bin.cpp index b784e16a98..f674697aa1 100644 --- a/cpp/src/mavsdk_server/src/mavsdk_server_bin.cpp +++ b/cpp/src/mavsdk_server/src/mavsdk_server_bin.cpp @@ -1,6 +1,8 @@ #include "mavsdk_server_api.h" +#include "mavsdk.hpp" #include +#include #include #include #include @@ -9,6 +11,7 @@ static auto constexpr default_connection = "udpin://0.0.0.0:14540"; static auto constexpr default_mavsdk_server_port = 50051; static auto constexpr default_sysid = 245; static auto constexpr default_compid = 190; +static auto constexpr default_heartbeat_watchdog_timeout_s = 0.0; static void usage(const char* bin_name); static bool is_integer(const std::string& tested_integer); @@ -19,6 +22,7 @@ int main(int argc, char** argv) int mavsdk_server_port = default_mavsdk_server_port; int mavsdk_sysid = default_sysid; int mavsdk_compid = default_compid; + double heartbeat_watchdog_timeout_s = default_heartbeat_watchdog_timeout_s; for (int i = 1; i < argc; i++) { const std::string current_arg = argv[i]; @@ -81,6 +85,23 @@ int main(int argc, char** argv) usage(argv[0]); return 1; } + } else if (current_arg == "--heartbeat-watchdog-timeout") { + if (argc <= i + 1) { + usage(argv[0]); + return 1; + } + + const std::string timeout(argv[i + 1]); + i++; + + char* end = nullptr; + heartbeat_watchdog_timeout_s = std::strtod(timeout.c_str(), &end); + + if (end == timeout.c_str() || *end != '\0' || + !mavsdk::is_valid_heartbeat_watchdog_timeout_s(heartbeat_watchdog_timeout_s)) { + usage(argv[0]); + return 1; + } } else { connection_url = current_arg; } @@ -88,12 +109,13 @@ int main(int argc, char** argv) MavsdkServer* mavsdk_server; mavsdk_server_init(&mavsdk_server); - const int ret = mavsdk_server_run_with_mavlink_ids( + const int ret = mavsdk_server_run_with_mavlink_ids_and_options( mavsdk_server, connection_url.c_str(), mavsdk_server_port, static_cast(mavsdk_sysid), - static_cast(mavsdk_compid)); + static_cast(mavsdk_compid), + heartbeat_watchdog_timeout_s); if (ret != 0) { std::cout << "Failed to start, exiting...\n"; @@ -128,7 +150,13 @@ void usage(const char* bin_name) << " --sysid : set the MAVLink system ID of the MAVSDK server itself,\n" << " (default is " << default_sysid << ", range 1..255)\n" << " --compid : set the MAVLink component ID of the MAVSDK server itself,\n" - << " (default is " << default_compid << ", range 1..255)\n"; + << " (default is " << default_compid << ", range 1..255)\n" + << " --heartbeat-watchdog-timeout :\n" + << " set the heartbeat watchdog timeout in seconds; when greater\n" + << " than 0, heartbeats are only sent while the\n" + << " FeedHeartbeatWatchdog RPC keeps being called within the\n" + << " timeout period; must be 0 (disabled) or at least 1 second\n" + << " (default is 0, watchdog disabled)\n"; } bool is_integer(const std::string& tested_integer) diff --git a/cpp/src/mavsdk_server/test/core_service_impl_test.cpp b/cpp/src/mavsdk_server/test/core_service_impl_test.cpp index 44300acda9..64bfaf961f 100644 --- a/cpp/src/mavsdk_server/test/core_service_impl_test.cpp +++ b/cpp/src/mavsdk_server/test/core_service_impl_test.cpp @@ -62,6 +62,65 @@ TEST_F(CoreServiceImplTest, subscribeConnectionStateSubscribesToChange) _core_service->stop(); } +TEST_F(CoreServiceImplTest, feedHeartbeatWatchdogFeeds) +{ + EXPECT_CALL(*_mavsdk, feed_heartbeat_watchdog()).Times(1); + + grpc::ClientContext context; + mavsdk::rpc::core::FeedHeartbeatWatchdogRequest request; + mavsdk::rpc::core::FeedHeartbeatWatchdogResponse response; + + const auto status = _stub->FeedHeartbeatWatchdog(&context, request, &response); + + EXPECT_TRUE(status.ok()); + _core_service->stop(); +} + +TEST_F(CoreServiceImplTest, setHeartbeatWatchdogTimeoutSetsTimeout) +{ + EXPECT_CALL(*_mavsdk, set_heartbeat_watchdog_timeout_s(2.5)).Times(1); + + grpc::ClientContext context; + mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest request; + request.set_timeout_s(2.5); + mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse response; + + const auto status = _stub->SetHeartbeatWatchdogTimeout(&context, request, &response); + + EXPECT_TRUE(status.ok()); + _core_service->stop(); +} + +TEST_F(CoreServiceImplTest, setHeartbeatWatchdogTimeoutDisable) +{ + EXPECT_CALL(*_mavsdk, set_heartbeat_watchdog_timeout_s(0.0)).Times(1); + + grpc::ClientContext context; + mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest request; + request.set_timeout_s(0.0); + mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse response; + + const auto status = _stub->SetHeartbeatWatchdogTimeout(&context, request, &response); + + EXPECT_TRUE(status.ok()); + _core_service->stop(); +} + +TEST_F(CoreServiceImplTest, setHeartbeatWatchdogTimeoutRejectsSubSecond) +{ + EXPECT_CALL(*_mavsdk, set_heartbeat_watchdog_timeout_s(_)).Times(0); + + grpc::ClientContext context; + mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutRequest request; + request.set_timeout_s(0.5); + mavsdk::rpc::core::SetHeartbeatWatchdogTimeoutResponse response; + + const auto status = _stub->SetHeartbeatWatchdogTimeout(&context, request, &response); + + EXPECT_EQ(grpc::StatusCode::INVALID_ARGUMENT, status.error_code()); + _core_service->stop(); +} + TEST_F(CoreServiceImplTest, connectionStateStreamEmptyIfCallbackNotCalled) { std::vector events; diff --git a/proto b/proto index 1aded99e4e..951bc12e43 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 1aded99e4e456b91f79d82909dfa5ad58012b093 +Subproject commit 951bc12e4310f741d1038785c8ff47781dde6b81 From 6157bae2212d313e2461ab362c2375e13f7e3664 Mon Sep 17 00:00:00 2001 From: Julian Oes Date: Fri, 17 Jul 2026 13:24:46 +1200 Subject: [PATCH 2/3] proto: point to fork for now, update docs --- .gitmodules | 2 +- .../mavsdk/core/heartbeat_watchdog_test.cpp | 4 +- cpp/src/mavsdk/core/mavsdk_time.cpp | 6 +-- .../mission_raw_server.grpc.pb.h | 2 +- .../api_reference/classmavsdk_1_1_mavsdk.md | 39 +++++++++++++++ ...lassmavsdk_1_1_mavsdk_1_1_configuration.md | 49 +++++++++++++++++++ docs/en/cpp/api_reference/namespacemavsdk.md | 22 +++++++++ proto | 2 +- 8 files changed, 118 insertions(+), 8 deletions(-) diff --git a/.gitmodules b/.gitmodules index 57964ae904..68d145e483 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "mavsdk-proto"] path = proto - url = https://github.com/mavlink/MAVSDK-Proto.git + url = https://github.com/tpayne-censystech/MAVSDK-Proto.git diff --git a/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp b/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp index 1297fa5472..de803beadc 100644 --- a/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp +++ b/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp @@ -182,8 +182,8 @@ TEST(HeartbeatWatchdog, ConfigurationStoresAndValidatesTimeout) // stored value unchanged. EXPECT_FALSE(configuration.set_heartbeat_watchdog_timeout_s(0.5)); EXPECT_FALSE(configuration.set_heartbeat_watchdog_timeout_s(-1.0)); - EXPECT_FALSE(configuration.set_heartbeat_watchdog_timeout_s( - std::numeric_limits::infinity())); + EXPECT_FALSE( + configuration.set_heartbeat_watchdog_timeout_s(std::numeric_limits::infinity())); EXPECT_FALSE( configuration.set_heartbeat_watchdog_timeout_s(std::numeric_limits::quiet_NaN())); EXPECT_DOUBLE_EQ(configuration.get_heartbeat_watchdog_timeout_s(), 2.5); diff --git a/cpp/src/mavsdk/core/mavsdk_time.cpp b/cpp/src/mavsdk/core/mavsdk_time.cpp index d89e341dc1..08cd382a49 100644 --- a/cpp/src/mavsdk/core/mavsdk_time.cpp +++ b/cpp/src/mavsdk/core/mavsdk_time.cpp @@ -94,9 +94,9 @@ void Time::sleep_for(std::chrono::nanoseconds ns) FakeTime::FakeTime() : Time() { // Start with current time so we don't start from 0. - _current_ns = std::chrono::duration_cast( - steady_clock::now().time_since_epoch()) - .count(); + _current_ns = + std::chrono::duration_cast(steady_clock::now().time_since_epoch()) + .count(); } SteadyTimePoint FakeTime::steady_time() diff --git a/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h b/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h index 8474f04e09..d8ed59edb5 100644 --- a/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h +++ b/cpp/src/mavsdk_server/src/generated/mission_raw_server/mission_raw_server.grpc.pb.h @@ -29,7 +29,7 @@ namespace mavsdk { namespace rpc { namespace mission_raw_server { -// Acts as a vehicle and receives incoming missions from GCS (in raw MAVLINK format). +// Acts as a vehicle and receives incoming missions from GCS (in raw MAVLINK format). // Provides current mission item state, so the server can progress through missions. class MissionRawServerService final { public: diff --git a/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk.md b/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk.md index 57b2b0aff7..63b8d4e19d 100644 --- a/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk.md +++ b/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk.md @@ -54,9 +54,11 @@ void | [unsubscribe_connection_errors](#classmavsdk_1_1_mavsdk_1a377ec6517ee7598 std::vector< std::shared_ptr< [System](classmavsdk_1_1_system.md) > > | [systems](#classmavsdk_1_1_mavsdk_1aca9c72b300d384341b00ff9ba2c6e5c5) () const | Get a vector of systems which have been discovered or set-up. std::optional< std::shared_ptr< [System](classmavsdk_1_1_system.md) > > | [first_autopilot](#classmavsdk_1_1_mavsdk_1aa1bcb865693dbd140478e75ce58699b7) (double timeout_s)const | Get the first autopilot that has been discovered. void | [set_configuration](#classmavsdk_1_1_mavsdk_1acaeea86253493dc15b6540d2100a1b86) ([Configuration](classmavsdk_1_1_mavsdk_1_1_configuration.md) configuration) | Set [Configuration](classmavsdk_1_1_mavsdk_1_1_configuration.md) of SDK. +bool | [set_heartbeat_watchdog_timeout_s](#classmavsdk_1_1_mavsdk_1a83a387c399355c2fa541e7992b591ca8) (double timeout_s) | Set the heartbeat watchdog timeout at runtime. void | [set_timeout_s](#classmavsdk_1_1_mavsdk_1a765f37b61462addcfd961e720585d2c6) (double timeout_s) | Set timeout of MAVLink transfers. void | [set_heartbeat_timeout_s](#classmavsdk_1_1_mavsdk_1afbab63cf2a795e4ca7262836d5fe4b46) (double timeout_s) | Set heartbeat timeout. double | [get_heartbeat_timeout_s](#classmavsdk_1_1_mavsdk_1a6179b858f74415251ef43da11bc6edbc) () const | Get heartbeat timeout. +void | [feed_heartbeat_watchdog](#classmavsdk_1_1_mavsdk_1ad786bca001160944e8321355a816fe90) () | Feed the heartbeat watchdog. void | [set_callback_executor](#classmavsdk_1_1_mavsdk_1a5de0ff39a51efe3b235fe022e6b58034) (std::function< void(std::function< void()>)> executor) | Set a custom callback executor. [NewSystemHandle](classmavsdk_1_1_mavsdk.md#classmavsdk_1_1_mavsdk_1ae0727f2bed9cbf276d161ada0a432b8c) | [subscribe_on_new_system](#classmavsdk_1_1_mavsdk_1a5b7c958ad2e4529dc7b950ab26618575) (const [NewSystemCallback](classmavsdk_1_1_mavsdk.md#classmavsdk_1_1_mavsdk_1a7a283c6a75e852a56be4c5862f8a3fab) & callback) | Get notification about a change in systems. void | [unsubscribe_on_new_system](#classmavsdk_1_1_mavsdk_1ad7f77f1295a700ee73cccc345019c1ff) ([NewSystemHandle](classmavsdk_1_1_mavsdk.md#classmavsdk_1_1_mavsdk_1ae0727f2bed9cbf276d161ada0a432b8c) handle) | unsubscribe from subscribe_on_new_system. @@ -414,6 +416,30 @@ The default configuration is `Configuration::GroundStation` The configuration is * [Configuration](classmavsdk_1_1_mavsdk_1_1_configuration.md) **configuration** - [Configuration](classmavsdk_1_1_mavsdk_1_1_configuration.md) chosen. +### set_heartbeat_watchdog_timeout_s() {#classmavsdk_1_1_mavsdk_1a83a387c399355c2fa541e7992b591ca8} +```cpp +bool mavsdk::Mavsdk::set_heartbeat_watchdog_timeout_s(double timeout_s) +``` + + +Set the heartbeat watchdog timeout at runtime. + +When set to a value greater than 0, the periodic heartbeats sent by MAVSDK are only sent as long as [feed_heartbeat_watchdog()](classmavsdk_1_1_mavsdk.md#classmavsdk_1_1_mavsdk_1ad786bca001160944e8321355a816fe90) keeps being called at least once per timeout period. Enabling or changing the timeout stops any running heartbeats until the watchdog is fed. + + +When set to 0, the watchdog is disabled and heartbeats follow the usual policy (always_send_heartbeats or a connected system). + + +This is an alternative to configuring the watchdog via [Configuration::set_heartbeat_watchdog_timeout_s()](classmavsdk_1_1_mavsdk_1_1_configuration.md#classmavsdk_1_1_mavsdk_1_1_configuration_1a53bdc9285ca6687487c18fed28b30dd2) at startup. + +**Parameters** + +* double **timeout_s** - Timeout in seconds: 0 (disabled) or at least 1. + +**Returns** + + bool - true if the value was accepted, false if it was rejected (invalid values are ignored and the previous value kept). + ### set_timeout_s() {#classmavsdk_1_1_mavsdk_1a765f37b61462addcfd961e720585d2c6} ```cpp void mavsdk::Mavsdk::set_timeout_s(double timeout_s) @@ -455,6 +481,19 @@ Get heartbeat timeout.  double - Timeout in seconds. +### feed_heartbeat_watchdog() {#classmavsdk_1_1_mavsdk_1ad786bca001160944e8321355a816fe90} +```cpp +void mavsdk::Mavsdk::feed_heartbeat_watchdog() +``` + + +Feed the heartbeat watchdog. + +Resets the watchdog timer configured with [Configuration::set_heartbeat_watchdog_timeout_s()](classmavsdk_1_1_mavsdk_1_1_configuration.md#classmavsdk_1_1_mavsdk_1_1_configuration_1a53bdc9285ca6687487c18fed28b30dd2), keeping the periodic heartbeats alive for another timeout period. If the watchdog had already expired, this restarts the heartbeats. + + +Has no effect if no watchdog is configured. A feed only clears the watchdog latch (set on expiry or whenever heartbeats stop while the watchdog is configured), and only restarts heartbeats if they are supposed to be sent in the first place (always_send_heartbeats is set or a system is connected). It never starts heartbeats that are off for any other reason. + ### set_callback_executor() {#classmavsdk_1_1_mavsdk_1a5de0ff39a51efe3b235fe022e6b58034} ```cpp void mavsdk::Mavsdk::set_callback_executor(std::function< void(std::function< void()>)> executor) diff --git a/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk_1_1_configuration.md b/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk_1_1_configuration.md index 503d2e84ca..85d06dcc7f 100644 --- a/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk_1_1_configuration.md +++ b/docs/en/cpp/api_reference/classmavsdk_1_1_mavsdk_1_1_configuration.md @@ -22,6 +22,8 @@ uint8_t | [get_component_id](#classmavsdk_1_1_mavsdk_1_1_configuration_1adfcae3d void | [set_component_id](#classmavsdk_1_1_mavsdk_1_1_configuration_1aa590fbafa8ca104e1a004ca537f5798e) (uint8_t component_id) | Set the component id of this configuration. bool | [get_always_send_heartbeats](#classmavsdk_1_1_mavsdk_1_1_configuration_1a0aa9008fe5a7498f374dbd2adad5f137) () const | Get whether to send heartbeats by default. void | [set_always_send_heartbeats](#classmavsdk_1_1_mavsdk_1_1_configuration_1a0ad68b52763e205012b34faa5120a792) (bool always_send_heartbeats) | Set whether to send heartbeats by default. +double | [get_heartbeat_watchdog_timeout_s](#classmavsdk_1_1_mavsdk_1_1_configuration_1ae438001c4f7aabe6ca220306297f6b8f) () const | Get the heartbeat watchdog timeout. +bool | [set_heartbeat_watchdog_timeout_s](#classmavsdk_1_1_mavsdk_1_1_configuration_1a53bdc9285ca6687487c18fed28b30dd2) (double timeout_s) | Set the heartbeat watchdog (deadman timer) timeout. [ComponentType](namespacemavsdk.md#namespacemavsdk_1a20fe7f7c8312779a187017111bf33d12) | [get_component_type](#classmavsdk_1_1_mavsdk_1_1_configuration_1a81d3645816f8a3072044498c3f539d12) () const | Component type of this configuration, used for automatic ID set. void | [set_component_type](#classmavsdk_1_1_mavsdk_1_1_configuration_1a06461b86734eaa9544e80a4a907c9754) ([ComponentType](namespacemavsdk.md#namespacemavsdk_1a20fe7f7c8312779a187017111bf33d12) component_type) | Set the component type of this configuration. uint8_t | [get_mav_type](#classmavsdk_1_1_mavsdk_1_1_configuration_1aafe9e8fc11dd0b688a836c123357e9ba) () const | Get the mav type (vehicle type) of this configuration. @@ -151,11 +153,58 @@ void mavsdk::Mavsdk::Configuration::set_always_send_heartbeats(bool always_send_ Set whether to send heartbeats by default. +Note: when a heartbeat watchdog is configured ([set_heartbeat_watchdog_timeout_s()](classmavsdk_1_1_mavsdk_1_1_configuration.md#classmavsdk_1_1_mavsdk_1_1_configuration_1a53bdc9285ca6687487c18fed28b30dd2)) and has expired, the watchdog latch takes precedence: heartbeats stay off until [Mavsdk::feed_heartbeat_watchdog()](classmavsdk_1_1_mavsdk.md#classmavsdk_1_1_mavsdk_1ad786bca001160944e8321355a816fe90) is called again, even if always_send_heartbeats is set. **Parameters** * bool **always_send_heartbeats** - +### get_heartbeat_watchdog_timeout_s() {#classmavsdk_1_1_mavsdk_1_1_configuration_1ae438001c4f7aabe6ca220306297f6b8f} +```cpp +double mavsdk::Mavsdk::Configuration::get_heartbeat_watchdog_timeout_s() const +``` + + +Get the heartbeat watchdog timeout. + + +**Returns** + + double - Timeout in seconds, 0 if the watchdog is disabled. + +### set_heartbeat_watchdog_timeout_s() {#classmavsdk_1_1_mavsdk_1_1_configuration_1a53bdc9285ca6687487c18fed28b30dd2} +```cpp +bool mavsdk::Mavsdk::Configuration::set_heartbeat_watchdog_timeout_s(double timeout_s) +``` + + +Set the heartbeat watchdog (deadman timer) timeout. + +When set to a value greater than 0, the periodic heartbeats sent by MAVSDK are only sent as long as [Mavsdk::feed_heartbeat_watchdog()](classmavsdk_1_1_mavsdk.md#classmavsdk_1_1_mavsdk_1ad786bca001160944e8321355a816fe90) keeps being called at least once per timeout period. Heartbeats never start (and any already-running heartbeats are stopped) until the watchdog has been fed - including when the watchdog is first enabled or its timeout is changed. If the watchdog times out, heartbeats are latched off - including across reconnects and new system discovery - until the watchdog is fed again. + + +Heartbeats are also latched off whenever they stop for any other reason while the watchdog is configured (e.g. when the connected system disconnects): after a reconnect, heartbeats only resume once the watchdog has been fed again. + + +This is useful when MAVSDK's heartbeats should reflect the liveness of the application: if the application hangs or dies, heartbeats stop. + + +While the watchdog is expired, the latch takes precedence over [set_always_send_heartbeats()](classmavsdk_1_1_mavsdk_1_1_configuration.md#classmavsdk_1_1_mavsdk_1_1_configuration_1a0ad68b52763e205012b34faa5120a792): heartbeats stay off until the watchdog is fed again. + + +When set to 0, the watchdog is disabled and heartbeats follow the usual policy (always_send_heartbeats or a connected system). + + +Default: 0 (disabled) + +**Parameters** + +* double **timeout_s** - Timeout in seconds: 0 (disabled) or at least 1. + +**Returns** + + bool - true if the value was accepted, false if it was rejected (invalid values are ignored and the previous value kept). + ### get_component_type() {#classmavsdk_1_1_mavsdk_1_1_configuration_1a81d3645816f8a3072044498c3f539d12} ```cpp ComponentType mavsdk::Mavsdk::Configuration::get_component_type() const diff --git a/docs/en/cpp/api_reference/namespacemavsdk.md b/docs/en/cpp/api_reference/namespacemavsdk.md index 9a31467a16..491819e73e 100644 --- a/docs/en/cpp/api_reference/namespacemavsdk.md +++ b/docs/en/cpp/api_reference/namespacemavsdk.md @@ -86,6 +86,7 @@ MAVSDK_PUBLIC [Quaternion](structmavsdk_1_1_quaternion.md) | [operator*](#namesp constexpr T | [to_rad_from_deg](#namespacemavsdk_1adca90fd4bd3af244bfde5561bc8d72d8) (T deg) | Convert degrees to radians. constexpr T | [to_deg_from_rad](#namespacemavsdk_1a43239ca183cfc12177d974821150f857) (T rad) | Convert radians to degrees. constexpr T | [constrain](#namespacemavsdk_1a37295e1b92021003968aa7f9b4f86d6e) (T input, T min, T max) | Clamp a value to a closed interval [min, max]. +MAVSDK_PUBLIC bool | [is_valid_heartbeat_watchdog_timeout_s](#namespacemavsdk_1a7be9f6b5e0a1cfdff8dcf242aa68d851) (double timeout_s) | Check whether a heartbeat watchdog timeout is valid.   | [overloaded](#namespacemavsdk_1a724e321aaff91eb2ba28279e0292e552) (Ts...)-> overloaded< Ts... > | Template deduction helper for `overloaded` std::ostream & | [operator<<](#namespacemavsdk_1a3e7a55e89629afd2a079d79c047e8dbd) (std::ostream & os, const [Vehicle](namespacemavsdk.md#namespacemavsdk_1a9e3a3a502dc8313cb931a8a44cc6f95b) & vehicle) | Stream operator to print information about a `Vehicle`. [Vehicle](namespacemavsdk.md#namespacemavsdk_1a9e3a3a502dc8313cb931a8a44cc6f95b) | [to_vehicle_from_mav_type](#namespacemavsdk_1a4dede924df915e32b4807aa87a98b5bb) (MAV_TYPE type) | Convert a 'MAV_TYPE' to a `Vehicle`. @@ -568,6 +569,27 @@ Clamp a value to a closed interval [min, max].  constexpr T - input clamped to [min, max]. +### is_valid_heartbeat_watchdog_timeout_s() {#namespacemavsdk_1a7be9f6b5e0a1cfdff8dcf242aa68d851} + +``` +#include: mavsdk.hpp +``` +```cpp +MAVSDK_PUBLIC bool mavsdk::is_valid_heartbeat_watchdog_timeout_s(double timeout_s) +``` + + +Check whether a heartbeat watchdog timeout is valid. + + +**Parameters** + +* double **timeout_s** - + +**Returns** + + MAVSDK_PUBLIC bool - true if timeout_s is 0 (disabled), or finite and at least heartbeat_watchdog_min_timeout_s. + ### overloaded() {#namespacemavsdk_1a724e321aaff91eb2ba28279e0292e552} ``` diff --git a/proto b/proto index 951bc12e43..d019861782 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 951bc12e4310f741d1038785c8ff47781dde6b81 +Subproject commit d019861782e03586efd24de81bebf5c8f4d19c9c From 95124afe89ea7b2584ab88927fb8d194f3f91db0 Mon Sep 17 00:00:00 2001 From: Tyler Payne Date: Fri, 17 Jul 2026 19:10:23 +0000 Subject: [PATCH 3/3] fix(core): avoid TSan race when packing injected heartbeats Use a checked-out MAVLink channel for test inject packing, and define MavlinkChannels::Instance() in the .cpp so shared builds share one allocator. --- .../mavsdk/core/heartbeat_watchdog_test.cpp | 75 ++++++++++++------- cpp/src/mavsdk/core/mavlink_channels.cpp | 7 ++ cpp/src/mavsdk/core/mavlink_channels.hpp | 12 ++- 3 files changed, 60 insertions(+), 34 deletions(-) diff --git a/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp b/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp index de803beadc..0a879a9b85 100644 --- a/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp +++ b/cpp/src/mavsdk/core/heartbeat_watchdog_test.cpp @@ -1,5 +1,6 @@ #include "mavsdk_impl.hpp" #include "mavsdk_time.hpp" +#include "mavlink_channels.hpp" #include "mavlink_include.hpp" #include @@ -13,6 +14,46 @@ using namespace mavsdk; namespace { +// Own a dedicated MAVLink pack channel for test-side packing. mavlink_msg_*_pack +// (no _chan) always finalizes on channel 0, which races MAVSDK's outbound +// packing on the same global channel status. Production code avoids that by +// checking out a channel and using *_pack_chan under its pack mutex. +class TestMavlinkPackChannel { +public: + TestMavlinkPackChannel() + { + EXPECT_TRUE(MavlinkChannels::Instance().checkout_free_channel(_channel)); + } + + ~TestMavlinkPackChannel() { MavlinkChannels::Instance().checkin_used_channel(_channel); } + + TestMavlinkPackChannel(const TestMavlinkPackChannel&) = delete; + TestMavlinkPackChannel& operator=(const TestMavlinkPackChannel&) = delete; + + uint8_t channel() const { return _channel; } + +private: + uint8_t _channel{0}; +}; + +void inject_autopilot_heartbeat(MavsdkImpl& mavsdk, uint8_t pack_channel, uint8_t sysid) +{ + mavlink_message_t message; + mavlink_msg_heartbeat_pack_chan( + sysid, + MAV_COMP_ID_AUTOPILOT1, + pack_channel, + &message, + MAV_TYPE_QUADROTOR, + MAV_AUTOPILOT_PX4, + 0, + 0, + MAV_STATE_ACTIVE); + uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; + const uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message); + mavsdk.pass_received_raw_bytes(reinterpret_cast(buffer), buffer_len); +} + // These tests run against MavsdkImpl with an injected FakeTime, so all // heartbeat and watchdog periods are simulated: fake_time.sleep_for() // advances the clock instantly. The io thread polls the timer handlers every @@ -107,26 +148,16 @@ TEST(Heartbeat, DisablingAlwaysSendDuringDiscoveryKeepsHeartbeatsRunning) FakeTime fake_time; MavsdkImpl mavsdk{configuration, fake_time}; + // Checkout after MavsdkImpl so inject packing does not share MAVSDK's channel. + TestMavlinkPackChannel pack_channel; HeartbeatCounter heartbeats{mavsdk}; // Note: adding the raw connection force-enables always_send_heartbeats. ASSERT_EQ( mavsdk.add_any_connection("raw://", ForwardingOption::ForwardingOff).first, ConnectionResult::Success); - auto inject_heartbeat = [&mavsdk](uint8_t sysid) { - mavlink_message_t message; - mavlink_msg_heartbeat_pack( - sysid, - MAV_COMP_ID_AUTOPILOT1, - &message, - MAV_TYPE_QUADROTOR, - MAV_AUTOPILOT_PX4, - 0, - 0, - MAV_STATE_ACTIVE); - uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; - const uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message); - mavsdk.pass_received_raw_bytes(reinterpret_cast(buffer), buffer_len); + auto inject_heartbeat = [&mavsdk, &pack_channel](uint8_t sysid) { + inject_autopilot_heartbeat(mavsdk, pack_channel.channel(), sysid); }; auto on_configuration = configuration; @@ -385,6 +416,8 @@ TEST(HeartbeatWatchdog, ConcurrentReconfigurationStress) FakeTime fake_time; MavsdkImpl mavsdk{configuration, fake_time}; + // Checkout after MavsdkImpl so inject packing does not share MAVSDK's channel. + TestMavlinkPackChannel pack_channel; ASSERT_EQ( mavsdk.add_any_connection("raw://", ForwardingOption::ForwardingOff).first, ConnectionResult::Success); @@ -412,19 +445,7 @@ TEST(HeartbeatWatchdog, ConcurrentReconfigurationStress) // deadlock with configuration updates, which held the server components // lock while taking the systems lock. for (int sysid = 1; sysid <= 150; ++sysid) { - mavlink_message_t message; - mavlink_msg_heartbeat_pack( - static_cast(sysid), - MAV_COMP_ID_AUTOPILOT1, - &message, - MAV_TYPE_QUADROTOR, - MAV_AUTOPILOT_PX4, - 0, - 0, - MAV_STATE_ACTIVE); - uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; - const uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message); - mavsdk.pass_received_raw_bytes(reinterpret_cast(buffer), buffer_len); + inject_autopilot_heartbeat(mavsdk, pack_channel.channel(), static_cast(sysid)); std::this_thread::sleep_for(std::chrono::milliseconds(2)); } diff --git a/cpp/src/mavsdk/core/mavlink_channels.cpp b/cpp/src/mavsdk/core/mavlink_channels.cpp index ba7e73687c..f3050c9f27 100644 --- a/cpp/src/mavsdk/core/mavlink_channels.cpp +++ b/cpp/src/mavsdk/core/mavlink_channels.cpp @@ -2,6 +2,13 @@ namespace mavsdk { +MavlinkChannels& MavlinkChannels::Instance() +{ + // Thread-safe in C++11. + static MavlinkChannels instance; + return instance; +} + MavlinkChannels::MavlinkChannels() : _channels_used{}, _channels_used_mutex() {} bool MavlinkChannels::checkout_free_channel(uint8_t& new_channel) diff --git a/cpp/src/mavsdk/core/mavlink_channels.hpp b/cpp/src/mavsdk/core/mavlink_channels.hpp index ea07d56094..ab0da9197a 100644 --- a/cpp/src/mavsdk/core/mavlink_channels.hpp +++ b/cpp/src/mavsdk/core/mavlink_channels.hpp @@ -8,13 +8,11 @@ namespace mavsdk { class MAVSDK_TEST_EXPORT MavlinkChannels { public: - static MavlinkChannels& Instance() - { - // This should be thread-safe in C++11. - static MavlinkChannels instance; - - return instance; - } + // Defined in the .cpp so shared-library builds have one allocator. An + // inline Meyers singleton in the header would give the library and each + // test binary their own channel map while still sharing + // mavlink_get_channel_status(), which races packing on channel 0. + static MavlinkChannels& Instance(); // delete copy and move constructors and assign operators MavlinkChannels(MavlinkChannels const&) = delete; // Copy construct