Skip to content

Commit 1df795e

Browse files
committed
hmon: add C++ interface to logic monitor
Add C++ interface to logic monitor.
1 parent f96e585 commit 1df795e

10 files changed

Lines changed: 1276 additions & 4 deletions

File tree

src/health_monitoring_lib/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ CC_SOURCES = [
2929
"cpp/common.cpp",
3030
"cpp/deadline_monitor.cpp",
3131
"cpp/heartbeat_monitor.cpp",
32+
"cpp/logic_monitor.cpp",
3233
"cpp/health_monitor.cpp",
3334
]
3435

@@ -37,6 +38,7 @@ CC_HDRS = [
3738
"cpp/include/score/hm/tag.h",
3839
"cpp/include/score/hm/deadline/deadline_monitor.h",
3940
"cpp/include/score/hm/heartbeat/heartbeat_monitor.h",
41+
"cpp/include/score/hm/logic/logic_monitor.h",
4042
"cpp/include/score/hm/health_monitor.h",
4143
]
4244

src/health_monitoring_lib/cpp/health_monitor.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ using namespace score::hm;
1919
using namespace score::hm::internal;
2020
using namespace score::hm::deadline;
2121
using namespace score::hm::heartbeat;
22+
using namespace score::hm::logic;
2223

2324
// Functions below must match functions defined in `crate::ffi`.
2425

@@ -34,12 +35,18 @@ FFICode health_monitor_builder_add_deadline_monitor(FFIHandle health_monitor_bui
3435
FFICode health_monitor_builder_add_heartbeat_monitor(FFIHandle health_monitor_builder_handle,
3536
const MonitorTag* monitor_tag,
3637
FFIHandle heartbeat_monitor_builder_handle);
38+
FFICode health_monitor_builder_add_logic_monitor(FFIHandle health_monitor_builder_handle,
39+
const MonitorTag* monitor_tag,
40+
FFIHandle logic_monitor_builder_handle);
3741
FFICode health_monitor_get_deadline_monitor(FFIHandle health_monitor_handle,
3842
const MonitorTag* monitor_tag,
3943
FFIHandle* deadline_monitor_handle_out);
4044
FFICode health_monitor_get_heartbeat_monitor(FFIHandle health_monitor_handle,
4145
const MonitorTag* monitor_tag,
4246
FFIHandle* heartbeat_monitor_handle_out);
47+
FFICode health_monitor_get_logic_monitor(FFIHandle health_monitor_handle,
48+
const MonitorTag* monitor_tag,
49+
FFIHandle* logic_monitor_handle_out);
4350
FFICode health_monitor_start(FFIHandle health_monitor_handle);
4451
FFICode health_monitor_destroy(FFIHandle health_monitor_handle);
4552
}
@@ -92,6 +99,20 @@ HealthMonitorBuilder HealthMonitorBuilder::add_heartbeat_monitor(const MonitorTa
9299
return std::move(*this);
93100
}
94101

102+
HealthMonitorBuilder HealthMonitorBuilder::add_logic_monitor(const MonitorTag& monitor_tag,
103+
LogicMonitorBuilder&& monitor) &&
104+
{
105+
auto monitor_handle = monitor.drop_by_rust();
106+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value());
107+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(health_monitor_builder_handle_.as_rust_handle().has_value());
108+
109+
auto result{health_monitor_builder_add_logic_monitor(
110+
health_monitor_builder_handle_.as_rust_handle().value(), &monitor_tag, monitor_handle.value())};
111+
SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess);
112+
113+
return std::move(*this);
114+
}
115+
95116
HealthMonitorBuilder HealthMonitorBuilder::with_internal_processing_cycle(std::chrono::milliseconds cycle_duration) &&
96117
{
97118
internal_processing_cycle_duration_ = cycle_duration;
@@ -155,6 +176,18 @@ score::cpp::expected<HeartbeatMonitor, Error> HealthMonitor::get_heartbeat_monit
155176
return score::cpp::expected<HeartbeatMonitor, Error>(HeartbeatMonitor{handle});
156177
}
157178

