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
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,221 @@
#include "can_broker.hpp"
#include <cstring>
#include <stdexcept>
#include <net/if.h>
#include <poll.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
namespace dpm1000 = can::protocol::dpm1000;
// FIXME (aw): this helper doesn't really belong here
static void throw_with_error(const std::string& msg) {
throw std::runtime_error(msg + ": (" + std::string(strerror(errno)) + ")");
}
CanBroker::CanBroker(const std::string& interface_name, uint8_t _device_src) : device_src(_device_src) {
can_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (can_fd == -1) {
throw_with_error("Failed to open socket");
}
// retrieve interface index from interface name
struct ifreq ifr;
if (interface_name.size() >= sizeof(ifr.ifr_name)) {
throw_with_error("Interface name too long: " + interface_name);
} else {
strcpy(ifr.ifr_name, interface_name.c_str());
}
if (ioctl(can_fd, SIOCGIFINDEX, &ifr) == -1) {
throw_with_error("Failed with ioctl/SIOCGIFINDEX on interface " + interface_name);
}
// bind to the interface
struct sockaddr_can addr;
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(can_fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == -1) {
throw_with_error("Failed with bind");
}
event_fd = eventfd(0, 0);
loop_thread = std::thread(&CanBroker::loop, this);
}
CanBroker::~CanBroker() {
uint64_t quit_value = 1;
write(event_fd, &quit_value, sizeof(quit_value));
loop_thread.join();
close(can_fd);
close(event_fd);
}
void CanBroker::loop() {
std::array<struct pollfd, 2> pollfds = {{
{can_fd, POLLIN, 0},
{event_fd, POLLIN, 0},
}};
while (true) {
const auto poll_result = poll(pollfds.data(), pollfds.size(), -1);
if (poll_result == 0) {
// timeout
continue;
}
if (pollfds[0].revents & POLLIN) {
struct can_frame frame;
read(can_fd, &frame, sizeof(frame));
handle_can_input(frame);
}
if (pollfds[1].revents & POLLIN) {
uint64_t tmp;
read(event_fd, &tmp, sizeof(tmp));
// new event, for now, we do not care, later on we could check, if it is an exit event code
return;
}
}
}
void CanBroker::set_state(bool enabled) {
struct can_frame frame;
dpm1000::power_on(frame, enabled, enabled);
dpm1000::set_header(frame, monitor_id, device_src);
write_to_can(frame);
// Do an extra module ON command as sometimes the bits in the header are not enough to actually switch on
set_data_int(dpm1000::def::SetValueType::SWITCH_ON_OFF_SETTING, (enabled ? 0 : 1));
}
CanBroker::AccessReturnType CanBroker::dispatch_frame(const struct can_frame& frame, uint16_t id,
uint32_t* return_payload) {
// wait until we get access
std::lock_guard<std::mutex> access_lock(access_mtx);
std::unique_lock<std::mutex> request_lock(request.mutex);
write_to_can(frame);
request.msg_type = id;
request.state = CanRequest::State::ISSUED;
const auto finished = request.cv.wait_for(request_lock, ACCESS_TIMEOUT,
[this]() { return request.state != CanRequest::State::ISSUED; });
if (not finished) {
return AccessReturnType::TIMEOUT;
}
if (request.state == CanRequest::State::FAILED) {
return AccessReturnType::FAILED;
}
// success
if (return_payload) {
memcpy(return_payload, request.response.data(), sizeof(std::remove_pointer_t<decltype(return_payload)>));
}
return AccessReturnType::SUCCESS;
}
CanBroker::AccessReturnType CanBroker::read_data(dpm1000::def::ReadValueType value_type, float& result) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
struct can_frame frame;
dpm1000::request_data(frame, value_type);
dpm1000::set_header(frame, monitor_id, device_src);
uint32_t tmp;
const auto status = dispatch_frame(frame, id, &tmp);
if (status == AccessReturnType::SUCCESS) {
memcpy(&result, &tmp, sizeof(result));
}
return status;
}
CanBroker::AccessReturnType CanBroker::read_data_int(dpm1000::def::ReadValueType value_type, uint32_t& result) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
struct can_frame frame;
dpm1000::request_data(frame, value_type);
dpm1000::set_header(frame, monitor_id, device_src);
uint32_t tmp;
const auto status = dispatch_frame(frame, id, &tmp);
if (status == AccessReturnType::SUCCESS) {
result = tmp;
}
return status;
}
CanBroker::AccessReturnType CanBroker::set_data(dpm1000::def::SetValueType value_type, float payload) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
uint8_t raw_payload[sizeof(payload)];
memcpy(raw_payload, &payload, sizeof(payload));
struct can_frame frame;
dpm1000::set_data(frame, value_type, {raw_payload[3], raw_payload[2], raw_payload[1], raw_payload[0]});
dpm1000::set_header(frame, monitor_id, device_src);
return dispatch_frame(frame, id);
}
CanBroker::AccessReturnType CanBroker::set_data_int(dpm1000::def::SetValueType value_type, uint32_t payload) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
uint8_t raw_payload[sizeof(payload)];
memcpy(raw_payload, &payload, sizeof(payload));
struct can_frame frame;
dpm1000::set_data(frame, value_type, {raw_payload[3], raw_payload[2], raw_payload[1], raw_payload[0]});
dpm1000::set_header(frame, monitor_id, device_src);
return dispatch_frame(frame, id);
}
void CanBroker::write_to_can(const struct can_frame& frame) {
write(can_fd, &frame, sizeof(frame));
}
void CanBroker::handle_can_input(const struct can_frame& frame) {
if (((frame.can_id >> dpm1000::def::MESSAGE_HEADER_BIT_SHIFT) & dpm1000::def::MESSAGE_HEADER_MASK) !=
dpm1000::def::MESSAGE_HEADER) {
return;
}
std::unique_lock<std::mutex> request_lock(request.mutex);
if ((request.state != CanRequest::State::ISSUED) or (request.msg_type != dpm1000::parse_msg_type(frame))) {
return;
}
if (dpm1000::is_error_flag_set(frame)) {
request.state = CanRequest::State::FAILED;
} else {
// this is ugly
for (auto i = 0; i < request.response.size(); ++i) {
request.response[i] = frame.data[7 - i];
}
request.state = CanRequest::State::COMPLETED;
}
request_lock.unlock();
request.cv.notify_one();
}

