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,190 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
#include <array>
#include <cstdio>
#include <cstring>
#include <stdexcept>
#include <thread>
#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <everest/slac/fsm/ev/fsm.hpp>
#include <everest/slac/fsm/ev/states/others.hpp>
#include <everest/slac/fsm/evse/fsm.hpp>
#include <everest/slac/fsm/evse/states/others.hpp>
#include "plc_emu.hpp"
#include "socket_pair_bridge.hpp"
class EvseFsmController {
public:
explicit EvseFsmController(int evse_fd);
void trigger_enter_bcd();
void run();
private:
static constexpr uint64_t ENTER_BCD_EVENT_CODE = 1;
slac::fsm::evse::ContextCallbacks callbacks;
slac::fsm::evse::Context ctx{callbacks};
slac::fsm::evse::FSM fsm;
std::array<struct pollfd, 2> pollfds;
};
EvseFsmController::EvseFsmController(int evse_fd) :
pollfds({{
{evse_fd, POLLIN, 0},
{eventfd(0, 0), POLLIN, 0},
}}) {
ctx.slac_config.chip_reset.enabled = false;
callbacks.log = [](const std::string& msg) { printf("EVSE log: %s\n", msg.c_str()); };
callbacks.send_raw_slac = [evse_fd](slac::messages::HomeplugMessage& msg) {
write(evse_fd, msg.get_raw_message_ptr(), msg.get_raw_msg_len());
};
fsm.reset<slac::fsm::evse::ResetState>(ctx);
}
void EvseFsmController::trigger_enter_bcd() {
write(pollfds[1].fd, &ENTER_BCD_EVENT_CODE, sizeof(ENTER_BCD_EVENT_CODE));
}
void EvseFsmController::run() {
while (true) {
auto feed_result = fsm.feed();
if (feed_result.transition()) {
// call immediately again
continue;
} else if (feed_result.internal_error() || feed_result.unhandled_event()) {
throw std::runtime_error("Evse fsm: internal error / unhandled event");
// FIXME (aw): would need to log here!
}
const auto timeout = (feed_result.has_value()) ? *feed_result : -1;
if (timeout == 0) {
continue;
}
const auto poll_result = poll(pollfds.data(), pollfds.size(), timeout);
if (poll_result == 0) {
// timeout
continue;
}
// check for fds
if (pollfds[0].revents & POLLIN) {
auto raw_msg = ctx.slac_message_payload.get_raw_message_ptr();
read(pollfds[0].fd, raw_msg, sizeof(slac::messages::homeplug_message));
fsm.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
}
if (pollfds[1].revents & POLLIN) {
uint64_t tmp;
read(pollfds[1].fd, &tmp, sizeof(tmp));
if (tmp == ENTER_BCD_EVENT_CODE) {
fsm.handle_event(slac::fsm::evse::Event::ENTER_BCD);
}
// new event, for now, we do not care, later on we could check, if it is an exit event code
}
}
}
class EvFsmController {
public:
explicit EvFsmController(int ev_fd);
void run();
private:
slac::fsm::ev::ContextCallbacks callbacks;
slac::fsm::ev::Context ctx{callbacks};
slac::fsm::ev::FSM fsm;
std::array<struct pollfd, 2> pollfds;
};
EvFsmController::EvFsmController(int ev_fd) {
pollfds = {{
{ev_fd, POLLIN, 0},
{eventfd(0, 0), POLLIN, 0},
}};
callbacks.log = [](const std::string& msg) { printf("EV log: %s\n", msg.c_str()); };
callbacks.send_raw_slac = [ev_fd](slac::messages::HomeplugMessage& msg) {
write(ev_fd, msg.get_raw_message_ptr(), msg.get_raw_msg_len());
};
fsm.reset<slac::fsm::ev::ResetState>(ctx);
}
void EvFsmController::run() {
// start connecting
fsm.handle_event(slac::fsm::ev::Event::TRIGGER_MATCHING);
while (true) {
auto feed_result = fsm.feed();
if (feed_result.transition()) {
// call immediately again
continue;
} else if (feed_result.internal_error() || feed_result.unhandled_event()) {
throw std::runtime_error("Evse fsm: internal error / unhandled event");
// FIXME (aw): would need to log here!
}
const auto timeout = (feed_result.has_value()) ? *feed_result : -1;
if (timeout == 0) {
continue;
}
const auto poll_result = poll(pollfds.data(), pollfds.size(), timeout);
if (poll_result == 0) {
// timeout
continue;
}
if (pollfds[0].revents & POLLIN) {
auto raw_msg = ctx.slac_message.get_raw_message_ptr();
read(pollfds[0].fd, raw_msg, sizeof(slac::messages::homeplug_message));
fsm.handle_event(slac::fsm::ev::Event::SLAC_MESSAGE);
}
if (pollfds[1].revents & POLLIN) {
uint64_t tmp;
read(pollfds[1].fd, &tmp, sizeof(tmp));
// new event, for now, we do not care, later on we could check, if it is an exit event code
}
}
}
auto main(int argc, char* argv[]) -> int {
SocketPairBridge spb{handle_ev_input, handle_evse_input};
EvseFsmController evse_ctrl{spb.get_evse_socket()};
EvFsmController ev_ctrl{spb.get_ev_socket()};
std::thread evse_thread(&EvseFsmController::run, &evse_ctrl);
// give the EVSE some time to get ready
std::this_thread::sleep_for(std::chrono::milliseconds(500));
evse_ctrl.trigger_enter_bcd();
std::thread ev_thread(&EvFsmController::run, &ev_ctrl);
// the fsm controller could poll on its fd by itself and look for new slac messages
evse_thread.join();
ev_thread.join();
// each controller needs to have a feeding thread, and an async input function for the slac message
return 0;
}