179+
score::cpp::expected<LogicMonitor, Error> HealthMonitor::get_logic_monitor(const MonitorTag& monitor_tag)
180+
{
181+
FFIHandle handle{nullptr};
182+
auto result{health_monitor_get_logic_monitor(health_monitor_, &monitor_tag, &handle)};
183+
if (result != kSuccess)
184+
{
185+
return score::cpp::unexpected(static_cast<Error>(result));
186+
}
187+
188+
return score::cpp::expected<LogicMonitor, Error>(LogicMonitor{handle});
189+
}
190+
158191
void HealthMonitor::start()
159192
{
160193
auto result{health_monitor_start(health_monitor_)};

src/health_monitoring_lib/cpp/include/score/hm/health_monitor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <score/hm/common.h>
1717
#include <score/hm/deadline/deadline_monitor.h>
1818
#include <score/hm/heartbeat/heartbeat_monitor.h>
19+
#include <score/hm/logic/logic_monitor.h>
1920
#include <score/hm/tag.h>
2021

2122
namespace score::hm
@@ -47,6 +48,9 @@ class HealthMonitorBuilder final
4748
HealthMonitorBuilder add_heartbeat_monitor(const MonitorTag& monitor_tag,
4849
heartbeat::HeartbeatMonitorBuilder&& monitor) &&;
4950

51+
/// Adds a logic monitor for a specific identifier tag.
52+
HealthMonitorBuilder add_logic_monitor(const MonitorTag& monitor_tag, logic::LogicMonitorBuilder&& monitor) &&;
53+
5054
/// Sets the cycle duration for supervisor API notifications.
5155
/// This duration determines how often the health monitor notifies the supervisor that the system is alive.
5256
HealthMonitorBuilder with_supervisor_api_cycle(std::chrono::milliseconds cycle_duration) &&;
@@ -78,6 +82,7 @@ class HealthMonitor final
7882

7983
score::cpp::expected<deadline::DeadlineMonitor, Error> get_deadline_monitor(const MonitorTag& monitor_tag);
8084
score::cpp::expected<heartbeat::HeartbeatMonitor, Error> get_heartbeat_monitor(const MonitorTag& monitor_tag);
85+
score::cpp::expected<logic::LogicMonitor, Error> get_logic_monitor(const MonitorTag& monitor_tag);
8186

8287
void start();
8388

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
#ifndef SCORE_HM_LOGIC_LOGIC_MONITOR_H
14+
#define SCORE_HM_LOGIC_LOGIC_MONITOR_H
15+
16+
#include "score/hm/common.h"
17+
#include "score/hm/tag.h"
18+
#include <score/expected.hpp>
19+
20+
namespace score::hm
21+
{
22+
// Forward declaration
23+
class HealthMonitor;
24+
class HealthMonitorBuilder;
25+
} // namespace score::hm
26+
27+
namespace score::hm::logic
28+
{
29+
30+
class LogicMonitorBuilder final : public internal::RustDroppable<LogicMonitorBuilder>
31+
{
32+
public:
33+
/// Create a new `LogicMonitorBuilder`.
34+
///
35+
/// - `initial_state` - starting point, implicitly added to the list of allowed states.
36+
LogicMonitorBuilder(const StateTag& initial_state);
37+
38+
LogicMonitorBuilder(const LogicMonitorBuilder&) = delete;
39+
LogicMonitorBuilder& operator=(const LogicMonitorBuilder&) = delete;
40+
41+
LogicMonitorBuilder(LogicMonitorBuilder&&) = default;
42+
LogicMonitorBuilder& operator=(LogicMonitorBuilder&&) = delete;
43+
44+
/// Add allowed state.
45+
LogicMonitorBuilder add_state(const StateTag& state) &&;
46+
47+
/// Add allowed transition.
48+
LogicMonitorBuilder add_transition(const StateTag& from_state, const StateTag& to_state) &&;
49+
50+
protected:
51+
std::optional<internal::FFIHandle> __drop_by_rust_impl()
52+
{
53+
return monitor_builder_handle_.drop_by_rust();
54+
}
55+
56+
private:
57+
internal::DroppableFFIHandle monitor_builder_handle_;
58+
59+
// Allow to hide drop_by_rust implementation
60+
friend class internal::RustDroppable<LogicMonitorBuilder>;
61+
62+
// Allow HealthMonitorBuilder to access drop_by_rust implementation
63+
friend class ::score::hm::HealthMonitorBuilder;
64+
};
65+
66+
class LogicMonitor final
67+
{
68+
public:
69+
LogicMonitor(const LogicMonitor&) = delete;
70+
LogicMonitor& operator=(const LogicMonitor&) = delete;
71+
72+
LogicMonitor(LogicMonitor&& other) noexcept = default;
73+
LogicMonitor& operator=(LogicMonitor&& other) noexcept = default;
74+
75+
/// Perform transition to a new state.
76+
score::cpp::expected<StateTag, Error> transition(const StateTag& state);
77+
78+
/// Current monitor state.
79+
score::cpp::expected<StateTag, Error> state();
80+
81+
private:
82+
explicit LogicMonitor(internal::FFIHandle monitor_handle);
83+
84+
// Only `HealthMonitor` is allowed to create `LogicMonitor` instances.
85+
friend class score::hm::HealthMonitor;
86+
internal::DroppableFFIHandle monitor_handle_;
87+
};
88+
89+
} // namespace score::hm::logic
90+
91+
#endif // SCORE_HM_LOGIC_LOGIC_MONITOR_H

src/health_monitoring_lib/cpp/include/score/hm/tag.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ namespace score::hm
2323
class Tag
2424
{
2525
public:
26+
/// Create an empty tag.
27+
Tag() : data_{nullptr}, length_{0} {}
28+
2629
/// Create a new tag from a C-style string.
2730
template <size_t N>
2831
explicit Tag(const char (&tag)[N]) : data_(tag), length_(N - 1)
@@ -49,6 +52,13 @@ class DeadlineTag : public Tag
4952
using Tag::Tag;
5053
};
5154

55+
/// State tag.
56+
class StateTag : public Tag
57+
{
58+
public:
59+
using Tag::Tag;
60+
};
61+
5262
} // namespace score::hm
5363