View File

@@ -0,0 +1,70 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef DPM1000_MAIN_DC_CAN_BROKER_HPP
#define DPM1000_MAIN_DC_CAN_BROKER_HPP
#include <array>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <everest/can/protocol/dpm1000.hpp>
struct CanRequest {
enum class State {
IDLE,
ISSUED,
COMPLETED,
FAILED,
} state{State::IDLE};
uint16_t msg_type;
std::array<uint8_t, 4> response;
std::condition_variable cv;
std::mutex mutex;
};
class CanBroker {
public:
enum class AccessReturnType {
SUCCESS,
FAILED,
TIMEOUT,
NOT_READY,
};
CanBroker(const std::string& interface_name, uint8_t _device_src);
AccessReturnType read_data(can::protocol::dpm1000::def::ReadValueType, float& result);
AccessReturnType read_data_int(can::protocol::dpm1000::def::ReadValueType, uint32_t& result);
AccessReturnType set_data(can::protocol::dpm1000::def::SetValueType, float value);
AccessReturnType set_data_int(can::protocol::dpm1000::def::SetValueType, uint32_t value);
void set_state(bool enabled);
~CanBroker();
private:
constexpr static auto ACCESS_TIMEOUT = std::chrono::milliseconds(250);
void loop();
void write_to_can(const struct can_frame& frame);
AccessReturnType dispatch_frame(const struct can_frame& frame, uint16_t id, uint32_t* result = nullptr);
void handle_can_input(const struct can_frame& frame);
uint8_t device_src;
std::mutex access_mtx;
CanRequest request;
const uint8_t monitor_id{0xf0};
std::thread loop_thread;
int event_fd{-1};
int can_fd{-1};
};
#endif // DPM1000_MAIN_DC_CAN_BROKER_HPP

View File

