fix bugs
This commit is contained in:
parent
e29fad52d5
commit
512f72a371
24
config.hpp
24
config.hpp
@ -24,6 +24,22 @@ using ::boost::system::errc::make_error_code;
|
|||||||
// if(auto _res = co_await (expr); !_res) \
|
// if(auto _res = co_await (expr); !_res) \
|
||||||
// co_return std::unexpected(_res.error())
|
// co_return std::unexpected(_res.error())
|
||||||
|
|
||||||
|
#define SYNC_TRY(failable) \
|
||||||
|
({ \
|
||||||
|
auto _result = failable; \
|
||||||
|
if(!_result) \
|
||||||
|
return unexpected{_result.error()}; \
|
||||||
|
*_result; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define TRY(failable) \
|
||||||
|
({ \
|
||||||
|
auto _result = failable; \
|
||||||
|
if(!_result) \
|
||||||
|
co_return unexpected{_result.error()}; \
|
||||||
|
*_result; \
|
||||||
|
})
|
||||||
|
|
||||||
#define ASYNC_TRY(failable) \
|
#define ASYNC_TRY(failable) \
|
||||||
({ \
|
({ \
|
||||||
auto _result = co_await (failable); \
|
auto _result = co_await (failable); \
|
||||||
@ -36,7 +52,7 @@ using ::boost::system::errc::make_error_code;
|
|||||||
({ \
|
({ \
|
||||||
auto _result = co_await (failable); \
|
auto _result = co_await (failable); \
|
||||||
if(!_result) \
|
if(!_result) \
|
||||||
co_return std::unexpected{transform(_result.error())}; \
|
co_return unexpected{transform(_result.error())}; \
|
||||||
*_result; \
|
*_result; \
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -45,7 +61,7 @@ using ::boost::system::errc::make_error_code;
|
|||||||
auto _result = co_await (failable); \
|
auto _result = co_await (failable); \
|
||||||
if(!_result) { \
|
if(!_result) { \
|
||||||
spdlog::error(__VA_ARGS__); \
|
spdlog::error(__VA_ARGS__); \
|
||||||
co_return std::unexpected{_result.error()}; \
|
co_return unexpected{_result.error()}; \
|
||||||
} \
|
} \
|
||||||
*_result; \
|
*_result; \
|
||||||
})
|
})
|
||||||
@ -61,7 +77,7 @@ using ::boost::system::errc::make_error_code;
|
|||||||
do { \
|
do { \
|
||||||
auto _result = co_await (failable); \
|
auto _result = co_await (failable); \
|
||||||
if(!_result) \
|
if(!_result) \
|
||||||
co_return std::unexpected(_result.error()); \
|
co_return unexpected(_result.error()); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
#define ASYNC_CHECK_MSG(failable, ...) \
|
#define ASYNC_CHECK_MSG(failable, ...) \
|
||||||
@ -69,7 +85,7 @@ using ::boost::system::errc::make_error_code;
|
|||||||
auto _result = co_await (failable); \
|
auto _result = co_await (failable); \
|
||||||
if(!_result) { \
|
if(!_result) { \
|
||||||
spdlog::error(__VA_ARGS__); \
|
spdlog::error(__VA_ARGS__); \
|
||||||
co_return std::unexpected{_result.error()}; \
|
co_return unexpected{_result.error()}; \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|||||||
@ -221,6 +221,10 @@ struct AsyncMqttClient::AsyncMqttClientImpl {
|
|||||||
spdlog::trace("co_spawn mqtt client");
|
spdlog::trace("co_spawn mqtt client");
|
||||||
boost::asio::co_spawn(_mqtt_client.get_executor(), listen(), boost::asio::detached);
|
boost::asio::co_spawn(_mqtt_client.get_executor(), listen(), boost::asio::detached);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cancel(){
|
||||||
|
_mqtt_client.cancel();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
AsyncMqttClient::AsyncMqttClient(const boost::asio::any_io_executor & executor)
|
AsyncMqttClient::AsyncMqttClient(const boost::asio::any_io_executor & executor)
|
||||||
@ -249,6 +253,14 @@ awaitable_expected<void> AsyncMqttClient::listen() const noexcept{
|
|||||||
co_return mqtt_expected_void{};
|
co_return mqtt_expected_void{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncMqttClient::cancel()
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(_impl);
|
||||||
|
spdlog::info("MQTT client cancel");
|
||||||
|
|
||||||
|
_impl->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
AsyncMqttClient::~AsyncMqttClient() = default;
|
AsyncMqttClient::~AsyncMqttClient() = default;
|
||||||
|
|
||||||
} // namespace ranczo
|
} // namespace ranczo
|
||||||
|
|||||||
@ -18,34 +18,42 @@ using executor = boost::asio::any_io_executor;
|
|||||||
|
|
||||||
namespace ranczo {
|
namespace ranczo {
|
||||||
|
|
||||||
|
class IAsyncMqttClient{
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class AsyncMqttClient {
|
class AsyncMqttClient {
|
||||||
public:
|
public:
|
||||||
struct CallbackData {
|
struct CallbackData {
|
||||||
/* topic */
|
/* topic */
|
||||||
std::string_view topic;
|
std::string_view topic;
|
||||||
|
|
||||||
/* response topic */
|
/* response topic */
|
||||||
std::optional< std::string_view > responseTopic;
|
std::optional< std::string_view > responseTopic;
|
||||||
|
|
||||||
/* value assosiated to request */
|
/* value assosiated to request */
|
||||||
const boost::json::value & value;
|
const boost::json::value & value;
|
||||||
};
|
};
|
||||||
|
|
||||||
using callback_t = std::function< awaitable_expected< void >(const boost::json::value & value) >;
|
using callback_t = std::function< awaitable_expected< void >(const boost::json::value & value) >;
|
||||||
|
|
||||||
struct AsyncMqttClientImpl;
|
struct AsyncMqttClientImpl;
|
||||||
std::unique_ptr< AsyncMqttClientImpl > _impl;
|
std::unique_ptr< AsyncMqttClientImpl > _impl;
|
||||||
|
|
||||||
AsyncMqttClient(const executor & executor);
|
AsyncMqttClient(const executor & executor);
|
||||||
~AsyncMqttClient();
|
~AsyncMqttClient();
|
||||||
|
|
||||||
/* subscribes to a topic, topic can contain wildcards */
|
/* subscribes to a topic, topic can contain wildcards */
|
||||||
awaitable_expected< void > subscribe(std::string_view topic, callback_t &&cb) noexcept;
|
awaitable_expected< void > subscribe(std::string_view topic, callback_t && cb) noexcept;
|
||||||
|
|
||||||
awaitable_expected< const boost::json::value & > request(std::string_view topic, const boost::json::value & value) noexcept;
|
awaitable_expected< const boost::json::value & > request(std::string_view topic, const boost::json::value & value) noexcept;
|
||||||
awaitable_expected< void > publish(std::string_view topic, const boost::json::value & value) noexcept;
|
awaitable_expected< void > publish(std::string_view topic, const boost::json::value & value) noexcept;
|
||||||
|
|
||||||
awaitable_expected< void > listen() const noexcept;
|
awaitable_expected< void > listen() const noexcept;
|
||||||
|
void cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ranczo
|
} // namespace ranczo
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
#include "heater_controller.hpp"
|
#include "heater_controller.hpp"
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include <boost/asio/experimental/parallel_group.hpp>
|
||||||
|
#include <boost/system/detail/errc.hpp>
|
||||||
|
#include <boost/system/errc.hpp>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <ranczo-io/utils/mqtt_client.hpp>
|
#include <ranczo-io/utils/mqtt_client.hpp>
|
||||||
@ -141,21 +144,36 @@ struct ResistiveFloorHeater::Impl : private boost::noncopyable {
|
|||||||
co_return heater_expected_void{};
|
co_return heater_expected_void{};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double to_double(const boost::json::value & v) const {
|
inline expected< double, boost::system::error_code > to_double(const boost::json::value & v) const {
|
||||||
if(v.is_double())
|
if(v.is_double())
|
||||||
return v.as_double();
|
return v.as_double();
|
||||||
else if(v.is_int64())
|
if(v.is_int64())
|
||||||
return static_cast< double >(v.as_int64());
|
return static_cast< double >(v.as_int64());
|
||||||
else if(v.is_uint64())
|
if(v.is_uint64())
|
||||||
return static_cast< double >(v.as_uint64());
|
return static_cast< double >(v.as_uint64());
|
||||||
throw std::runtime_error("Invalid type for double conversion");
|
if(v.is_string()) {
|
||||||
|
const auto & s = v.as_string();
|
||||||
|
double out{};
|
||||||
|
if(auto res = std::from_chars(s.data(), s.data() + s.size(), out); res.ec == std::errc{})
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
return unexpected{make_error_code(boost::system::errc::invalid_argument)};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline expected< double, boost::system::error_code > get_value(const boost::json::value & jv, std::string_view key) {
|
||||||
|
if(auto * obj = jv.if_object()) {
|
||||||
|
if(auto * pv = obj->if_contains(key)) {
|
||||||
|
return SYNC_TRY(to_double(*pv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unexpected{make_error_code(boost::system::errc::invalid_argument)};
|
||||||
}
|
}
|
||||||
|
|
||||||
awaitable_expected< void > subscribeToTemperatureUpdate() {
|
awaitable_expected< void > subscribeToTemperatureUpdate() {
|
||||||
auto topic = topic::temperature::floor(_room);
|
auto topic = topic::temperature::floor(_room);
|
||||||
|
|
||||||
auto cb = [=, this](const boost::json::value & object) -> awaitable_expected< void > {
|
auto cb = [=, this](const boost::json::value & object) -> awaitable_expected< void > {
|
||||||
temperature = to_double(object.at("value"));
|
temperature = TRY(get_value(object, "value"));
|
||||||
spdlog::trace("Heater temperature update {} for {}", temperature, _room);
|
spdlog::trace("Heater temperature update {} for {}", temperature, _room);
|
||||||
_measurements.push_back({std::chrono::system_clock::now(), temperature});
|
_measurements.push_back({std::chrono::system_clock::now(), temperature});
|
||||||
update = true;
|
update = true;
|
||||||
@ -163,42 +181,32 @@ struct ResistiveFloorHeater::Impl : private boost::noncopyable {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ASYNC_CHECK(subscribe(topic, std::move(cb)));
|
ASYNC_CHECK(subscribe(topic, std::move(cb)));
|
||||||
|
|
||||||
co_return heater_expected_void{};
|
co_return heater_expected_void{};
|
||||||
}
|
}
|
||||||
|
|
||||||
awaitable_expected< void > subscribeToCommandUpdate() {
|
awaitable_expected< void > subscribeToCommandUpdate() {
|
||||||
auto topic = topic::heating::command(_room);
|
auto topic = topic::heating::command(_room);
|
||||||
|
|
||||||
auto cb = [=, this](const boost::json::value & object) -> awaitable_expected< void > {
|
auto cb = [=, this](const boost::json::value & jv) -> awaitable_expected< void > {
|
||||||
targetTemperature = to_double(object.at("value"));
|
targetTemperature = TRY(get_value(jv, "value"));
|
||||||
spdlog::trace("Heater target temperature update {} for {}", targetTemperature, _room);
|
spdlog::trace("Heater target temperature update {} for {}", targetTemperature, _room);
|
||||||
update = true;
|
update = true;
|
||||||
co_return heater_expected_void{};
|
co_return heater_expected_void{};
|
||||||
};
|
};
|
||||||
|
|
||||||
ASYNC_CHECK(subscribe(topic, std::move(cb)));
|
ASYNC_CHECK(subscribe(topic, std::move(cb)));
|
||||||
|
|
||||||
co_return heater_expected_void{};
|
|
||||||
}
|
|
||||||
|
|
||||||
awaitable_expected< void > subscribeToTargetProfileUpdate() {
|
|
||||||
auto topic = std::format("home/{}/floor/heating/profile/set", _room);
|
|
||||||
|
|
||||||
auto cb = [=, this](const boost::json::value & object) -> awaitable_expected< void > { //
|
|
||||||
spdlog::warn("not implemented");
|
|
||||||
co_return heater_expected_void{};
|
|
||||||
};
|
|
||||||
|
|
||||||
ASYNC_CHECK(subscribe(topic, std::move(cb)));
|
|
||||||
|
|
||||||
co_return heater_expected_void{};
|
co_return heater_expected_void{};
|
||||||
}
|
}
|
||||||
|
|
||||||
awaitable_expected< void > start() {
|
awaitable_expected< void > start() {
|
||||||
|
// subscribe to a thermometer
|
||||||
ASYNC_CHECK_MSG(subscribeToTemperatureUpdate(), "subscribe to temp update failed");
|
ASYNC_CHECK_MSG(subscribeToTemperatureUpdate(), "subscribe to temp update failed");
|
||||||
|
|
||||||
|
// subscribe to a thermostat
|
||||||
ASYNC_CHECK_MSG(subscribeToCommandUpdate(), "subscribe to temp update failed");
|
ASYNC_CHECK_MSG(subscribeToCommandUpdate(), "subscribe to temp update failed");
|
||||||
ASYNC_CHECK_MSG(subscribeToTargetProfileUpdate(), "subscribe to profile update failed");
|
|
||||||
|
/// TODO subscribe to energy measurements
|
||||||
|
///
|
||||||
|
|
||||||
ASYNC_CHECK_MSG(_tickTimer.start(), "failed to start timer");
|
ASYNC_CHECK_MSG(_tickTimer.start(), "failed to start timer");
|
||||||
ASYNC_CHECK_MSG(_mqtt_client.listen(), "failed to listen");
|
ASYNC_CHECK_MSG(_mqtt_client.listen(), "failed to listen");
|
||||||
|
|||||||
@ -11,17 +11,17 @@ class any_io_executor;
|
|||||||
|
|
||||||
namespace ranczo {
|
namespace ranczo {
|
||||||
|
|
||||||
class IHeater {
|
class IHeaterController {
|
||||||
public:
|
public:
|
||||||
template < typename T >
|
template < typename T >
|
||||||
using awaitable = boost::asio::awaitable< T >;
|
using awaitable = boost::asio::awaitable< T >;
|
||||||
|
|
||||||
virtual ~IHeater() = default;
|
virtual ~IHeaterController() = default;
|
||||||
|
|
||||||
virtual awaitable_expected< void > start() noexcept = 0;
|
virtual awaitable_expected< void > start() noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ResistiveFloorHeater : public IHeater {
|
class ResistiveFloorHeater : public IHeaterController {
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr< Impl > _impl;
|
std::unique_ptr< Impl > _impl;
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ void signal_handler(int signum) {
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
spdlog::set_level(spdlog::level::trace);
|
spdlog::set_level(spdlog::level::trace);
|
||||||
std::vector< std::shared_ptr< ranczo::IHeater > > _heaters;
|
std::vector< std::shared_ptr< ranczo::IHeaterController > > _heaters;
|
||||||
|
|
||||||
boost::asio::io_context io_context;
|
boost::asio::io_context io_context;
|
||||||
g_io = &io_context;
|
g_io = &io_context;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user