remove exceptions from operation subsystem

This commit is contained in:
Bartosz Wieczorek 2025-06-12 14:41:50 +02:00
parent df5458a43a
commit 1fa8cd8e47
5 changed files with 116 additions and 100 deletions

View File

@ -81,7 +81,7 @@ BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 140
ColumnLimit: 16666660
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4

View File

@ -4,6 +4,7 @@
#include "message_type_id.hpp"
#include <proto/gpio.pb.h>
#include <proto/operation.pb.h>
#include <proto/temperature.pb.h>
struct Error {
@ -35,18 +36,26 @@ ERR(wouldblock, EWOULDBLOCK);
} // namespace zephyr::gpio
namespace rims::gpio {
ERR_CATEGORY(rims_gpio, gpio_receiver_tag);
ERR(bad_id, gpio_Error_BadId);
ERR(ioerror, gpio_Error_BadId);
} // namespace rims::gpio
namespace rims::temperature{
namespace rims::temperature {
ERR_CATEGORY(rims_temperature, temperature_receiver_tag);
ERR(unknownChannel, temperature_Error_UnknownChannel);
ERR(sensorDisconnected, temperature_Error_TemperatureSensorDisconnected);
ERR(sensorBroken, temperature_Error_TemperatureSensorBroken);
ERR(hwError, 100);
ERR(calibrationError, 101);
}
} // namespace rims::temperature
namespace rims::op {
ERR_CATEGORY(rims_op, operation_receiver_tag);
ERR(max_number_of_channels, op_Error_MaxNumberOfChannels);
ERR(channel_used_multiple_times, op_Error_ControlChannelUsedMultipleTimes);
ERR(channel_not_found, op_Error_ChannelNotFound);
ERR(control_channel_disabled, op_Error_ControlChannelDisabled);
ERR(mode_does_not_accept_target_temperature, op_Error_ModeDoesNotHandleTargetTemperatureMessage);
} // namespace rims::op

View File