@@ -0,0 +1,294 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "power_supply_DCImpl.hpp"
#include <memory>
#include "can_broker.hpp"
#include <fmt/core.h>
#include <utils/formatter.hpp>
std::unique_ptr<CanBroker> can_broker;
namespace dpm1000 = can::protocol::dpm1000;
namespace module {
namespace main {
static void log_status_on_fail(const std::string& msg, CanBroker::AccessReturnType status) {
using ReturnStatus = CanBroker::AccessReturnType;
std::string reason;
switch (status) {
case ReturnStatus::FAILED:
reason = "failed";
break;
case ReturnStatus::NOT_READY:
reason = "not ready";
break;
case ReturnStatus::TIMEOUT:
reason = "timeout";
default:
return;
}
EVLOG_info << msg << " reason: (" << reason << ")";
}
static std::string alarm_to_string(uint32_t alarm) {
std::string alarmflags;
if (alarm & (1 << (int)dpm1000::def::Alarm::FUSE_BURN_OUT))
alarmflags += "[FUSE_BURN_OUT]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PFC_DCDC_COMMUNICATION_ERROR))
alarmflags += "[PFC_DCDC_COMMUNICATION_ERROR]";
if (alarm & (1 << (int)dpm1000::def::Alarm::UNBALANCED_BUS_VOLTAGE))
alarmflags += "[UNBALANCED_BUS_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::BUS_OVER_VOLTAGE))
alarmflags += "[BUS_OVER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::BUS_ABNORMAL_VOLTAGE))
alarmflags += "[BUS_ABNORMAL_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PHASE_OVER_VOLTAGE))
alarmflags += "[PHASE_OVER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::ID_NUMBER_REPETITION))
alarmflags += "[ID_NUMBER_REPETITION]";
if (alarm & (1 << (int)dpm1000::def::Alarm::BUS_UNDER_VOLTAGE))
alarmflags += "[BUS_UNDER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PHASE_LOSS))
alarmflags += "[PHASE_LOSS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PHASE_UNDER_VOLTAGE))
alarmflags += "[PHASE_UNDER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::CAN_COMMUNICATION_FAULT))
alarmflags += "[CAN_COMMUNICATION_FAULT]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_UNEVEN_CURRENT_SHARING))
alarmflags += "[DCDC_UNEVEN_CURRENT_SHARING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PFC_POWER_OFF))
alarmflags += "[PFC_POWER_OFF]";
if (alarm & (1 << (int)dpm1000::def::Alarm::FAN_FULL_SPEED))
alarmflags += "[FAN_FULL_SPEED]";
if (alarm & (1 << (int)dpm1000::def::Alarm::POWER_LIMITING))
alarmflags += "[POWER_LIMITING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_POWER_OFF))
alarmflags += "[DCDC_POWER_OFF]";
if (alarm & (1 << (int)dpm1000::def::Alarm::TEMPERATURE_POWER_LIMITING))
alarmflags += "[TEMPERATURE_POWER_LIMITING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::AC_POWER_LIMITING))
alarmflags += "[AC_POWER_LIMITING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_EEPROM_FAULTS))
alarmflags += "[DCDC_EEPROM_FAULTS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::FAN_FAULTS))
alarmflags += "[FAN_FAULTS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_SHORT_CIRCUIT))
alarmflags += "[DCDC_SHORT_CIRCUIT]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PFC_EEPROM_FAULTS))
alarmflags += "[PFC_EEPROM_FAULTS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_OVER_TEMPERATURE))
alarmflags += "[DCDC_OVER_TEMPERATURE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_OUTPUT_OVER_VOLTAGE))
alarmflags += "[DCDC_OUTPUT_OVER_VOLTAGE]";
return alarmflags;
}
void power_supply_DCImpl::init() {
current = 0;
voltage = 300;
config_current_limit = mod->config.current_limit_A;
config_voltage_limit = mod->config.voltage_limit_V;
config_power_limit = mod->config.power_limit_W;
if (!mod->config.discharge_gpio_chip.empty()) {
discharge_gpio.open(mod->config.discharge_gpio_chip, mod->config.discharge_gpio_line,
!mod->config.discharge_gpio_polarity);
discharge_gpio.set_output(false);
}
can_broker = std::make_unique<CanBroker>(mod->config.device, mod->config.device_address);
// ensure the module is switched off
can_broker->set_state(false);
// Configure module for series or parallel mode
// 0 is automatic switching mode
float series_parallel_mode = 0.;
if (mod->config.series_parallel_mode == "Series") {
series_parallel_mode = 1050.;
config_min_voltage_limit = 300.;
parallel_mode = false;
} else if (mod->config.series_parallel_mode == "Parallel") {
series_parallel_mode = 520.;
if (config_voltage_limit > 520) {
config_voltage_limit = 520;
}
parallel_mode = true;
}
// WTF: This really uses a float to set one of the three modes automatic, series or parallel.
auto status = can_broker->set_data(dpm1000::def::SetValueType::SERIES_PARALLEL_MODE, series_parallel_mode);
log_status_on_fail("Set current limit failed", status);
}
void power_supply_DCImpl::ready() {
types::power_supply_DC::Capabilities caps;
caps.bidirectional = false;
caps.max_export_current_A = config_current_limit;
caps.max_export_voltage_V = config_voltage_limit;
caps.min_export_current_A = 0;
caps.min_export_voltage_V = config_min_voltage_limit;
caps.max_export_power_W = config_power_limit;
caps.current_regulation_tolerance_A = 0.5;
caps.peak_current_ripple_A = 1;
caps.conversion_efficiency_export = 0.95;
publish_capabilities(caps);
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
// Send voltage, current and power limits
auto status = can_broker->set_data(dpm1000::def::SetValueType::CURRENT_LIMIT, current);
log_status_on_fail("Set current limit failed", status);
status = can_broker->set_data(dpm1000::def::SetValueType::DEFAULT_CURRENT_LIMIT, 1.0);
log_status_on_fail("Set default current limit failed", status);
status = can_broker->set_data(dpm1000::def::SetValueType::VOLTAGE, voltage);
log_status_on_fail("Set voltage failed", status);
status = can_broker->set_data(dpm1000::def::SetValueType::POWER_LIMIT, 1.0);
log_status_on_fail("Set current limit failed", status);
// Read voltage and current
float tmp;
types::power_supply_DC::VoltageCurrent vc;
status = can_broker->read_data(dpm1000::def::ReadValueType::VOLTAGE, tmp);
log_status_on_fail("Read voltage failed", status);
if (status != CanBroker::AccessReturnType::SUCCESS) {
continue;
}
vc.voltage_V = tmp;
status = can_broker->read_data(dpm1000::def::ReadValueType::CURRENT, tmp);
log_status_on_fail("Read current failed", status);
if (status != CanBroker::AccessReturnType::SUCCESS) {
continue;
}
// Publish voltage and current var
// Current scaling depends on series/parallel mode operation.
vc.current_A = tmp;
if (parallel_mode) {
vc.current_A *= 2.;
}
publish_voltage_current(vc);
// read alarm flags
uint32_t alarm = 0;
status = can_broker->read_data_int(dpm1000::def::ReadValueType::ALARM, alarm);
log_status_on_fail("Read alarm failed", status);
if (status == CanBroker::AccessReturnType::SUCCESS) {
if (last_alarm_flags != alarm) {
auto alarmflags = alarm_to_string(alarm);
if (alarmflags != "") {
EVLOG_warning << "Alarm flags changed: " << alarmflags;
} else {
EVLOG_info << "All Alarm flags cleared.";
}
last_alarm_flags = alarm;
}
}
// Discharge output if it is higher then setpoint voltage.
// Note that this has no timeout, so HW must be designed to sustain the worst case load (e.g. 1000V) continously
if (vc.voltage_V > (voltage + 10)) {
discharge_gpio.set(true);
} else {
discharge_gpio.set(false);
}
if (mod->config.debug_print_all_telemetry) {
// read additional meta data
float current_real_part = 0, current_limit = 0, dcdc_temperature = 0, ac_voltage = 0, voltage_limit = 0,
pfc0_voltage = 0, pfc1_voltage = 0, env_temperature = 0, ac_voltage_phase_a = 0,
ac_voltage_phase_b = 0, ac_voltage_phase_c = 0, pfc_temperature = 0, power_limit = 0;
status = can_broker->read_data(dpm1000::def::ReadValueType::CURRENT_REAL_PART, current_real_part);
log_status_on_fail("Read CURRENT_REAL_PART failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::CURRENT_LIMIT, current_limit);
log_status_on_fail("Read CURRENT_LIMIT failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::DCDC_TEMPERATURE, dcdc_temperature);
log_status_on_fail("Read DCDC_TEMPERATURE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE, ac_voltage);
log_status_on_fail("Read AC_VOLTAGE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::VOLTAGE_LIMIT, voltage_limit);
log_status_on_fail("Read VOLTAGE_LIMIT failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::PFC0_VOLTAGE, pfc0_voltage);
log_status_on_fail("Read PFC0_VOLTAGE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::PFC1_VOLTAGE, pfc1_voltage);
log_status_on_fail("Read PFC1_VOLTAGE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::ENV_TEMPERATURE, env_temperature);
log_status_on_fail("Read ENV_TEMPERATURE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE_PHASE_A, ac_voltage_phase_a);
log_status_on_fail("Read AC_VOLTAGE_PHASE_A failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE_PHASE_B, ac_voltage_phase_b);
log_status_on_fail("Read AC_VOLTAGE_PHASE_B failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE_PHASE_C, ac_voltage_phase_c);
log_status_on_fail("Read AC_VOLTAGE_PHASE_C failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::PFC_TEMPERATURE, pfc_temperature);
log_status_on_fail("Read PFC_TEMPERATURE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::POWER_LIMIT, power_limit);
log_status_on_fail("Read POWER_LIMIT failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::ENV_TEMPERATURE, env_temperature);
log_status_on_fail("Read ENV_TEMPERATURE failed", status);
EVLOG_info << fmt::format(
"set_voltage {} set_current {} vc.current_A {} vc.voltage_V {} current_real_part {} current_limit {} "
"dcdc_temperature {} ac_voltage {} "
"voltage_limit "
"{} pfc0_voltage {} pfc1_voltage {} env_temperature {} ac_voltage_phase_a {} ac_voltage_phase_b {} "
"ac_voltage_phase_c {} pfc_temperature {} power_limit {}",
voltage, current, vc.current_A, vc.voltage_V, current_real_part, current_limit, dcdc_temperature,
ac_voltage, voltage_limit, pfc0_voltage, pfc1_voltage, env_temperature, ac_voltage_phase_a,
ac_voltage_phase_b, ac_voltage_phase_c, pfc_temperature, power_limit);
}
}
}
void power_supply_DCImpl::handle_setMode(types::power_supply_DC::Mode& mode,
types::power_supply_DC::ChargingPhase& phase) {
if (mode == types::power_supply_DC::Mode::Export) {
can_broker->set_state(true);
} else {
can_broker->set_state(false);
}
}
void power_supply_DCImpl::handle_setExportVoltageCurrent(double& voltage, double& current) {
if (voltage <= config_voltage_limit && voltage >= config_min_voltage_limit && current <= config_current_limit) {
this->voltage = voltage;
this->current = current / 100.;
} else {
EVLOG_error << fmt::format("Out of range voltage/current settings ignored: {}V / {}A", voltage, current);
}
}
void power_supply_DCImpl::handle_setImportVoltageCurrent(double& voltage, double& current) {
// power supply is uni directional only
}
} // namespace main
} // namespace module

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef MAIN_POWER_SUPPLY_DC_IMPL_HPP
#define MAIN_POWER_SUPPLY_DC_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/power_supply_DC/Implementation.hpp>
#include "../DPM1000.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
#include <atomic>
#include <everest/gpio/gpio.hpp>
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace main {
struct Conf {};
class power_supply_DCImpl : public power_supply_DCImplBase {
public:
power_supply_DCImpl() = delete;
power_supply_DCImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<DPM1000>& mod, Conf& config) :
power_supply_DCImplBase(ev, "main"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual void handle_setMode(types::power_supply_DC::Mode& mode,
types::power_supply_DC::ChargingPhase& phase) override;
virtual void handle_setExportVoltageCurrent(double& voltage, double& current) override;
virtual void handle_setImportVoltageCurrent(double& voltage, double& current) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<DPM1000>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
std::atomic<float> voltage;
std::atomic<float> current;
std::atomic<uint32_t> last_alarm_flags{0};
float config_current_limit{0};
float config_voltage_limit{0};
float config_power_limit{0};
float config_min_voltage_limit{50.};
Everest::Gpio discharge_gpio;
bool parallel_mode{false};
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace main
} // namespace module
#endif // MAIN_POWER_SUPPLY_DC_IMPL_HPP