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,155 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "BUDCExternalDerate.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp"
#include "ftxui/component/component_options.hpp"
#include "ftxui/component/event.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/elements.hpp"
#include "ftxui/screen/color.hpp"
#include <regex>
#include <string>
namespace module {
using namespace ftxui;
void BUDCExternalDerate::init() {
}
void BUDCExternalDerate::ready() {
auto screen = ScreenInteractive::Fullscreen();
auto msg_component = Container::Vertical({Renderer([] { return text(""); })});
std::map<std::string, std::map<std::string, Component>> messages;
r_derate->subscribe_all_errors(
[&](Everest::error::Error const& error) {
std::scoped_lock lock(data_mutex);
auto error_elem = Renderer([=] { return text(" - " + error.type); });
auto sub_elem = Renderer([=] { return text(" " + error.sub_type); });
auto msg_elem = Renderer([=] { return text(" " + error.message); });
auto component = Container::Vertical({
error_elem,
sub_elem,
msg_elem,
});
messages[error.type][error.sub_type] = component;
msg_component->Add(component);
screen.Post(Event::Custom);
},
[&](Everest::error::Error const& error) {
std::scoped_lock lock(data_mutex);
if (messages.count(error.type)) {
auto& sub = messages.at(error.type);
if (sub.count(error.sub_type)) {
auto& elem = sub.at(error.sub_type);
elem->Detach();
sub.erase(error.sub_type);
}
if (not sub.size()) {
messages.erase(error.type);
}
}
screen.Post(Event::Custom);
});
auto msg_component_holder = Container::Horizontal({msg_component});
auto msg_component_renderer = Renderer(msg_component_holder, [&] {
auto win = window(text("Active Errors"), msg_component_holder->Render());
return vbox({
hbox({
win,
}),
}) |
flex_grow;
});
auto var_component =
Container::Vertical({Renderer([&] { return text("plug_temperature_C: " + std::to_string(plug_temp_C)); })});
auto var_component_holder = Container::Horizontal({var_component});
auto var_component_renderer = Renderer(var_component_holder, [&] {
auto win = window(text("Vars"), var_component_holder->Render());
return vbox({
hbox({
win,
}),
}) |
flex_grow;
});
r_derate->subscribe_plug_temperature_C([&](double temp) {
std::scoped_lock lock(data_mutex);
plug_temp_C = temp;
screen.Post(Event::Custom);
});
InputOption o;
o.multiline = false;
o.cursor_position = 0;
auto max_export_current_A_input = Input(&max_export_current_A, "100.0", o);
auto max_import_current_A_input = Input(&max_import_current_A, "200.0", o);
auto max_export_power_W_input = Input(&max_export_power_W, "300.0", o);
auto max_import_power_W_input = Input(&max_import_power_W, "400.0", o);
Component ovm_start = Button(
"Set",
[&] {
types::dc_external_derate::ExternalDerating val;
val.max_export_current_A = std::stof(max_export_current_A);
val.max_import_current_A = std::stof(max_import_current_A);
val.max_export_power_W = std::stof(max_export_power_W);
val.max_import_power_W = std::stof(max_import_power_W);
r_derate->call_set_external_derating(val);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White)) |
flex_grow;
auto action_component = Container::Horizontal({
Container::Vertical({
max_export_current_A_input,
max_import_current_A_input,
max_export_power_W_input,
max_import_power_W_input,
ovm_start,
}),
});
auto action_component_renderer = Renderer(action_component, [&] {
return vbox({hbox(ovm_start->Render()),
vbox(hbox(text(" Max Export Current (A): "), max_export_current_A_input->Render()),
hbox(text(" Max Import Current (A): "), max_import_current_A_input->Render()),
hbox(text(" Max Export Power (W): "), max_export_power_W_input->Render()),
hbox(text(" Max Import Power (W): "), max_import_power_W_input->Render()))});
});
auto action_renderer = Renderer(action_component, [&] {
return vbox({
hbox({
window(text("DC External Derate Commands"), action_component_renderer->Render()),
}),
}) |
flex_grow;
});
auto main_container = Container::Horizontal({action_renderer, var_component_renderer, msg_component_renderer});
auto main_renderer = Renderer(main_container, [&] {
std::scoped_lock lock(data_mutex);
return vbox({
text("DC External Derate Bringup") | bold | hcenter,
hbox({main_container->Render()}),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,66 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUDCEXTERNAL_DERATE_HPP
#define BUDCEXTERNAL_DERATE_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/dc_external_derate/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUDCExternalDerate : public Everest::ModuleBase {
public:
BUDCExternalDerate() = delete;
BUDCExternalDerate(const ModuleInfo& info, std::unique_ptr<dc_external_derateIntf> r_derate, Conf& config) :
ModuleBase(info), r_derate(std::move(r_derate)), config(config){};
const std::unique_ptr<dc_external_derateIntf> r_derate;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::string max_export_current_A{"20.0"};
std::string max_import_current_A{"0.0"};
std::string max_export_power_W{"20000.0"};
std::string max_import_power_W{"0.0"};
double plug_temp_C{0.};
std::mutex data_mutex;
//
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUDCEXTERNAL_DERATE_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for the dc_external_derate interface
requires:
derate:
interface: dc_external_derate
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Jan Christoph Habig

View File

@@ -0,0 +1,546 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "BUDisplayMessage.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/table.hpp"
#include <generated/types/text_message.hpp>
using namespace ftxui;
namespace {
constexpr std::array<types::display_message::MessagePriorityEnum, 3> MessagePriorityEnumValues = {
types::display_message::MessagePriorityEnum::AlwaysFront, types::display_message::MessagePriorityEnum::InFront,
types::display_message::MessagePriorityEnum::NormalCycle};
constexpr std::array<types::display_message::MessageStateEnum, 4> MessageStateEnumValues = {
types::display_message::MessageStateEnum::Charging, types::display_message::MessageStateEnum::Faulted,
types::display_message::MessageStateEnum::Idle, types::display_message::MessageStateEnum::Unavailable};
constexpr std::array<types::text_message::MessageFormat, 4> MessageFormatValues = {
types::text_message::MessageFormat::ASCII, types::text_message::MessageFormat::HTML,
types::text_message::MessageFormat::URI, types::text_message::MessageFormat::UTF8};
constexpr std::array<types::display_message::IdentifierType, 3> Identifier_typeValues = {
types::display_message::IdentifierType::IdToken, types::display_message::IdentifierType::SessionId,
types::display_message::IdentifierType::TransactionId};
// Enum-String to be used for Dropdown GUI elements
std::vector<std::string> MessagePriorityEnumStrings;
std::vector<std::string> MessageStateEnumStrings;
std::vector<std::string> MessageFormatStrings;
std::vector<std::string> IdentifierTypeStrings;
struct MessageContentParameters {
std::string content;
std::string language;
std::string msg_id;
std::string timestamp_from;
std::string timestamp_to;
std::string identifier_id;
std::string qr_code;
int priority;
int state;
int format;
int id_type;
};
types::display_message::DisplayMessage inputs2dspmsg_types(const MessageContentParameters& params) {
int set_msg_id = 0;
if (not params.msg_id.empty()) {
set_msg_id = std::stoi(params.msg_id);
}
types::text_message::MessageContent msg_content{
params.content,
params.format == MessageFormatValues.size()
? std::nullopt
: std::optional<types::text_message::MessageFormat>(MessageFormatValues[params.format]),
params.language.empty() ? std::nullopt : std::optional<std::string>{params.language}};
types::display_message::DisplayMessage msg{
msg_content,
params.msg_id.empty() ? std::nullopt : std::optional<int>(set_msg_id),
params.priority == MessagePriorityEnumValues.size()
? std::nullopt
: std::optional<types::display_message::MessagePriorityEnum>(MessagePriorityEnumValues[params.priority]),
params.state == MessageStateEnumValues.size()
? std::nullopt
: std::optional<types::display_message::MessageStateEnum>(MessageStateEnumValues[params.state]),
params.timestamp_from.empty() ? std::nullopt : std::optional<std::string>(params.timestamp_from),
params.timestamp_to.empty() ? std::nullopt : std::optional<std::string>(params.timestamp_to),
params.identifier_id.empty() ? std::nullopt : std::optional<std::string>(params.identifier_id),
params.id_type == Identifier_typeValues.size()
? std::nullopt
: std::optional<types::display_message::IdentifierType>(Identifier_typeValues[params.id_type]),
params.qr_code.empty() ? std::nullopt : std::optional<std::string>(params.qr_code)};
return msg;
}
auto input_parameters_to_display_messages(
const std::vector<std::unique_ptr<MessageContentParameters>>& set_msg_parameter_vector) {
std::vector<types::display_message::DisplayMessage> display_messages;
for (auto& msg_params : set_msg_parameter_vector) {
if (msg_params->content.empty()) {
continue;
}
display_messages.push_back(inputs2dspmsg_types(*msg_params));
}
return display_messages;
}
auto comma_separated_ids_to_vector(const std::string& get_msg_id_str) {
std::optional<std::vector<int32_t>> get_msg_ids = std::vector<int32_t>{};
int get_msg_id;
std::stringstream msg_ids_ss(get_msg_id_str);
while (msg_ids_ss.good()) {
std::string singleNumber_str;
getline(msg_ids_ss, singleNumber_str, ',');
try {
int singleNumber = std::stoi(singleNumber_str);
get_msg_ids->push_back(singleNumber);
} catch (...) {
// just ignore it
}
}
if (get_msg_ids->size() == 0) {
get_msg_ids = std::nullopt;
}
return get_msg_ids;
}
auto msg_priority_int_to_optional_enum(int get_msg_priority_selected) {
return (get_msg_priority_selected < MessagePriorityEnumValues.size())
? std::optional<
types::display_message::MessagePriorityEnum>{MessagePriorityEnumValues[get_msg_priority_selected]}
: std::nullopt;
}
auto msg_state_int_to_optional_enum(int get_msg_state_selected) {
return (get_msg_state_selected < MessageStateEnumValues.size())
? std::optional<types::display_message::MessageStateEnum>{MessageStateEnumValues[get_msg_state_selected]}
: std::nullopt;
}
void log_cmd_response(std::vector<std::string>* log, const std::string& log_prefix, const std::string& status,
const std::optional<std::string>& status_info) {
log->insert(log->begin(), log_prefix + ": " + status + ": '" + status_info.value_or("") + "'");
}
void log_message(std::vector<std::string>* log, types::display_message::DisplayMessage msg) {
std::string message_str = msg.message.content;
std::string format_str = (msg.message.format ? message_format_to_string(msg.message.format.value()) : "<no fmt>");
std::string lang_str = msg.message.language.value_or("<no lang>");
std::string id_str = (msg.id ? std::to_string(msg.id.value()) : "<no id>");
std::string prio_str = (msg.priority ? message_priority_enum_to_string(msg.priority.value()) : "<no prio>");
std::string state_str = (msg.state ? message_state_enum_to_string(msg.state.value()) : "<no stat>");
std::string tstmp_fr_str = msg.timestamp_from.value_or("<no t_from>");
std::string tstmp_to_str = msg.timestamp_to.value_or("<no t_to>");
std::string id_id_str = msg.identifier_id.value_or("<no id_id>");
std::string id_type_str =
(msg.identifier_type ? identifier_type_to_string(msg.identifier_type.value()) : "<no id_typ>");
std::string qr_str = msg.qr_code.value_or("<no qr>");
std::vector<std::string> strings_to_join = {message_str, format_str, lang_str, id_str, prio_str, state_str,
tstmp_fr_str, tstmp_to_str, id_id_str, id_type_str, qr_str};
std::string joined_string = std::accumulate(
std::begin(strings_to_join), std::end(strings_to_join), std::string{},
[](std::string& first, std::string& second) { return first.empty() ? second : first + ", " + second; });
log->insert(log->begin(), joined_string);
}
class MessageInputMask : public ComponentBase {
public:
MessageInputMask(const std::string& heading, MessageContentParameters* parameters) :
m_heading(heading), m_parameters(parameters) {
m_msg_prio_dd = Dropdown(&MessagePriorityEnumStrings, &(m_parameters->priority));
m_msg_stat_dd = Dropdown(&MessageStateEnumStrings, &(m_parameters->state));
m_msg_form_dd = Dropdown(&MessageFormatStrings, &(m_parameters->format));
m_id_type_dd = Dropdown(&IdentifierTypeStrings, &(m_parameters->id_type));
InputOption id_input_o;
id_input_o.multiline = false;
id_input_o.cursor_position = 0;
id_input_o.on_change = [&]() {
if (m_parameters->identifier_id.size() > 36) {
m_parameters->identifier_id.resize(36);
}
};
InputOption msg_id_input_o;
msg_id_input_o.multiline = false;
msg_id_input_o.cursor_position = 0;
msg_id_input_o.on_change = [&]() {
m_parameters->msg_id.erase(std::remove_if(m_parameters->msg_id.begin(), m_parameters->msg_id.end(),
[](char c) { return !std::isdigit(c); }),
m_parameters->msg_id.end());
};
InputOption input_o;
input_o.multiline = false;
m_msg_input = Input(&(m_parameters->content), "<required>", input_o);
m_msg_id_input = Input(&(m_parameters->msg_id), "<none>", msg_id_input_o);
m_qr_input = Input(&(m_parameters->qr_code), "<none>", input_o);
m_lang_input = Input(&(m_parameters->language), "<none>", input_o);
m_t_from_input = Input(&(m_parameters->timestamp_from), "<none>", input_o);
m_t_to_input = Input(&(m_parameters->timestamp_to), "<none>", input_o);
m_id_input = Input(&(m_parameters->identifier_id), "<none>", id_input_o);
m_msg_details_component = Container::Vertical({
m_msg_prio_dd,
m_msg_stat_dd,
m_id_type_dd,
m_msg_form_dd,
m_msg_id_input,
m_lang_input,
m_id_input,
m_t_from_input,
m_t_to_input,
m_qr_input,
});
auto set_parameters_renderer = Renderer(m_msg_details_component, [&] {
return vbox({
hbox(hbox(text(" [Priority] : ") | vcenter, m_msg_prio_dd->Render()), filler(),
hbox(text(" [State] : ") | vcenter, m_msg_stat_dd->Render()), filler()),
hbox(hbox(text(" [Identifier Type] : ") | vcenter, m_id_type_dd->Render()), filler(),
hbox(text(" [Format] : ") | vcenter, m_msg_form_dd->Render()), filler()),
hbox(text(" [Message Id] : "), m_msg_id_input->Render()),
hbox(text(" [Language] : "), m_lang_input->Render()),
hbox(text(" [Identifier] : "), m_id_input->Render()),
hbox(text(" [Date-Time From]: "), m_t_from_input->Render()),
hbox(text(" [Date-Time To] : "), m_t_to_input->Render()),
hbox(text(" [QR-Code] : "), m_qr_input->Render()),
});
});
m_details_collapsible = Collapsible("Message Details", set_parameters_renderer);
auto set_msg_component = Container::Vertical({
m_msg_input,
m_details_collapsible,
});
// Add it to the container so it can receive focus.
Add(set_msg_component);
}
// Override the Render method to display label and input together.
Element OnRender() override {
return vbox({
text(m_heading) | inverted,
hbox(text(" Message : ") | bold, m_msg_input->Render()),
m_details_collapsible->Render(),
text(""),
});
}
private:
std::string m_heading;
MessageContentParameters* m_parameters;
Component m_msg_prio_dd;
Component m_msg_form_dd;
Component m_id_type_dd;
Component m_msg_stat_dd;
Component m_msg_input;
Component m_msg_id_input;
Component m_qr_input;
Component m_lang_input;
Component m_t_from_input;
Component m_t_to_input;
Component m_id_input;
Component m_msg_details_component;
Component m_details_collapsible;
};
Component MessageInputMaskComponent(const std::string& heading, MessageContentParameters* parameters) {
return Make<MessageInputMask>(heading, parameters);
}
} // namespace
namespace module {
void BUDisplayMessage::init() {
}
void BUDisplayMessage::ready() {
// Prepare Enum-String to be used for Dropdown GUI elements
for (auto msgPriority : MessagePriorityEnumValues) {
MessagePriorityEnumStrings.push_back(message_priority_enum_to_string(msgPriority));
}
MessagePriorityEnumStrings.push_back("<none>");
for (auto msgState : MessageStateEnumValues) {
MessageStateEnumStrings.push_back(message_state_enum_to_string(msgState));
}
MessageStateEnumStrings.push_back("<none>");
for (auto msgFormat : MessageFormatValues) {
MessageFormatStrings.push_back(message_format_to_string(msgFormat));
}
MessageFormatStrings.push_back("<none>");
for (auto idType : Identifier_typeValues) {
IdentifierTypeStrings.push_back(identifier_type_to_string(idType));
}
IdentifierTypeStrings.push_back("<none>");
/***************************************************************************************+
* Start of FTXUI-GUI
****************************************************************************************/
auto screen = ScreenInteractive::Fullscreen();
int log_pane_index = 0;
std::vector<std::string> log;
auto log_pane_raw_component = Menu(&log, &log_pane_index);
auto log_pane_component = log_pane_raw_component | vscroll_indicator;
auto log_pane_renderer =
Renderer(log_pane_component, [&] { return window(text(" Command Log "), log_pane_component->Render()); });
/***************************************************************************************+
* Set Display Messages
****************************************************************************************/
/* ********************** INPUTS ******************************************** */
std::vector<std::unique_ptr<MessageContentParameters>> set_msg_parameter_vector{};
int message_input_mask_count;
std::vector<Component> set_msg_input_masks{};
auto msg_inputs_component = Container::Vertical({});
auto add_msg_input_widget = [&] {
set_msg_parameter_vector.push_back(std::make_unique<MessageContentParameters>());
// Initialize dropdown choices to be <none> for all dropdowns
set_msg_parameter_vector.back()->priority = MessagePriorityEnumStrings.size() - 1;
set_msg_parameter_vector.back()->state = MessageStateEnumStrings.size() - 1;
set_msg_parameter_vector.back()->format = MessageFormatStrings.size() - 1;
set_msg_parameter_vector.back()->id_type = IdentifierTypeStrings.size() - 1;
message_input_mask_count += 1;
set_msg_input_masks.push_back(MessageInputMaskComponent("Message " + std::to_string(message_input_mask_count),
set_msg_parameter_vector.back().get()));
msg_inputs_component->Add(set_msg_input_masks.back());
// Tell FTXUI to redraw
screen.PostEvent(Event::Custom);
};
auto reset_msg_input_widgets = [&] {
msg_inputs_component->DetachAllChildren();
set_msg_input_masks.clear();
set_msg_parameter_vector.clear();
message_input_mask_count = 0;
add_msg_input_widget();
};
add_msg_input_widget();
/* ********************** BUTTONS ******************************************* */
auto add_msg_button = Button(
"Add Message!", [&] { add_msg_input_widget(); }, ButtonOption::Border());
auto set_button = Button(
"Set!",
[&] {
auto display_messages = input_parameters_to_display_messages(set_msg_parameter_vector);
if (display_messages.empty()) {
return;
}
auto [status, status_info] = r_dm->call_set_display_message(display_messages);
std::string log_prefix{"Set " + std::to_string(display_messages.size()) + " message(s)"};
log_cmd_response(&log, log_prefix, display_message_status_enum_to_string(status), status_info);
reset_msg_input_widgets();
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
/* ********************** COMPOSITION ******************************************** */
auto set_parameters_renderer = Renderer(msg_inputs_component, [&] {
Elements elements;
for (auto& input_mask : set_msg_input_masks) {
elements.push_back(input_mask->Render());
}
return vbox(std::move(elements));
});
auto set_component = Container::Vertical({set_parameters_renderer, add_msg_button, set_button});
/***************************************************************************************+
* Get Display Messages
****************************************************************************************/
/* ********************** INPUTS ******************************************** */
std::string get_msg_id_str;
InputOption get_msg_id_input_o;
get_msg_id_input_o.multiline = false;
get_msg_id_input_o.cursor_position = 0;
get_msg_id_input_o.on_change = [&]() {
get_msg_id_str.erase(std::remove_if(get_msg_id_str.begin(), get_msg_id_str.end(),
[](char c) { return !std::isdigit(c) and c != ','; }),
get_msg_id_str.end());
};
auto get_msg_id_input = Input(&get_msg_id_str, "<comma-separated list of IDs or none>", get_msg_id_input_o);
/* ********************** DROPDOWNS ******************************************** */
int get_msg_priority_selected = MessagePriorityEnumStrings.size() - 1;
int get_msg_state_selected = MessageStateEnumStrings.size() - 1;
auto get_msg_priority_dropdown = Dropdown(&MessagePriorityEnumStrings, &get_msg_priority_selected);
auto get_msg_state_dropdown = Dropdown(&MessageStateEnumStrings, &get_msg_state_selected);
/* ********************** BUTTON ******************************************** */
auto get_button = Button(
"Get!",
[&] {
auto get_msg_ids = comma_separated_ids_to_vector(get_msg_id_str);
auto msgPriorityChoice = msg_priority_int_to_optional_enum(get_msg_priority_selected);
auto msgStateChoice = msg_state_int_to_optional_enum(get_msg_state_selected);
auto [status_info, messages] =
r_dm->call_get_display_messages({get_msg_ids, msgPriorityChoice, msgStateChoice});
std::string log_prefix{"Got " + (messages ? std::to_string(messages->size()) : "no") + " message(s)"};
log_cmd_response(&log, log_prefix, "", status_info);
for (auto msg : messages.value_or(std::vector<types::display_message::DisplayMessage>())) {
log_message(&log, msg);
}
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
/* ********************** COMPOSITION ******************************************** */
auto get_dropdowns_component = Container::Horizontal({get_msg_priority_dropdown, get_msg_state_dropdown});
auto get_parameters_component = Container::Vertical({
get_dropdowns_component,
get_msg_id_input,
});
auto get_parameters_renderer = Renderer(get_parameters_component, [&] {
return vbox({
text(" Command Parameters ") | inverted,
hbox(hbox(text(" [Priority] : ") | vcenter, get_msg_priority_dropdown->Render()), filler(),
hbox(text(" [State] : ") | vcenter, get_msg_state_dropdown->Render()), filler()),
hbox(text(" [Message Id] : "), get_msg_id_input->Render()),
text(" "),
});
});
auto get_component = Container::Vertical({get_parameters_renderer, get_button});
/***************************************************************************************+
* Clear Display Messages
****************************************************************************************/
/* ********************** INPUTS ******************************************** */
std::string clear_msg_id_str;
InputOption o;
o.multiline = false;
o.cursor_position = 0;
o.on_change = [&]() {
clear_msg_id_str.erase(
std::remove_if(clear_msg_id_str.begin(), clear_msg_id_str.end(), [](char c) { return !std::isdigit(c); }),
clear_msg_id_str.end());
};
auto clear_id_input = Input(&clear_msg_id_str, "<single id (int) required>", o);
/* ********************** BUTTON ******************************************** */
auto clear_button = Button(
"Clear!",
[&] {
int clear_msg_id;
try {
clear_msg_id = std::stoi(clear_msg_id_str);
clear_msg_id_str = std::to_string(clear_msg_id);
} catch (...) {
return;
}
auto [status, status_info] = r_dm->call_clear_display_message({clear_msg_id});
std::string log_prefix{"Cleared Messages with ID " + std::to_string(clear_msg_id)};
log_cmd_response(&log, log_prefix, clear_message_response_enum_to_string(status), status_info);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
/* ********************** COMPOSITION ******************************************** */
auto clear_parameters_renderer = Renderer(clear_id_input, [&] {
return vbox({
text(" Command Parameters ") | inverted,
hbox(text(" Message Id : ") | bold, clear_id_input->Render()),
text(" "),
});
});
auto clear_component = Container::Vertical({clear_parameters_renderer, clear_button});
/***************************************************************************************+
* Composition of full display
****************************************************************************************/
std::vector<std::string> tab_values{
"Set Message",
"Get Message",
"Clear Message",
};
int tab_selected = 0;
auto tab_toggle_component = Toggle(&tab_values, &tab_selected);
auto tab_toggle_renderer =
Renderer(tab_toggle_component, [&] { return window(text(" Commands "), tab_toggle_component->Render()); });
auto tab_container_component = Container::Tab(
{
set_component,
get_component,
clear_component,
},
&tab_selected);
auto main_container = Container::Vertical({
tab_toggle_renderer,
tab_container_component,
log_pane_renderer,
});
auto main_renderer = Renderer(main_container, [&] {
return vbox({
text("Display Message") | bold | hcenter,
main_container->Render(),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUDISPLAY_MESSAGE_HPP
#define BUDISPLAY_MESSAGE_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/display_message/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUDisplayMessage : public Everest::ModuleBase {
public:
BUDisplayMessage() = delete;
BUDisplayMessage(const ModuleInfo& info, std::unique_ptr<display_messageIntf> r_dm, Conf& config) :
ModuleBase(info), r_dm(std::move(r_dm)), config(config){};
const std::unique_ptr<display_messageIntf> r_dm;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUDISPLAY_MESSAGE_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for Display Message interface
requires:
dm:
interface: display_message
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Christoph Burandt

View File

@@ -0,0 +1,511 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "BUEvseBoardSupport.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/table.hpp"
#include "generated/types/evse_manager.hpp"
using namespace ftxui;
namespace module {
static std::vector<std::vector<std::string>> to_string(types::evse_board_support::HardwareCapabilities c) {
std::vector<std::vector<std::string>> hw_caps;
hw_caps.push_back(
{"Max/Min Current", fmt::format("Imp: {}A/{}A Exp: {}A/{}A", c.max_current_A_import, c.min_current_A_import,
c.max_current_A_export, c.min_current_A_export)});
hw_caps.push_back({"Max/Min Phase Count",
fmt::format("Imp: {}ph/{}ph Exp: {}ph/{}ph", c.max_phase_count_import, c.min_phase_count_import,
c.max_phase_count_export, c.min_phase_count_export)});
hw_caps.push_back({"Connector Type", types::evse_board_support::connector_type_to_string(c.connector_type)});
hw_caps.push_back({"CP State E Support", c.supports_cp_state_E ? "Yes" : "No"});
if (c.max_plug_temperature_C.has_value()) {
hw_caps.push_back({"Max plug temperature", fmt::format("{}C", c.max_plug_temperature_C.value())});
}
return hw_caps;
}
void BUEvseBoardSupport::init() {
}
void BUEvseBoardSupport::ready() {
auto screen = ScreenInteractive::Fullscreen();
r_bsp->subscribe_event([this, &screen](const types::board_support_common::BspEvent e) {
{
std::scoped_lock lock(data_mutex);
switch (e.event) {
case types::board_support_common::Event::A:
cp_state = "A";
break;
case types::board_support_common::Event::B:
cp_state = "B";
break;
case types::board_support_common::Event::C:
cp_state = "C";
break;
case types::board_support_common::Event::D:
cp_state = "D";
break;
case types::board_support_common::Event::E:
cp_state = "E";
break;
case types::board_support_common::Event::F:
cp_state = "F";
break;
case types::board_support_common::Event::PowerOn:
relais_feedback = fmt::format("PowerOn (after {} ms)",
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - last_allow_power_on_time_point)
.count());
break;
case types::board_support_common::Event::PowerOff:
relais_feedback = fmt::format("PowerOff (after {} ms)",
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - last_allow_power_on_time_point)
.count());
break;
}
}
screen.Post(Event::Custom);
});
r_bsp->subscribe_capabilities([this, &screen](const types::evse_board_support::HardwareCapabilities c) {
{
std::scoped_lock lock(data_mutex);
hw_caps = to_string(c);
}
screen.Post(Event::Custom);
});
r_bsp->subscribe_telemetry([this, &screen](const types::evse_board_support::Telemetry t) {
{
std::scoped_lock lock(data_mutex);
telemetry = fmt::format("EVSE {}C Fan {}rpm Supply {}V/{}V, Relais {}", t.evse_temperature_C, t.fan_rpm,
t.supply_voltage_12V, t.supply_voltage_minus_12V, t.relais_on);
if (t.plug_temperature_C.has_value()) {
telemetry = telemetry += fmt::format(" Plug {}C ", t.plug_temperature_C.value());
}
}
screen.Post(Event::Custom);
});
r_bsp->subscribe_ac_pp_ampacity([this, &screen](const types::board_support_common::ProximityPilot pp) {
{
std::scoped_lock lock(data_mutex);
proximity_pilot = types::board_support_common::ampacity_to_string(pp.ampacity);
}
screen.Post(Event::Custom);
});
r_bsp->subscribe_request_stop_transaction([this, &screen](const types::evse_manager::StopTransactionRequest t) {
{
std::scoped_lock lock(data_mutex);
stop_transaction = stop_transaction_reason_to_string(t.reason);
}
screen.Post(Event::Custom);
});
// telemetry, pp (needs to be requested as well)
auto error_handler = [this, &screen](const Everest::error::Error& error) {
{
std::scoped_lock lock(this->data_mutex);
this->last_error_raised = fmt::format("Fault raised: {}, {}", error.type, error.sub_type);
}
screen.Post(Event::Custom);
};
auto error_cleared_handler = [this, &screen](const Everest::error::Error& error) {
{
std::scoped_lock lock(this->data_mutex);
this->last_error_cleared = fmt::format("Fault cleared: {}, {}", error.type, error.sub_type);
}
screen.Post(Event::Custom);
};
r_bsp->subscribe_all_errors(error_handler, error_cleared_handler);
if (!r_ac_rcd.empty()) {
r_ac_rcd.at(0)->subscribe_all_errors(error_handler, error_cleared_handler);
}
if (!r_lock_motor.empty()) {
r_lock_motor.at(0)->subscribe_all_errors(error_handler, error_cleared_handler);
}
std::string last_command = "None";
// ---------------------------------------------------------------------------
// Relais allow power on
// ---------------------------------------------------------------------------
Component relais_on_button = Button(
"Allow power on",
[&] {
last_command = "Allow power on";
last_allow_power_on_time_point = std::chrono::steady_clock::now();
r_bsp->call_allow_power_on({true, types::evse_board_support::Reason::FullPowerCharging});
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component relais_off_button = Button(
"Force power off",
[&] {
last_command = "Force power off";
last_allow_power_on_time_point = std::chrono::steady_clock::now();
r_bsp->call_allow_power_on({false, types::evse_board_support::Reason::PowerOff});
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
auto relais_component = Container::Horizontal({
Container::Vertical({
relais_on_button,
relais_off_button,
}),
});
auto relais_renderer = Renderer(relais_component, [&] {
auto relais_win = window(text("Relais"), relais_component->Render());
return vbox({
hbox({
relais_win,
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// PWM control
// ---------------------------------------------------------------------------
std::string pwm_duty_cycle_str{"5.0"};
Component cp_state_X1_button = Button(
"CP State X1 (PWM Off)",
[&] {
last_command = "CP State X1 (PWM Off)";
r_bsp->call_cp_state_X1();
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component pwm_on_button = Button(
"PWM On",
[&] {
if (pwm_duty_cycle_str.empty())
pwm_duty_cycle_str = "5.0";
last_command = "PWM On (" + pwm_duty_cycle_str + ")";
r_bsp->call_pwm_on(std::stof(pwm_duty_cycle_str.c_str()));
},
ButtonOption::Animated(Color::Green, Color::White, Color::GreenLight, Color::White));
Component cp_state_E_button = Button(
"CP State E",
[&] {
last_command = "CP State E";
r_bsp->call_cp_state_E();
},
ButtonOption::Animated(Color::Magenta, Color::White, Color::MagentaLight, Color::White));
Component cp_state_F_button = Button(
"CP State F",
[&] {
last_command = "CP State F";
r_bsp->call_cp_state_F();
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
InputOption o;
o.multiline = false;
o.cursor_position = 0;
o.on_enter = [&]() {
last_command = "Update PWM DC to " + pwm_duty_cycle_str;
r_bsp->call_pwm_on(std::stof(pwm_duty_cycle_str.c_str()));
};
auto pwm_dc = Input(&pwm_duty_cycle_str, "5.0", o);
auto pwm_component = Container::Horizontal({
Container::Vertical({
cp_state_E_button,
cp_state_F_button,
cp_state_X1_button,
pwm_on_button,
pwm_dc,
}),
});
auto pwm_renderer = Renderer(pwm_component, [&] {
auto pwm_win = window(text("PWM"), pwm_component->Render());
return vbox({
hbox({
pwm_win,
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Enable/Disable
// ---------------------------------------------------------------------------
Component enable_on_button = Button(
"Enable",
[&] {
last_command = "Enable";
r_bsp->call_enable(true);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component enable_off_button = Button(
"Disable",
[&] {
last_command = "Disable";
r_bsp->call_enable(false);
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
auto enable_component = Container::Vertical({
enable_on_button,
enable_off_button,
});
auto enable_renderer =
Renderer(enable_component, [&] { return window(text("Enable"), enable_component->Render()); });
// ---------------------------------------------------------------------------
// AC switch phases while charging
// ---------------------------------------------------------------------------
Component three_phase_button = Button(
"ThreePhases",
[&] {
last_command = "Switch ThreePhases";
r_bsp->call_ac_switch_three_phases_while_charging(true);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component single_phase_button = Button(
"SinglePhase",
[&] {
last_command = "Switch SinglePhase";
r_bsp->call_ac_switch_three_phases_while_charging(false);
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
auto phase_switch_component = Container::Vertical({
three_phase_button,
single_phase_button,
});
auto phase_switch_renderer = Renderer(
phase_switch_component, [&] { return window(text("AC Phase Switch"), phase_switch_component->Render()); });
// ---------------------------------------------------------------------------
// AC Overcurrent Limit command
// ---------------------------------------------------------------------------
std::string ac_oc_limit_str = "16.0"; // Default current limit (A)
Component ac_oc_limit_button = Button(
"Set Limit",
[&] {
if (ac_oc_limit_str.empty())
ac_oc_limit_str = "16.0";
last_command = "Set AC Overcurrent Limit (" + ac_oc_limit_str + " A)";
r_bsp->call_ac_set_overcurrent_limit_A(std::stof(ac_oc_limit_str.c_str()));
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
InputOption ac_oc_input_opt;
ac_oc_input_opt.multiline = false;
ac_oc_input_opt.cursor_position = 0;
ac_oc_input_opt.on_enter = [&]() {
if (ac_oc_limit_str.empty())
ac_oc_limit_str = "16.0";
last_command = "Set AC Overcurrent Limit (" + ac_oc_limit_str + " A)";
r_bsp->call_ac_set_overcurrent_limit_A(std::stof(ac_oc_limit_str.c_str()));
};
auto ac_oc_input = Input(&ac_oc_limit_str, "16.0", ac_oc_input_opt);
auto ac_oc_component = Container::Vertical({
ac_oc_limit_button,
ac_oc_input,
});
auto ac_oc_renderer =
Renderer(ac_oc_component, [&] { return window(text("AC Overcurrent Limit"), ac_oc_component->Render()); });
// ---------------------------------------------------------------------------
// Vars
// ---------------------------------------------------------------------------
auto vars_renderer = Renderer([&] {
std::vector<std::vector<std::string>> table_content;
{
std::scoped_lock lock(data_mutex);
table_content = {{"CP State", cp_state},
{"Relais", relais_feedback},
{"Telemetry", telemetry},
{"Proximity Pilot", proximity_pilot}};
}
auto vars = Table(table_content);
vars.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < table_content.size(); i++) {
vars.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(
text("Information"),
vbox({text("Last command: " + last_command), text("Last error raised: " + last_error_raised),
text("Last error cleared: " + last_error_cleared), vars.Render()})),
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Request stop transaction
// ---------------------------------------------------------------------------
auto vars_stop_transaction_renderer = Renderer([&] {
Element reason = text("Request stop transaction: " + stop_transaction);
return vbox({
hbox({
window(text("Buttons"), reason),
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Capabilities
// ---------------------------------------------------------------------------
types::evse_board_support::HardwareCapabilities dummy_caps;
dummy_caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Cable;
hw_caps = to_string(dummy_caps);
auto caps_renderer = Renderer([&] {
std::vector<std::vector<std::string>> caps_table_content;
{
std::scoped_lock lock(data_mutex);
caps_table_content = hw_caps;
}
auto caps = Table(caps_table_content);
caps.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < caps_table_content.size(); i++) {
caps.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Capabilities"), caps.Render()),
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// AC RCD
// ---------------------------------------------------------------------------
auto no_rcd_text = Renderer([] { return text("No AC RCD module connected in config."); });
auto rcd_component = Container::Vertical({
no_rcd_text,
});
if (!r_ac_rcd.empty()) {
std::string rcd_current_display = "N/A";
std::string rcd_reset_result = "";
Component rcd_self_test_button = Button(
"Self Test",
[&] {
last_command = "AC RCD Self Test";
r_ac_rcd.at(0)->call_self_test();
},
ButtonOption::Animated(Color::Green, Color::White, Color::GreenLight, Color::White));
Component rcd_reset_button = Button(
"Reset",
[&] {
last_command = "AC RCD Reset";
bool reset_result = r_ac_rcd.at(0)->call_reset();
rcd_reset_result = fmt::format("Reset result: {}", reset_result ? "Success" : "Failed");
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
rcd_component = Container::Vertical({
rcd_self_test_button,
rcd_reset_button,
});
}
// ---------------------------------------------------------------------------
// Connector Lock
// ---------------------------------------------------------------------------
auto no_lock_text = Renderer([] { return text("No Connector Lock module connected in config."); });
auto lock_component = Container::Vertical({
no_lock_text,
});
if (!r_lock_motor.empty()) {
Component lock_button = Button(
"Connector Lock",
[&] {
last_command = "Connector Lock";
r_lock_motor.at(0)->call_lock();
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
Component unlock_button = Button(
"Connector Unlock",
[&] {
last_command = "Connector Unlock";
r_lock_motor.at(0)->call_unlock();
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
lock_component = Container::Vertical({
lock_button,
unlock_button,
});
}
// ---------------------------------------------------------------------------
// Main layout
// ---------------------------------------------------------------------------
auto command_row =
Container::Horizontal({pwm_renderer, relais_renderer, enable_renderer, phase_switch_renderer, ac_oc_renderer});
auto info_row = Container::Horizontal({
vars_renderer,
caps_renderer,
vars_stop_transaction_renderer,
});
// Add RCD and Lock components to the interactive container tree
auto main_container = Container::Vertical({info_row, command_row, rcd_component, lock_component});
auto main_renderer = Renderer(main_container, [&] {
return vbox(text("EVSE Board Support") | bold | hcenter, separator(), info_row->Render(), command_row->Render(),
separator(),
hbox(window(text("AC RCD"), vbox(text("RCD Current: " + rcd_current_display + " mA"), separator(),
rcd_component->Render(), text(rcd_reset_result))) |
flex,
separator(), window(text("Connector Lock"), lock_component->Render()) | flex));
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,81 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUEVSE_BOARD_SUPPORT_HPP
#define BUEVSE_BOARD_SUPPORT_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/ac_rcd/Interface.hpp>
#include <generated/interfaces/connector_lock/Interface.hpp>
#include <generated/interfaces/evse_board_support/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUEvseBoardSupport : public Everest::ModuleBase {
public:
BUEvseBoardSupport() = delete;
BUEvseBoardSupport(const ModuleInfo& info, std::unique_ptr<evse_board_supportIntf> r_bsp,
std::vector<std::unique_ptr<connector_lockIntf>> r_lock_motor,
std::vector<std::unique_ptr<ac_rcdIntf>> r_ac_rcd, Conf& config) :
ModuleBase(info),
r_bsp(std::move(r_bsp)),
r_lock_motor(std::move(r_lock_motor)),
r_ac_rcd(std::move(r_ac_rcd)),
config(config){};
const std::unique_ptr<evse_board_supportIntf> r_bsp;
const std::vector<std::unique_ptr<connector_lockIntf>> r_lock_motor;
const std::vector<std::unique_ptr<ac_rcdIntf>> r_ac_rcd;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::mutex data_mutex;
std::string cp_state;
std::string relais_feedback;
std::string telemetry;
std::string proximity_pilot;
std::string stop_transaction;
std::vector<std::vector<std::string>> hw_caps;
std::chrono::time_point<std::chrono::steady_clock> last_allow_power_on_time_point;
std::string last_error_raised;
std::string last_error_cleared;
std::string rcd_current_display;
std::string rcd_reset_result;
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUEVSE_BOARD_SUPPORT_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,16 @@
description: Interactive bring up helper for EVSE Board support interface
requires:
bsp:
interface: evse_board_support
lock_motor:
interface: connector_lock
min_connections: 0
max_connections: 1
ac_rcd:
interface: ac_rcd
min_connections: 0
max_connections: 1
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Cornelius Claussen

View File

@@ -0,0 +1,132 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "BUIsolationMonitor.hpp"
#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Horizontal, Vertical, Input, Menu, Radiobox, ResizableSplitLeft, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/component_options.hpp" // for MenuOption, InputOption
#include "ftxui/component/event.hpp" // for Event, Event::Custom
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, color, operator|, bgcolor, filler, Element, vbox, size, hbox, separator, flex, window, graph, EQUAL, paragraph, WIDTH, hcenter, Elements, bold, vscroll_indicator, HEIGHT, flexbox, hflow, border, frame, flex_grow, gauge, paragraphAlignCenter, paragraphAlignJustify, paragraphAlignLeft, paragraphAlignRight, dim, spinner, LESS_THAN, center, yframe, GREATER_THAN
#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig
#include "ftxui/dom/table.hpp" // for Table
#include "ftxui/screen/color.hpp" // for Color, Color::BlueLight, Color::RedLight, Color::Black, Color::Blue, Color::Cyan, Color::CyanLight, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::White, Color::Yellow, Color::YellowLight, Color::Default, Color::Palette256, ftxui
#include "ftxui/screen/color_info.hpp" // for ColorInfo
#include "ftxui/screen/terminal.hpp" // for Size, Dimensions
using namespace ftxui;
namespace module {
static std::vector<std::vector<std::string>> to_table(types::isolation_monitor::IsolationMeasurement m) {
std::vector<std::vector<std::string>> measurement;
measurement.push_back({"Resistance F", fmt::format("{} Ohm", m.resistance_F_Ohm)});
if (m.voltage_V.has_value()) {
measurement.push_back({"DC Voltage", fmt::format("{} V", m.voltage_V.value())});
}
if (m.voltage_to_earth_l1e_V.has_value()) {
measurement.push_back({"DC Voltage to Earth L1E", fmt::format("{} V", m.voltage_to_earth_l1e_V.value())});
}
if (m.voltage_to_earth_l2e_V.has_value()) {
measurement.push_back({"DC Voltage to Earth L2E", fmt::format("{} V", m.voltage_to_earth_l2e_V.value())});
}
measurement.push_back({"Timestamp: ", Everest::Date::to_rfc3339(date::utc_clock::now())});
return measurement;
}
void BUIsolationMonitor::init() {
types::isolation_monitor::IsolationMeasurement m;
m.resistance_F_Ohm = 0;
last_measurement = to_table(m);
}
void BUIsolationMonitor::ready() {
auto screen = ScreenInteractive::Fullscreen();
r_imd->subscribe_isolation_measurement([this, &screen](const types::isolation_monitor::IsolationMeasurement m) {
std::scoped_lock lock(data_mutex);
last_measurement = to_table(m);
screen.Post(Event::Custom);
});
// ---------------------------------------------------------------------------
// Start IMD Measurements
// ---------------------------------------------------------------------------
Component start_button = Button(
"Start Measurements", [&] { r_imd->call_start(); },
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component stop_button = Button(
"Stop Measurements", [&] { r_imd->call_stop(); },
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component selftest_button = Button(
"Run Self-Test", [&] { r_imd->call_start_self_test(500); },
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
auto ctrl_component = Container::Horizontal({
Container::Vertical({
start_button,
stop_button,
selftest_button,
}),
});
auto ctrl_renderer = Renderer(ctrl_component, [&] {
auto ctrl_win = window(text("Control"), ctrl_component->Render());
return vbox({
hbox({
ctrl_win,
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Vars
// ---------------------------------------------------------------------------
auto vars_renderer = Renderer([&] {
std::vector<std::vector<std::string>> table_content;
{
std::scoped_lock lock(data_mutex);
table_content = last_measurement;
// table_content = {{"CP State", "cp_state"}, {"Relais", "relais_feedback"}, {"Telemetry", "telemetry"}};
}
auto vars = Table(table_content);
vars.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < table_content.size(); i++) {
vars.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Information"), vars.Render()),
}),
}) |
flex_grow;
});
auto main_container = Container::Horizontal({
ctrl_renderer,
vars_renderer,
});
auto main_renderer = Renderer(main_container, [&] {
return vbox({
text("Isolation Monitor") | bold | hcenter,
main_container->Render(),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUISOLATION_MONITOR_HPP
#define BUISOLATION_MONITOR_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/isolation_monitor/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUIsolationMonitor : public Everest::ModuleBase {
public:
BUIsolationMonitor() = delete;
BUIsolationMonitor(const ModuleInfo& info, std::unique_ptr<isolation_monitorIntf> r_imd, Conf& config) :
ModuleBase(info), r_imd(std::move(r_imd)), config(config){};
const std::unique_ptr<isolation_monitorIntf> r_imd;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::mutex data_mutex;
std::vector<std::vector<std::string>> last_measurement;
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUISOLATION_MONITOR_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for Isolation Monitor interface
requires:
imd:
interface: isolation_monitor
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Cornelius Claussen

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "BUOcppConsumer.hpp"
// for the moment use std::cout
// #include <ftxui/component/component.hpp>
// #include <ftxui/component/screen_interactive.hpp>
// #include <ftxui/dom/table.hpp>
#include <iostream>
namespace module {
void BUOcppConsumer::init() {
invoke_init(*p_ocpp);
invoke_init(*p_ocpp_data_transfer);
}
void BUOcppConsumer::ready() {
invoke_ready(*p_ocpp);
invoke_ready(*p_ocpp_data_transfer);
std::cout << "Ready...\n";
}
void BUOcppConsumer::event(const types::ocpp::GetVariableResult& ev) {
std::cout << "GetVariable: " << ev.component_variable.variable.name << '=' << ev.value.value_or("<none>") << " ("
<< ev.status << ")\n";
}
void BUOcppConsumer::event(const std::string& value, const types::ocpp::SetVariableResult& ev) {
std::cout << "SetVariable: " << ev.component_variable.variable.name << '=' << value << " (" << ev.status << ")\n";
}
void BUOcppConsumer::event_monitor_variable(const std::string& name) {
std::cout << "MonitorVariable: " << name << '\n';
}
} // namespace module

View File

@@ -0,0 +1,67 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef BUOCPP_CONSUMER_HPP
#define BUOCPP_CONSUMER_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for provided interface implementations
#include <generated/interfaces/ocpp/Implementation.hpp>
#include <generated/interfaces/ocpp_data_transfer/Implementation.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUOcppConsumer : public Everest::ModuleBase {
public:
BUOcppConsumer() = delete;
BUOcppConsumer(const ModuleInfo& info, std::unique_ptr<ocppImplBase> p_ocpp,
std::unique_ptr<ocpp_data_transferImplBase> p_ocpp_data_transfer, Conf& config) :
ModuleBase(info),
p_ocpp(std::move(p_ocpp)),
p_ocpp_data_transfer(std::move(p_ocpp_data_transfer)),
config(config){};
const std::unique_ptr<ocppImplBase> p_ocpp;
const std::unique_ptr<ocpp_data_transferImplBase> p_ocpp_data_transfer;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
void event(const types::ocpp::GetVariableResult& ev);
void event(const std::string& value, const types::ocpp::SetVariableResult& ev);
void event_monitor_variable(const std::string& name);
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUOCPP_CONSUMER_HPP

View File

@@ -0,0 +1,27 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
# target_link_libraries(${MODULE_NAME}
# PRIVATE ftxui::screen
# PRIVATE ftxui::dom
# PRIVATE ftxui::component
# )
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
"ocpp/ocppImpl.cpp"
"ocpp_data_transfer/ocpp_data_transferImpl.cpp"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,12 @@
description: Interactive bring up helper for OCPP consumer interface
provides:
ocpp:
description: OCPP inteface
interface: ocpp
ocpp_data_transfer:
description: OCPP data transfer towards the CSMS
interface: ocpp_data_transfer
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- James Chapman

View File

@@ -0,0 +1,92 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "ocppImpl.hpp"
namespace module {
namespace ocpp {
void ocppImpl::init() {
// TODO(james-ctc): populate variable_store with some initial values
}
void ocppImpl::ready() {
}
bool ocppImpl::handle_stop() {
// your code for cmd stop goes here
return true;
}
bool ocppImpl::handle_restart() {
// your code for cmd restart goes here
return true;
}
void ocppImpl::handle_security_event(types::ocpp::SecurityEvent& event) {
// your code for cmd security_event goes here
}
std::vector<types::ocpp::GetVariableResult>
ocppImpl::handle_get_variables(std::vector<types::ocpp::GetVariableRequest>& requests) {
// your code for cmd get_variables goes here
std::vector<types::ocpp::GetVariableResult> resultList;
for (const auto& i : requests) {
types::ocpp::GetVariableResult result{types::ocpp::GetVariableStatusEnumType::Rejected, i.component_variable};
const std::string& name = i.component_variable.variable.name;
if (const auto it = variable_store.find(name); it != variable_store.end()) {
result.value = it->second;
result.status = types::ocpp::GetVariableStatusEnumType::Accepted;
}
mod->event(result);
resultList.push_back(result);
}
return resultList;
}
std::vector<types::ocpp::SetVariableResult>
ocppImpl::handle_set_variables(std::vector<types::ocpp::SetVariableRequest>& requests, std::string& source) {
// your code for cmd set_variables goes here
std::vector<types::ocpp::SetVariableResult> resultList;
for (const auto& i : requests) {
types::ocpp::SetVariableResult result{types::ocpp::SetVariableStatusEnumType::Rejected, i.component_variable};
const std::string& name = i.component_variable.variable.name;
if (!name.empty()) {
variable_store[name] = i.value;
result.status = types::ocpp::SetVariableStatusEnumType::Accepted;
if (const auto it = std::find(variable_monitor.begin(), variable_monitor.end(), name);
it != variable_monitor.end()) {
types::ocpp::EventData data{i.component_variable,
-1,
Everest::Date::to_rfc3339(date::utc_clock::now()),
types::ocpp::EventTriggerEnum::Delta,
i.value,
types::ocpp::EventNotificationType::PreconfiguredMonitor};
mod->p_ocpp->publish_event_data(data);
}
}
mod->event(i.value, result);
resultList.push_back(result);
}
return resultList;
}
types::ocpp::ChangeAvailabilityResponse
ocppImpl::handle_change_availability(types::ocpp::ChangeAvailabilityRequest& request) {
// your code for cmd change_availability goes here
return {};
}
void ocppImpl::handle_monitor_variables(std::vector<types::ocpp::ComponentVariable>& component_variables) {
// your code for cmd monitor_variables goes here
for (const auto& i : component_variables) {
const std::string& name = i.variable.name;
if (!name.empty()) {
variable_monitor.push_back(name);
mod->event_monitor_variable(name);
}
}
}
} // namespace ocpp
} // namespace module

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_OCPP_IMPL_HPP
#define OCPP_OCPP_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ocpp/Implementation.hpp>
#include "../BUOcppConsumer.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
#include <map>
#include <vector>
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace ocpp {
struct Conf {};
class ocppImpl : public ocppImplBase {
public:
ocppImpl() = delete;
ocppImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<BUOcppConsumer>& mod, Conf& config) :
ocppImplBase(ev, "ocpp"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
using Store = std::map<std::string, std::string>;
using Monitor = std::vector<std::string>;
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual bool handle_stop() override;
virtual bool handle_restart() override;
virtual void handle_security_event(types::ocpp::SecurityEvent& event) override;
virtual std::vector<types::ocpp::GetVariableResult>
handle_get_variables(std::vector<types::ocpp::GetVariableRequest>& requests) override;
virtual std::vector<types::ocpp::SetVariableResult>
handle_set_variables(std::vector<types::ocpp::SetVariableRequest>& requests, std::string& source) override;
virtual types::ocpp::ChangeAvailabilityResponse
handle_change_availability(types::ocpp::ChangeAvailabilityRequest& request) override;
virtual void handle_monitor_variables(std::vector<types::ocpp::ComponentVariable>& component_variables) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
Store variable_store;
Monitor variable_monitor;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<BUOcppConsumer>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// 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 ocpp
} // namespace module
#endif // OCPP_OCPP_IMPL_HPP

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "ocpp_data_transferImpl.hpp"
namespace module {
namespace ocpp_data_transfer {
void ocpp_data_transferImpl::init() {
}
void ocpp_data_transferImpl::ready() {
}
types::ocpp::DataTransferResponse
ocpp_data_transferImpl::handle_data_transfer(types::ocpp::DataTransferRequest& request) {
// your code for cmd data_transfer goes here
return {};
}
} // namespace ocpp_data_transfer
} // namespace module

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP
#define OCPP_DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ocpp_data_transfer/Implementation.hpp>
#include "../BUOcppConsumer.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace ocpp_data_transfer {
struct Conf {};
class ocpp_data_transferImpl : public ocpp_data_transferImplBase {
public:
ocpp_data_transferImpl() = delete;
ocpp_data_transferImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<BUOcppConsumer>& mod, Conf& config) :
ocpp_data_transferImplBase(ev, "ocpp_data_transfer"), 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 types::ocpp::DataTransferResponse handle_data_transfer(types::ocpp::DataTransferRequest& request) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<BUOcppConsumer>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// 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 ocpp_data_transfer
} // namespace module
#endif // OCPP_DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP

View File

@@ -0,0 +1,167 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "BUOverVoltageMonitor.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/table.hpp"
#include <regex>
namespace module {
using namespace ftxui;
void BUOverVoltageMonitor::init() {
}
void BUOverVoltageMonitor::ready() {
auto screen = ScreenInteractive::Fullscreen();
auto msg_component = Container::Vertical({Renderer([] { return text(""); })});
std::map<std::string, std::map<std::string, Component>> messages;
r_ovm->subscribe_all_errors(
[&](Everest::error::Error const& error) {
std::scoped_lock lock(data_mutex);
std::regex filter("over_voltage_monitor/");
auto error_type = std::regex_replace(error.type, filter, "");
auto error_elem = Renderer([=] { return text(" - " + error_type); });
auto sub_elem = Renderer([=] { return text(" " + error.sub_type); });
auto msg_elem = Renderer([=] { return text(" " + error.message); });
auto component = Container::Vertical({
error_elem,
sub_elem,
msg_elem,
});
messages[error.type][error.sub_type] = component;
msg_component->Add(component);
screen.Post(Event::Custom);
},
[&](Everest::error::Error const& error) {
std::scoped_lock lock(data_mutex);
if (messages.count(error.type)) {
auto& sub = messages.at(error.type);
if (sub.count(error.sub_type)) {
auto& elem = sub.at(error.sub_type);
elem->Detach();
sub.erase(error.sub_type);
}
if (not sub.size()) {
messages.erase(error.type);
}
}
screen.Post(Event::Custom);
});
r_ovm->subscribe_voltage_measurement_V([this, &screen](float v) {
std::scoped_lock lock(data_mutex);
voltage_measurement_V = fmt::format("{}V", v);
screen.Post(Event::Custom);
});
auto vars_renderer = Renderer([&] {
std::vector<std::vector<std::string>> table_content;
table_content = {{"Voltage measurement", voltage_measurement_V}};
auto vars = Table(table_content);
vars.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < table_content.size(); i++) {
vars.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Information"), vbox({vars.Render()})),
}),
}) |
flex_grow;
});
auto msg_component_holder = Container::Horizontal({msg_component});
auto msg_component_renderer = Renderer(msg_component_holder, [&] {
auto win = window(text("Active Errors"), msg_component_holder->Render());
return vbox({
hbox({
win,
}),
}) |
flex_grow;
});
InputOption o;
o.multiline = false;
o.cursor_position = 0;
auto error_limit_input = Input(&error_limit, "800.0", o);
auto emergency_limit_input = Input(&emergency_limit, "1000.0", o);
Component ovm_set_limit = Button(
"Set Error/Emergency Limits",
[&] {
auto error_val = std::stof(error_limit);
auto emergency_val = std::stof(emergency_limit);
r_ovm->call_set_limits(emergency_val, error_val);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White)) |
flex_grow;
Component ovm_start = Button(
"Start", [&] { r_ovm->call_start(); },
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White)) |
flex_grow;
Component ovm_stop = Button(
"Stop", [&] { r_ovm->call_stop(); },
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White)) |
flex_grow;
Component ovm_reset = Button(
"Reset", [&] { r_ovm->call_reset_over_voltage_error(); },
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White)) |
flex_grow;
auto action_component = Container::Horizontal({
Container::Vertical({
ovm_set_limit,
error_limit_input,
emergency_limit_input,
ovm_start,
ovm_stop,
ovm_reset,
}),
});
auto action_component_renderer = Renderer(action_component, [&] {
return vbox({hbox(text(" Over voltage error limit (V): "), error_limit_input->Render()),
hbox(text(" Over voltage emergency limit (V): "), emergency_limit_input->Render()),
hbox(ovm_set_limit->Render()), hbox(ovm_start->Render()), hbox(ovm_stop->Render()),
hbox(ovm_reset->Render())});
});
auto action_renderer = Renderer(action_component, [&] {
return vbox({
hbox({
window(text("Over Voltage Monitor Commands"), action_component_renderer->Render()),
}),
}) |
flex_grow;
});
auto main_container = Container::Horizontal({action_renderer, msg_component_renderer, vars_renderer});
auto main_renderer = Renderer(main_container, [&] {
std::scoped_lock lock(data_mutex);
return vbox({
text("Over Voltage Monitor Bringup") | bold | hcenter,
hbox({main_container->Render()}),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUOVER_VOLTAGE_MONITOR_HPP
#define BUOVER_VOLTAGE_MONITOR_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/over_voltage_monitor/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUOverVoltageMonitor : public Everest::ModuleBase {
public:
BUOverVoltageMonitor() = delete;
BUOverVoltageMonitor(const ModuleInfo& info, std::unique_ptr<over_voltage_monitorIntf> r_ovm, Conf& config) :
ModuleBase(info), r_ovm(std::move(r_ovm)), config(config){};
const std::unique_ptr<over_voltage_monitorIntf> r_ovm;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::string error_limit{"800.0"};
std::string emergency_limit{"1000.0"};
std::mutex data_mutex;
std::string voltage_measurement_V{"0.0V"};
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUOVER_VOLTAGE_MONITOR_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for over_voltage_monitor interface
requires:
ovm:
interface: over_voltage_monitor
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Jan Christoph Habig

View File

@@ -0,0 +1,254 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "BUPowerSupplyDC.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/table.hpp"
using namespace ftxui;
namespace module {
static std::vector<std::vector<std::string>> to_string(types::power_supply_DC::Capabilities c) {
std::vector<std::vector<std::string>> hw_caps;
hw_caps.push_back({"Bi-Directional", (c.bidirectional ? "yes" : "no")});
hw_caps.push_back({"Export", fmt::format("{}V/{}V {}A/{}A {}W Efficiency {}", c.max_export_voltage_V,
c.min_export_voltage_V, c.max_export_current_A, c.min_export_current_A,
c.max_export_power_W, c.conversion_efficiency_export.value_or(1.0))});
if (c.bidirectional) {
hw_caps.push_back(
{"Import", fmt::format("{}V/{}V {}A/{}A {}W Efficiency {}", c.max_import_voltage_V.value_or(0),
c.min_import_voltage_V.value_or(0), c.max_import_current_A.value_or(0),
c.min_import_current_A.value_or(0), c.max_import_power_W.value_or(0),
c.conversion_efficiency_import.value_or(1.0))});
}
hw_caps.push_back({"Current", fmt::format("Ripple {}A Regulation {}A", c.peak_current_ripple_A,
c.current_regulation_tolerance_A)});
return hw_caps;
}
void BUPowerSupplyDC::init() {
}
void BUPowerSupplyDC::ready() {
auto screen = ScreenInteractive::Fullscreen();
r_psu->subscribe_voltage_current([this, &screen](const types::power_supply_DC::VoltageCurrent v) {
std::scoped_lock lock(data_mutex);
actual_voltage = fmt::format("{}V", v.voltage_V);
actual_current = fmt::format("{}A", v.current_A);
screen.Post(Event::Custom);
});
r_psu->subscribe_mode([this, &screen](const types::power_supply_DC::Mode mode) {
std::scoped_lock lock(data_mutex);
actual_mode = mode_to_string(mode);
screen.Post(Event::Custom);
});
r_psu->subscribe_capabilities([this, &screen](const types::power_supply_DC::Capabilities caps) {
std::scoped_lock lock(data_mutex);
raw_caps = caps;
screen.Post(Event::Custom);
});
std::string last_command = "None";
// ---------------------------------------------------------------------------
// Select mode
// ---------------------------------------------------------------------------
Component psu_off = Button(
"Power Off",
[&] {
last_command = "Switch Off";
r_psu->call_setMode(types::power_supply_DC::Mode::Off, types::power_supply_DC::ChargingPhase::Other);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component psu_export_cablecheck = Button(
"Export (cablecheck EV)",
[&] {
last_command = "Export (precharge EV)";
r_psu->call_setMode(types::power_supply_DC::Mode::Export,
types::power_supply_DC::ChargingPhase::CableCheck);
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
Component psu_export_precharge = Button(
"Export (precharge EV)",
[&] {
last_command = "Export (precharge EV)";
r_psu->call_setMode(types::power_supply_DC::Mode::Export, types::power_supply_DC::ChargingPhase::PreCharge);
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
Component psu_export = Button(
"Export (charge EV)",
[&] {
last_command = "Export (charge EV)";
r_psu->call_setMode(types::power_supply_DC::Mode::Export, types::power_supply_DC::ChargingPhase::Charging);
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
Component psu_import = Button(
"Import (discharge EV)",
[&] {
last_command = "Import (discharge EV)";
r_psu->call_setMode(types::power_supply_DC::Mode::Import, types::power_supply_DC::ChargingPhase::Other);
},
ButtonOption::Animated(Color::Red, Color::White, Color::RedLight, Color::White));
auto mode_component = Container::Horizontal({
Container::Vertical({
psu_off,
psu_export_cablecheck,
psu_export_precharge,
psu_export,
psu_import,
}),
});
auto mode_renderer = Renderer(mode_component, [&] {
auto mode_win = window(text("Relais"), mode_component->Render());
return vbox({
hbox({
mode_win,
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// PWM control
// ---------------------------------------------------------------------------
std::string setpoint_export_voltage{"10.0"};
std::string setpoint_export_current{"1.0"};
std::string setpoint_import_voltage{"10.0"};
std::string setpoint_import_current{"1.0"};
Component set_export_voltage_current = Button(
"Set Export voltage/current limit",
[&] {
float v = std::stof(setpoint_export_voltage);
float c = std::stof(setpoint_export_current);
last_command = fmt::format("Set Export voltage/current limit ({}V / {}A)", v, c);
r_psu->call_setExportVoltageCurrent(v, c);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
Component set_import_voltage_current = Button(
"Set Import voltage/current limit",
[&] {
float v = std::stof(setpoint_import_voltage);
float c = std::stof(setpoint_import_current);
last_command = fmt::format("Set Import voltage/current limit ({}V / {}A)", v, c);
r_psu->call_setImportVoltageCurrent(v, c);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::BlueLight, Color::White));
InputOption o;
o.multiline = false;
o.cursor_position = 0;
auto export_voltage_input = Input(&setpoint_export_voltage, "10.0", o);
auto export_current_input = Input(&setpoint_export_current, "1.0", o);
auto import_voltage_input = Input(&setpoint_import_voltage, "10.0", o);
auto import_current_input = Input(&setpoint_import_current, "1.0", o);
auto voltage_current_component = Container::Horizontal({
Container::Vertical({
set_export_voltage_current,
set_import_voltage_current,
export_voltage_input,
export_current_input,
import_voltage_input,
import_current_input,
}),
});
auto voltage_current_component_renderer = Renderer(voltage_current_component, [&] {
return vbox({hbox(set_export_voltage_current->Render()),
hbox(text(" Export voltage (V): "), export_voltage_input->Render()),
hbox(text(" Export current (A): "), export_current_input->Render()),
hbox(set_import_voltage_current->Render()),
hbox(text(" Import voltage (V): "), import_voltage_input->Render()),
hbox(text(" Import current (A): "), import_current_input->Render())});
});
auto voltage_current_renderer = Renderer(voltage_current_component, [&] {
return vbox({
hbox({
window(text("Voltage/Current Setting"), voltage_current_component_renderer->Render()),
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Vars
// ---------------------------------------------------------------------------
auto vars_renderer = Renderer([&] {
Element last_command_element = text("Last command: " + last_command);
std::vector<std::vector<std::string>> table_content;
{
std::scoped_lock lock(data_mutex);
table_content = {
{"Actual voltage", actual_voltage}, {"Actual current", actual_current}, {"Actual mode", actual_mode}};
}
auto vars = Table(table_content);
vars.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < table_content.size(); i++) {
vars.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Information"), vbox({text("Last command: " + last_command), vars.Render()})),
}),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Capabilities
// ---------------------------------------------------------------------------
auto caps_renderer = Renderer([&] {
std::vector<std::vector<std::string>> caps_table_content;
{
std::scoped_lock lock(data_mutex);
caps_table_content = to_string(raw_caps);
}
auto caps = Table(caps_table_content);
caps.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < caps_table_content.size(); i++) {
caps.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Capabilities"), caps.Render()),
}),
}) |
flex_grow;
});
auto main_container = Container::Horizontal(
{voltage_current_renderer, mode_renderer, Container::Vertical({caps_renderer, vars_renderer})});
auto main_renderer = Renderer(main_container, [&] {
return vbox({
text("Power Supply DC Bringup") | bold | hcenter,
main_container->Render(),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUPOWER_SUPPLY_DC_HPP
#define BUPOWER_SUPPLY_DC_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/power_supply_DC/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUPowerSupplyDC : public Everest::ModuleBase {
public:
BUPowerSupplyDC() = delete;
BUPowerSupplyDC(const ModuleInfo& info, std::unique_ptr<power_supply_DCIntf> r_psu, Conf& config) :
ModuleBase(info), r_psu(std::move(r_psu)), config(config){};
const std::unique_ptr<power_supply_DCIntf> r_psu;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::mutex data_mutex;
types::power_supply_DC::Capabilities raw_caps;
std::string actual_voltage;
std::string actual_current;
std::string actual_mode;
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUPOWER_SUPPLY_DC_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for power supply DC interface
requires:
psu:
interface: power_supply_DC
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Cornelius Claussen

View File

@@ -0,0 +1,326 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "BUPowermeter.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/table.hpp"
#include <ranges>
using namespace ftxui;
namespace module {
void optional_add(std::vector<std::vector<std::string>>& table, std::string text, std::string s_value) {
table.push_back({text, s_value});
}
void optional_add(std::vector<std::vector<std::string>>& table, std::string text, std::optional<float> o_value) {
if (o_value.has_value()) {
table.push_back({text, std::to_string(o_value.value())});
}
}
void optional_add(std::vector<std::vector<std::string>>& table, std::string text, std::optional<std::string> o_value) {
if (o_value.has_value()) {
table.push_back({text, o_value.value()});
}
}
void BUPowermeter::init() {
}
// Helper function to wrap long text into multiple lines
std::vector<std::string> WrapText(const std::string& text, size_t max_width) {
std::vector<std::string> lines;
size_t start = 0;
if (max_width < text.size()) {
while (start < text.size()) {
size_t end = std::min(start + max_width, text.size());
size_t last_space = text.rfind(' ', end);
// If there's no space, split at max_width
if (last_space == std::string::npos || last_space < start) {
lines.push_back(text.substr(start, max_width));
start += max_width;
} else {
lines.push_back(text.substr(start, last_space - start));
start = last_space + 1; // Skip the space
}
}
} else {
lines.push_back(text);
}
return lines;
}
// Helper function to transform text strings into FTXUI Elements
std::vector<Element> WrapTextToElements(const std::vector<std::string>& strings) {
std::vector<Element> elements;
elements.reserve(strings.size()); // Preallocate memory
// Use std::transform to convert strings to FTXUI elements
std::transform(strings.begin(), strings.end(), std::back_inserter(elements),
[](const std::string& line) { return text(line); } // Convert each string to FTXUI text
);
return elements;
}
// Helper function to format and render the table content
std::vector<std::vector<Element>> FormatTableContent(const std::vector<std::vector<std::string>>& raw_table_content,
size_t max_width) {
std::vector<std::vector<Element>> table_elements;
for (const auto& row : raw_table_content) {
std::vector<Element> formatted_row;
for (const auto& cell : row) {
// Wrap long strings into multiple lines
std::vector<std::string> wrapped_lines = WrapText(cell, max_width);
// Convert wrapped lines into FTXUI text elements
auto cell_element = vbox(WrapTextToElements(wrapped_lines));
formatted_row.push_back(cell_element);
}
table_elements.push_back(std::move(formatted_row));
}
return table_elements;
}
void BUPowermeter::ready() {
auto screen = ScreenInteractive::Fullscreen();
r_powermeter->subscribe_public_key_ocmf([this, &screen](std::string pk) {
public_key = pk;
last_event_received = event_received;
event_received = std::chrono::steady_clock::now();
screen.Post(Event::Custom);
});
r_powermeter->subscribe_powermeter([this, &screen](types::powermeter::Powermeter pm) {
powermeter = pm;
last_event_received = event_received;
event_received = std::chrono::steady_clock::now();
screen.Post(Event::Custom);
});
// ---------------------------------------------------------------------------
// Commands that can be sent over Powermeter interface
// ---------------------------------------------------------------------------
Component start_transaction_button = Button(
"Start transaction",
[&] {
types::powermeter::OCMFUserIdentificationStatus
identification_status; ///< OCMF Identification Status (IS): General status for user assignment
std::vector<types::powermeter::OCMFIdentificationFlags>
identification_flags; ///< OCMF Identification Flags (IF): Detailed statements about the user
///< assignment, represented by one or more identifiers
types::powermeter::OCMFIdentificationType
identification_type; ///< OCMF Identification Type (IT): Type of identification data
std::optional<types::powermeter::OCMFIdentificationLevel>
identification_level; ///< OCMF Identification Level (IL): Encoded overall status of the user assignment
std::optional<std::string>
identification_data; ///< OCMF Identification Data (ID): The actual identification data e.g. a hex-coded
///< UID according to ISO 14443.
std::optional<std::string> tariff_text; ///< Tariff text
last_command = "Start transaction";
last_command_duration = "";
screen.Post(Event::Custom);
types::powermeter::TransactionReq tr;
tr.evse_id = config.evse_id;
tr.transaction_id = config.transaction_id;
tr.identification_status = types::powermeter::OCMFUserIdentificationStatus::ASSIGNED;
tr.identification_type = types::powermeter::OCMFIdentificationType::ISO14443;
tr.identification_data = config.identification_data;
tr.tariff_text = config.tariff_text;
auto now_b = std::chrono::steady_clock::now();
tr_start = r_powermeter->call_start_transaction(tr);
auto now_e = std::chrono::steady_clock::now();
last_command_duration =
fmt::format("{} ms", std::chrono::duration_cast<std::chrono::milliseconds>(now_e - now_b).count());
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
Component stop_transaction_button = Button(
"Stop transaction",
[&] {
last_command = "Stop transaction";
std::string transaction_id = config.transaction_id;
auto now_b = std::chrono::steady_clock::now();
tr_stop = r_powermeter->call_stop_transaction(transaction_id);
auto now_e = std::chrono::steady_clock::now();
last_command_duration =
fmt::format("{} ms", std::chrono::duration_cast<std::chrono::milliseconds>(now_e - now_b).count());
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
Component stop_transaction0_button = Button(
"Stop transaction empty",
[&] {
last_command = "Stop transaction";
std::string transaction_id = "";
auto now_b = std::chrono::steady_clock::now();
tr_stop = r_powermeter->call_stop_transaction(transaction_id);
auto now_e = std::chrono::steady_clock::now();
last_command_duration =
fmt::format("{} ms", std::chrono::duration_cast<std::chrono::milliseconds>(now_e - now_b).count());
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
Component stop_transaction1_button = Button(
"Stop transaction dummy",
[&] {
last_command = "Stop transaction";
std::string transaction_id = "DUMMY";
auto now_b = std::chrono::steady_clock::now();
tr_stop = r_powermeter->call_stop_transaction(transaction_id);
auto now_e = std::chrono::steady_clock::now();
last_command_duration =
fmt::format("{} ms", std::chrono::duration_cast<std::chrono::milliseconds>(now_e - now_b).count());
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
auto cmds_component = Container::Horizontal({
start_transaction_button,
stop_transaction_button,
stop_transaction0_button,
stop_transaction1_button,
});
auto cmds_renderer = Renderer(cmds_component, [&] {
auto cmds_win = window(text("Commands"), cmds_component->Render());
return vbox({
hbox({cmds_win}) | center,
separator(),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Vars
// ---------------------------------------------------------------------------
auto vars_renderer = Renderer([&] {
std::vector<std::vector<std::string>> table_content;
auto last_event_duration = fmt::format(
"{} ms",
std::chrono::duration_cast<std::chrono::milliseconds>(event_received - last_event_received).count());
types::units::Energy ed = {};
types::units::Power pd = {};
types::units::Voltage vd = {};
types::units_signed::SignedMeterValue svd = {};
types::units::Current cd = {};
types::units::Frequency fd = {};
optional_add(table_content, "Transaction start response: status",
types::powermeter::transaction_request_status_to_string(tr_start.status));
optional_add(table_content, "Transaction start response: error", tr_start.error);
optional_add(table_content, "Transaction start response: transaction_min_stop_time",
tr_start.transaction_min_stop_time);
optional_add(table_content, "Transaction start response: transaction_max_stop_time",
tr_start.transaction_max_stop_time);
optional_add(table_content, "Transaction stop response: status",
types::powermeter::transaction_request_status_to_string(tr_stop.status));
optional_add(table_content, "Transaction stop response: error", tr_stop.error);
optional_add(table_content, "Transaction stop response: signed_meter_value",
tr_stop.signed_meter_value.value_or(svd).signed_meter_data);
if (powermeter.signed_meter_value.has_value()) {
optional_add(table_content, "Powermeter: signed meter value",
powermeter.signed_meter_value.value().signed_meter_data);
optional_add(table_content, "Powermeter: signing method",
powermeter.signed_meter_value.value().signing_method);
optional_add(table_content, "Powermeter: encoding method",
powermeter.signed_meter_value.value().encoding_method);
optional_add(table_content, "Powermeter: public key", powermeter.signed_meter_value.value().public_key);
}
optional_add(table_content, "Powermeter: time stamp", powermeter.timestamp);
if (powermeter.temperatures.has_value()) {
int i = 1;
for (const auto& sensor : *powermeter.temperatures) {
std::string label = "Temperature sensor" + std::to_string(i++);
optional_add(table_content, label, sensor.temperature);
}
}
optional_add(table_content, "Powermeter: imported energy in Wh (from grid), total",
std::to_string(powermeter.energy_Wh_import.total));
optional_add(table_content, "Powermeter: imported energy in Wh (from grid): L1",
powermeter.energy_Wh_import.L1);
optional_add(table_content, "Powermeter: imported energy in Wh (from grid): L2",
powermeter.energy_Wh_import.L2);
optional_add(table_content, "Powermeter: imported energy in Wh (from grid): L3",
powermeter.energy_Wh_import.L3);
optional_add(table_content, "Powermeter: exported energy in Wh (to grid), total",
std::to_string(powermeter.energy_Wh_export.value_or(ed).total));
optional_add(table_content, "Powermeter: exported energy in Wh (to grid): L1",
powermeter.energy_Wh_export.value_or(ed).L1);
optional_add(table_content, "Powermeter: exported energy in Wh (to grid): L2",
powermeter.energy_Wh_export.value_or(ed).L2);
optional_add(table_content, "Powermeter: exported energy in Wh (to grid): L3",
powermeter.energy_Wh_export.value_or(ed).L3);
optional_add(table_content, "Powermeter: user defined meter ID", powermeter.meter_id);
optional_add(table_content, "Powermeter: 3 phase rotation error (ccw)", powermeter.phase_seq_error);
optional_add(table_content, "Powermeter: voltage in V, DC", powermeter.voltage_V.value_or(vd).DC);
optional_add(table_content, "Powermeter: voltage in V: L1", powermeter.voltage_V.value_or(vd).L1);
optional_add(table_content, "Powermeter: voltage in V: L2", powermeter.voltage_V.value_or(vd).L2);
optional_add(table_content, "Powermeter: voltage in V: L3", powermeter.voltage_V.value_or(vd).L3);
optional_add(table_content, "Powermeter: current in A, DC", powermeter.current_A.value_or(cd).DC);
optional_add(table_content, "Powermeter: current in A: L1", powermeter.current_A.value_or(cd).L1);
optional_add(table_content, "Powermeter: current in A: L2", powermeter.current_A.value_or(cd).L2);
optional_add(table_content, "Powermeter: current in A: L3", powermeter.current_A.value_or(cd).L3);
optional_add(table_content, "Powermeter: frequency in Hz: L1", powermeter.frequency_Hz.value_or(fd).L1);
optional_add(table_content, "Powermeter: frequency in Hz: L2", powermeter.frequency_Hz.value_or(fd).L2);
optional_add(table_content, "Powermeter: frequency in Hz: L3", powermeter.frequency_Hz.value_or(fd).L3);
int i = 1;
for (const auto& sensor : *powermeter.temperatures) {
std::string label = "Temperature sensor" + std::to_string(i++);
optional_add(table_content, label, sensor.temperature);
}
optional_add(table_content, "Public key", public_key);
size_t max_width = 120;
auto formatted_content = FormatTableContent(table_content, max_width);
auto vars = Table(std::move(formatted_content));
vars.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < table_content.size(); i++) {
vars.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Information"),
vbox({text("Last command: " + last_command),
text("Last command duration: " + last_command_duration),
text("Time between events: " + last_event_duration), vars.Render()})),
}) | center,
}) |
flex_grow;
});
auto main_container = Container::Vertical({
cmds_renderer,
vars_renderer,
});
auto main_renderer = Renderer(main_container, [&] {
return vbox({
text("Powermeter Component") | bold | hcenter,
main_container->Render(),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUPOWERMETER_HPP
#define BUPOWERMETER_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/powermeter/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {
std::string evse_id;
std::string transaction_id;
std::string identification_data;
std::string tariff_text;
};
class BUPowermeter : public Everest::ModuleBase {
public:
BUPowermeter() = delete;
BUPowermeter(const ModuleInfo& info, std::unique_ptr<powermeterIntf> r_powermeter, Conf& config) :
ModuleBase(info), r_powermeter(std::move(r_powermeter)), config(config){};
const std::unique_ptr<powermeterIntf> r_powermeter;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::string last_command;
std::string last_command_duration;
std::chrono::time_point<std::chrono::steady_clock> last_event_received;
std::chrono::time_point<std::chrono::steady_clock> event_received;
std::string public_key;
types::powermeter::Powermeter powermeter;
types::powermeter::TransactionStartResponse tr_start;
types::powermeter::TransactionStopResponse tr_stop;
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUPOWERMETER_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,25 @@
description: Interactive bring up helper for Powermeter interface
config:
evse_id:
description: "Id of this EVSE"
type: string
default: "EVSE_ID"
transaction_id:
description: "OCPP transaction UUID"
type: string
default: "12345678-1234-5678-1234-567812345678"
identification_data:
description: "User identification means, e.g. RFID card ID"
type: string
default: "A1B2C3D4E5F6"
tariff_text:
description: "Tariff Text"
type: string
default: "custom"
requires:
powermeter:
interface: powermeter
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Florin Mihut

View File

@@ -0,0 +1,145 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "BUSlac.hpp"
#include "ftxui/dom/table.hpp"
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
using namespace ftxui;
namespace module {
void BUSlac::init() {
}
void BUSlac::ready() {
auto screen = ScreenInteractive::Fullscreen();
r_slac->subscribe_state([this, &screen](const types::slac::State new_state) {
{
std::scoped_lock lock(data_mutex);
state = types::slac::state_to_string(new_state);
}
screen.PostEvent(Event::Custom);
});
r_slac->subscribe_dlink_ready([this, &screen](const bool& is_ready) {
{
std::scoped_lock lock(data_mutex);
dlink = is_ready ? "true" : "false";
}
screen.PostEvent(Event::Custom);
});
r_slac->subscribe_request_error_routine([this, &screen]() {
{
std::scoped_lock lock(data_mutex);
last_request_error_routine_timestamp = std::to_string(
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
}
screen.PostEvent(Event::Custom);
});
r_slac->subscribe_ev_mac_address([this, &screen](const std::string& mac) {
{
std::scoped_lock lock(data_mutex);
ev_mac_address = mac;
}
screen.PostEvent(Event::Custom);
});
// -------------------------------------------------------------------
// Left column (Var Display)
// -------------------------------------------------------------------
auto data_renderer = Renderer([&] {
std::vector<std::vector<std::string>> table_content;
{
std::scoped_lock lock(data_mutex);
table_content = {
{"State", state},
{"DLink Ready", dlink},
{"Last Request Error Routine", last_request_error_routine_timestamp},
{"EV MAC Address", ev_mac_address},
};
}
auto table = Table(table_content);
table.SelectAll().Border(LIGHT);
table.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < (int)table_content.size(); ++i)
table.SelectRow(i).Border(LIGHT);
return vbox({
window(text("Module Data"), vbox({
table.Render(),
})) |
size(WIDTH, EQUAL, 40),
}) |
flex_grow;
});
// -------------------------------------------------------------------
// Right column (Command Buttons)
// -------------------------------------------------------------------
auto button_start_slac = Button("Start SLAC", [&] { r_slac->call_reset(true); });
auto button_stop_slac = Button("Stop SLAC", [&] { r_slac->call_reset(false); });
auto button_enter_bcd = Button("Enter BCD", [&] { r_slac->call_enter_bcd(); });
auto button_leave_bcd = Button("Leave BCD", [&] { r_slac->call_leave_bcd(); });
auto button_dlink_terminate = Button("DLink Terminate", [&] { r_slac->call_dlink_terminate(); });
auto button_dlink_error = Button("DLink Error", [&] { r_slac->call_dlink_error(); });
auto button_dlink_pause = Button("DLink Pause", [&] { r_slac->call_dlink_pause(); });
// Compose the command panel layout
auto command_container = Container::Vertical({
Container::Horizontal({button_start_slac, button_stop_slac}),
button_enter_bcd,
button_leave_bcd,
button_dlink_terminate,
button_dlink_error,
button_dlink_pause,
});
auto command_renderer = Renderer(command_container, [&] {
return vbox({
text("Commands") | bold | center,
separator(),
button_start_slac->Render(),
button_stop_slac->Render(),
button_enter_bcd->Render(),
button_leave_bcd->Render(),
button_dlink_terminate->Render(),
button_dlink_error->Render(),
button_dlink_pause->Render(),
}) |
border | size(WIDTH, EQUAL, 40);
});
// -------------------------------------------------------------------
// Combine columns
// -------------------------------------------------------------------
auto layout = Container::Horizontal({
data_renderer,
command_renderer,
});
auto main_renderer = Renderer(layout, [&] {
return vbox({
text("SLAC BringUp") | bold | center,
separator(),
hbox({
data_renderer->Render(),
command_renderer->Render(),
}),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef BUSLAC_HPP
#define BUSLAC_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/slac/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUSlac : public Everest::ModuleBase {
public:
BUSlac() = delete;
BUSlac(const ModuleInfo& info, std::unique_ptr<slacIntf> r_slac, Conf& config) :
ModuleBase(info), r_slac(std::move(r_slac)), config(config){};
const std::unique_ptr<slacIntf> r_slac;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::mutex data_mutex;
std::string state;
std::string dlink;
std::string last_request_error_routine_timestamp;
std::string ev_mac_address;
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUSLAC_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for SLAC interface
requires:
slac:
interface: slac
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Piet Gömpel

View File

@@ -0,0 +1,202 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "BUSystem.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/table.hpp"
using namespace ftxui;
namespace module {
void BUSystem::init() {
}
void BUSystem::ready() {
auto screen = ScreenInteractive::Fullscreen();
r_system->subscribe_firmware_update_status([this, &screen](const types::system::FirmwareUpdateStatus fus) {
firmware_update_status = types::system::firmware_update_status_enum_to_string(fus.firmware_update_status);
screen.Post(Event::Custom);
});
r_system->subscribe_log_status([this, &screen](const types::system::LogStatus ls) {
log_status = types::system::log_status_enum_to_string(ls.log_status);
screen.Post(Event::Custom);
});
// ---------------------------------------------------------------------------
// Commands that can be sent over System interface
// ---------------------------------------------------------------------------
Component allow_firmware_installation_button = Button(
"Allow update",
[&] {
last_command = "Allow update";
r_system->call_allow_firmware_installation();
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
Component get_reset_allowed_button = Button(
"Is reset allowed",
[&] {
last_command = "Is reset allowed";
auto allowed = r_system->call_is_reset_allowed(types::system::ResetType::Soft);
is_reset_allowed = (allowed) ? "true" : "false";
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
Component reset_button = Button(
"Reset",
[&] {
last_command = "Reset";
types::system::ResetType type = types::system::ResetType::Soft;
bool scheduled = false;
r_system->call_reset(type, scheduled);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
Component get_boot_reason_button = Button(
"Get boot reason",
[&] {
last_command = "Get boot reason";
auto reason = r_system->call_get_boot_reason();
boot_reason = types::system::boot_reason_to_string(reason);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
auto cmds_component = Container::Horizontal({
allow_firmware_installation_button,
get_reset_allowed_button,
reset_button,
get_boot_reason_button,
});
auto cmds_renderer = Renderer(cmds_component, [&] {
auto cmds_win = window(text("Commands"), cmds_component->Render());
return vbox({
hbox({cmds_win}) | center,
separator(),
}) |
flex_grow;
});
auto log_location = Input(&log_upload_location, "Upload location");
Component log_button = Button(
"Upload logs",
[&] {
last_command = "Upload logs";
types::system::UploadLogsRequest ulr;
ulr.location = log_upload_location;
ulr.retries = 1;
ulr.retry_interval_s = 1;
auto result = r_system->call_upload_logs(ulr);
upload_logs_status =
fmt::format("Upload status:{}", upload_logs_status_to_string(result.upload_logs_status));
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
auto log_container = Container::Horizontal({
log_location,
log_button,
});
auto input_log_renderer = Renderer(log_container, [&] {
return vbox({
hbox(text(" Upload logs URL : "), log_location->Render(), log_button->Render()),
}) |
flex_grow;
});
auto input_time = Input(&new_system_time, "New system time in RFC3339 format");
Component time_button = Button(
"Set system time",
[&] {
auto result = r_system->call_set_system_time(new_system_time);
last_command = (result) ? "Set system time - successful" : "Set system time - unsuccessful";
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
auto input_time_container = Container::Horizontal({
input_time,
time_button,
});
auto input_time_renderer = Renderer(input_time_container, [&] {
return vbox({
hbox(text(" New system time : "), input_time->Render(), time_button->Render()),
}) |
flex_grow;
});
auto input_address = Input(&firmware_update_url, "URL where to take the update from");
Component update_button = Button(
"Update firmware",
[&] {
last_command = "Update";
types::system::FirmwareUpdateRequest fur;
fur.request_id = 1;
fur.location = firmware_update_url;
auto result = r_system->call_update_firmware(fur);
update_firmware_response = update_firmware_response_to_string(result);
},
ButtonOption::Animated(Color::Blue, Color::White, Color::RedLight, Color::White));
auto input_container = Container::Horizontal({
input_address,
update_button,
});
auto input_renderer = Renderer(input_container, [&] {
return vbox({
hbox(text(" URL of the update : "), input_address->Render(), update_button->Render()),
}) |
flex_grow;
});
// ---------------------------------------------------------------------------
// Vars
// ---------------------------------------------------------------------------
auto vars_renderer = Renderer([&] {
Element last_command_element = text("Last command: " + last_command);
std::vector<std::vector<std::string>> table_content;
{
// std::scoped_lock lock(data_mutex);
table_content = {{"Firmware update status", firmware_update_status},
{"Firmware update response", update_firmware_response},
{"Get boot reason", boot_reason},
{"Upload log status", upload_logs_status},
{"Log status", log_status},
{"Is reset allowed", is_reset_allowed}};
}
auto vars = Table(table_content);
vars.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < table_content.size(); i++) {
vars.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Information"), vbox({text("Last command: " + last_command), vars.Render()})),
}) | center,
}) |
flex_grow;
});
auto main_container = Container::Vertical({
input_renderer,
input_time_renderer,
input_log_renderer,
cmds_renderer,
vars_renderer,
});
auto main_renderer = Renderer(main_container, [&] {
return vbox({
text("System Component") | bold | hcenter,
main_container->Render(),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUSYSTEM_HPP
#define BUSYSTEM_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/system/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUSystem : public Everest::ModuleBase {
public:
BUSystem() = delete;
BUSystem(const ModuleInfo& info, std::unique_ptr<systemIntf> r_system, Conf& config) :
ModuleBase(info), r_system(std::move(r_system)), config(config){};
const std::unique_ptr<systemIntf> r_system;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::string last_command;
std::string firmware_update_status;
std::string log_status;
std::string is_reset_allowed;
std::string boot_reason;
std::string firmware_update_url;
std::string new_system_time;
std::string log_upload_location;
std::string upload_logs_status;
std::string update_firmware_response;
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUSYSTEM_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for System component
requires:
system:
interface: system
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Florin Mihut

View File

@@ -0,0 +1,84 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include <chrono>
#include "BUTokenProvider.hpp"
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/table.hpp"
namespace module {
using namespace ftxui;
static std::vector<std::vector<std::string>> to_table(types::authorization::ProvidedIdToken t) {
std::vector<std::vector<std::string>> token;
token.push_back({"Protocol", fmt::format("{}:", id_token_type_to_string(t.id_token.type))});
token.push_back({"UID", fmt::format("{}", t.id_token.value)});
std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm* p_tm = std::localtime(&now);
std::ostringstream oss;
oss << std::put_time(p_tm, "%Y-%m-%d %H:%M:%S");
token.push_back({"Timestamp", oss.str()});
return token;
}
void BUTokenProvider::init() {
types::authorization::ProvidedIdToken t;
t.id_token.type = types::authorization::IdTokenType::NoAuthorization;
t.id_token.value = std::string("0");
last_token = to_table(t);
}
void BUTokenProvider::ready() {
auto screen = ScreenInteractive::Fullscreen();
r_token_provider->subscribe_provided_token([this, &screen](const types::authorization::ProvidedIdToken t) {
std::scoped_lock lock(data_mutex);
last_token = to_table(t);
screen.Post(Event::Custom);
});
// ---------------------------------------------------------------------------
// Vars
// ---------------------------------------------------------------------------
auto vars_renderer = Renderer([&] {
std::vector<std::vector<std::string>> table_content;
{
std::scoped_lock lock(data_mutex);
table_content = last_token;
}
auto vars = Table(table_content);
vars.SelectColumn(0).Border(LIGHT);
for (int i = 0; i < table_content.size(); i++) {
vars.SelectRow(i).Border(LIGHT);
}
return vbox({
hbox({
window(text("Information"), vars.Render()),
}),
}) |
flex_grow;
});
auto main_container = Container::Horizontal({
vars_renderer,
});
auto main_renderer = Renderer(main_container, [&] {
return vbox({
text("NFC Token Provider") | bold | hcenter,
main_container->Render(),
});
});
screen.Loop(main_renderer);
}
} // namespace module

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#ifndef BUTOKEN_PROVIDER_HPP
#define BUTOKEN_PROVIDER_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for required interface implementations
#include <generated/interfaces/auth_token_provider/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class BUTokenProvider : public Everest::ModuleBase {
public:
BUTokenProvider() = delete;
BUTokenProvider(const ModuleInfo& info, std::unique_ptr<auth_token_providerIntf> r_token_provider, Conf& config) :
ModuleBase(info), r_token_provider(std::move(r_token_provider)), config(config){};
const std::unique_ptr<auth_token_providerIntf> r_token_provider;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::mutex data_mutex;
std::vector<std::vector<std::string>> last_token;
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // BUTOKEN_PROVIDER_HPP

View File

@@ -0,0 +1,21 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
target_link_libraries(${MODULE_NAME}
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,8 @@
description: Interactive bring up helper for Token Provider interface
requires:
token_provider:
interface: auth_token_provider
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Christoph Burandt

View File

@@ -0,0 +1,15 @@
if (NOT ftxui_FOUND AND NOT DEFINED ftxui_SOURCE_DIR)
message(WARNING "Not building BringUp modules because dependency ftxui is not available")
else()
ev_add_module(BUDCExternalDerate)
ev_add_module(BUDisplayMessage)
ev_add_module(BUEvseBoardSupport)
ev_add_module(BUIsolationMonitor)
ev_add_module(BUOcppConsumer)
ev_add_module(BUOverVoltageMonitor)
ev_add_module(BUPowerSupplyDC)
ev_add_module(BUPowermeter)
ev_add_module(BUSlac)
ev_add_module(BUSystem)
ev_add_module(BUTokenProvider)
endif()