@ -1,5 +1,6 @@
#include "operation.hpp"
#include "common.hpp"
#include "error.hpp"
#include "inplace_vector.hpp"
#include "log.hpp"
#include "messenger.hpp"
@ -18,42 +19,6 @@
namespace rims {
namespace op {
struct error : std::exception {
op_Error _err;
error() : _err{op_Error_UnknownError} {
}
error(op_Error e) : _err{e} {
}
};
struct max_number_of_channels : error {
max_number_of_channels() : error{op_Error_MaxNumberOfChannels} {
}
};
struct channel_used_multiple_times : error {
channel_used_multiple_times() : error{op_Error_ControlChannelUsedMultipleTimes} {
}
};
struct channel_not_found : error {
channel_not_found() : error{op_Error_ChannelNotFound} {
}
};
struct control_channel_disabled : error {
control_channel_disabled() : error{op_Error_ControlChannelDisabled} {
}
};
struct mode_does_not_accept_target_temperature : error {
mode_does_not_accept_target_temperature() : error{op_Error_ModeDoesNotHandleTargetTemperatureMessage} {
}
};
} // namespace op
template <typename T>
concept HasSetTargetTemperature = requires(T t, const op_TargetTemperature &temp) {
{ t.setTargetTemperature(temp) } -> std::same_as<void>;
@ -80,11 +45,11 @@ fifo_queue<op_EgressMessage, 2> operationEgressQueue{operationEggress};
constexpr auto op_factory = handler_factory<op_IngressMessage, op_EgressMessage, op::error>{};
constexpr std::array<handler, 5> op_handlers = {
op_factory.make_handler<op_ActiveChannelsRequest, &OperationOrchestrator::handle_activeChannelsRequest>(),
op_factory.make_handler<op_InitializeChannelRequest, &OperationOrchestrator::handle_initializeChannelRequest>(),
op_factory.make_handler<op_DeinitializeChannelRequest, &OperationOrchestrator::handle_deinitializeChannelRequest>(),
op_factory.make_handler<op_ModeRequest, &OperationOrchestrator::handle_modeRequest>(),
op_factory.make_handler<op_TargetTemperatureRequest, &OperationOrchestrator::handle_targetTemperatureRequest>(),
op_factory.make_handler_expected<op_ActiveChannelsRequest, &OperationOrchestrator::handle_activeChannelsRequest>(),
op_factory.make_handler_expected<op_InitializeChannelRequest, &OperationOrchestrator::handle_initializeChannelRequest>(),
op_factory.make_handler_expected<op_DeinitializeChannelRequest, &OperationOrchestrator::handle_deinitializeChannelRequest>(),
op_factory.make_handler_expected<op_ModeRequest, &OperationOrchestrator::handle_modeRequest>(),
op_factory.make_handler_expected<op_TargetTemperatureRequest, &OperationOrchestrator::handle_targetTemperatureRequest>(),
};
OperationOrchestrator::OperationOrchestrator() {
@ -131,26 +96,28 @@ void OperationOrchestrator::event_operationMessageArrived() {
});
}
void OperationOrchestrator::handle_activeChannelsRequest(const ActiveChannelsRequest &req, ActiveChannelsResponse &resp) const {
std::expected<void, op::error> OperationOrchestrator::handle_activeChannelsRequest(const ActiveChannelsRequest &req, ActiveChannelsResponse &resp) const {
ULOG_INFO("active channels request handler");
resp.data_count = _channels.size();
for (auto i = 0; i < resp.data_count; i++) {
resp.data[i].channel = _channels.at(i).id();
resp.data[i].ctrl_channel_ids_count = _channels.at(i).copyControlChannels(resp.data[i].ctrl_channel_ids);
}
return {};
}
void OperationOrchestrator::handle_initializeChannelRequest(const InitializeChannelRequest &req, InitializeChannelResponse &resp) {
std::expected<void, op::error>
OperationOrchestrator::handle_initializeChannelRequest(const InitializeChannelRequest &req, InitializeChannelResponse &resp) {
ULOG_INFO("channel init request handler");
if (_channels.full()) throw op::max_number_of_channels{};
CHECK(freeSlot());
// check if any of needed channels are already controlld by somebody else
for (auto i = 0; i < req.ctrl_channels_count; i++) {
const auto ch = req.ctrl_channels[i];
for (const auto &opchannel : _channels) {
if (opchannel.usesControlChannel(ch)) {
throw op::channel_used_multiple_times{};
return std::unexpected{op::channel_used_multiple_times{}};
}
}
}
@ -160,42 +127,48 @@ void OperationOrchestrator::handle_initializeChannelRequest(const InitializeChan
channels.push_back(req.ctrl_channels[i]);
}
auto find_id = [&]() {
auto find_id = [&]() -> std::expected<uint8_t, op::error> {
for (uint8_t id = 0; id < 255; ++id) {
bool used = std::any_of(_channels.begin(), _channels.end(), [id](const OperationChannel &ch) { return ch.id() == id; });
if (!used) return id;
}
throw op::max_number_of_channels{};
return std::unexpected{op::max_number_of_channels{}};
};
// create OperationChannel for channels and id
const auto &newChannel = _channels.emplace_back(std::span{channels.begin(), channels.end()}, find_id());
auto activatedChannels = TRY(OperationChannel::check_channels(std::span{channels}));
auto id = TRY(find_id());
const auto &newChannel = _channels.emplace_back(activatedChannels, id);
resp.has_data = true;
resp.data.channel = newChannel.id();
resp.data.ctrl_channel_ids_count = newChannel.copyControlChannels(resp.data.ctrl_channel_ids);
return {};
}
void OperationOrchestrator::handle_deinitializeChannelRequest(const DeinitializeChannelRequest &req, DeinitializeChannelResponse &resp) {
std::expected<void, op::error> OperationOrchestrator::handle_deinitializeChannelRequest(const DeinitializeChannelRequest &req, DeinitializeChannelResponse &resp) {
auto found = std::find_if(_channels.begin(), _channels.end(), [&](const OperationChannel &ch) { return ch.id() == req.channel_id; });
if (found == _channels.end()) {
ULOG_WARNING("channel %d not found", req.channel_id);
throw op::channel_not_found{};
return std::unexpected{ op::channel_not_found{}};
}
_channels.erase(found);
return {};
}
void OperationOrchestrator::handle_modeRequest(const ModeRequest &request, ModeResponse &resp) {
std::expected<void, op::error> OperationOrchestrator::handle_modeRequest(const ModeRequest &request, ModeResponse &resp) {
ULOG_INFO("mode request handler");
if (not channelExists(request.channel_id)) throw op::channel_not_found{};
CHECK(channelValid(request.channel_id));
if (request.has_data) {
setMode(request.channel_id, request.data);
}
resp.data.mode = mode(request.channel_id);
return{};
}
TemperatureSlopeMode::TemperatureSlopeMode(PowerControl &pc, PIDController &pid, const op_TemperatureSlopeConfig &config)
@ -265,25 +238,28 @@ void ConstantTemperatureMode::update() {
_powerControl.get().setPower(outputPower);
}
void OperationOrchestrator::handle_targetTemperatureRequest(const TargetTemperatureRequest &req, TargetTemperatureResponse &resp) {
std::expected<void, op::error>
OperationOrchestrator::handle_targetTemperatureRequest(const TargetTemperatureRequest &req, TargetTemperatureResponse &resp) {
ULOG_INFO("target temperature request handler");
if (not channelExists(req.channel_id)) throw op::channel_not_found{};
find(req.channel_id).handle_targetTemperatureRequest(req, resp);
CHECK(channelValid(req.channel_id));
CHECK(TRY(find(req.channel_id)).get().handle_targetTemperatureRequest(req, resp));
return {};
}
void OperationChannel::handle_targetTemperatureRequest(const TargetTemperatureRequest &req, TargetTemperatureResponse &resp) {
std::expected<void, op::error> OperationChannel::handle_targetTemperatureRequest(const TargetTemperatureRequest &req, TargetTemperatureResponse &resp) {
ULOG_INFO("target temperature request handler");
if (req.has_target_temperature) {
std::visit(
[&](auto &obj) {
CHECK(std::visit(
[&](auto &obj) -> std::expected<void, op::error> {
if constexpr (HasSetTargetTemperature<decltype(obj)>) {
obj.setTargetTemperature(req.target_temperature);
return {};
} else {
throw op::mode_does_not_accept_target_temperature{};
return std::unexpected{op::mode_does_not_accept_target_temperature{}};
}
},
_mode
);
));
}
std::visit(
@ -297,10 +273,11 @@ void OperationChannel::handle_targetTemperatureRequest(const TargetTemperatureRe
},
_mode
);
return {};
}
void OperationOrchestrator::setMode(uint8_t ch, const op_ModeData &data) {
if (not channelExists(ch)) throw op::channel_not_found{};
CHECK_ASSERT(channelValid(ch));
ULOG_INFO("channel %d mode change to %d", ch, data.mode);
_channels.at(ch).setMode(data.mode, optcref(data.has_mode_config, data.mode_config));
@ -336,8 +313,9 @@ void OperationChannel::setMode(Mode mode, std::optional<cref<op_ModeConfig>> con
}
op_Mode OperationOrchestrator::mode(uint8_t ch) const noexcept {
if (not channelExists(ch)) return op_Mode_Disabled;
else return find(ch).mode();
/// TODO should return some other status on disabled channel
if (not channelValid(ch)) return op_Mode_Disabled;
else return find(ch).value().get().mode();
}
Mode OperationChannel::mode() const noexcept {
@ -351,40 +329,30 @@ Mode OperationChannel::mode() const noexcept {
return op_Mode_Disabled;
}
bool OperationOrchestrator::channelExists(uint8_t ch) const {
auto found = std::find_if(_channels.begin(), _channels.end(), [&](const OperationChannel &opch) { return opch.id() == ch; });
return found != _channels.end();
}
void OperationThread::threadMain() {
ULOG_DEBUG("OperationThread start");
OperationOrchestrator thread{};
thread.loop();
}
PowerControl::PowerControl(std::span<uint8_t> channels) : _control_channels{channels.begin(), channels.end()} {
ULOG_DEBUG("PowerControl start");
std::expected<void, op::error> PowerControl::check_channels(std::span<uint8_t> _control_channels) {
for (const auto ch : _control_channels) {
ctrl_PowerControlAlgorithmRequest plar = ctrl_PowerControlAlgorithmRequest_init_zero;
plar.channel_id = ch;
auto resp = MessengerThread::ipc_request(plar);
if (not resp.has_value()) {
ULOG_ERROR("Something went really bad ;( during PowerControlAlgorithm check");
throw op::error{};
}
if (resp->has_error) {
ULOG_WARNING("ctrl returned error:%d", resp->error);
throw op::error{};
}
assert(resp.has_value()); // internal error, should not occour ever
if (resp->data.alg == ctrl_PowerControlAlgorithm_NoModulation) {
ULOG_WARNING("ctrl channel %d disabled", ch);
throw op::control_channel_disabled{};
return std::unexpected{op::control_channel_disabled{}};
}
}
return {};
}
PowerControl::PowerControl(std::span<uint8_t> channels) : _control_channels{channels.begin(), channels.end()} {
ULOG_DEBUG("PowerControl start");
}
void PowerControl::setPower(float percent) {

View File

@ -2,6 +2,7 @@
/* Kernel event for notifying other threads */
#include "common.hpp"
#include "error.hpp"
#include "fifo_queue.hpp"
#include "inplace_vector.hpp"
@ -15,6 +16,7 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <functional>
#include <optional>
#include <span>
@ -70,7 +72,7 @@ class ModeStrategy {
return {};
}
virtual ~ModeStrategy() {
virtual ~ModeStrategy(){
/// todo set channel power to 0 at exit
};
@ -80,6 +82,7 @@ class ModeStrategy {
class PowerControl {
public:
static std::expected<void, op::error> check_channels(std::span<uint8_t> channels);
PowerControl(std::span<uint8_t> channels);
void setPower(float percent);
@ -161,12 +164,27 @@ class OperationChannel {
ConstantTemperatureMode,
TemperatureSlopeMode>;
struct ChannelsActivated {
std::span<uint8_t> channels;
ChannelsActivated() = delete;
explicit ChannelsActivated(std::span<uint8_t> channels) : channels{channels} {
}
};
public:
void setMode(Mode mode, std::optional<cref<op_ModeConfig>> config);
Mode mode() const noexcept;
public:
OperationChannel(std::span<uint8_t> control_channels, uint8_t id) : _pc{control_channels}, _ownId{id} {
NDIS static std::expected<ChannelsActivated, op::error> check_channels(std::span<uint8_t> channels) noexcept {
if (auto status = PowerControl::check_channels(channels); status.has_value()) {
return ChannelsActivated{channels};
} else {
return std::unexpected{status.error()};
}
}
OperationChannel(ChannelsActivated control_channels, uint8_t id) : _pc{control_channels.channels}, _ownId{id} {
}
~OperationChannel() {
}
@ -193,7 +211,7 @@ class OperationChannel {
std::visit([](auto &mode) { mode.update(); }, _mode);
}
void handle_targetTemperatureRequest(const TargetTemperatureRequest &request, TargetTemperatureResponse &resp);
NDIS std::expected<void, op::error> handle_targetTemperatureRequest(const TargetTemperatureRequest &request, TargetTemperatureResponse &resp);
private:
WorkMode _mode;
@ -211,12 +229,12 @@ class OperationOrchestrator {
OperationOrchestrator();
void loop();
void handle_activeChannelsRequest(const ActiveChannelsRequest &req, ActiveChannelsResponse &resp) const;
void handle_initializeChannelRequest(const InitializeChannelRequest &req, InitializeChannelResponse &resp);
void handle_deinitializeChannelRequest(const DeinitializeChannelRequest &req, DeinitializeChannelResponse &resp);
NDIS std::expected<void, op::error> handle_activeChannelsRequest(const ActiveChannelsRequest &req, ActiveChannelsResponse &resp) const;
NDIS std::expected<void, op::error> handle_initializeChannelRequest(const InitializeChannelRequest &req, InitializeChannelResponse &resp);
NDIS std::expected<void, op::error> handle_deinitializeChannelRequest(const DeinitializeChannelRequest &req, DeinitializeChannelResponse &resp);
void handle_modeRequest(const ModeRequest &request, ModeResponse &resp);
void handle_targetTemperatureRequest(const TargetTemperatureRequest &request, TargetTemperatureResponse &resp);
NDIS std::expected<void, op::error> handle_modeRequest(const ModeRequest &request, ModeResponse &resp);
NDIS std::expected<void, op::error> handle_targetTemperatureRequest(const TargetTemperatureRequest &request, TargetTemperatureResponse &resp);
private:
void event_tick();
@ -225,14 +243,33 @@ class OperationOrchestrator {
void setMode(uint8_t ch, const op_ModeData &mode);
op_Mode mode(uint8_t ch) const noexcept;
bool channelExists(uint8_t ch) const;
OperationChannel &find(uint8_t ch) {
NDIS std::expected<void, op::error> channelValid(uint8_t ch) const {
auto found = std::find_if(_channels.begin(), _channels.end(), [&](const OperationChannel &opch) { return opch.id() == ch; });
if (found != _channels.end()) return std::unexpected{op::channel_not_found{}};
return {};
}
NDIS std::expected<void, op::error> freeSlot() const {
if (_channels.full()) return std::unexpected{op::max_number_of_channels{}};
return {};
}
NDIS std::expected<std::reference_wrapper<OperationChannel>, op::error> find(uint8_t ch) {
auto found = std::find_if(_channels.begin(), _channels.end(), [&](const OperationChannel &opch) { return opch.id() == ch; });
if (found == _channels.end()) {
return std::unexpected{op::channel_not_found{}};
}
return *found;
}
const OperationChannel &find(uint8_t ch) const {
NDIS std::expected<std::reference_wrapper<const OperationChannel>, op::error> find(uint8_t ch) const {
auto found = std::find_if(_channels.begin(), _channels.end(), [&](const OperationChannel &opch) { return opch.id() == ch; });
if (found == _channels.end()) {
return std::unexpected{op::channel_not_found{}};
}
return *found;
}
@ -246,7 +283,7 @@ class OperationOrchestrator {
class OperationThread : public ZephyrThread {
public:
OperationThread(TStackBase &stack) : ZephyrThread(stack, 5, 0, "Operation") {};
OperationThread(TStackBase &stack) : ZephyrThread(stack, 5, 0, "Operation"){};
void threadMain() override;
};

View File

@ -19,6 +19,8 @@
#error "Unsupported compiler: TRY macro only supported for GCC and Clang"
#endif
#define NDIS [[nodiscard]]
#define TRY(failable) \
({ \
auto result = (failable); \