From dbe89b64ca0557f2e32bef33c6c9a9b9358ee9d2 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 28 Nov 2025 09:18:05 +0100 Subject: [PATCH] Add energymeter service to services --- libs/ranczo-io/utils/mqtt_topic_builder.hpp | 36 ++ services/CMakeLists.txt | 3 +- services/energymeter_svc/CMakeLists.txt | 63 +++ services/energymeter_svc/ORNO_517.cpp | 0 services/energymeter_svc/ORNO_517.hpp | 0 services/energymeter_svc/energymeter.hpp | 0 services/energymeter_svc/main.cpp | 413 ++++++++++++++++++ services/energymeter_svc/postinst | 14 + services/energymeter_svc/postrm | 14 + services/energymeter_svc/prerm | 10 + services/energymeter_svc/pstryk.cpp | 0 services/energymeter_svc/pstryk.hpp | 0 .../ranczo-io_energymeter.service.in | 20 + 13 files changed, 572 insertions(+), 1 deletion(-) create mode 100644 services/energymeter_svc/CMakeLists.txt create mode 100644 services/energymeter_svc/ORNO_517.cpp create mode 100644 services/energymeter_svc/ORNO_517.hpp create mode 100644 services/energymeter_svc/energymeter.hpp create mode 100644 services/energymeter_svc/main.cpp create mode 100644 services/energymeter_svc/postinst create mode 100644 services/energymeter_svc/postrm create mode 100644 services/energymeter_svc/prerm create mode 100644 services/energymeter_svc/pstryk.cpp create mode 100644 services/energymeter_svc/pstryk.hpp create mode 100644 services/energymeter_svc/ranczo-io_energymeter.service.in diff --git a/libs/ranczo-io/utils/mqtt_topic_builder.hpp b/libs/ranczo-io/utils/mqtt_topic_builder.hpp index bb6d932..04e6ce2 100644 --- a/libs/ranczo-io/utils/mqtt_topic_builder.hpp +++ b/libs/ranczo-io/utils/mqtt_topic_builder.hpp @@ -142,6 +142,42 @@ namespace topic { } } // namespace temperature + + namespace utilities{ + // home/utilities/power/// + /* + * : main, heating, housing + * : active, reactive, apparent, voltage, current, frequency, pf + * : 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/// + /* + * : main, heating, housing + * : tatol_active_energy + * : 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 diff --git a/services/CMakeLists.txt b/services/CMakeLists.txt index 574f13a..3dbde0c 100644 --- a/services/CMakeLists.txt +++ b/services/CMakeLists.txt @@ -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) diff --git a/services/energymeter_svc/CMakeLists.txt b/services/energymeter_svc/CMakeLists.txt new file mode 100644 index 0000000..10bc285 --- /dev/null +++ b/services/energymeter_svc/CMakeLists.txt @@ -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 +) diff --git a/services/energymeter_svc/ORNO_517.cpp b/services/energymeter_svc/ORNO_517.cpp new file mode 100644 index 0000000..e69de29 diff --git a/services/energymeter_svc/ORNO_517.hpp b/services/energymeter_svc/ORNO_517.hpp new file mode 100644 index 0000000..e69de29 diff --git a/services/energymeter_svc/energymeter.hpp b/services/energymeter_svc/energymeter.hpp new file mode 100644 index 0000000..e69de29 diff --git a/services/energymeter_svc/main.cpp b/services/energymeter_svc/main.cpp new file mode 100644 index 0000000..a532548 --- /dev/null +++ b/services/energymeter_svc/main.cpp @@ -0,0 +1,413 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +// ─────────────────────────────────────────────────────────────────────────── +// 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::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::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 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 publish() + { + using namespace std::chrono_literals; + + const std::size_t LiveRegistersSize = _registers.size() * 2; + + std::vector housingRaw(LiveRegistersSize); + std::vector heatingRaw(LiveRegistersSize); + + const auto housingTimepoint = to_iso_timestamp(); + auto r1 = co_await _modbus.read_holding_registers( + 1, _baseAddress, static_cast(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(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 + { + 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(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{}; + }; + + // 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{}; + } + + protected: + boost::asio::any_io_executor _ex; + MqttClient& _mqtt; + ModbusClient& _modbus; + + std::span _registers; + const char* _name; + const std::uint16_t _baseAddress; + + std::vector _housingUsage; + std::vector _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(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(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 run() + { + using boost::asio::co_spawn; + using boost::asio::detached; + + co_spawn(_ex, + [this]() -> awaitable { + co_await live_loop(); + }, + detached); + + co_spawn(_ex, + [this]() -> awaitable { + co_await total_loop(); + }, + detached); + + co_return; + } + + private: + awaitable 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 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; +}; diff --git a/services/energymeter_svc/postinst b/services/energymeter_svc/postinst new file mode 100644 index 0000000..df0930c --- /dev/null +++ b/services/energymeter_svc/postinst @@ -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 diff --git a/services/energymeter_svc/postrm b/services/energymeter_svc/postrm new file mode 100644 index 0000000..d448257 --- /dev/null +++ b/services/energymeter_svc/postrm @@ -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 diff --git a/services/energymeter_svc/prerm b/services/energymeter_svc/prerm new file mode 100644 index 0000000..5a8e599 --- /dev/null +++ b/services/energymeter_svc/prerm @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +case "$1" in + remove|upgrade|deconfigure) + systemctl stop ranczo-io_energymeter.service || true + ;; +esac + +exit 0 diff --git a/services/energymeter_svc/pstryk.cpp b/services/energymeter_svc/pstryk.cpp new file mode 100644 index 0000000..e69de29 diff --git a/services/energymeter_svc/pstryk.hpp b/services/energymeter_svc/pstryk.hpp new file mode 100644 index 0000000..e69de29 diff --git a/services/energymeter_svc/ranczo-io_energymeter.service.in b/services/energymeter_svc/ranczo-io_energymeter.service.in new file mode 100644 index 0000000..b0a3d5e --- /dev/null +++ b/services/energymeter_svc/ranczo-io_energymeter.service.in @@ -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