208 lines
10 KiB
C++
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
|