Fix unexpected issues

This commit is contained in:
Bartosz Wieczorek 2025-11-17 18:44:48 +01:00
parent 40775d53cf
commit 49f4d24242

View File

@ -49,10 +49,10 @@ namespace ranczo {
enum class ThermostatState { Enabled, Disabled, Error };
enum class Trend { Fall, Const, Rise };
std::optional<ThermostatState> ThermostatState_from_string(std::string_view state){
std::optional< ThermostatState > ThermostatState_from_string(std::string_view state) {
std::string s(state.begin(), state.end());
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast< char >(std::tolower(c)); });
if(s == "enabled")
return ThermostatState::Enabled;
if(s == "disabled")
@ -62,8 +62,8 @@ std::optional<ThermostatState> ThermostatState_from_string(std::string_view stat
return std::nullopt;
}
std::string ThermostatState_to_string(ThermostatState state){
switch (state) {
std::string ThermostatState_to_string(ThermostatState state) {
switch(state) {
case ThermostatState::Enabled:
return "Enabled";
case ThermostatState::Disabled:
@ -95,7 +95,7 @@ inline expected< T > readValue(const boost::json::value & jv, std::string_view k
if(not v)
return unexpected{make_error_code(boost::system::errc::invalid_argument)};
return *v;
} else if constexpr(is_chrono_duration_v< T >) {
if(pv->is_string()) {
auto sv = pv->as_string();
@ -358,7 +358,7 @@ struct RelayThermostat::Impl : private boost::noncopyable {
private:
executor & _io;
AsyncMqttClient & _mqtt;
ComponentSettingsStore _settings;
/// relay control
@ -454,7 +454,8 @@ struct RelayThermostat::Impl : private boost::noncopyable {
}
auto trend = *trendOpt;
const bool relayOn = (_relay->state() == Relay::State::On);
auto st = ASYNC_TRY(_relay->state());
const bool relayOn = (st == Relay::State::On);
// 2a) relay OFF, a temperatura rośnie => przekaźnik zawiesił się na ON
if(!relayOn && trend == Trend::Rise) {
@ -470,7 +471,8 @@ struct RelayThermostat::Impl : private boost::noncopyable {
}
awaitable_expected< void > handleErrorState() {
if(_relay->state() == Relay::State::On) {
auto st = ASYNC_TRY(_relay->state());
if(st == Relay::State::On) {
spdlog::warn("Forcing relay OFF in ERROR state for {}/{}", _room, _zone);
ASYNC_CHECK_MSG(_relay->off(), "Emergency relay OFF failed!");
}
@ -478,7 +480,8 @@ struct RelayThermostat::Impl : private boost::noncopyable {
}
awaitable_expected< void > handleDisabledState() {
if(_relay->state() == Relay::State::On) {
auto st = ASYNC_TRY(_relay->state());
if(st == Relay::State::On) {
spdlog::info("RelayThermostat disabling relay because thermostat is Disabled for {}/{}", _room, _zone);
ASYNC_CHECK_MSG(_relay->off(), "relay OFF failed");
}
@ -496,7 +499,8 @@ struct RelayThermostat::Impl : private boost::noncopyable {
const auto now = system_clock::now();
const auto minElapsed = now - _lastStateChange;
const bool relayOn = (_relay->state() == Relay::State::On);
auto st = ASYNC_TRY(_relay->state());
const bool relayOn = (st == Relay::State::On);
// grzejemy jeżeli temp < setpoint - histereza
if(!relayOn) {
@ -537,7 +541,8 @@ struct RelayThermostat::Impl : private boost::noncopyable {
_state = ThermostatState::Error;
// Bezpieczeństwo: wyłącz przekaźnik natychmiastowo
if(_relay->state() == Relay::State::On) {
auto st = ASYNC_TRY(_relay->state());
if(st == Relay::State::On) {
ASYNC_CHECK(_relay->off());
}
@ -549,12 +554,18 @@ struct RelayThermostat::Impl : private boost::noncopyable {
public:
Impl(executor & io,
AsyncMqttClient & mqtt,
SettingsStore &setup,
SettingsStore & setup,
std::unique_ptr< Relay > relay,
std::unique_ptr< Thermometer > thermometer,
std::string_view room,
int zone)
: _io{io}, _settings{setup, std::string{room}}, _mqtt{mqtt}, _relay{std::move(relay)}, _thermo{_io, std::move(thermometer)}, _room{room}, _zone{zone} {
: _io{io},
_settings{setup, std::string{room}},
_mqtt{mqtt},
_relay{std::move(relay)},
_thermo{_io, std::move(thermometer)},
_room{room},
_zone{zone} {
BOOST_ASSERT(_relay);
BOOST_ASSERT(not _room.empty());
BOOST_ASSERT(_zone > 0);
@ -565,42 +576,40 @@ struct RelayThermostat::Impl : private boost::noncopyable {
awaitable_expected< void > start() {
using namespace std::placeholders;
spdlog::info("RelayThermostat::start room : {}", _room);
auto get_state = [this](std::string key, ThermostatState _default) -> awaitable_expected<ThermostatState>{
auto get_state = [this](std::string key, ThermostatState _default) -> awaitable_expected< ThermostatState > {
auto contains = ASYNC_TRY(_settings.async_contains(key));
if(not contains){
if(not contains) {
ASYNC_CHECK(_settings.async_save(key, ThermostatState_to_string(_default)));
}
/// TODO can throw
auto value = ASYNC_TRY(_settings.async_get(key)).value(); // we have this value at this point
auto ThermostatStateStr = value.get_if<std::string>(); // TODO can be different type
auto state = ThermostatState_from_string(ThermostatStateStr);
auto value = ASYNC_TRY(_settings.async_get(key)).value(); // we have this value at this point
auto ThermostatStateStr = value.get_if< std::string >(); // TODO can be different type
auto state = ThermostatState_from_string(ThermostatStateStr);
if(state)
co_return *state;
co_return unexpected{make_error_code(boost::system::errc::invalid_argument)};
};
auto get_double = [this](std::string key, double _default) -> awaitable_expected<double>{
auto get_double = [this](std::string key, double _default) -> awaitable_expected< double > {
auto contains = ASYNC_TRY(_settings.async_contains(key));
if(not contains){
if(not contains) {
ASYNC_CHECK(_settings.async_save(key, _default));
}
/// TODO can throw
auto value = ASYNC_TRY(_settings.async_get(key)).value(); // we have this value at this point
co_return value.get_if<decltype(_default)>(); // TODO can be different type
co_return value.get_if< decltype(_default) >(); // TODO can be different type
};
_state = ASYNC_TRY(get_state("state", ThermostatState::Disabled));
_state = ASYNC_TRY(get_state("state", ThermostatState::Disabled));
_targetTemperature = ASYNC_TRY(get_double("target_temperature", 20.0));
_hysteresis = ASYNC_TRY(get_double("hysteresis",2));// [°C]
_hysteresis = ASYNC_TRY(get_double("hysteresis", 2)); // [°C]
std::chrono::nanoseconds _tickTime{std::chrono::seconds(60)}; // minimalny czas ON/OFF
std::chrono::nanoseconds _slopeWindow{std::chrono::minutes(5)};
_slopeDT_c = ASYNC_TRY(get_double("slope_delta_t", 1));// [°C / min]
std::chrono::minutes _sensorTimeout{std::chrono::minutes(5)};
_slopeDT_c = ASYNC_TRY(get_double("slope_delta_t", 1)); // [°C / min]
std::chrono::minutes _sensorTimeout{std::chrono::minutes(5)};
// subscribe to a thermostat commands feed
spdlog::info("RelayThermostat::start room : {} subscribe to mqtt", _room);
@ -624,12 +633,12 @@ struct RelayThermostat::Impl : private boost::noncopyable {
}
awaitable_expected< void > subscribeToAllCommands() {
ASYNC_TRY(subscribeCommand< commands::TemperatureSetpointChange >());
ASYNC_TRY(subscribeCommand< commands::StateChange >());
ASYNC_TRY(subscribeCommand< commands::HisteresisChange >());
ASYNC_TRY(subscribeCommand< commands::TickTimeChange >());
ASYNC_TRY(subscribeCommand< commands::SlopeWindowChange >());
ASYNC_TRY(subscribeCommand< commands::SlopeTemperatureDiffChange >());
ASYNC_CHECK(subscribeCommand< commands::TemperatureSetpointChange >());
ASYNC_CHECK(subscribeCommand< commands::StateChange >());
ASYNC_CHECK(subscribeCommand< commands::HisteresisChange >());
ASYNC_CHECK(subscribeCommand< commands::TickTimeChange >());
ASYNC_CHECK(subscribeCommand< commands::SlopeWindowChange >());
ASYNC_CHECK(subscribeCommand< commands::SlopeTemperatureDiffChange >());
co_return _void{};
}
@ -673,14 +682,15 @@ struct RelayThermostat::Impl : private boost::noncopyable {
spdlog::warn("Ignoring attempt to enable thermostat in ERROR state for {}/{}", _room, _zone);
co_return _void{};
}
_state = cmd.state;
// Bezpiecznik: jeśli zostało ustawione Disabled wyłącz przekaźnik
if(_state == ThermostatState::Disabled && _relay->state() == Relay::State::On) {
// Bezpiecznik: jeśli zostało ustawione Disabled wyłącz przekaźnik
auto st = ASYNC_TRY(_relay->state());
if(_state == ThermostatState::Disabled && st == Relay::State::On) {
ASYNC_CHECK(_relay->off());
}
co_return _void{};
}
@ -711,14 +721,12 @@ struct RelayThermostat::Impl : private boost::noncopyable {
RelayThermostat::RelayThermostat(executor & io,
AsyncMqttClient & mqtt,
SettingsStore &setup,
SettingsStore & setup,
std::unique_ptr< Relay > relay,
std::unique_ptr< Thermometer > thermometer,
std::string_view room,
int zone_id)
: _impl{std::make_unique< Impl >(io, mqtt, setup, std::move(relay), std::move(thermometer), room, zone_id)} {
}
: _impl{std::make_unique< Impl >(io, mqtt, setup, std::move(relay), std::move(thermometer), room, zone_id)} {}
RelayThermostat::~RelayThermostat() = default;