5464
#endif // SCORE_HM_TAG_H
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
#include "score/hm/logic/logic_monitor.h"
14+
#include <score/assert.hpp>
15+
16+
namespace
17+
{
18+
extern "C" {
19+
using namespace score::hm;
20+
using namespace score::hm::internal;
21+
using namespace score::hm::logic;
22+
23+
FFICode logic_monitor_builder_create(const StateTag* initial_state, FFIHandle* logic_monitor_builder_handle_out);
24+
FFICode logic_monitor_builder_destroy(FFIHandle logic_monitor_builder_handle);
25+
FFICode logic_monitor_builder_add_state(FFIHandle logic_monitor_builder_handle, const StateTag* state);
26+
FFICode logic_monitor_builder_add_transition(FFIHandle logic_monitor_builder_handle,
27+
const StateTag* from_state,
28+
const StateTag* to_state);
29+
FFICode logic_monitor_destroy(FFIHandle logic_monitor_handle);
30+
FFICode logic_monitor_transition(FFIHandle logic_monitor_handle, const StateTag* to_state);
31+
FFICode logic_monitor_state(FFIHandle logic_monitor_handle, StateTag* state_out);
32+
}
33+
34+
FFIHandle logic_monitor_builder_create_wrapper(const StateTag& initial_state)
35+
{
36+
FFIHandle handle{nullptr};
37+
auto result{logic_monitor_builder_create(&initial_state, &handle)};
38+
SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess);
39+
return handle;
40+
}
41+
} // namespace
42+
43+
namespace score::hm::logic
44+
{
45+
LogicMonitorBuilder::LogicMonitorBuilder(const StateTag& initial_state)
46+
: monitor_builder_handle_{logic_monitor_builder_create_wrapper(initial_state), &logic_monitor_builder_destroy}
47+
{
48+
}
49+
50+
LogicMonitorBuilder LogicMonitorBuilder::add_state(const StateTag& state) &&
51+
{
52+
auto monitor_builder_handle{monitor_builder_handle_.as_rust_handle()};
53+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_builder_handle.has_value());
54+
55+
auto result{logic_monitor_builder_add_state(monitor_builder_handle.value(), &state)};
56+
SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess);
57+
58+
return std::move(*this);
59+
}
60+
61+
LogicMonitorBuilder LogicMonitorBuilder::add_transition(const StateTag& from_state, const StateTag& to_state) &&
62+
{
63+
auto monitor_builder_handle{monitor_builder_handle_.as_rust_handle()};
64+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_builder_handle.has_value());
65+
66+
auto result{logic_monitor_builder_add_transition(monitor_builder_handle.value(), &from_state, &to_state)};
67+
SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess);
68+
69+
return std::move(*this);
70+
}
71+
72+
LogicMonitor::LogicMonitor(FFIHandle monitor_handle) : monitor_handle_{monitor_handle, &logic_monitor_destroy} {}
73+
74+
score::cpp::expected<StateTag, Error> LogicMonitor::transition(const StateTag& state)
75+
{
76+
auto monitor_handle{monitor_handle_.as_rust_handle()};
77+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value());
78+
79+
auto result{logic_monitor_transition(monitor_handle.value(), &state)};
80+
if (result != kSuccess)
81+
{
82+
return score::cpp::unexpected(static_cast<Error>(result));
83+
}
84+
85+
return score::cpp::expected<StateTag, Error>(state);
86+
}
87+
88+
score::cpp::expected<StateTag, Error> LogicMonitor::state()
89+
{
90+
auto monitor_handle{monitor_handle_.as_rust_handle()};
91+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value());
92+
93+
StateTag state_tag;
94+
auto result{logic_monitor_state(monitor_handle.value(), &state_tag)};
95+
if (result != kSuccess)
96+
{
97+
return score::cpp::unexpected(static_cast<Error>(result));
98+
}
99+
100+
return score::cpp::expected<StateTag, Error>(state_tag);
101+
}
102+
103+
} // namespace score::hm::logic

