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:
41
tools/EVerest-main/lib/everest/slac/test/CMakeLists.txt
Normal file
41
tools/EVerest-main/lib/everest/slac/test/CMakeLists.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
add_executable(evse_slac_test)
|
||||
target_sources(evse_slac_test
|
||||
PRIVATE
|
||||
evse_slac_test.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(evse_slac_test
|
||||
PRIVATE
|
||||
slac_fsm_evse
|
||||
fmt::fmt
|
||||
)
|
||||
target_compile_features(evse_slac_test PRIVATE cxx_std_17)
|
||||
|
||||
add_executable(evse_vs_ev)
|
||||
target_sources(evse_vs_ev
|
||||
PRIVATE
|
||||
evse_vs_ev/socket_pair_bridge.cpp
|
||||
evse_vs_ev/plc_emu.cpp
|
||||
evse_vs_ev/main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(evse_vs_ev
|
||||
PRIVATE
|
||||
slac::fsm::evse
|
||||
slac::fsm::ev
|
||||
Threads::Threads
|
||||
)
|
||||
target_compile_features(evse_vs_ev PRIVATE cxx_std_17)
|
||||
|
||||
add_executable(bridger)
|
||||
target_sources(bridger
|
||||
PRIVATE
|
||||
evse_vs_ev/plc_emu.cpp
|
||||
bridger.cpp
|
||||
)
|
||||
target_link_libraries(bridger
|
||||
PRIVATE
|
||||
slac::slac
|
||||
fmt::fmt
|
||||
)
|
||||
target_compile_features(bridger PRIVATE cxx_std_17)
|
||||
130
tools/EVerest-main/lib/everest/slac/test/bridger.cpp
Normal file
130
tools/EVerest-main/lib/everest/slac/test/bridger.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <slac/slac.hpp>
|
||||
|
||||
#include "evse_vs_ev/plc_emu.hpp"
|
||||
|
||||
void print_mac(const uint8_t* mac) {
|
||||
printf("%2X:%2X:%2X:%2X:%2X:%2X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
struct InterfaceInfo {
|
||||
uint8_t mac[ETH_ALEN];
|
||||
int interface_index{-1};
|
||||
};
|
||||
|
||||
InterfaceInfo get_interface_info(const std::string& interface_name) {
|
||||
InterfaceInfo if_info;
|
||||
|
||||
struct ifaddrs* if_addrs;
|
||||
if (-1 == getifaddrs(&if_addrs)) {
|
||||
// FIXME (aw): proper error handling?
|
||||
return if_info;
|
||||
}
|
||||
|
||||
// iterate through them and list them
|
||||
struct ifaddrs* cur_if_addr = if_addrs;
|
||||
while (cur_if_addr) {
|
||||
if (cur_if_addr->ifa_addr && cur_if_addr->ifa_addr->sa_family == AF_PACKET) {
|
||||
if (0 == interface_name.compare(cur_if_addr->ifa_name)) {
|
||||
const auto* addr_info = reinterpret_cast<struct sockaddr_ll*>(cur_if_addr->ifa_addr);
|
||||
memcpy(if_info.mac, addr_info->sll_addr, sizeof(if_info.mac));
|
||||
if_info.interface_index = addr_info->sll_ifindex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cur_if_addr = cur_if_addr->ifa_next;
|
||||
}
|
||||
|
||||
freeifaddrs(if_addrs);
|
||||
|
||||
return if_info;
|
||||
}
|
||||
|
||||
int create_raw_homeplug_socket(const InterfaceInfo& interface_info) {
|
||||
const uint16_t homeplug_protocol = slac::defs::ETH_P_HOMEPLUG_GREENPHY;
|
||||
const auto socket_fd = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(homeplug_protocol));
|
||||
|
||||
if (socket_fd == -1) {
|
||||
exit_with_error("Couldn't create the socket");
|
||||
}
|
||||
|
||||
// bind this packet socket to a specific interface
|
||||
struct sockaddr_ll sock_addr = {
|
||||
AF_PACKET, // sll_family
|
||||
htons(homeplug_protocol), // sll_protocol
|
||||
interface_info.interface_index, // sll_ifindex
|
||||
0x00, // sll_hatype, set on receiving
|
||||
0x00, // sll_pkttype, set on receiving
|
||||
ETH_ALEN, // sll_halen
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // sll_addr[8]
|
||||
};
|
||||
|
||||
if (-1 == bind(socket_fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr))) {
|
||||
exit_with_error("Failed to bind the socket");
|
||||
}
|
||||
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
void loop(int ev_fd, int evse_fd) {
|
||||
constexpr static auto EV_INDEX = 0;
|
||||
constexpr static auto EVSE_INDEX = 1;
|
||||
|
||||
struct pollfd pollfds[] = {
|
||||
{ev_fd, POLLIN, 0},
|
||||
{evse_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[EV_INDEX].revents & POLLIN) {
|
||||
printf("Received ev input\n");
|
||||
handle_ev_input(ev_fd, evse_fd);
|
||||
}
|
||||
|
||||
if (pollfds[EVSE_INDEX].revents & POLLIN) {
|
||||
printf("Received evse input\n");
|
||||
handle_evse_input(evse_fd, ev_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const auto ev_bridge_device_info = get_interface_info("vev-bridge");
|
||||
const auto evse_bridge_device_info = get_interface_info("vevse-bridge");
|
||||
const auto ev_fd = create_raw_homeplug_socket(ev_bridge_device_info);
|
||||
const auto evse_fd = create_raw_homeplug_socket(evse_bridge_device_info);
|
||||
|
||||
loop(ev_fd, evse_fd);
|
||||
|
||||
// printf("mac: ");
|
||||
// print_mac(info.mac);
|
||||
// printf("index: %d\n", info.interface_index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
311
tools/EVerest-main/lib/everest/slac/test/evse_slac_test.cpp
Normal file
311
tools/EVerest-main/lib/everest/slac/test/evse_slac_test.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include <everest/slac/fsm/evse/fsm.hpp>
|
||||
#include <everest/slac/fsm/evse/states/others.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
static auto create_cm_set_key_cnf() {
|
||||
// FIXME (aw): needs to be fully implemented!
|
||||
slac::messages::cm_set_key_cnf set_key_cnf;
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_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);
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
static auto create_cm_validate_req() {
|
||||
slac::messages::cm_validate_req validate_req;
|
||||
validate_req.signal_type = slac::defs::CM_VALIDATE_REQ_SIGNAL_TYPE;
|
||||
validate_req.timer = 0;
|
||||
validate_req.result = slac::defs::CM_VALIDATE_REQ_RESULT_READY;
|
||||
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_payload(&validate_req, sizeof(validate_req),
|
||||
(slac::defs::MMTYPE_CM_VALIDATE | slac::defs::MMTYPE_MODE_REQ), slac::defs::MMV::AV_1_1);
|
||||
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
struct EVSession {
|
||||
EVSession(const std::array<uint8_t, 8>& run_id_, const std::array<uint8_t, 6>& mac_) : run_id(run_id_), mac(mac_){};
|
||||
|
||||
// FIXME (aw): all these create_cm_* need to be fully implemented!
|
||||
auto create_cm_slac_parm_req() {
|
||||
slac::messages::cm_slac_parm_req parm_req;
|
||||
std::copy(run_id.begin(), run_id.end(), parm_req.run_id);
|
||||
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_ethernet_header(mac.data(), mac.data());
|
||||
hp_message.setup_payload(&parm_req, sizeof(parm_req),
|
||||
(slac::defs::MMTYPE_CM_SLAC_PARAM | slac::defs::MMTYPE_MODE_REQ),
|
||||
slac::defs::MMV::AV_1_1);
|
||||
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
auto create_cm_start_atten_char_ind() {
|
||||
slac::messages::cm_start_atten_char_ind atten_char_ind;
|
||||
std::copy(run_id.begin(), run_id.end(), atten_char_ind.run_id);
|
||||
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_ethernet_header(mac.data(), mac.data());
|
||||
hp_message.setup_payload(&atten_char_ind, sizeof(atten_char_ind),
|
||||
(slac::defs::MMTYPE_CM_START_ATTEN_CHAR | slac::defs::MMTYPE_MODE_IND),
|
||||
slac::defs::MMV::AV_1_1);
|
||||
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
auto create_cm_mnbc_sound_ind() {
|
||||
slac::messages::cm_mnbc_sound_ind sound_ind;
|
||||
std::copy(run_id.begin(), run_id.end(), sound_ind.run_id);
|
||||
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_ethernet_header(mac.data(), mac.data());
|
||||
hp_message.setup_payload(&sound_ind, sizeof(sound_ind),
|
||||
(slac::defs::MMTYPE_CM_MNBC_SOUND | slac::defs::MMTYPE_MODE_IND),
|
||||
slac::defs::MMV::AV_1_1);
|
||||
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
auto create_cm_atten_profile_ind() {
|
||||
slac::messages::cm_atten_profile_ind profile_ind;
|
||||
std::copy(mac.begin(), mac.end(), profile_ind.pev_mac);
|
||||
profile_ind.num_groups = slac::defs::AAG_LIST_LEN;
|
||||
|
||||
for (int i = 0; i < slac::defs::AAG_LIST_LEN; ++i) {
|
||||
profile_ind.aag[i] = i;
|
||||
}
|
||||
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_ethernet_header(mac.data(), mac.data());
|
||||
hp_message.setup_payload(&profile_ind, sizeof(profile_ind),
|
||||
(slac::defs::MMTYPE_CM_ATTEN_PROFILE | slac::defs::MMTYPE_MODE_IND),
|
||||
slac::defs::MMV::AV_1_1);
|
||||
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
auto create_cm_atten_char_rsp() {
|
||||
slac::messages::cm_atten_char_rsp atten_char;
|
||||
std::copy(run_id.begin(), run_id.end(), atten_char.run_id);
|
||||
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_ethernet_header(mac.data(), mac.data());
|
||||
hp_message.setup_payload(&atten_char, sizeof(atten_char),
|
||||
(slac::defs::MMTYPE_CM_ATTEN_CHAR | slac::defs::MMTYPE_MODE_RSP),
|
||||
slac::defs::MMV::AV_1_1);
|
||||
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
auto create_cm_slac_match_req() {
|
||||
slac::messages::cm_slac_match_req match_req;
|
||||
std::copy(run_id.begin(), run_id.end(), match_req.run_id);
|
||||
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_ethernet_header(mac.data(), mac.data());
|
||||
hp_message.setup_payload(&match_req, sizeof(match_req),
|
||||
(slac::defs::MMTYPE_CM_SLAC_MATCH | slac::defs::MMTYPE_MODE_REQ),
|
||||
slac::defs::MMV::AV_1_1);
|
||||
|
||||
return hp_message;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<uint8_t, 8> run_id;
|
||||
const std::array<uint8_t, ETH_ALEN>& mac;
|
||||
};
|
||||
|
||||
void feed_machine_for(slac::fsm::evse::FSM& machine, int period_ms,
|
||||
fsm::FeedResult<slac::fsm::evse::FSMReturnType> feed_result) {
|
||||
using namespace std::chrono;
|
||||
|
||||
auto end_tp = steady_clock::now() + milliseconds(period_ms);
|
||||
|
||||
while (true) {
|
||||
if (feed_result.transition()) {
|
||||
// need to call feed
|
||||
} else if (feed_result.unhandled_event()) {
|
||||
printf("DEBUG: got an unhandled event\n");
|
||||
break;
|
||||
} else if (feed_result.internal_error()) {
|
||||
printf("ERROR: internal FSM error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (feed_result.has_value() == false) {
|
||||
// nothing to do
|
||||
break;
|
||||
} else if (feed_result.has_value() == true) {
|
||||
const auto timeout = *feed_result;
|
||||
if (timeout == 0) {
|
||||
// need to call feed directly
|
||||
} else {
|
||||
auto next_tp = steady_clock::now() + milliseconds(timeout);
|
||||
if (next_tp > end_tp) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_until(next_tp);
|
||||
}
|
||||
} else {
|
||||
printf("ERROR: unknown feed result case\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
feed_result = machine.feed();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_until(end_tp);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const auto ATTENUATION_ADJUSTMENT = 10;
|
||||
printf("Hi from SLAC!\n");
|
||||
|
||||
std::optional<slac::messages::HomeplugMessage> msg_in;
|
||||
|
||||
slac::fsm::evse::ContextCallbacks callbacks;
|
||||
callbacks.log = [](const std::string& text) { fmt::print("SLAC LOG: {}\n", text); };
|
||||
|
||||
callbacks.send_raw_slac = [&msg_in](slac::messages::HomeplugMessage& hp_message) { msg_in = hp_message; };
|
||||
|
||||
auto ctx = slac::fsm::evse::Context(callbacks);
|
||||
ctx.slac_config.sounding_atten_adjustment = ATTENUATION_ADJUSTMENT;
|
||||
ctx.slac_config.chip_reset.enabled = false;
|
||||
|
||||
auto machine = slac::fsm::evse::FSM();
|
||||
|
||||
//
|
||||
// reset machine
|
||||
//
|
||||
machine.reset<slac::fsm::evse::ResetState>(ctx);
|
||||
auto fr = machine.feed();
|
||||
|
||||
// assert that CM_SET_KEY_REQ gets set!
|
||||
if (!msg_in.has_value() || msg_in->get_mmtype() != (slac::defs::MMTYPE_CM_SET_KEY | slac::defs::MMTYPE_MODE_REQ)) {
|
||||
printf("Expected CM_SET_KEY_REQ!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
msg_in.reset();
|
||||
}
|
||||
|
||||
feed_machine_for(machine, 230, fr);
|
||||
|
||||
// feed in CM_SET_KEY_CNF
|
||||
ctx.slac_message_payload = create_cm_set_key_cnf();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
// should be idle state now, send ENTER_BCD, to enter MATCHING
|
||||
machine.handle_event(slac::fsm::evse::Event::ENTER_BCD);
|
||||
fr = machine.feed();
|
||||
|
||||
feed_machine_for(machine, 300, fr);
|
||||
|
||||
// create session 1 and inject CM_SLAC_PARM_REQ
|
||||
auto session_1 = EVSession({0, 1, 2, 3, 4, 5, 6, 7}, {0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe});
|
||||
ctx.slac_message_payload = session_1.create_cm_slac_parm_req();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
// assert that CM_SLAC_PARM_CNF gets set!
|
||||
if (!msg_in.has_value() ||
|
||||
msg_in->get_mmtype() != (slac::defs::MMTYPE_CM_SLAC_PARAM | slac::defs::MMTYPE_MODE_CNF)) {
|
||||
printf("Expected CM_SLAC_PARM_CNF!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
msg_in.reset();
|
||||
}
|
||||
|
||||
feed_machine_for(machine, 233, fr);
|
||||
|
||||
// inject CM_START_ATTEN_CHAR_IND
|
||||
ctx.slac_message_payload = session_1.create_cm_start_atten_char_ind();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
// inject all the soundings ...
|
||||
for (int i = 0; i < slac::defs::CM_SLAC_PARM_CNF_NUM_SOUNDS - 1; i++) {
|
||||
ctx.slac_message_payload = session_1.create_cm_mnbc_sound_ind();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
ctx.slac_message_payload = session_1.create_cm_atten_profile_ind();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
}
|
||||
|
||||
feed_machine_for(machine, 700 + 1 * 45, fr);
|
||||
|
||||
// assert that CM_ATTEN_CHAR_IND gets set!
|
||||
if (!msg_in.has_value() ||
|
||||
msg_in->get_mmtype() != (slac::defs::MMTYPE_CM_ATTEN_CHAR | slac::defs::MMTYPE_MODE_IND)) {
|
||||
printf("Expected CM_ATTEN_CHAR_IND!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
auto atten_char_ind = msg_in->get_payload<slac::messages::cm_atten_char_ind>();
|
||||
for (int i = 0; i < slac::defs::AAG_LIST_LEN; ++i) {
|
||||
if (atten_char_ind.attenuation_profile.aag[i] != i + ATTENUATION_ADJUSTMENT) {
|
||||
printf("Averaging not correct in ATTEN_CHAR_IND\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
msg_in.reset();
|
||||
}
|
||||
|
||||
// "async" insert an CM_VALIDATE.REQ
|
||||
ctx.slac_message_payload = create_cm_validate_req();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
// assert that CM_VALIDATE.CNF gets set!
|
||||
if (!msg_in.has_value() || msg_in->get_mmtype() != (slac::defs::MMTYPE_CM_VALIDATE | slac::defs::MMTYPE_MODE_CNF)) {
|
||||
printf("Expected CM_VALIDATE.CNF!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
// check for correct "failure" result
|
||||
auto validate_cnf = msg_in->get_payload<slac::messages::cm_validate_cnf>();
|
||||
if (validate_cnf.result != slac::defs::CM_VALIDATE_REQ_RESULT_FAILURE) {
|
||||
printf("Expected result field of CM_VALIDATE.CNF to be set to failure\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
// inject CM_ATTEN_CHAR_RSP
|
||||
ctx.slac_message_payload = session_1.create_cm_atten_char_rsp();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
feed_machine_for(machine, 1000, fr);
|
||||
|
||||
// inject messages from a second session
|
||||
auto session_2 = EVSession({9, 1, 2, 3, 4, 5, 6, 7}, {0xbe, 0xaf, 0xbe, 0xaf, 0xbe, 0xaf});
|
||||
ctx.slac_message_payload = session_2.create_cm_slac_parm_req();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
feed_machine_for(machine, 1000, fr);
|
||||
|
||||
// inject CM_SLAC_MATCH_REQ
|
||||
ctx.slac_message_payload = session_1.create_cm_slac_match_req();
|
||||
machine.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
|
||||
fr = machine.feed();
|
||||
|
||||
// assert that CM_SLAC_MATCH_CNF gets set!
|
||||
if (!msg_in.has_value() ||
|
||||
msg_in->get_mmtype() != (slac::defs::MMTYPE_CM_SLAC_MATCH | slac::defs::MMTYPE_MODE_CNF)) {
|
||||
printf("Expected CM_ATTEN_CHAR_IND!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
msg_in.reset();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
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