View File

@@ -0,0 +1,101 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
#include "plc_emu.hpp"
#include <cstdio>
#include <cstring>
#include <random>
#include <unistd.h>
#include <slac/slac.hpp>
// NOTE (aw): this is only intended to be used for this test, if multiple instances use these handle functions, they
// will race the buffer
static slac::messages::HomeplugMessage homeplug_message;
constexpr static uint8_t EV_MAC_ADDR[ETH_ALEN] = {0x00, 0x7d, 0xfa, 0x09, 0xfe, 0x76};
constexpr static uint8_t EVSE_MAC_ADDR[ETH_ALEN] = {0x6e, 0x3f, 0x46, 0x32, 0xbf, 0xc6};
constexpr static uint8_t PLC_SRC_MAC_ADDR[ETH_ALEN] = {0x00, 0x01, 0x87, 0x0e, 0xa3, 0x55};
static void handle_set_key_req(int origin_fd) {
slac::messages::cm_set_key_cnf set_key_cnf;
// FIXME (aw): proper message and mac header setup!
homeplug_message.setup_payload(&set_key_cnf, sizeof(set_key_cnf),
(slac::defs::MMTYPE_CM_SET_KEY | slac::defs::MMTYPE_MODE_CNF),
slac::defs::MMV::AV_1_1);
auto raw = homeplug_message.get_raw_message_ptr();
memcpy(raw->ethernet_header.ether_dhost, raw->ethernet_header.ether_shost,
sizeof(raw->ethernet_header.ether_dhost));
memcpy(raw->ethernet_header.ether_shost, PLC_SRC_MAC_ADDR, sizeof(PLC_SRC_MAC_ADDR));
write(origin_fd, raw, homeplug_message.get_raw_msg_len());
}
static void attach_atten_profile(int evse_bridge_fd) {
slac::messages::cm_atten_profile_ind atten_profile;
memcpy(atten_profile.pev_mac, homeplug_message.get_src_mac(), sizeof(atten_profile.pev_mac));
atten_profile.num_groups = slac::defs::AAG_LIST_LEN;
std::random_device rnd_dev;
std::mt19937 rng(rnd_dev());
std::uniform_int_distribution<std::mt19937::result_type> db_dist(20, 27);
for (auto i = 0; i < atten_profile.num_groups; ++i) {
atten_profile.aag[i] = db_dist(rng);
}
homeplug_message.setup_payload(&atten_profile, sizeof(atten_profile),
(slac::defs::MMTYPE_CM_ATTEN_PROFILE | slac::defs::MMTYPE_MODE_IND),
slac::defs::MMV::AV_1_1);
auto raw = homeplug_message.get_raw_message_ptr();
memcpy(raw->ethernet_header.ether_shost, PLC_SRC_MAC_ADDR, sizeof(PLC_SRC_MAC_ADDR));
write(evse_bridge_fd, raw, homeplug_message.get_raw_msg_len());
}
void handle_ev_input(int ev_bridge_fd, int evse_bridge_fd) {
auto raw_hp_message = homeplug_message.get_raw_message_ptr();
auto bytes_read = read(ev_bridge_fd, raw_hp_message, sizeof(slac::messages::homeplug_message));
const auto mmtype = homeplug_message.get_mmtype();
// printf("EV send message of size %d and type 0x%hx\n", bytes_read, mmtype);
// patch in "our" mac address
memcpy(raw_hp_message->ethernet_header.ether_shost, EV_MAC_ADDR, sizeof(EV_MAC_ADDR));
if (mmtype == (slac::defs::MMTYPE_CM_SET_KEY | slac::defs::MMTYPE_MODE_REQ)) {
handle_set_key_req(ev_bridge_fd);
} else {
// default: forward message
write(evse_bridge_fd, raw_hp_message, bytes_read);
}
if (mmtype == (slac::defs::MMTYPE_CM_MNBC_SOUND | slac::defs::MMTYPE_MODE_IND)) {
// also attach CM_ATTEN_PROFILE.IND
attach_atten_profile(evse_bridge_fd);
}
}
void handle_evse_input(int evse_bridge_fd, int ev_bridge_fd) {
auto raw_hp_message = homeplug_message.get_raw_message_ptr();
auto bytes_read = read(evse_bridge_fd, raw_hp_message, sizeof(slac::messages::homeplug_message));
const auto mmtype = homeplug_message.get_mmtype();
// patch in "our" mac address
memcpy(raw_hp_message->ethernet_header.ether_shost, EVSE_MAC_ADDR, sizeof(EVSE_MAC_ADDR));
if (mmtype == (slac::defs::MMTYPE_CM_SET_KEY | slac::defs::MMTYPE_MODE_REQ)) {
handle_set_key_req(evse_bridge_fd);
} else {
// default: forward message
write(ev_bridge_fd, raw_hp_message, bytes_read);
}
}

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef TESTS_EVSE_VS_EV_PLC_EMU_HPP
#define TESTS_EVSE_VS_EV_PLC_EMU_HPP
#include "socket_pair_bridge.hpp"
void handle_ev_input(int ev_bridge_fd, int evse_bridge_fd);
void handle_evse_input(int evse_bridge_fd, int ev_bridge_fd);
#endif // TESTS_EVSE_VS_EV_PLC_EMU_HPP

