fixes in thermometer service
This commit is contained in:
parent
b4bbdb3ed3
commit
a1bfc4c218
@ -1,4 +1,7 @@
|
||||
#include "relay.hpp"
|
||||
#include "config.hpp"
|
||||
#include "ranczo-io/utils/mqtt_topic_builder.hpp"
|
||||
#include <chrono>
|
||||
namespace ranczo {
|
||||
ModbusRelay::ModbusRelay(boost::asio::any_io_executor ex, std::shared_ptr< ModbusDevice > dev, int channel, uint16_t base_coil_addr)
|
||||
: _executor{ex},
|
||||
@ -11,23 +14,25 @@ ModbusRelay::ModbusRelay(boost::asio::any_io_executor ex, std::shared_ptr< Modbu
|
||||
_channel{channel},
|
||||
_coil_addr{static_cast< std::uint16_t >(base_coil_addr + channel)} {
|
||||
BOOST_ASSERT(_dev);
|
||||
}
|
||||
|
||||
boost::asio::co_spawn(
|
||||
_executor,
|
||||
[=, this]() -> awaitable_expected< void > {
|
||||
auto state = co_await _dev->async_read_holding_register(channel);
|
||||
if(state.has_value()) {
|
||||
_log.info("status at create: {}", state.value());
|
||||
_state = state.value() == 1 ? State::On : State::Off;
|
||||
} else {
|
||||
_log.info("failed to read status {}", state.error().value());
|
||||
}
|
||||
co_return _void{};
|
||||
},
|
||||
boost::asio::detached);
|
||||
std::chrono::nanoseconds ModbusRelay::timeSinceLastRead() const
|
||||
{
|
||||
BOOST_ASSERT(_lastRead.has_value());
|
||||
return std::chrono::system_clock::now() - _lastRead.value();
|
||||
}
|
||||
|
||||
awaitable_expected<Relay::State> ModbusRelay::state() const noexcept {
|
||||
BOOST_ASSERT(_dev);
|
||||
_log.debug("STATUS (coil={})", _coil_addr);
|
||||
|
||||
if(not _lastRead || timeSinceLastRead() > std::chrono::seconds{1}) {
|
||||
auto reg_value = ASYNC_TRY(_dev->async_read_holding_register(_coil_addr));
|
||||
_state = reg_value == 1 ? State::On : State::Off;
|
||||
_lastRead = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
BOOST_ASSERT(_lastRead.has_value());
|
||||
co_return _state;
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <boost/regex/config.hpp>
|
||||
#include <boost/smart_ptr/shared_ptr.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <config.hpp>
|
||||
|
||||
#include <ranczo-io/utils/logger.hpp>
|
||||
@ -26,23 +27,25 @@ class Relay {
|
||||
};
|
||||
|
||||
class ModbusRelay : public Relay {
|
||||
boost::asio::any_io_executor _executor;
|
||||
ModuleLogger _log;
|
||||
std::shared_ptr< ranczo::ModbusDevice > _dev;
|
||||
int _channel;
|
||||
Relay::State _state;
|
||||
std::uint16_t _coil_addr; // adres cewki (0-based)
|
||||
/// cached
|
||||
mutable Relay::State _state;
|
||||
mutable std::optional< std::chrono::system_clock::time_point > _lastRead;
|
||||
|
||||
public:
|
||||
// Prost y mapping: coil_addr = base_addr + channel
|
||||
ModbusRelay(boost::asio::any_io_executor ex,
|
||||
std::shared_ptr< ranczo::ModbusDevice > dev,
|
||||
int channel,
|
||||
std::uint16_t base_coil_addr = 0);
|
||||
std::chrono::nanoseconds timeSinceLastRead() const;
|
||||
|
||||
awaitable_expected< Relay::State > state() const noexcept override;
|
||||
awaitable_expected< void > on() noexcept override;
|
||||
awaitable_expected< void > off() noexcept override;
|
||||
boost::asio::any_io_executor _executor;
|
||||
ModuleLogger _log;
|
||||
std::shared_ptr< ranczo::ModbusDevice > _dev;
|
||||
int _channel;
|
||||
std::uint16_t _coil_addr; // adres cewki (0-based)
|
||||
|
||||
public:
|
||||
// Prost y mapping: coil_addr = base_addr + channel
|
||||
ModbusRelay(boost::asio::any_io_executor ex, std::shared_ptr< ranczo::ModbusDevice > dev, int channel, std::uint16_t base_coil_addr = 0);
|
||||
|
||||
awaitable_expected< Relay::State > state() const noexcept override;
|
||||
awaitable_expected< void > on() noexcept override;
|
||||
awaitable_expected< void > off() noexcept override;
|
||||
};
|
||||
|
||||
} // namespace ranczo
|
||||
|
||||
@ -31,26 +31,42 @@ using namespace std::string_view_literals;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::vector< std::tuple< std::string_view, std::string_view, Type > > mapping = {
|
||||
{"28-012113ed0fef", "beadroom_zone1", Type::floor}, // home/beadroom/sensor/floor/1/temperature
|
||||
{"28-0121142e6c88", "beadroom_zone2", Type::floor}, // home/beadroom/sensor/floor/2/temperature
|
||||
{"28-0119513810ff", "maciek_room", Type::floor},
|
||||
{"28-0119517084ff", "office", Type::floor},
|
||||
{"28-0119516f39ff", "bathroom_guest", Type::floor},
|
||||
{"28-012113f4d852", "bathroom_private", Type::floor},
|
||||
{"28-0119517084ff", "office", Type::floor}, // home/office/sensor/floor/1/temperature
|
||||
{"28-0119514282ff", "aska_room", Type::floor},
|
||||
{"28-01195141aeff", "playroom", Type::floor},
|
||||
{"28-0119516f39ff", "bathroom_guest", Type::floor},
|
||||
{"28-011951477dff", "living_room", Type::air},
|
||||
{"28-011951477dff", "living_room", Type::air}, // home/beadroom/sensor/air/1/temperature
|
||||
{"28-0b2398fcaed3", "living_room_zone1", Type::floor},
|
||||
{"28-0b23990cdefb", "living_room_zone2", Type::floor},
|
||||
{"28-0121142e6c88", "beadroom_zone2", Type::floor},
|
||||
{"28-012113f4d852", "bathroom_private", Type::floor},
|
||||
{"28-012113f7d95d", "wardrobe", Type::floor},
|
||||
{"28-0118790a57ff", "corridor", Type::floor},
|
||||
};
|
||||
|
||||
std::vector< std::tuple< std::string_view, std::string_view, Type > > mappingOld = {
|
||||
{"28-012113ed0fef", "beadroom_zone1", Type::floor},
|
||||
{"28-0121142e6c88", "beadroom_zone2", Type::floor},
|
||||
{"28-0119513810ff", "maciek_room", Type::floor},
|
||||
{"28-0119516f39ff", "bathroom_1", Type::floor}, // bathroom_guast
|
||||
{"28-012113f4d852", "bathroom_2", Type::floor}, // bathroom_private
|
||||
{"28-0119517084ff", "office", Type::floor},
|
||||
{"28-0119514282ff", "askaRoom", Type::floor},
|
||||
{"28-01195141aeff", "playroom", Type::floor},
|
||||
{"28-011951477dff", "livingroom", Type::air}, // living_room air ?? gdzie jest ten termometr???????
|
||||
{"28-0b2398fcaed3", "livingroom_zone1", Type::floor},
|
||||
{"28-0b23990cdefb", "livingroom_zone2", Type::floor},
|
||||
{"28-012113f7d95d", "wardrobe", Type::floor},
|
||||
{"28-0118790a57ff", "corridor", Type::floor},
|
||||
};
|
||||
|
||||
int main() {
|
||||
using namespace ranczo;
|
||||
boost::asio::io_context io_context;
|
||||
|
||||
memory_resource::HeapPoolResource _pool{1024*1024*1024}; // 1M
|
||||
|
||||
memory_resource::HeapPoolResource _pool{1024 * 1024 * 1024}; // 1M
|
||||
std::pmr::set_default_resource(&_pool);
|
||||
|
||||
// Register signal handler
|
||||
@ -81,11 +97,11 @@ int main() {
|
||||
|
||||
// create a modbus client to use with all internal objects
|
||||
AsyncMqttClient mqttClient{io_executor};
|
||||
|
||||
MqttMeasurementPublisher pub{mqttClient, std::span{ mapping}, &_pool};
|
||||
|
||||
const fs::path w1_root = "/home/bartoszek/mnt/w1remote/devices";
|
||||
|
||||
|
||||
MqttMeasurementPublisher pub{mqttClient, std::span{mapping}, std::span{mappingOld}, &_pool};
|
||||
|
||||
// const fs::path w1_root = "/home/bartoszek/mnt/w1remote/devices";
|
||||
const fs::path w1_root = "/sys/devices";
|
||||
std::pmr::vector< fs::path > bus_paths{&_pool};
|
||||
|
||||
spdlog::info("Iterating paths");
|
||||
@ -100,7 +116,7 @@ int main() {
|
||||
spdlog::error("Nie znaleziono żadnych magistral 1-Wire");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
std::pmr::vector< ranczo::OneWireBus > buses{&_pool};
|
||||
buses.reserve(bus_paths.size());
|
||||
|
||||
@ -124,7 +140,7 @@ int main() {
|
||||
|
||||
work_guard.reset();
|
||||
io_context.stop(); // ok na tym samym wątku
|
||||
spdlog::info("Graceful shutdown posted");
|
||||
spdlog::info("Graceful shutdown posted");
|
||||
});
|
||||
|
||||
stop_promise.set_value(); // pobudzi main
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#include "measurement_publisher.hpp"
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <memory_resource>
|
||||
#include <ranczo-io/utils/json_helpers.hpp>
|
||||
#include <ranczo-io/utils/memory_resource.hpp>
|
||||
#include <ranczo-io/utils/mqtt_client.hpp>
|
||||
@ -20,8 +22,64 @@
|
||||
// }
|
||||
|
||||
namespace ranczo {
|
||||
static std::optional< int > extract_zone_number(std::string_view sv) {
|
||||
// znajdź początek ciągu cyfr idącego od końca
|
||||
size_t end = sv.size();
|
||||
if(end == 0)
|
||||
return std::nullopt;
|
||||
|
||||
size_t start = end;
|
||||
while(start > 0 && std::isdigit(static_cast< unsigned char >(sv[start - 1]))) {
|
||||
--start;
|
||||
}
|
||||
|
||||
if(start == end) {
|
||||
// brak cyfr
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
for(size_t i = start; i < end; ++i) {
|
||||
value = value * 10 + (sv[i] - '0');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::pmr::string remove_zone_suffix(std::string_view sv, std::pmr::memory_resource * mr) {
|
||||
// spodziewamy się końcówki: _zoneN
|
||||
constexpr std::string_view suffix = "_zone";
|
||||
|
||||
if(sv.size() <= suffix.size())
|
||||
return std::pmr::string(sv, mr);
|
||||
|
||||
// sprawdź czy kończy się na "_zone"
|
||||
if(!sv.ends_with(suffix) && (sv.size() < suffix.size() + 1))
|
||||
return std::pmr::string(sv, mr);
|
||||
|
||||
// sprawdź czy rzeczywiście występuje _zone
|
||||
size_t pos = sv.rfind(suffix);
|
||||
if(pos == std::string_view::npos)
|
||||
return std::pmr::string(sv, mr);
|
||||
|
||||
// teraz po _zone muszą być cyfry
|
||||
size_t num_start = pos + suffix.size();
|
||||
if(num_start >= sv.size())
|
||||
return std::pmr::string(sv, mr); // nie ma N
|
||||
|
||||
// sprawdź czy końcówka to wszystkie cyfry
|
||||
for(size_t i = num_start; i < sv.size(); ++i) {
|
||||
if(!std::isdigit(static_cast< unsigned char >(sv[i])))
|
||||
return std::pmr::string(sv, mr);
|
||||
}
|
||||
|
||||
// OK → usuń "_zoneN"
|
||||
return std::pmr::string(sv.substr(0, pos), mr);
|
||||
}
|
||||
|
||||
MqttMeasurementPublisher::MqttMeasurementPublisher(AsyncMqttClient & client,
|
||||
std::span< std::tuple< std::string_view, std::string_view, Type > > ds18b20mapping,
|
||||
std::span< std::tuple< std::string_view, std::string_view, Type > > ds18b20mappingOld,
|
||||
std::pmr::memory_resource * mr,
|
||||
std::source_location sl)
|
||||
: _client(client), _mr(4096, mr, sl) {
|
||||
@ -30,10 +88,22 @@ MqttMeasurementPublisher::MqttMeasurementPublisher(AsyncMqttClient & client,
|
||||
const auto & room_name_view = std::get< 1 >(entry); // nazwa pomieszczenia
|
||||
const auto & sensor_type_enum = std::get< 2 >(entry); // Type
|
||||
|
||||
std::pmr::string sensor_id{sensor_id_view, &_mr};
|
||||
std::pmr::string room_name{remove_zone_suffix(room_name_view, &_mr), &_mr};
|
||||
|
||||
_mapping.emplace(
|
||||
std::move(sensor_id), std::make_tuple(std::move(room_name), extract_zone_number(room_name_view).value_or(1), sensor_type_enum));
|
||||
}
|
||||
|
||||
for(const auto & entry : ds18b20mappingOld) {
|
||||
const auto & sensor_id_view = std::get< 0 >(entry); // id DS-a
|
||||
const auto & room_name_view = std::get< 1 >(entry); // nazwa pomieszczenia
|
||||
const auto & sensor_type_enum = std::get< 2 >(entry); // Type
|
||||
|
||||
std::pmr::string sensor_id{sensor_id_view, &_mr};
|
||||
std::pmr::string room_name{room_name_view, &_mr};
|
||||
|
||||
_mapping.emplace(std::move(sensor_id), std::make_pair(std::move(room_name), sensor_type_enum));
|
||||
_mapping_old.emplace(std::move(sensor_id), std::make_pair(std::move(room_name), sensor_type_enum));
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,15 +111,16 @@ awaitable_expected< void >
|
||||
MqttMeasurementPublisher::publish_measurement(std::string_view sensor_type, std::string_view sensor_id, double measurement) noexcept {
|
||||
using namespace memory_resource::literals;
|
||||
memory_resource::MonotonicHeapResource mr{1_kB, &_mr};
|
||||
|
||||
if(not _mapping.contains(sensor_id)){
|
||||
|
||||
if(not _mapping.contains(sensor_id)) {
|
||||
spdlog::warn("Nie znaleziono {}", sensor_id);
|
||||
co_return _void{};
|
||||
}
|
||||
|
||||
const auto &[room, type] = _mapping.find(sensor_id)->second;
|
||||
|
||||
auto topic = ranczo::topic::temperature::publishFloor(room, 1, &mr);
|
||||
|
||||
const auto & [room, zone, type] = _mapping.find(sensor_id)->second;
|
||||
|
||||
auto topic = type == Type::floor ? ranczo::topic::temperature::publishFloor(room, zone, &mr) :
|
||||
ranczo::topic::temperature::publishAir(room, zone, &mr);
|
||||
|
||||
using namespace std::chrono;
|
||||
const auto now = date::floor< std::chrono::milliseconds >(system_clock::now());
|
||||
@ -66,6 +137,13 @@ MqttMeasurementPublisher::publish_measurement(std::string_view sensor_type, std:
|
||||
|
||||
boost::json::value payload{std::move(obj)};
|
||||
|
||||
if(_mapping_old.contains(sensor_id)) {
|
||||
const auto & [room, type] = _mapping_old.find(sensor_id)->second;
|
||||
// home/bathroom_2/floor/temperature
|
||||
auto oldTopic = fmt::format("home/{}/{}/temperature", room, type == Type::floor ? "floor" : "air");
|
||||
co_await _client.publish(oldTopic, payload);
|
||||
}
|
||||
|
||||
// 4. Log (topic + JSON)
|
||||
spdlog::debug("Publishing MQTT measurement topic='{}'", topic);
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ class MqttMeasurementPublisher {
|
||||
|
||||
MqttMeasurementPublisher(AsyncMqttClient & client,
|
||||
std::span< std::tuple< std::string_view, std::string_view, Type > > ds18b20mapping,
|
||||
std::span< std::tuple< std::string_view, std::string_view, Type > > ds18b20mappingOld,
|
||||
std::pmr::memory_resource * mr = std::pmr::get_default_resource(),
|
||||
std::source_location sl = std::source_location::current()
|
||||
);
|
||||
@ -33,6 +34,7 @@ class MqttMeasurementPublisher {
|
||||
AsyncMqttClient & _client;
|
||||
memory_resource::HeapPoolResource _mr;
|
||||
|
||||
std::pmr::map< std::pmr::string, std::pair< std::pmr::string, Type >, std::less<> > _mapping{&_mr};
|
||||
std::pmr::map< std::pmr::string, std::tuple< std::pmr::string, int, Type >, std::less<> > _mapping{&_mr};
|
||||
std::pmr::map< std::pmr::string, std::pair< std::pmr::string, Type >, std::less<> > _mapping_old{&_mr};
|
||||
};
|
||||
} // namespace ranczo
|
||||
|
||||
Loading…
Reference in New Issue
Block a user