Files
cariflex/tools/EVerest-main/modules/API/API/limit_decimal_places.cpp
Eric F d398a6ced2 Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
2026-06-08 00:38:27 -04:00

314 lines
15 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "limit_decimal_places.hpp"
#include <cmath>
#include <c4/format.hpp>
#include <ryml.hpp>
#include <ryml_std.hpp>
namespace module {
std::string LimitDecimalPlaces::limit(const types::powermeter::Powermeter& powermeter) {
ryml::Tree tree;
ryml::NodeRef root = tree.rootref();
root |= ryml::MAP;
// add informative power meter entries
root["timestamp"] << powermeter.timestamp;
if (powermeter.meter_id.has_value()) {
root["meter_id"] << powermeter.meter_id.value();
}
if (powermeter.phase_seq_error.has_value()) {
root["phase_seq_error"] << ryml::fmt::boolalpha(powermeter.phase_seq_error.value());
}
// limit decimal places
// energy_Wh_import always exists
root["energy_Wh_import"] |= ryml::MAP;
root["energy_Wh_import"]["total"] << ryml::fmt::real(
this->round_to_nearest_step(powermeter.energy_Wh_import.total, this->config.powermeter_energy_import_round_to),
this->config.powermeter_energy_import_decimal_places);
if (powermeter.energy_Wh_import.L1.has_value()) {
root["energy_Wh_import"]["L1"] << ryml::fmt::real(
this->round_to_nearest_step(powermeter.energy_Wh_import.L1.value(),
this->config.powermeter_energy_import_round_to),
this->config.powermeter_energy_import_decimal_places);
}
if (powermeter.energy_Wh_import.L2.has_value()) {
root["energy_Wh_import"]["L2"] << ryml::fmt::real(
this->round_to_nearest_step(powermeter.energy_Wh_import.L2.value(),
this->config.powermeter_energy_import_round_to),
this->config.powermeter_energy_import_decimal_places);
}
if (powermeter.energy_Wh_import.L3.has_value()) {
root["energy_Wh_import"]["L3"] << ryml::fmt::real(
this->round_to_nearest_step(powermeter.energy_Wh_import.L3.value(),
this->config.powermeter_energy_import_round_to),
this->config.powermeter_energy_import_decimal_places);
}
// everything else in the power meter is optional
if (powermeter.energy_Wh_export.has_value()) {
auto& energy_Wh_export = powermeter.energy_Wh_export.value();
root["energy_Wh_export"] |= ryml::MAP;
root["energy_Wh_export"]["total"] << ryml::fmt::real(
this->round_to_nearest_step(energy_Wh_export.total, this->config.powermeter_energy_export_round_to),
this->config.powermeter_energy_export_decimal_places);
if (energy_Wh_export.L1.has_value()) {
root["energy_Wh_export"]["L1"]
<< ryml::fmt::real(this->round_to_nearest_step(energy_Wh_export.L1.value(),
this->config.powermeter_energy_export_round_to),
this->config.powermeter_energy_export_decimal_places);
}
if (energy_Wh_export.L2.has_value()) {
root["energy_Wh_export"]["L2"]
<< ryml::fmt::real(this->round_to_nearest_step(energy_Wh_export.L2.value(),
this->config.powermeter_energy_export_round_to),
this->config.powermeter_energy_export_decimal_places);
}
if (energy_Wh_export.L3.has_value()) {
root["energy_Wh_export"]["L3"]
<< ryml::fmt::real(this->round_to_nearest_step(energy_Wh_export.L3.value(),
this->config.powermeter_energy_export_round_to),
this->config.powermeter_energy_export_decimal_places);
}
}
if (powermeter.power_W.has_value()) {
auto& power_W = powermeter.power_W.value();
root["power_W"] |= ryml::MAP;
root["power_W"]["total"] << ryml::fmt::real(
this->round_to_nearest_step(power_W.total, this->config.powermeter_power_round_to),
this->config.powermeter_power_decimal_places);
if (power_W.L1.has_value()) {
root["power_W"]["L1"] << ryml::fmt::real(
this->round_to_nearest_step(power_W.L1.value(), this->config.powermeter_power_round_to),
this->config.powermeter_power_decimal_places);
}
if (power_W.L2.has_value()) {
root["power_W"]["L2"] << ryml::fmt::real(
this->round_to_nearest_step(power_W.L2.value(), this->config.powermeter_power_round_to),
this->config.powermeter_power_decimal_places);
}
if (power_W.L3.has_value()) {
root["power_W"]["L3"] << ryml::fmt::real(
this->round_to_nearest_step(power_W.L3.value(), this->config.powermeter_power_round_to),
this->config.powermeter_power_decimal_places);
}
}
if (powermeter.voltage_V.has_value()) {
auto& voltage_V = powermeter.voltage_V.value();
root["voltage_V"] |= ryml::MAP;
if (voltage_V.DC.has_value()) {
root["voltage_V"]["DC"] << ryml::fmt::real(
this->round_to_nearest_step(voltage_V.DC.value(), this->config.powermeter_voltage_round_to),
this->config.powermeter_voltage_decimal_places);
}
if (voltage_V.L1.has_value()) {
root["voltage_V"]["L1"] << ryml::fmt::real(
this->round_to_nearest_step(voltage_V.L1.value(), this->config.powermeter_voltage_round_to),
this->config.powermeter_voltage_decimal_places);
}
if (voltage_V.L2.has_value()) {
root["voltage_V"]["L2"] << ryml::fmt::real(
this->round_to_nearest_step(voltage_V.L2.value(), this->config.powermeter_voltage_round_to),
this->config.powermeter_voltage_decimal_places);
}
if (voltage_V.L3.has_value()) {
root["voltage_V"]["L3"] << ryml::fmt::real(
this->round_to_nearest_step(voltage_V.L3.value(), this->config.powermeter_voltage_round_to),
this->config.powermeter_voltage_decimal_places);
}
}
if (powermeter.VAR.has_value()) {
auto& VAR = powermeter.VAR.value();
root["VAR"] |= ryml::MAP;
root["VAR"]["total"] << ryml::fmt::real(
this->round_to_nearest_step(VAR.total, this->config.powermeter_VAR_round_to),
this->config.powermeter_VAR_decimal_places);
if (VAR.L1.has_value()) {
root["VAR"]["L1"] << ryml::fmt::real(
this->round_to_nearest_step(VAR.L1.value(), this->config.powermeter_VAR_round_to),
this->config.powermeter_VAR_decimal_places);
}
if (VAR.L2.has_value()) {
root["VAR"]["L2"] << ryml::fmt::real(
this->round_to_nearest_step(VAR.L2.value(), this->config.powermeter_VAR_round_to),
this->config.powermeter_VAR_decimal_places);
}
if (VAR.L3.has_value()) {
root["VAR"]["L3"] << ryml::fmt::real(
this->round_to_nearest_step(VAR.L3.value(), this->config.powermeter_VAR_round_to),
this->config.powermeter_VAR_decimal_places);
}
}
if (powermeter.current_A.has_value()) {
auto& current_A = powermeter.current_A.value();
root["current_A"] |= ryml::MAP;
if (current_A.DC.has_value()) {
root["current_A"]["DC"] << ryml::fmt::real(
this->round_to_nearest_step(current_A.DC.value(), this->config.powermeter_current_round_to),
this->config.powermeter_current_decimal_places);
}
if (current_A.L1.has_value()) {
root["current_A"]["L1"] << ryml::fmt::real(
this->round_to_nearest_step(current_A.L1.value(), this->config.powermeter_current_round_to),
this->config.powermeter_current_decimal_places);
}
if (current_A.L2.has_value()) {
root["current_A"]["L2"] << ryml::fmt::real(
this->round_to_nearest_step(current_A.L2.value(), this->config.powermeter_current_round_to),
this->config.powermeter_current_decimal_places);
}
if (current_A.L3.has_value()) {
root["current_A"]["L3"] << ryml::fmt::real(
this->round_to_nearest_step(current_A.L3.value(), this->config.powermeter_current_round_to),
this->config.powermeter_current_decimal_places);
}
if (current_A.N.has_value()) {
root["current_A"]["N"] << ryml::fmt::real(
this->round_to_nearest_step(current_A.N.value(), this->config.powermeter_current_round_to),
this->config.powermeter_current_decimal_places);
}
}
if (powermeter.frequency_Hz.has_value()) {
auto& frequency_Hz = powermeter.frequency_Hz.value();
root["frequency_Hz"] |= ryml::MAP;
root["frequency_Hz"]["L1"] << ryml::fmt::real(
this->round_to_nearest_step(frequency_Hz.L1, this->config.powermeter_frequency_round_to),
this->config.powermeter_frequency_decimal_places);
if (frequency_Hz.L2.has_value()) {
root["frequency_Hz"]["L2"] << ryml::fmt::real(
this->round_to_nearest_step(frequency_Hz.L2.value(), this->config.powermeter_frequency_round_to),
this->config.powermeter_frequency_decimal_places);
}
if (frequency_Hz.L3.has_value()) {
root["frequency_Hz"]["L3"] << ryml::fmt::real(
this->round_to_nearest_step(frequency_Hz.L3.value(), this->config.powermeter_frequency_round_to),
this->config.powermeter_frequency_decimal_places);
}
}
std::stringstream power_meter_stream;
power_meter_stream << ryml::as_json(tree);
return power_meter_stream.str();
}
std::string LimitDecimalPlaces::limit(const types::evse_board_support::HardwareCapabilities& hw_capabilities) {
ryml::Tree tree;
ryml::NodeRef root = tree.rootref();
root |= ryml::MAP;
// add informative hardware capabilities entries
root["max_phase_count_import"] << hw_capabilities.max_phase_count_import;
root["min_phase_count_import"] << hw_capabilities.min_phase_count_import;
root["max_phase_count_export"] << hw_capabilities.max_phase_count_export;
root["min_phase_count_export"] << hw_capabilities.min_phase_count_export;
root["supports_changing_phases_during_charging"]
<< ryml::fmt::boolalpha(hw_capabilities.supports_changing_phases_during_charging);
// limit decimal places
root["max_current_A_import"] << ryml::fmt::real(
this->round_to_nearest_step(hw_capabilities.max_current_A_import,
this->config.hw_caps_max_current_import_round_to),
this->config.hw_caps_max_current_import_decimal_places);
root["min_current_A_import"] << ryml::fmt::real(
this->round_to_nearest_step(hw_capabilities.min_current_A_import,
this->config.hw_caps_min_current_import_round_to),
this->config.hw_caps_max_current_import_decimal_places);
root["max_current_A_export"] << ryml::fmt::real(
this->round_to_nearest_step(hw_capabilities.max_current_A_export,
this->config.hw_caps_max_current_export_round_to),
this->config.hw_caps_max_current_import_decimal_places);
root["min_current_A_export"] << ryml::fmt::real(
this->round_to_nearest_step(hw_capabilities.min_current_A_export,
this->config.hw_caps_min_current_export_round_to),
this->config.hw_caps_min_current_export_decimal_places);
if (hw_capabilities.max_plug_temperature_C.has_value()) {
root["max_plug_temperature_C"] << ryml::fmt::real(
this->round_to_nearest_step(hw_capabilities.max_plug_temperature_C.value(),
this->config.hw_caps_max_plug_temperature_C_round_to),
this->config.hw_caps_max_plug_temperature_C_decimal_places);
}
root["connector_type"] << types::evse_board_support::connector_type_to_string(hw_capabilities.connector_type);
std::stringstream hardware_capabilities_stream;
hardware_capabilities_stream << ryml::as_json(tree);
return hardware_capabilities_stream.str();
}
std::string LimitDecimalPlaces::limit(const types::evse_manager::Limits& limits) {
ryml::Tree tree;
ryml::NodeRef root = tree.rootref();
root |= ryml::MAP;
// add informative limits entries
if (limits.uuid.has_value()) {
root["uuid"] << limits.uuid.value();
}
root["nr_of_phases_available"] << limits.nr_of_phases_available;
// limit decimal places
root["max_current"] << ryml::fmt::real(
this->round_to_nearest_step(limits.max_current, this->config.limits_max_current_round_to),
this->config.limits_max_current_decimal_places);
std::stringstream limits_stream;
limits_stream << ryml::as_json(tree);
return limits_stream.str();
}
std::string LimitDecimalPlaces::limit(const types::evse_board_support::Telemetry& telemetry) {
ryml::Tree tree;
ryml::NodeRef root = tree.rootref();
root |= ryml::MAP;
root["phase_seq_error"] << ryml::fmt::boolalpha(telemetry.relais_on);
// limit decimal places
root["temperature"] << ryml::fmt::real(
this->round_to_nearest_step(telemetry.evse_temperature_C, this->config.telemetry_evse_temperature_C_round_to),
this->config.telemetry_evse_temperature_C_decimal_places);
root["fan_rpm"] << ryml::fmt::real(
this->round_to_nearest_step(telemetry.fan_rpm, this->config.telemetry_fan_rpm_round_to),
this->config.telemetry_fan_rpm_decimal_places);
root["supply_voltage_12V"] << ryml::fmt::real(
this->round_to_nearest_step(telemetry.supply_voltage_12V, this->config.telemetry_supply_voltage_12V_round_to),
this->config.telemetry_supply_voltage_12V_decimal_places);
root["supply_voltage_minus_12V"] << ryml::fmt::real(
this->round_to_nearest_step(telemetry.supply_voltage_minus_12V,
this->config.telemetry_supply_voltage_minus_12V_round_to),
this->config.telemetry_supply_voltage_minus_12V_decimal_places);
if (telemetry.plug_temperature_C.has_value()) {
root["plug_temperature_C"] << ryml::fmt::real(
this->round_to_nearest_step(telemetry.plug_temperature_C.value(),
this->config.telemetry_plug_temperature_C_round_to),
this->config.telemetry_plug_temperature_C_decimal_places);
}
std::stringstream telemetry_stream;
telemetry_stream << ryml::as_json(tree);
return telemetry_stream.str();
}
double LimitDecimalPlaces::round_to_nearest_step(double value, double step) {
if (step <= 0) {
return value;
}
return std::round(value / step) * step;
}
} // namespace module