ranczo-io/services/energymeter_svc/ORNO_517.cpp
Bartosz Wieczorek 6da01a2f6b Add HTTP get
2025-12-12 16:57:05 +01:00

208 lines
10 KiB
C++

#include "ranczo-io/utils/date_utils.hpp"
#include "ranczo-io/utils/modbus.hpp"
#include <boost/asio/any_io_executor.hpp>
#include <chrono>
#include <cstdint>
#include <modbus/modbus.h>
namespace ranczo {
// ───────────────────────────────────────────────────────────────────────────
// 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;
}
// ───────────────────────────────────────────────────────────────────────────
// Konkrety: odczyty "live"
// ───────────────────────────────────────────────────────────────────────────
class EnergymeterReadings {
public:
EnergymeterReadings(boost::asio::any_io_executor ex,
ModbusDevice & modbus,
std::span< const Register > regs,
const char * name,
std::uint16_t baseAddress)
: _ex(ex),
_modbus(modbus),
_registers(regs),
_name(name),
_baseAddress(baseAddress),
_housingUsage(regs.size(), 0.0f),
_heatingUsage(regs.size(), 0.0f) {}
// slave 1: "housing", slave 2: "heating"
ranczo::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 = date::to_iso_timestamp(std::chrono::system_clock::now());
// auto r1 =
// co_await _modbus.async_read_holding_registers(_baseAddress, static_cast< std::uint16_t >(LiveRegistersSize), );
// if(!r1) {
// spdlog::error("Modbus read (housing) failed: {}", r1.error().message());
// co_return unexpected(r1.error());
// }
// const auto heatingTimepoint = date::to_iso_timestamp(std::chrono::system_clock::now());
// 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 = date::to_iso_timestamp(std::chrono::system_clock::now());
// auto housingPrev = _housingUsage.begin();
// auto heatingPrev = _heatingUsage.begin();
// 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;
// }
co_return ranczo::expected< void >{};
}
protected:
boost::asio::any_io_executor _ex;
ModbusDevice &_modbus;
std::span< const Register > _registers;
const char * _name;
const std::uint16_t _baseAddress;
std::vector< float > _housingUsage;
std::vector< float > _heatingUsage;
};
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, ModbusDevice & modbus)
: EnergymeterReadings(ex,
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, ModbusDevice & modbus)
: EnergymeterReadings(ex,
modbus,
std::span< const Register >(totalRegisters_, std::size(totalRegisters_)),
"total",
_baseAddressTotal) {}
};
} // namespace ranczo