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:
190
tools/EVerest-main/lib/everest/slac/test/evse_vs_ev/main.cpp
Normal file
190
tools/EVerest-main/lib/everest/slac/test/evse_vs_ev/main.cpp
Normal 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;
|
||||
}
|
||||
101
tools/EVerest-main/lib/everest/slac/test/evse_vs_ev/plc_emu.cpp
Normal file
101
tools/EVerest-main/lib/everest/slac/test/evse_vs_ev/plc_emu.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user