Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions components/ocs_core/func_value_monitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2026 Tendry Lab
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <functional>

#include "ocs_core/ivalue_monitor.h"
#include "ocs_core/noncopyable.h"
#include "ocs_status/code.h"

namespace ocs {
namespace core {

//! Value monitor that delegates the update to the provided function.
template <typename T>
class FuncValueMonitor : public IValueMonitor<T>, private NonCopyable<> {
public:
//! Function to update the underlying monitor.
using Func = std::function<status::StatusCode()>;

//! Initialize.
//!
//! @params
//! - @p monitor to get the latest sampled value.
//! - @p func to update the @p monitor, e.g. asynchronously on a specific task.
FuncValueMonitor(IValueMonitor<T>& monitor, Func func)
: monitor_(monitor)
, func_(func) {
}

//! Get latest sampled value from the underlying monitor.
T get() const override {
return monitor_.get();
}

//! Call the function to update the underlying monitor.
status::StatusCode update() override {
return func_();
}

private:
IValueMonitor<T>& monitor_;
Func func_;
};

} // namespace core
} // namespace ocs
1 change: 1 addition & 0 deletions components/ocs_core/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ idf_component_register(
"test_byte_writer.cpp"
"test_byte_reader.cpp"
"test_byte_transceiver.cpp"
"test_func_value_monitor.cpp"

REQUIRES
"unity"
Expand Down
88 changes: 88 additions & 0 deletions components/ocs_core/test/test_func_value_monitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* SPDX-FileCopyrightText: 2026 Tendry Lab
* SPDX-License-Identifier: Apache-2.0
*/

#include "unity.h"

#include "ocs_core/func_value_monitor.h"
#include "ocs_core/noncopyable.h"

namespace ocs {
namespace core {

namespace {

class TestValueMonitor : public IValueMonitor<int>, private NonCopyable<> {
public:
int get() const override {
return value;
}

status::StatusCode update() override {
++update_call_count;

return status::StatusCode::OK;
}

int value { 0 };
size_t update_call_count { 0 };
};

} // namespace

TEST_CASE("Func value monitor: get value from the underlying monitor",
"[func_value_monitor], [ocs_core]") {
TestValueMonitor monitor;
size_t func_call_count = 0;

FuncValueMonitor<int> func_monitor(monitor, [&func_call_count]() {
++func_call_count;

return status::StatusCode::OK;
});

monitor.value = 42;
TEST_ASSERT_EQUAL(42, func_monitor.get());

monitor.value = -1;
TEST_ASSERT_EQUAL(-1, func_monitor.get());

TEST_ASSERT_EQUAL(0, func_call_count);
TEST_ASSERT_EQUAL(0, monitor.update_call_count);
}

TEST_CASE("Func value monitor: update calls the function",
"[func_value_monitor], [ocs_core]") {
TestValueMonitor monitor;
size_t func_call_count = 0;

FuncValueMonitor<int> func_monitor(monitor, [&func_call_count]() {
++func_call_count;

return status::StatusCode::OK;
});

TEST_ASSERT_EQUAL(status::StatusCode::OK, func_monitor.update());
TEST_ASSERT_EQUAL(1, func_call_count);

TEST_ASSERT_EQUAL(status::StatusCode::OK, func_monitor.update());
TEST_ASSERT_EQUAL(2, func_call_count);

// The underlying monitor is only updated by the function.
TEST_ASSERT_EQUAL(0, monitor.update_call_count);
}

TEST_CASE("Func value monitor: update propagates the function failure",
"[func_value_monitor], [ocs_core]") {
TestValueMonitor monitor;

FuncValueMonitor<int> func_monitor(monitor, []() {
return status::StatusCode::Error;
});

TEST_ASSERT_EQUAL(status::StatusCode::Error, func_monitor.update());
}

} // namespace core
} // namespace ocs
Loading