View File

@@ -0,0 +1,100 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
#include "socket_pair_bridge.hpp"
#include <cstdio>
#include <cstring>
#include <errno.h>
#include <poll.h>
#include <stdlib.h>
#include <sys/eventfd.h>
#include <sys/socket.h>
#include <unistd.h>
// FIXME (aw): this helper doesn't really belong here
static void exit_with_error(const char* msg) {
fprintf(stderr, "%s (%s)\n", msg, strerror(errno));
exit(-EXIT_FAILURE);
}
SocketPairBridge::SocketPairBridge(const SocketInputHandler& ev_input_handler,
const SocketInputHandler& evse_input_handler) :
handle_ev_input(ev_input_handler), handle_evse_input(evse_input_handler) {
std::array<int, 2> fd_pair;
auto ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, fd_pair.data());
if (ret) {
exit_with_error("ev socketpair creation failed");
}
sockets.ev_fd = fd_pair.at(0);
sockets.ev_bridge_fd = fd_pair.at(1);
ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, fd_pair.data());
if (ret) {
exit_with_error("evse socketpair creation failed");
}
sockets.evse_fd = fd_pair.at(0);
sockets.evse_bridge_fd = fd_pair.at(1);
event_fd = eventfd(0, 0);
if (event_fd == -1) {
exit_with_error("eventfd failed");
}
// NOTE (aw): using 'this' here is safe
loop_thread = std::thread(&SocketPairBridge::loop, this);
}
SocketPairBridge::~SocketPairBridge() {
uint64_t event_value = 0x1;
write(event_fd, &event_value, sizeof(event_value));
loop_thread.join();
close(event_fd);
close(sockets.evse_bridge_fd);
close(sockets.evse_fd);
close(sockets.ev_bridge_fd);
close(sockets.ev_fd);
}
void SocketPairBridge::loop() {
constexpr static auto EV_INDEX = 0;
constexpr static auto EVSE_INDEX = 1;
constexpr static auto ABORT_INDEX = 2;
struct pollfd pollfds[] = {
{sockets.ev_bridge_fd, POLLIN, 0},
{sockets.evse_bridge_fd, POLLIN, 0},
{event_fd, POLLIN, 0},
};
static constexpr auto num_fds = sizeof(pollfds) / sizeof(struct pollfd);
while (true) {
const auto status = poll(pollfds, num_fds, -1);
if (status == -1) {
exit_with_error("bridge poll");
}
if (pollfds[ABORT_INDEX].revents & POLLIN) {
uint64_t tmp;
read(event_fd, &tmp, sizeof(tmp));
printf("Received shutdown event, exiting\n");
return;
}
if (pollfds[EV_INDEX].revents & POLLIN) {
handle_ev_input(sockets.ev_bridge_fd, sockets.evse_bridge_fd);
}
if (pollfds[EVSE_INDEX].revents & POLLIN) {
handle_evse_input(sockets.evse_bridge_fd, sockets.ev_bridge_fd);
}
}
}

View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef TESTS_EVSE_VS_EV_SOCKET_PAIR_BRIDGE_HPP
#define TESTS_EVSE_VS_EV_SOCKET_PAIR_BRIDGE_HPP
#include <cstdint>
#include <functional>
#include <thread>
class SocketPairBridge {
public:
struct Sockets {
int ev_fd;
int ev_bridge_fd;
int evse_fd;
int evse_bridge_fd;
};
using SocketInputHandler = std::function<void(int, int)>;
SocketPairBridge(const SocketInputHandler& ev_input_handler, const SocketInputHandler& evse_input_handler);
// FIXME (aw): disable copy constructors!
int get_ev_socket() {
return sockets.ev_fd;
};
int get_evse_socket() {
return sockets.evse_fd;
}
~SocketPairBridge();
private:
SocketInputHandler handle_ev_input;
SocketInputHandler handle_evse_input;
void loop();
Sockets sockets{};
int event_fd;
std::thread loop_thread;
uint8_t transfer_buffer[1024];
};
#endif // TESTS_EVSE_VS_EV_SOCKET_PAIR_BRIDGE_HPP