diff --git a/.vscode/settings.json b/.vscode/settings.json index 4761cb3f..78843b62 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "rust-analyzer.cargo.features": [ "stub_supervisor_api_client" ], + "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.check.command": "clippy", "rust-analyzer.rustfmt.overrideCommand": [ "${workspaceFolder}/.vscode/rustfmt.sh" diff --git a/src/health_monitoring_lib/BUILD b/src/health_monitoring_lib/BUILD index c58d9773..89943f59 100644 --- a/src/health_monitoring_lib/BUILD +++ b/src/health_monitoring_lib/BUILD @@ -29,6 +29,7 @@ CC_SOURCES = [ "cpp/common.cpp", "cpp/deadline_monitor.cpp", "cpp/heartbeat_monitor.cpp", + "cpp/logic_monitor.cpp", "cpp/health_monitor.cpp", ] @@ -37,6 +38,7 @@ CC_HDRS = [ "cpp/include/score/hm/tag.h", "cpp/include/score/hm/deadline/deadline_monitor.h", "cpp/include/score/hm/heartbeat/heartbeat_monitor.h", + "cpp/include/score/hm/logic/logic_monitor.h", "cpp/include/score/hm/health_monitor.h", ] diff --git a/src/health_monitoring_lib/cpp/health_monitor.cpp b/src/health_monitoring_lib/cpp/health_monitor.cpp index 61b9a396..e022c548 100644 --- a/src/health_monitoring_lib/cpp/health_monitor.cpp +++ b/src/health_monitoring_lib/cpp/health_monitor.cpp @@ -19,6 +19,7 @@ using namespace score::hm; using namespace score::hm::internal; using namespace score::hm::deadline; using namespace score::hm::heartbeat; +using namespace score::hm::logic; // Functions below must match functions defined in `crate::ffi`. @@ -34,12 +35,18 @@ FFICode health_monitor_builder_add_deadline_monitor(FFIHandle health_monitor_bui FFICode health_monitor_builder_add_heartbeat_monitor(FFIHandle health_monitor_builder_handle, const MonitorTag* monitor_tag, FFIHandle heartbeat_monitor_builder_handle); +FFICode health_monitor_builder_add_logic_monitor(FFIHandle health_monitor_builder_handle, + const MonitorTag* monitor_tag, + FFIHandle logic_monitor_builder_handle); FFICode health_monitor_get_deadline_monitor(FFIHandle health_monitor_handle, const MonitorTag* monitor_tag, FFIHandle* deadline_monitor_handle_out); FFICode health_monitor_get_heartbeat_monitor(FFIHandle health_monitor_handle, const MonitorTag* monitor_tag, FFIHandle* heartbeat_monitor_handle_out); +FFICode health_monitor_get_logic_monitor(FFIHandle health_monitor_handle, + const MonitorTag* monitor_tag, + FFIHandle* logic_monitor_handle_out); FFICode health_monitor_start(FFIHandle health_monitor_handle); FFICode health_monitor_destroy(FFIHandle health_monitor_handle); } @@ -92,6 +99,20 @@ HealthMonitorBuilder HealthMonitorBuilder::add_heartbeat_monitor(const MonitorTa return std::move(*this); } +HealthMonitorBuilder HealthMonitorBuilder::add_logic_monitor(const MonitorTag& monitor_tag, + LogicMonitorBuilder&& monitor) && +{ + auto monitor_handle = monitor.drop_by_rust(); + SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value()); + SCORE_LANGUAGE_FUTURECPP_PRECONDITION(health_monitor_builder_handle_.as_rust_handle().has_value()); + + auto result{health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle_.as_rust_handle().value(), &monitor_tag, monitor_handle.value())}; + SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess); + + return std::move(*this); +} + HealthMonitorBuilder HealthMonitorBuilder::with_internal_processing_cycle(std::chrono::milliseconds cycle_duration) && { internal_processing_cycle_duration_ = cycle_duration; @@ -155,6 +176,18 @@ score::cpp::expected HealthMonitor::get_heartbeat_monit return score::cpp::expected(HeartbeatMonitor{handle}); } +score::cpp::expected HealthMonitor::get_logic_monitor(const MonitorTag& monitor_tag) +{ + FFIHandle handle{nullptr}; + auto result{health_monitor_get_logic_monitor(health_monitor_, &monitor_tag, &handle)}; + if (result != kSuccess) + { + return score::cpp::unexpected(static_cast(result)); + } + + return score::cpp::expected(LogicMonitor{handle}); +} + void HealthMonitor::start() { auto result{health_monitor_start(health_monitor_)}; diff --git a/src/health_monitoring_lib/cpp/include/score/hm/health_monitor.h b/src/health_monitoring_lib/cpp/include/score/hm/health_monitor.h index 4ae11863..84fc9bdc 100644 --- a/src/health_monitoring_lib/cpp/include/score/hm/health_monitor.h +++ b/src/health_monitoring_lib/cpp/include/score/hm/health_monitor.h @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace score::hm @@ -47,6 +48,9 @@ class HealthMonitorBuilder final HealthMonitorBuilder add_heartbeat_monitor(const MonitorTag& monitor_tag, heartbeat::HeartbeatMonitorBuilder&& monitor) &&; + /// Adds a logic monitor for a specific identifier tag. + HealthMonitorBuilder add_logic_monitor(const MonitorTag& monitor_tag, logic::LogicMonitorBuilder&& monitor) &&; + /// Sets the cycle duration for supervisor API notifications. /// This duration determines how often the health monitor notifies the supervisor that the system is alive. HealthMonitorBuilder with_supervisor_api_cycle(std::chrono::milliseconds cycle_duration) &&; @@ -78,6 +82,7 @@ class HealthMonitor final score::cpp::expected get_deadline_monitor(const MonitorTag& monitor_tag); score::cpp::expected get_heartbeat_monitor(const MonitorTag& monitor_tag); + score::cpp::expected get_logic_monitor(const MonitorTag& monitor_tag); void start(); diff --git a/src/health_monitoring_lib/cpp/include/score/hm/logic/logic_monitor.h b/src/health_monitoring_lib/cpp/include/score/hm/logic/logic_monitor.h new file mode 100644 index 00000000..d7acdd47 --- /dev/null +++ b/src/health_monitoring_lib/cpp/include/score/hm/logic/logic_monitor.h @@ -0,0 +1,91 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_HM_LOGIC_LOGIC_MONITOR_H +#define SCORE_HM_LOGIC_LOGIC_MONITOR_H + +#include "score/hm/common.h" +#include "score/hm/tag.h" +#include +#include + +namespace score::hm +{ +// Forward declaration +class HealthMonitor; +class HealthMonitorBuilder; +} // namespace score::hm + +namespace score::hm::logic +{ + +class LogicMonitorBuilder final : public internal::RustDroppable +{ + public: + /// Create a new `LogicMonitorBuilder`. + /// + /// - `initial_state` - starting point. + LogicMonitorBuilder(const StateTag& initial_state); + + LogicMonitorBuilder(const LogicMonitorBuilder&) = delete; + LogicMonitorBuilder& operator=(const LogicMonitorBuilder&) = delete; + + LogicMonitorBuilder(LogicMonitorBuilder&&) = default; + LogicMonitorBuilder& operator=(LogicMonitorBuilder&&) = delete; + + /// Add state along with allowed transitions. + /// If state already exists - it is overwritten. + LogicMonitorBuilder add_state(const StateTag& state, const std::vector& allowed_states) &&; + + protected: + std::optional _drop_by_rust_impl() + { + return monitor_builder_handle_.drop_by_rust(); + } + + private: + internal::DroppableFFIHandle monitor_builder_handle_; + + // Allow to hide drop_by_rust implementation + friend class internal::RustDroppable; + + // Allow HealthMonitorBuilder to access drop_by_rust implementation + friend class ::score::hm::HealthMonitorBuilder; +}; + +class LogicMonitor final +{ + public: + LogicMonitor(const LogicMonitor&) = delete; + LogicMonitor& operator=(const LogicMonitor&) = delete; + + LogicMonitor(LogicMonitor&& other) noexcept = default; + LogicMonitor& operator=(LogicMonitor&& other) noexcept = default; + + /// Perform transition to a new state. + /// On success, current state is returned. + score::cpp::expected transition(const StateTag& state); + + /// Current monitor state. + score::cpp::expected state(); + + private: + explicit LogicMonitor(internal::FFIHandle monitor_handle); + + // Only `HealthMonitor` is allowed to create `LogicMonitor` instances. + friend class score::hm::HealthMonitor; + internal::DroppableFFIHandle monitor_handle_; +}; + +} // namespace score::hm::logic + +#endif // SCORE_HM_LOGIC_LOGIC_MONITOR_H diff --git a/src/health_monitoring_lib/cpp/include/score/hm/tag.h b/src/health_monitoring_lib/cpp/include/score/hm/tag.h index 5cb47d37..9a2eb280 100644 --- a/src/health_monitoring_lib/cpp/include/score/hm/tag.h +++ b/src/health_monitoring_lib/cpp/include/score/hm/tag.h @@ -15,11 +15,13 @@ #define SCORE_HM_TAG_H #include +#include namespace score::hm { /// Common string-based tag. +template class Tag { public: @@ -29,21 +31,40 @@ class Tag { } + bool operator==(const T& other) const noexcept + { + std::string_view this_sv{data_, length_}; + std::string_view other_sv{other.data_, other.length_}; + return this_sv.compare(other_sv) == 0; + } + + bool operator!=(const T& other) const noexcept + { + return !(*this == other); + } + private: /// SAFETY: This has to be FFI compatible with the Rust side representation. - const char* const data_; + const char* data_; size_t length_; }; /// Monitor tag. -class MonitorTag : public Tag +class MonitorTag : public Tag { public: using Tag::Tag; }; /// Deadline tag. -class DeadlineTag : public Tag +class DeadlineTag : public Tag +{ + public: + using Tag::Tag; +}; + +/// State tag. +class StateTag : public Tag { public: using Tag::Tag; diff --git a/src/health_monitoring_lib/cpp/logic_monitor.cpp b/src/health_monitoring_lib/cpp/logic_monitor.cpp new file mode 100644 index 00000000..b54f28d1 --- /dev/null +++ b/src/health_monitoring_lib/cpp/logic_monitor.cpp @@ -0,0 +1,94 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/hm/logic/logic_monitor.h" +#include + +namespace +{ +extern "C" { +using namespace score::hm; +using namespace score::hm::internal; +using namespace score::hm::logic; + +FFICode logic_monitor_builder_create(const StateTag* initial_state, FFIHandle* logic_monitor_builder_handle_out); +FFICode logic_monitor_builder_destroy(FFIHandle logic_monitor_builder_handle); +FFICode logic_monitor_builder_add_state(FFIHandle logic_monitor_builder_handle, + const StateTag* state, + const StateTag* allowed_states, + size_t num_allowed_states); +FFICode logic_monitor_destroy(FFIHandle logic_monitor_handle); +FFICode logic_monitor_transition(FFIHandle logic_monitor_handle, const StateTag* target_state); +FFICode logic_monitor_state(FFIHandle logic_monitor_handle, StateTag* state_out); +} + +FFIHandle logic_monitor_builder_create_wrapper(const StateTag& initial_state) +{ + FFIHandle handle{nullptr}; + auto result{logic_monitor_builder_create(&initial_state, &handle)}; + SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess); + return handle; +} +} // namespace + +namespace score::hm::logic +{ +LogicMonitorBuilder::LogicMonitorBuilder(const StateTag& initial_state) + : monitor_builder_handle_{logic_monitor_builder_create_wrapper(initial_state), &logic_monitor_builder_destroy} +{ +} + +LogicMonitorBuilder LogicMonitorBuilder::add_state(const StateTag& state, + const std::vector& allowed_states) && +{ + auto monitor_builder_handle{monitor_builder_handle_.as_rust_handle()}; + SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_builder_handle.has_value()); + + auto result{logic_monitor_builder_add_state( + monitor_builder_handle.value(), &state, allowed_states.data(), allowed_states.size())}; + SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess); + + return std::move(*this); +} + +LogicMonitor::LogicMonitor(FFIHandle monitor_handle) : monitor_handle_{monitor_handle, &logic_monitor_destroy} {} + +score::cpp::expected LogicMonitor::transition(const StateTag& state) +{ + auto monitor_handle{monitor_handle_.as_rust_handle()}; + SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value()); + + auto result{logic_monitor_transition(monitor_handle.value(), &state)}; + if (result != kSuccess) + { + return score::cpp::unexpected(static_cast(result)); + } + + return score::cpp::expected(state); +} + +score::cpp::expected LogicMonitor::state() +{ + auto monitor_handle{monitor_handle_.as_rust_handle()}; + SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value()); + + StateTag state_tag{""}; + auto result{logic_monitor_state(monitor_handle.value(), &state_tag)}; + if (result != kSuccess) + { + return score::cpp::unexpected(static_cast(result)); + } + + return score::cpp::expected(state_tag); +} + +} // namespace score::hm::logic diff --git a/src/health_monitoring_lib/cpp/tests/health_monitor_test.cpp b/src/health_monitoring_lib/cpp/tests/health_monitor_test.cpp index cd0e6524..d555d43c 100644 --- a/src/health_monitoring_lib/cpp/tests/health_monitor_test.cpp +++ b/src/health_monitoring_lib/cpp/tests/health_monitor_test.cpp @@ -49,9 +49,17 @@ TEST_F(HealthMonitorTest, TestName) const TimeRange heartbeat_range{std::chrono::milliseconds{100}, std::chrono::milliseconds{200}}; auto heartbeat_monitor_builder = heartbeat::HeartbeatMonitorBuilder(heartbeat_range); + // Setup logic monitor construction. + const MonitorTag logic_monitor_tag{"logic_monitor"}; + StateTag from_state{"from_state"}; + StateTag to_state{"to_state"}; + auto logic_monitor_builder = + logic::LogicMonitorBuilder{from_state}.add_state(from_state, std::vector{to_state}).add_state(to_state, {}); + auto hm = HealthMonitorBuilder() .add_deadline_monitor(deadline_monitor_tag, std::move(deadline_monitor_builder)) .add_heartbeat_monitor(heartbeat_monitor_tag, std::move(heartbeat_monitor_builder)) + .add_logic_monitor(logic_monitor_tag, std::move(logic_monitor_builder)) .with_internal_processing_cycle(std::chrono::milliseconds(50)) .with_supervisor_api_cycle(std::chrono::milliseconds(50)) .build(); @@ -80,11 +88,28 @@ TEST_F(HealthMonitorTest, TestName) auto heartbeat_monitor{std::move(*heartbeat_monitor_res)}; + // Obtain logic monitor from HMON. + auto logic_monitor_res{hm.get_logic_monitor(logic_monitor_tag)}; + EXPECT_TRUE(logic_monitor_res.has_value()); + + { + // Try again to get the same monitor. + auto logic_monitor_res{hm.get_logic_monitor(logic_monitor_tag)}; + EXPECT_FALSE(logic_monitor_res.has_value()); + } + + auto logic_monitor{std::move(*logic_monitor_res)}; + // Start HMON. hm.start(); heartbeat_monitor.heartbeat(); + EXPECT_TRUE(logic_monitor.transition(to_state).has_value()); + auto current_state_res{logic_monitor.state()}; + EXPECT_TRUE(current_state_res.has_value()); + EXPECT_EQ(current_state_res.value(), to_state); + auto deadline_res = deadline_mon.get_deadline(DeadlineTag("deadline_1")); { diff --git a/src/health_monitoring_lib/rust/ffi.rs b/src/health_monitoring_lib/rust/ffi.rs index 65d26610..4dbb31e8 100644 --- a/src/health_monitoring_lib/rust/ffi.rs +++ b/src/health_monitoring_lib/rust/ffi.rs @@ -13,6 +13,7 @@ use crate::deadline::ffi::DeadlineMonitorCpp; use crate::deadline::DeadlineMonitorBuilder; use crate::heartbeat::HeartbeatMonitorBuilder; +use crate::logic::LogicMonitorBuilder; use crate::tag::MonitorTag; use crate::{HealthMonitor, HealthMonitorBuilder, HealthMonitorError}; use core::mem::ManuallyDrop; @@ -206,6 +207,39 @@ pub extern "C" fn health_monitor_builder_add_heartbeat_monitor( FFICode::Success } +#[unsafe(no_mangle)] +pub extern "C" fn health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle: FFIHandle, + monitor_tag: *const MonitorTag, + logic_monitor_builder_handle: FFIHandle, +) -> FFICode { + if health_monitor_builder_handle.is_null() || monitor_tag.is_null() || logic_monitor_builder_handle.is_null() { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of the pointer is ensured. + // `MonitorTag` type must be compatible between C++ and Rust. + let monitor_tag = unsafe { *monitor_tag }; + + // SAFETY: + // Validity of this pointer is ensured. + // It is assumed that the pointer was created by a call to `logic_monitor_builder_create`. + // It is assumed that the pointer was not consumed by a call to `logic_monitor_builder_destroy`. + let logic_monitor_builder = unsafe { Box::from_raw(logic_monitor_builder_handle as *mut LogicMonitorBuilder) }; + + // SAFETY: + // Validity of the pointer is ensured. + // It is assumed that the pointer was created by a call to `health_monitor_builder_create`. + // It is assumed that the pointer was not consumed by calls to `health_monitor_builder_destroy` or `health_monitor_builder_build`. + let mut health_monitor_builder = + FFIBorrowed::new(unsafe { Box::from_raw(health_monitor_builder_handle as *mut HealthMonitorBuilder) }); + + health_monitor_builder.add_logic_monitor_internal(monitor_tag, *logic_monitor_builder); + + FFICode::Success +} + #[unsafe(no_mangle)] pub extern "C" fn health_monitor_get_deadline_monitor( health_monitor_handle: FFIHandle, @@ -268,6 +302,37 @@ pub extern "C" fn health_monitor_get_heartbeat_monitor( } } +#[unsafe(no_mangle)] +pub extern "C" fn health_monitor_get_logic_monitor( + health_monitor_handle: FFIHandle, + monitor_tag: *const MonitorTag, + logic_monitor_handle_out: *mut FFIHandle, +) -> FFICode { + if health_monitor_handle.is_null() || monitor_tag.is_null() || logic_monitor_handle_out.is_null() { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of the pointer is ensured. + // `MonitorTag` type must be compatible between C++ and Rust. + let monitor_tag = unsafe { *monitor_tag }; + + // SAFETY: + // Validity of the pointer is ensured. + // It is assumed that the pointer was created by a call to `health_monitor_builder_build`. + // It is assumed that the pointer was not consumed by a call to `health_monitor_destroy`. + let mut health_monitor = FFIBorrowed::new(unsafe { Box::from_raw(health_monitor_handle as *mut HealthMonitor) }); + + if let Some(logic_monitor) = health_monitor.get_logic_monitor(monitor_tag) { + unsafe { + *logic_monitor_handle_out = Box::into_raw(Box::new(logic_monitor)).cast(); + } + FFICode::Success + } else { + FFICode::NotFound + } +} + #[unsafe(no_mangle)] pub extern "C" fn health_monitor_start(health_monitor_handle: FFIHandle) -> FFICode { if health_monitor_handle.is_null() { @@ -311,16 +376,42 @@ mod tests { }; use crate::ffi::{ health_monitor_builder_add_deadline_monitor, health_monitor_builder_add_heartbeat_monitor, - health_monitor_builder_build, health_monitor_builder_create, health_monitor_builder_destroy, - health_monitor_destroy, health_monitor_get_deadline_monitor, health_monitor_get_heartbeat_monitor, - health_monitor_start, FFICode, FFIHandle, + health_monitor_builder_add_logic_monitor, health_monitor_builder_build, health_monitor_builder_create, + health_monitor_builder_destroy, health_monitor_destroy, health_monitor_get_deadline_monitor, + health_monitor_get_heartbeat_monitor, health_monitor_get_logic_monitor, health_monitor_start, FFICode, + FFIHandle, }; use crate::heartbeat::ffi::{ heartbeat_monitor_builder_create, heartbeat_monitor_builder_destroy, heartbeat_monitor_destroy, }; - use crate::tag::MonitorTag; + use crate::logic::ffi::{ + logic_monitor_builder_add_state, logic_monitor_builder_create, logic_monitor_builder_destroy, + logic_monitor_destroy, + }; + use crate::tag::{MonitorTag, StateTag}; use core::ptr::null_mut; + fn def_logic_monitor_builder() -> FFIHandle { + let mut logic_monitor_builder_handle = null_mut(); + let state1 = StateTag::from("state1"); + let state2 = StateTag::from("state2"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let _ = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + [state2].as_ptr(), + 1, + ); + let _ = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state2 as *const StateTag, [].as_ptr(), 0); + + logic_monitor_builder_handle + } + #[test] fn health_monitor_builder_create_succeeds() { let mut health_monitor_builder_handle: FFIHandle = null_mut(); @@ -611,6 +702,95 @@ mod tests { health_monitor_builder_destroy(health_monitor_builder_handle); } + #[test] + fn health_monitor_builder_add_logic_monitor_succeeds() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let initial_state = StateTag::from("initial_state"); + let _ = logic_monitor_builder_create( + &initial_state as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let health_monitor_builder_add_logic_monitor_result = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + assert_eq!(health_monitor_builder_add_logic_monitor_result, FFICode::Success); + + // Clean-up. + health_monitor_builder_destroy(health_monitor_builder_handle); + } + + #[test] + fn health_monitor_builder_add_logic_monitor_null_hmon_builder() { + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let initial_state = StateTag::from("initial_state"); + let _ = logic_monitor_builder_create( + &initial_state as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let health_monitor_builder_add_logic_monitor_result = health_monitor_builder_add_logic_monitor( + null_mut(), + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + assert_eq!(health_monitor_builder_add_logic_monitor_result, FFICode::NullParameter); + + // Clean-up. + logic_monitor_builder_destroy(logic_monitor_builder_handle); + } + + #[test] + fn health_monitor_builder_add_logic_monitor_null_monitor_tag() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let initial_state = StateTag::from("initial_state"); + let _ = logic_monitor_builder_create( + &initial_state as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let health_monitor_builder_add_logic_monitor_result = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + null_mut(), + logic_monitor_builder_handle, + ); + assert_eq!(health_monitor_builder_add_logic_monitor_result, FFICode::NullParameter); + + // Clean-up. + logic_monitor_builder_destroy(logic_monitor_builder_handle); + health_monitor_builder_destroy(health_monitor_builder_handle); + } + + #[test] + fn health_monitor_builder_add_logic_monitor_null_logic_monitor_builder() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + + let health_monitor_builder_add_logic_monitor_result = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + null_mut(), + ); + assert_eq!(health_monitor_builder_add_logic_monitor_result, FFICode::NullParameter); + + // Clean-up. + health_monitor_builder_destroy(health_monitor_builder_handle); + } + #[test] fn health_monitor_get_deadline_monitor_succeeds() { let mut health_monitor_builder_handle: FFIHandle = null_mut(); @@ -973,6 +1153,182 @@ mod tests { health_monitor_destroy(health_monitor_handle); } + #[test] + fn health_monitor_get_logic_monitor_succeeds() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let logic_monitor_builder_handle = def_logic_monitor_builder(); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + + let health_monitor_get_logic_monitor_result = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + assert!(!logic_monitor_handle.is_null()); + assert_eq!(health_monitor_get_logic_monitor_result, FFICode::Success); + + // Clean-up. + logic_monitor_destroy(logic_monitor_handle); + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn health_monitor_get_logic_monitor_already_taken() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_1_handle: FFIHandle = null_mut(); + let mut logic_monitor_2_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let logic_monitor_builder_handle = def_logic_monitor_builder(); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + + // First get. + let health_monitor_get_logic_monitor_result_1 = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_1_handle as *mut FFIHandle, + ); + assert!(!logic_monitor_1_handle.is_null()); + assert_eq!(health_monitor_get_logic_monitor_result_1, FFICode::Success); + + // Second get. + let health_monitor_get_logic_monitor_result_2 = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_2_handle as *mut FFIHandle, + ); + assert!(logic_monitor_2_handle.is_null()); + assert_eq!(health_monitor_get_logic_monitor_result_2, FFICode::NotFound); + + // Clean-up. + logic_monitor_destroy(logic_monitor_1_handle); + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn health_monitor_get_logic_monitor_null_hmon() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let logic_monitor_builder_handle = def_logic_monitor_builder(); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + + let health_monitor_get_logic_monitor_result = health_monitor_get_logic_monitor( + null_mut(), + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + assert!(logic_monitor_handle.is_null()); + assert_eq!(health_monitor_get_logic_monitor_result, FFICode::NullParameter); + + // Clean-up. + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn health_monitor_get_logic_monitor_null_monitor_tag() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let logic_monitor_builder_handle = def_logic_monitor_builder(); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + + let health_monitor_get_logic_monitor_result = health_monitor_get_logic_monitor( + health_monitor_handle, + null_mut(), + &mut logic_monitor_handle as *mut FFIHandle, + ); + assert!(logic_monitor_handle.is_null()); + assert_eq!(health_monitor_get_logic_monitor_result, FFICode::NullParameter); + + // Clean-up. + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn health_monitor_get_logic_monitor_null_logic_monitor() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let logic_monitor_builder_handle = def_logic_monitor_builder(); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + + let health_monitor_get_logic_monitor_result = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + null_mut(), + ); + assert_eq!(health_monitor_get_logic_monitor_result, FFICode::NullParameter); + + // Clean-up. + health_monitor_destroy(health_monitor_handle); + } + #[test] fn health_monitor_start_succeeds() { let mut health_monitor_builder_handle: FFIHandle = null_mut(); diff --git a/src/health_monitoring_lib/rust/logic/ffi.rs b/src/health_monitoring_lib/rust/logic/ffi.rs new file mode 100644 index 00000000..e45cc41f --- /dev/null +++ b/src/health_monitoring_lib/rust/logic/ffi.rs @@ -0,0 +1,631 @@ +// ******************************************************************************* +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +use crate::ffi::{FFIBorrowed, FFICode, FFIHandle}; +use crate::logic::{LogicMonitor, LogicMonitorBuilder}; +use crate::tag::StateTag; + +#[unsafe(no_mangle)] +pub extern "C" fn logic_monitor_builder_create( + initial_state: *const StateTag, + logic_monitor_builder_handle_out: *mut FFIHandle, +) -> FFICode { + if initial_state.is_null() || logic_monitor_builder_handle_out.is_null() { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of the pointer is ensured. + // `StateTag` type must be compatible between C++ and Rust. + let initial_state = unsafe { *initial_state }; + + let logic_monitor_builder = LogicMonitorBuilder::new(initial_state); + unsafe { + *logic_monitor_builder_handle_out = Box::into_raw(Box::new(logic_monitor_builder)).cast(); + } + + FFICode::Success +} + +#[unsafe(no_mangle)] +pub extern "C" fn logic_monitor_builder_destroy(logic_monitor_builder_handle: FFIHandle) -> FFICode { + if logic_monitor_builder_handle.is_null() { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of the pointer is ensured. + // It is assumed that the pointer was created by a call to `logic_monitor_builder_create`. + unsafe { + let _ = Box::from_raw(logic_monitor_builder_handle as *mut LogicMonitorBuilder); + } + + FFICode::Success +} + +#[unsafe(no_mangle)] +pub extern "C" fn logic_monitor_builder_add_state( + logic_monitor_builder_handle: FFIHandle, + state: *const StateTag, + allowed_states: *const StateTag, + num_allowed_states: usize, +) -> FFICode { + if logic_monitor_builder_handle.is_null() || state.is_null() { + return FFICode::NullParameter; + } + // Null is only allowed when `num_allowed_states` equals 0! + if allowed_states.is_null() && num_allowed_states > 0 { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of the pointer is ensured. + // `StateTag` type must be compatible between C++ and Rust. + let state = unsafe { *state }; + + // SAFETY: + // `allowed_states` must contain a valid continuous array. + // Number of elements must match `num_allowed_states`. + // Null is allowed when `num_allowed_states` equals 0. + let allowed_states = if num_allowed_states > 0 { + unsafe { core::slice::from_raw_parts(allowed_states, num_allowed_states) } + } else { + &[] + }; + + // SAFETY: + // Validity of this pointer is ensured. + // It is assumed that the pointer was created by a call to `logic_monitor_builder_create`. + // It is assumed that the pointer was not consumed by a call to `logic_monitor_builder_destroy`. + let mut logic_monitor_builder = + FFIBorrowed::new(unsafe { Box::from_raw(logic_monitor_builder_handle as *mut LogicMonitorBuilder) }); + + logic_monitor_builder.add_state_internal(state, allowed_states); + + FFICode::Success +} + +#[unsafe(no_mangle)] +pub extern "C" fn logic_monitor_destroy(logic_monitor_handle: FFIHandle) -> FFICode { + if logic_monitor_handle.is_null() { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of the pointer is ensured. + // It is assumed that the pointer was created by a call to `health_monitor_get_logic_monitor`. + unsafe { + let _ = Box::from_raw(logic_monitor_handle as *mut LogicMonitor); + } + + FFICode::Success +} + +#[unsafe(no_mangle)] +pub extern "C" fn logic_monitor_transition(logic_monitor_handle: FFIHandle, target_state: *const StateTag) -> FFICode { + if logic_monitor_handle.is_null() || target_state.is_null() { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of the pointer is ensured. + // `StateTag` type must be compatible between C++ and Rust. + let target_state = unsafe { *target_state }; + + // SAFETY: + // Validity of this pointer is ensured. + // It is assumed that the pointer was created by a call to `health_monitor_get_logic_monitor`. + // It is assumed that the pointer was not consumed by a call to `logic_monitor_destroy`. + let monitor = FFIBorrowed::new(unsafe { Box::from_raw(logic_monitor_handle as *mut LogicMonitor) }); + + // `transition` method returns new state tag on success. + // This can be handled in C++ layer. + match monitor.transition(target_state) { + Ok(_) => FFICode::Success, + Err(_) => FFICode::Failed, + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn logic_monitor_state(logic_monitor_handle: FFIHandle, state_out: *mut StateTag) -> FFICode { + if logic_monitor_handle.is_null() || state_out.is_null() { + return FFICode::NullParameter; + } + + // SAFETY: + // Validity of this pointer is ensured. + // It is assumed that the pointer was created by a call to `health_monitor_get_logic_monitor`. + // It is assumed that the pointer was not consumed by a call to `logic_monitor_destroy`. + let monitor = FFIBorrowed::new(unsafe { Box::from_raw(logic_monitor_handle as *mut LogicMonitor) }); + + match monitor.state() { + Ok(state) => { + unsafe { + *state_out = state; + } + FFICode::Success + }, + Err(_) => FFICode::Failed, + } +} + +#[score_testing_macros::test_mod_with_log] +#[cfg(all(test, not(loom)))] +mod tests { + use crate::ffi::{ + health_monitor_builder_add_logic_monitor, health_monitor_builder_build, health_monitor_builder_create, + health_monitor_destroy, health_monitor_get_logic_monitor, FFICode, FFIHandle, + }; + use crate::logic::ffi::{ + logic_monitor_builder_add_state, logic_monitor_builder_create, logic_monitor_builder_destroy, + logic_monitor_destroy, logic_monitor_state, logic_monitor_transition, + }; + use crate::tag::{MonitorTag, StateTag}; + use core::mem::MaybeUninit; + use core::ptr::null_mut; + + #[test] + fn logic_monitor_builder_create_succeeds() { + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let initial_state = StateTag::from("initial_state"); + let logic_monitor_builder_create_result = logic_monitor_builder_create( + &initial_state as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + assert!(!logic_monitor_builder_handle.is_null()); + assert_eq!(logic_monitor_builder_create_result, FFICode::Success); + + // Clean-up. + // NOTE: `logic_monitor_builder_destroy` positive path is already tested here. + let logic_monitor_builder_destroy_result = logic_monitor_builder_destroy(logic_monitor_builder_handle); + assert_eq!(logic_monitor_builder_destroy_result, FFICode::Success); + } + + #[test] + fn logic_monitor_builder_create_null_builder() { + let initial_state = StateTag::from("initial_state"); + let logic_monitor_builder_create_result = + logic_monitor_builder_create(&initial_state as *const StateTag, null_mut()); + assert_eq!(logic_monitor_builder_create_result, FFICode::NullParameter); + } + + #[test] + fn logic_monitor_builder_create_null_initial_state() { + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let logic_monitor_builder_create_result = + logic_monitor_builder_create(null_mut(), &mut logic_monitor_builder_handle as *mut FFIHandle); + assert_eq!(logic_monitor_builder_create_result, FFICode::NullParameter); + + // Clean-up. + logic_monitor_builder_destroy(logic_monitor_builder_handle); + } + + #[test] + fn logic_monitor_builder_destroy_null_builder() { + let logic_monitor_builder_destroy_result = logic_monitor_builder_destroy(null_mut()); + assert_eq!(logic_monitor_builder_destroy_result, FFICode::NullParameter); + } + + #[test] + fn logic_monitor_builder_add_state_succeeds() { + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let state1 = StateTag::from("state1"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let state2 = StateTag::from("state2"); + let allowed_states = [state2]; + let result = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + allowed_states.as_ptr(), + allowed_states.len(), + ); + assert_eq!(result, FFICode::Success); + + // Clean-up. + logic_monitor_builder_destroy(logic_monitor_builder_handle); + } + + #[test] + fn logic_monitor_builder_add_state_null_builder() { + let state = StateTag::from("state"); + let allowed_states = []; + let result = logic_monitor_builder_add_state( + null_mut(), + &state as *const StateTag, + allowed_states.as_ptr(), + allowed_states.len(), + ); + assert_eq!(result, FFICode::NullParameter); + } + + #[test] + fn logic_monitor_builder_add_state_null_tag() { + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let state1 = StateTag::from("state1"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let allowed_states = []; + let result = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + null_mut(), + allowed_states.as_ptr(), + allowed_states.len(), + ); + assert_eq!(result, FFICode::NullParameter); + + // Clean-up. + logic_monitor_builder_destroy(logic_monitor_builder_handle); + } + + #[test] + fn logic_monitor_builder_add_state_null_allowed_states_zero_elements() { + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let state1 = StateTag::from("state1"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let result = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state1 as *const StateTag, null_mut(), 0); + assert_eq!(result, FFICode::Success); + + // Clean-up. + logic_monitor_builder_destroy(logic_monitor_builder_handle); + } + + #[test] + fn logic_monitor_builder_add_state_null_allowed_states_many_elements() { + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + + let state1 = StateTag::from("state1"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + + let result = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state1 as *const StateTag, null_mut(), 2); + assert_eq!(result, FFICode::NullParameter); + + // Clean-up. + logic_monitor_builder_destroy(logic_monitor_builder_handle); + } + + #[test] + fn logic_monitor_destroy_null_monitor() { + let logic_monitor_destroy_result = logic_monitor_destroy(null_mut()); + assert_eq!(logic_monitor_destroy_result, FFICode::NullParameter); + } + + #[test] + fn logic_monitor_transition_succeeds() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let state1 = StateTag::from("state1"); + let state2 = StateTag::from("state2"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + let _ = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + [state2].as_ptr(), + 1, + ); + let _ = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state2 as *const StateTag, [].as_ptr(), 0); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + let _ = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + + let result = logic_monitor_transition(logic_monitor_handle, &state2 as *const StateTag); + assert_eq!(result, FFICode::Success); + + // Clean-up. + logic_monitor_destroy(logic_monitor_handle); + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn logic_monitor_transition_fails() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let state1 = StateTag::from("state1"); + let state2 = StateTag::from("state2"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + let _ = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + [state2].as_ptr(), + 1, + ); + let _ = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state2 as *const StateTag, [].as_ptr(), 0); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + let _ = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + + let state3 = StateTag::from("state3"); + let result = logic_monitor_transition(logic_monitor_handle, &state3 as *const StateTag); + assert_eq!(result, FFICode::Failed); + + // Clean-up. + logic_monitor_destroy(logic_monitor_handle); + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn logic_monitor_transition_null_monitor() { + let state1 = StateTag::from("state1"); + let result = logic_monitor_transition(null_mut(), &state1 as *const StateTag); + assert_eq!(result, FFICode::NullParameter); + } + + #[test] + fn logic_monitor_transition_null_target_state() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let state1 = StateTag::from("state1"); + let state2 = StateTag::from("state2"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + let _ = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + [state2].as_ptr(), + 1, + ); + let _ = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state2 as *const StateTag, [].as_ptr(), 0); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + let _ = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + + let result = logic_monitor_transition(logic_monitor_handle, null_mut()); + assert_eq!(result, FFICode::NullParameter); + + // Clean-up. + logic_monitor_destroy(logic_monitor_handle); + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn logic_monitor_state_succeeds() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let state1 = StateTag::from("state1"); + let state2 = StateTag::from("state2"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + let _ = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + [state2].as_ptr(), + 1, + ); + let _ = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state2 as *const StateTag, [].as_ptr(), 0); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + let _ = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + + let mut current_state = MaybeUninit::uninit(); + let result = logic_monitor_state(logic_monitor_handle, current_state.as_mut_ptr()); + assert_eq!(result, FFICode::Success); + assert_eq!(unsafe { current_state.assume_init() }, state1); + + // Clean-up. + logic_monitor_destroy(logic_monitor_handle); + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn logic_monitor_state_fails() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let state1 = StateTag::from("state1"); + let state2 = StateTag::from("state2"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + let _ = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + [state2].as_ptr(), + 1, + ); + let _ = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state2 as *const StateTag, [].as_ptr(), 0); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + let _ = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + + let state3 = StateTag::from("state3"); + let _ = logic_monitor_transition(logic_monitor_handle, &state3 as *const StateTag); + + let mut current_state = MaybeUninit::uninit(); + let result = logic_monitor_state(logic_monitor_handle, current_state.as_mut_ptr()); + assert_eq!(result, FFICode::Failed); + + // Clean-up. + logic_monitor_destroy(logic_monitor_handle); + health_monitor_destroy(health_monitor_handle); + } + + #[test] + fn logic_monitor_state_null_monitor() { + let mut current_state = MaybeUninit::uninit(); + let result = logic_monitor_state(null_mut(), current_state.as_mut_ptr()); + assert_eq!(result, FFICode::NullParameter); + } + + #[test] + fn logic_monitor_state_null_state() { + let mut health_monitor_builder_handle: FFIHandle = null_mut(); + let mut health_monitor_handle: FFIHandle = null_mut(); + let mut logic_monitor_builder_handle: FFIHandle = null_mut(); + let mut logic_monitor_handle: FFIHandle = null_mut(); + + let logic_monitor_tag = MonitorTag::from("logic_monitor"); + let _ = health_monitor_builder_create(&mut health_monitor_builder_handle as *mut FFIHandle); + let state1 = StateTag::from("state1"); + let state2 = StateTag::from("state2"); + let _ = logic_monitor_builder_create( + &state1 as *const StateTag, + &mut logic_monitor_builder_handle as *mut FFIHandle, + ); + let _ = logic_monitor_builder_add_state( + logic_monitor_builder_handle, + &state1 as *const StateTag, + [state2].as_ptr(), + 1, + ); + let _ = + logic_monitor_builder_add_state(logic_monitor_builder_handle, &state2 as *const StateTag, [].as_ptr(), 0); + let _ = health_monitor_builder_add_logic_monitor( + health_monitor_builder_handle, + &logic_monitor_tag as *const MonitorTag, + logic_monitor_builder_handle, + ); + let _ = health_monitor_builder_build( + health_monitor_builder_handle, + 200, + 100, + &mut health_monitor_handle as *mut FFIHandle, + ); + let _ = health_monitor_get_logic_monitor( + health_monitor_handle, + &logic_monitor_tag as *const MonitorTag, + &mut logic_monitor_handle as *mut FFIHandle, + ); + + let result = logic_monitor_state(logic_monitor_handle, null_mut()); + assert_eq!(result, FFICode::NullParameter); + + // Clean-up. + logic_monitor_destroy(logic_monitor_handle); + health_monitor_destroy(health_monitor_handle); + } +} diff --git a/src/health_monitoring_lib/rust/logic/logic_monitor.rs b/src/health_monitoring_lib/rust/logic/logic_monitor.rs index ce82f8ec..19e58c28 100644 --- a/src/health_monitoring_lib/rust/logic/logic_monitor.rs +++ b/src/health_monitoring_lib/rust/logic/logic_monitor.rs @@ -89,7 +89,7 @@ impl LogicMonitorBuilder { } /// Add state along with allowed transitions. - /// If state already exist - it is overwritten. + /// If state already exists - it is overwritten. pub fn add_state(mut self, state: StateTag, allowed_targets: &[StateTag]) -> Self { self.add_state_internal(state, allowed_targets); self diff --git a/src/health_monitoring_lib/rust/logic/mod.rs b/src/health_monitoring_lib/rust/logic/mod.rs index 6242e7f6..f4aef708 100644 --- a/src/health_monitoring_lib/rust/logic/mod.rs +++ b/src/health_monitoring_lib/rust/logic/mod.rs @@ -15,3 +15,6 @@ mod logic_monitor; mod logic_state; pub use logic_monitor::{LogicEvaluationError, LogicMonitor, LogicMonitorBuilder}; + +// FFI bindings +pub(super) mod ffi;