Add energymeter service to services
This commit is contained in:
parent
12f4a8ce25
commit
dbe89b64ca
@ -142,6 +142,42 @@ namespace topic {
|
||||
}
|
||||
|
||||
} // namespace temperature
|
||||
|
||||
namespace utilities{
|
||||
// home/utilities/power/<meter>/<kind>/<channel>
|
||||
/*
|
||||
* <meter>: main, heating, housing
|
||||
* <kind>: active, reactive, apparent, voltage, current, frequency, pf
|
||||
* <channel>: total, L1, L2, L3 itp.
|
||||
*/
|
||||
inline std::pmr::string
|
||||
publishPowerReading(std::string_view meter, std::string_view kind, std::string_view channel, std::pmr::memory_resource * mr = std::pmr::get_default_resource()) {
|
||||
BOOST_ASSERT(mr);
|
||||
BOOST_ASSERT(meter.size());
|
||||
BOOST_ASSERT(kind.size());
|
||||
BOOST_ASSERT(channel == "total" || channel == "L1" || channel == "L2" || channel == "L3");
|
||||
using namespace std::string_view_literals;
|
||||
return make_topic(*mr, "home"sv, "utilities"sv, "power"sv, "electricity"sv, meter, kind, channel);
|
||||
}
|
||||
|
||||
// home/utilities/energy/<meter>/<kind>/<channel>
|
||||
/*
|
||||
* <meter>: main, heating, housing
|
||||
* <kind>: tatol_active_energy
|
||||
* <channel>: total, L1, L2, L3 itp.
|
||||
*/
|
||||
inline std::pmr::string
|
||||
publishEnergyReading(std::string_view meter, std::string_view kind, std::string_view channel, std::pmr::memory_resource * mr = std::pmr::get_default_resource()) {
|
||||
BOOST_ASSERT(mr);
|
||||
BOOST_ASSERT(meter.size());
|
||||
BOOST_ASSERT(kind.size());
|
||||
BOOST_ASSERT(channel == "total" || channel == "L1" || channel == "L2" || channel == "L3");
|
||||
using namespace std::string_view_literals;
|
||||
return make_topic(*mr, "home"sv, "utilities"sv, "energy"sv, "electricity"sv, meter, kind, channel);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace topic
|
||||
|
||||
} // namespace ranczo
|
||||
|
||||
@ -5,7 +5,8 @@ set(RANCZO_GROUP "ranczoio")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
add_subdirectory(floorheat_svc)
|
||||
add_subdirectory(temperature_svc)
|
||||
add_subdirectory(floorheat_svc)
|
||||
# add_subdirectory(energymeter_svc)
|
||||
add_subdirectory(output_svc)
|
||||
add_subdirectory(input_svc)
|
||||
|
||||
63
services/energymeter_svc/CMakeLists.txt
Normal file
63
services/energymeter_svc/CMakeLists.txt
Normal file
@ -0,0 +1,63 @@
|
||||
add_executable(ranczo-io_energymeter
|
||||
main.cpp
|
||||
|
||||
energymeter.hpp
|
||||
ORNO_517.hpp ORNO_517.cpp
|
||||
pstryk.hpp pstryk.cpp
|
||||
|
||||
ranczo-io_energymeter.service.in
|
||||
postinst
|
||||
prerm
|
||||
postrm
|
||||
)
|
||||
|
||||
target_link_libraries(ranczo-io_energymeter
|
||||
PUBLIC
|
||||
ranczo-io::utils
|
||||
fmt::fmt
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS ranczo-io_energymeter
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT energymeter
|
||||
)
|
||||
|
||||
# Opis komponentu energymeter
|
||||
set(CPACK_COMPONENT_ENERGYMETER_DISPLAY_NAME "Ranczo-IO Energy Reading Service" CACHE INTERNAL "package section")
|
||||
set(CPACK_COMPONENT_ENERGYMETER_DESCRIPTION "Serwis odczytujący aktualne zużycie energii." CACHE INTERNAL "package section")
|
||||
set(CPACK_COMPONENT_ENERGYMETER_REQUIRED ON CACHE INTERNAL "package section")
|
||||
|
||||
# Nazwa samego pakietu DEB dla komponentu
|
||||
set(CPACK_DEBIAN_ENERGYMETER_PACKAGE_NAME "ranczo-io-energymeter" CACHE INTERNAL "package name")
|
||||
set(CPACK_DEBIAN_ENERGYMETER_PACKAGE_SECTION "utils" CACHE INTERNAL "package section")
|
||||
set(CPACK_DEBIAN_ENERGYMETER_PACKAGE_MAINTAINER "b.wieczorek@dx.net.pl" CACHE INTERNAL "package maintainer")
|
||||
set(CPACK_DEBIAN_ENERGYMETER_PACKAGE_CONTROL_EXTRA
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/postinst"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/prerm"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/postrm"
|
||||
CACHE INTERNAL "package extra"
|
||||
)
|
||||
|
||||
set(CPACK_DEBIAN_ENERGYMETER_PACKAGE_DEPENDS "libsqlite3-0, systemd (>= 245)" CACHE INTERNAL "package depends")
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ranczo-io_energymeter.service.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ranczo-io_energymeter.service
|
||||
@ONLY
|
||||
)
|
||||
# Instalacja unita systemd
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/ranczo-io_energymeter.service
|
||||
DESTINATION ${SYSTEMD_UNIT_DIR}
|
||||
COMPONENT energymeter
|
||||
)
|
||||
|
||||
# tworzenie katalogów
|
||||
install(DIRECTORY
|
||||
DESTINATION /var/lib/ranczo-io/energymeter
|
||||
COMPONENT energymeter
|
||||
)
|
||||
0
services/energymeter_svc/ORNO_517.cpp
Normal file
0
services/energymeter_svc/ORNO_517.cpp
Normal file
0
services/energymeter_svc/ORNO_517.hpp
Normal file
0
services/energymeter_svc/ORNO_517.hpp
Normal file
0
services/energymeter_svc/energymeter.hpp
Normal file
0
services/energymeter_svc/energymeter.hpp
Normal file
413
services/energymeter_svc/main.cpp
Normal file
413
services/energymeter_svc/main.cpp
Normal file
@ -0,0 +1,413 @@
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <fmt/core.h>
|
||||
#include <modbus/modbus.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <config.hpp>
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// Pomocnicze: timestamp ISO
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
inline std::string to_iso_timestamp()
|
||||
{
|
||||
using clock = std::chrono::system_clock;
|
||||
auto now = clock::now();
|
||||
std::time_t t = clock::to_time_t(now);
|
||||
|
||||
std::tm tm{};
|
||||
#if defined(_WIN32)
|
||||
gmtime_s(&tm, &t);
|
||||
#else
|
||||
gmtime_r(&t, &tm);
|
||||
#endif
|
||||
|
||||
char buf[32];
|
||||
std::strftime(buf, sizeof(buf), "%FT%TZ", &tm);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// Rejestry energomierza
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
struct Register {
|
||||
const char* measurement_name;
|
||||
const char* unit;
|
||||
const char* register_name;
|
||||
std::uint16_t offset; // offset w słowach 16-bit od _baseAddress
|
||||
|
||||
float (*read)(const std::uint16_t*);
|
||||
float (*total)(float lhs, float rhs);
|
||||
|
||||
float do_read(const std::uint16_t* data) const {
|
||||
return read(data + offset);
|
||||
}
|
||||
};
|
||||
|
||||
inline float read_float(const std::uint16_t* data) {
|
||||
return modbus_get_float_abcd(data);
|
||||
}
|
||||
|
||||
inline float from_kilo(const std::uint16_t* data) {
|
||||
return read_float(data) * 1000.0f;
|
||||
}
|
||||
|
||||
inline float add(float lhs, float rhs) {
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
inline float avg(float lhs, float rhs) {
|
||||
return (lhs + rhs) * 0.5f;
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// Formatowanie topiców / payloadów MQTT
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
inline std::size_t topic_to(
|
||||
char* buffer,
|
||||
const Register& reg,
|
||||
std::string_view type,
|
||||
std::string_view event)
|
||||
{
|
||||
const auto end = fmt::format_to(
|
||||
buffer,
|
||||
"home/utilities/electricity/{}/{}/{}/{}",
|
||||
type,
|
||||
event,
|
||||
reg.measurement_name,
|
||||
reg.register_name);
|
||||
|
||||
*end = '\0';
|
||||
auto len = static_cast<std::size_t>(std::distance(buffer, end) + 1);
|
||||
spdlog::debug("MQTT topic: {}", buffer);
|
||||
return len;
|
||||
}
|
||||
|
||||
inline std::size_t payload_to(
|
||||
std::uint8_t* buffer,
|
||||
const Register& reg,
|
||||
float value,
|
||||
bool update,
|
||||
std::string_view ts)
|
||||
{
|
||||
const auto end = fmt::format_to(
|
||||
buffer,
|
||||
R"json({{"value":{},"unit":"{}","source":"energymeter_service","update":{},"timestamp":"{}"}})json",
|
||||
value,
|
||||
reg.unit,
|
||||
update ? "true" : "false",
|
||||
ts);
|
||||
|
||||
auto len = static_cast<std::size_t>(std::distance(buffer, end));
|
||||
return len;
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// Bazowa klasa odczytu energomierza (async, z boost::asio)
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class EnergymeterReadings {
|
||||
public:
|
||||
EnergymeterReadings(
|
||||
boost::asio::any_io_executor ex,
|
||||
MqttClient& mqtt,
|
||||
ModbusClient& modbus,
|
||||
std::span<const Register> regs,
|
||||
const char* name,
|
||||
std::uint16_t baseAddress)
|
||||
: _ex(ex)
|
||||
, _mqtt(mqtt)
|
||||
, _modbus(modbus)
|
||||
, _registers(regs)
|
||||
, _name(name)
|
||||
, _baseAddress(baseAddress)
|
||||
, _housingUsage(regs.size(), 0.0f)
|
||||
, _heatingUsage(regs.size(), 0.0f)
|
||||
{}
|
||||
|
||||
// slave 1: "housing", slave 2: "heating"
|
||||
awaitable_expected<void> publish()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const std::size_t LiveRegistersSize = _registers.size() * 2;
|
||||
|
||||
std::vector<std::uint16_t> housingRaw(LiveRegistersSize);
|
||||
std::vector<std::uint16_t> heatingRaw(LiveRegistersSize);
|
||||
|
||||
const auto housingTimepoint = to_iso_timestamp();
|
||||
auto r1 = co_await _modbus.read_holding_registers(
|
||||
1, _baseAddress, static_cast<std::uint16_t>(LiveRegistersSize), housingRaw.data());
|
||||
if (!r1) {
|
||||
spdlog::error("Modbus read (housing) failed: {}", r1.error().message());
|
||||
co_return unexpected(r1.error());
|
||||
}
|
||||
|
||||
const auto heatingTimepoint = to_iso_timestamp();
|
||||
auto r2 = co_await _modbus.read_holding_registers(
|
||||
2, _baseAddress, static_cast<std::uint16_t>(LiveRegistersSize), heatingRaw.data());
|
||||
if (!r2) {
|
||||
spdlog::error("Modbus read (heating) failed: {}", r2.error().message());
|
||||
co_return unexpected(r2.error());
|
||||
}
|
||||
|
||||
const auto totalTimepoint = to_iso_timestamp();
|
||||
|
||||
auto housingPrev = _housingUsage.begin();
|
||||
auto heatingPrev = _heatingUsage.begin();
|
||||
|
||||
std::uint8_t payloadBuffer[256];
|
||||
char topicBuffer[256];
|
||||
|
||||
for (const auto& reg : _registers) {
|
||||
const float housingValue = reg.do_read(housingRaw.data());
|
||||
const float heatingValue = reg.do_read(heatingRaw.data());
|
||||
const float totalValue = reg.total(heatingValue, housingValue);
|
||||
|
||||
const bool housingUpdated = housingValue != *housingPrev;
|
||||
const bool heatingUpdated = heatingValue != *heatingPrev;
|
||||
const bool totalUpdated = housingUpdated || heatingUpdated;
|
||||
|
||||
*housingPrev = housingValue;
|
||||
*heatingPrev = heatingValue;
|
||||
++housingPrev;
|
||||
++heatingPrev;
|
||||
|
||||
auto doPublish = [&](float value,
|
||||
bool updated,
|
||||
std::string_view ts,
|
||||
const char* type) -> awaitable_expected<void>
|
||||
{
|
||||
const std::size_t topicLen = topic_to(topicBuffer, reg, _name, type);
|
||||
const std::size_t payloadLen = payload_to(
|
||||
payloadBuffer, reg, value, updated, ts);
|
||||
|
||||
std::string_view topic(topicBuffer, topicLen - 1); // bez '\0'
|
||||
std::string_view payload(
|
||||
reinterpret_cast<const char*>(payloadBuffer),
|
||||
payloadLen);
|
||||
|
||||
auto res = co_await _mqtt.publish(topic, payload, 0);
|
||||
if (!res) {
|
||||
spdlog::warn("MQTT publish failed on topic {}: {}",
|
||||
topic, res.error().message());
|
||||
co_return unexpected(res.error());
|
||||
}
|
||||
|
||||
co_return expected<void>{};
|
||||
};
|
||||
|
||||
// housing
|
||||
(void) co_await doPublish(housingValue, housingUpdated, housingTimepoint, "housing");
|
||||
// heating
|
||||
(void) co_await doPublish(heatingValue, heatingUpdated, heatingTimepoint, "heating");
|
||||
// total
|
||||
(void) co_await doPublish(totalValue, totalUpdated, totalTimepoint, "ALL");
|
||||
}
|
||||
|
||||
co_return expected<void>{};
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::any_io_executor _ex;
|
||||
MqttClient& _mqtt;
|
||||
ModbusClient& _modbus;
|
||||
|
||||
std::span<const Register> _registers;
|
||||
const char* _name;
|
||||
const std::uint16_t _baseAddress;
|
||||
|
||||
std::vector<float> _housingUsage;
|
||||
std::vector<float> _heatingUsage;
|
||||
};
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// Konkrety: odczyty "live"
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class EnergymeterLiveReading : public EnergymeterReadings {
|
||||
private:
|
||||
static constexpr std::uint16_t _baseAddressLive = 0x000E;
|
||||
|
||||
static constexpr Register liveRegisters_[] = {
|
||||
{"Voltage", "V", "L1", std::uint16_t{0x000E} - _baseAddressLive, read_float, avg},
|
||||
{"Voltage", "V", "L2", std::uint16_t{0x0010} - _baseAddressLive, read_float, avg},
|
||||
{"Voltage", "V", "L3", std::uint16_t{0x0012} - _baseAddressLive, read_float, avg},
|
||||
{"Frequency", "Hz", "Grid", std::uint16_t{0x0014} - _baseAddressLive, read_float, avg},
|
||||
{"Current", "A", "L1", std::uint16_t{0x0016} - _baseAddressLive, read_float, add},
|
||||
{"Current", "A", "L2", std::uint16_t{0x0018} - _baseAddressLive, read_float, add},
|
||||
{"Current", "A", "L3", std::uint16_t{0x001A} - _baseAddressLive, read_float, add},
|
||||
{"ActivePower", "W", "Total", std::uint16_t{0x001C} - _baseAddressLive, from_kilo, add},
|
||||
{"ActivePower", "W", "L1", std::uint16_t{0x001E} - _baseAddressLive, from_kilo, add},
|
||||
{"ActivePower", "W", "L2", std::uint16_t{0x0020} - _baseAddressLive, from_kilo, add},
|
||||
{"ActivePower", "W", "L3", std::uint16_t{0x0022} - _baseAddressLive, from_kilo, add},
|
||||
{"ReactivePower", "Var", "Total", std::uint16_t{0x0024} - _baseAddressLive, from_kilo, add},
|
||||
{"ReactivePower", "Var", "L1", std::uint16_t{0x0026} - _baseAddressLive, from_kilo, add},
|
||||
{"ReactivePower", "Var", "L2", std::uint16_t{0x0028} - _baseAddressLive, from_kilo, add},
|
||||
{"ReactivePower", "Var", "L3", std::uint16_t{0x002A} - _baseAddressLive, from_kilo, add},
|
||||
{"ApparentPower", "VA", "Total", std::uint16_t{0x002C} - _baseAddressLive, from_kilo, add},
|
||||
{"ApparentPower", "VA", "L1", std::uint16_t{0x002E} - _baseAddressLive, from_kilo, add},
|
||||
{"ApparentPower", "VA", "L2", std::uint16_t{0x0030} - _baseAddressLive, from_kilo, add},
|
||||
{"ApparentPower", "VA", "L3", std::uint16_t{0x0032} - _baseAddressLive, from_kilo, add},
|
||||
{"PowerFactor", "", "Total", std::uint16_t{0x0034} - _baseAddressLive, read_float, avg},
|
||||
{"PowerFactor", "", "L1", std::uint16_t{0x0036} - _baseAddressLive, read_float, avg},
|
||||
{"PowerFactor", "", "L2", std::uint16_t{0x0038} - _baseAddressLive, read_float, avg},
|
||||
{"PowerFactor", "", "L3", std::uint16_t{0x003A} - _baseAddressLive, read_float, avg},
|
||||
};
|
||||
|
||||
public:
|
||||
EnergymeterLiveReading(
|
||||
boost::asio::any_io_executor ex,
|
||||
MqttClient& mqtt,
|
||||
ModbusClient& modbus)
|
||||
: EnergymeterReadings(
|
||||
ex,
|
||||
mqtt,
|
||||
modbus,
|
||||
std::span<const Register>(liveRegisters_, std::size(liveRegisters_)),
|
||||
"current",
|
||||
_baseAddressLive)
|
||||
{}
|
||||
};
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// Konkrety: odczyty "total"
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class EnergymeterTotalReading : public EnergymeterReadings {
|
||||
private:
|
||||
static constexpr std::uint16_t _baseAddressTotal = 0x0100;
|
||||
|
||||
static constexpr Register totalRegisters_[] = {
|
||||
{"ActiveEnergy", "Wh", "Total", std::uint16_t{0x0100} - _baseAddressTotal, from_kilo, add},
|
||||
{"ActiveEnergy", "Wh", "L1", std::uint16_t{0x0102} - _baseAddressTotal, from_kilo, add},
|
||||
{"ActiveEnergy", "Wh", "L2", std::uint16_t{0x0104} - _baseAddressTotal, from_kilo, add},
|
||||
{"ActiveEnergy", "Wh", "L3", std::uint16_t{0x0106} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardActiveEnergy", "Wh", "Total", std::uint16_t{0x0108} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardActiveEnergy", "Wh", "L1", std::uint16_t{0x010A} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardActiveEnergy", "Wh", "L2", std::uint16_t{0x010C} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardActiveEnergy", "Wh", "L3", std::uint16_t{0x010E} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseActiveEnergy", "Wh", "Total", std::uint16_t{0x0110} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseActiveEnergy", "Wh", "L1", std::uint16_t{0x0112} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseActiveEnergy", "Wh", "L2", std::uint16_t{0x0114} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseActiveEnergy", "Wh", "L3", std::uint16_t{0x0116} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReactiveEnergy", "Varh", "Total", std::uint16_t{0x0118} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReactiveEnergy", "Varh", "L1", std::uint16_t{0x011A} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReactiveEnergy", "Varh", "L2", std::uint16_t{0x011C} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReactiveEnergy", "Varh", "L3", std::uint16_t{0x011E} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardReactiveEnergy", "Varh", "Total", std::uint16_t{0x0120} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardReactiveEnergy", "Varh", "L1", std::uint16_t{0x0122} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardReactiveEnergy", "Varh", "L2", std::uint16_t{0x0124} - _baseAddressTotal, from_kilo, add},
|
||||
{"ForwardReactiveEnergy", "Varh", "L3", std::uint16_t{0x0126} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseReactiveEnergy", "Varh", "Total", std::uint16_t{0x0128} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseReactiveEnergy", "Varh", "L1", std::uint16_t{0x012A} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseReactiveEnergy", "Varh", "L2", std::uint16_t{0x012C} - _baseAddressTotal, from_kilo, add},
|
||||
{"ReverseReactiveEnergy", "Varh", "L3", std::uint16_t{0x012E} - _baseAddressTotal, from_kilo, add},
|
||||
};
|
||||
|
||||
public:
|
||||
EnergymeterTotalReading(
|
||||
boost::asio::any_io_executor ex,
|
||||
MqttClient& mqtt,
|
||||
ModbusClient& modbus)
|
||||
: EnergymeterReadings(
|
||||
ex,
|
||||
mqtt,
|
||||
modbus,
|
||||
std::span<const Register>(totalRegisters_, std::size(totalRegisters_)),
|
||||
"total",
|
||||
_baseAddressTotal)
|
||||
{}
|
||||
};
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// Serwis: dwa taski asynchroniczne (live + total) na timerach
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class EnergymeterService {
|
||||
public:
|
||||
EnergymeterService(
|
||||
boost::asio::any_io_executor ex,
|
||||
MqttClient& mqtt,
|
||||
ModbusClient& modbus)
|
||||
: _ex(ex)
|
||||
, _live(ex, mqtt, modbus)
|
||||
, _total(ex, mqtt, modbus)
|
||||
{}
|
||||
|
||||
// uruchamia oba taski; wołaj np. przez co_spawn(service.run(), detached)
|
||||
awaitable<void> run()
|
||||
{
|
||||
using boost::asio::co_spawn;
|
||||
using boost::asio::detached;
|
||||
|
||||
co_spawn(_ex,
|
||||
[this]() -> awaitable<void> {
|
||||
co_await live_loop();
|
||||
},
|
||||
detached);
|
||||
|
||||
co_spawn(_ex,
|
||||
[this]() -> awaitable<void> {
|
||||
co_await total_loop();
|
||||
},
|
||||
detached);
|
||||
|
||||
co_return;
|
||||
}
|
||||
|
||||
private:
|
||||
awaitable<void> live_loop()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
boost::asio::steady_timer timer(_ex);
|
||||
|
||||
while (true) {
|
||||
timer.expires_after(3s);
|
||||
boost::system::error_code ec;
|
||||
co_await timer.async_wait(boost::asio::redirect_error(boost::asio::use_awaitable, ec));
|
||||
if (ec == boost::asio::error::operation_aborted)
|
||||
co_return;
|
||||
|
||||
auto res = co_await _live.publish();
|
||||
if (!res) {
|
||||
spdlog::warn("Live reading publish error: {}", res.error().message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
awaitable<void> total_loop()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
boost::asio::steady_timer timer(_ex);
|
||||
|
||||
while (true) {
|
||||
timer.expires_after(60s);
|
||||
boost::system::error_code ec;
|
||||
co_await timer.async_wait(boost::asio::redirect_error(boost::asio::use_awaitable, ec));
|
||||
if (ec == boost::asio::error::operation_aborted)
|
||||
co_return;
|
||||
|
||||
auto res = co_await _total.publish();
|
||||
if (!res) {
|
||||
spdlog::warn("Total reading publish error: {}", res.error().message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::any_io_executor _ex;
|
||||
EnergymeterLiveReading _live;
|
||||
EnergymeterTotalReading _total;
|
||||
};
|
||||
14
services/energymeter_svc/postinst
Normal file
14
services/energymeter_svc/postinst
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
# Jeżeli tworzysz usera, możesz to zrobić tutaj (albo w osobnym pakiecie)
|
||||
# adduser --system --group --no-create-home ranczoio || true
|
||||
|
||||
systemctl daemon-reload || true
|
||||
systemctl enable --now ranczo-io_energymeter.service || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
14
services/energymeter_svc/postrm
Normal file
14
services/energymeter_svc/postrm
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
systemctl daemon-reload || true
|
||||
;;
|
||||
purge)
|
||||
systemctl disable ranczo-io_energymeter.service || true
|
||||
systemctl daemon-reload || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
10
services/energymeter_svc/prerm
Normal file
10
services/energymeter_svc/prerm
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
remove|upgrade|deconfigure)
|
||||
systemctl stop ranczo-io_energymeter.service || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
0
services/energymeter_svc/pstryk.cpp
Normal file
0
services/energymeter_svc/pstryk.cpp
Normal file
0
services/energymeter_svc/pstryk.hpp
Normal file
0
services/energymeter_svc/pstryk.hpp
Normal file
20
services/energymeter_svc/ranczo-io_energymeter.service.in
Normal file
20
services/energymeter_svc/ranczo-io_energymeter.service.in
Normal file
@ -0,0 +1,20 @@
|
||||
[Unit]
|
||||
Description=Ranczo-IO Energy Meters read service
|
||||
After=network.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=@RANCZO_USER@
|
||||
Group=@RANCZO_GROUP@
|
||||
ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/ranczo-io_energymeter
|
||||
WorkingDirectory=/var/lib/ranczo-io/energymeter
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# /run/ranczo-io-energymeter będzie robione automatycznie
|
||||
RuntimeDirectory=ranczo-io_energymeter
|
||||
RuntimeDirectoryMode=0755
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Loading…
Reference in New Issue
Block a user