src/health_monitoring_lib/cpp/tests/health_monitor_test.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,17 @@ TEST_F(HealthMonitorTest, TestName)
4949
const TimeRange heartbeat_range{std::chrono::milliseconds{100}, std::chrono::milliseconds{200}};
5050
auto heartbeat_monitor_builder = heartbeat::HeartbeatMonitorBuilder(heartbeat_range);
5151

52+
// Setup logic monitor construction.
53+
const MonitorTag logic_monitor_tag{"logic_monitor"};
54+
StateTag from_state{"from_state"};
55+
StateTag to_state{"to_state"};
56+
auto logic_monitor_builder =
57+
logic::LogicMonitorBuilder{from_state}.add_state(to_state).add_transition(from_state, to_state);
58+
5259
auto hm = HealthMonitorBuilder()
5360
.add_deadline_monitor(deadline_monitor_tag, std::move(deadline_monitor_builder))
5461
.add_heartbeat_monitor(heartbeat_monitor_tag, std::move(heartbeat_monitor_builder))
62+
.add_logic_monitor(logic_monitor_tag, std::move(logic_monitor_builder))
5563
.with_internal_processing_cycle(std::chrono::milliseconds(50))
5664
.with_supervisor_api_cycle(std::chrono::milliseconds(50))
5765
.build();
@@ -80,11 +88,25 @@ TEST_F(HealthMonitorTest, TestName)
8088

8189
auto heartbeat_monitor{std::move(*heartbeat_monitor_res)};
8290

91+
// Obtain logic monitor from HMON.
92+
auto logic_monitor_res{hm.get_logic_monitor(logic_monitor_tag)};
93+
EXPECT_TRUE(logic_monitor_res.has_value());
94+
95+
{
96+
// Try again to get the same monitor.
97+
auto logic_monitor_res{hm.get_logic_monitor(logic_monitor_tag)};
98+
EXPECT_FALSE(logic_monitor_res.has_value());
99+
}
100+
101+
auto logic_monitor{std::move(*logic_monitor_res)};
102+
83103
// Start HMON.
84104
hm.start();
85105

86106
heartbeat_monitor.heartbeat();
87107

108+
EXPECT_TRUE(logic_monitor.transition(to_state).has_value());
109+
88110
auto deadline_res = deadline_mon.get_deadline(DeadlineTag("deadline_1"));
89111

90112
{

0 commit comments

Comments
 (0)