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:
21
tools/EVerest-main/lib/everest/slac/fsm/ev/CMakeLists.txt
Normal file
21
tools/EVerest-main/lib/everest/slac/fsm/ev/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
add_library(slac_fsm_ev)
|
||||
add_library(slac::fsm::ev ALIAS slac_fsm_ev)
|
||||
ev_register_library_target(slac_fsm_ev)
|
||||
|
||||
target_include_directories(slac_fsm_ev
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
)
|
||||
|
||||
target_sources(slac_fsm_ev
|
||||
PRIVATE
|
||||
src/context.cpp
|
||||
src/states/others.cpp
|
||||
src/states/sounding.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(slac_fsm_ev
|
||||
PUBLIC
|
||||
slac::slac
|
||||
fsm::fsm
|
||||
)
|
||||
@@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef EV_SLAC_CONTEXT_HPP
|
||||
#define EV_SLAC_CONTEXT_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <slac/slac.hpp>
|
||||
|
||||
namespace slac::fsm::ev {
|
||||
|
||||
namespace _context_detail {
|
||||
template <typename SlacMessageType> struct MMTYPE;
|
||||
|
||||
template <> struct MMTYPE<slac::messages::cm_slac_parm_req> {
|
||||
static const uint16_t value = slac::defs::MMTYPE_CM_SLAC_PARAM | slac::defs::MMTYPE_MODE_REQ;
|
||||
};
|
||||
|
||||
template <> struct MMTYPE<slac::messages::cm_start_atten_char_ind> {
|
||||
static const uint16_t value = slac::defs::MMTYPE_CM_START_ATTEN_CHAR | slac::defs::MMTYPE_MODE_IND;
|
||||
};
|
||||
|
||||
template <> struct MMTYPE<slac::messages::cm_mnbc_sound_ind> {
|
||||
static const uint16_t value = slac::defs::MMTYPE_CM_MNBC_SOUND | slac::defs::MMTYPE_MODE_IND;
|
||||
};
|
||||
|
||||
template <> struct MMTYPE<slac::messages::cm_atten_char_rsp> {
|
||||
static const uint16_t value = slac::defs::MMTYPE_CM_ATTEN_CHAR | slac::defs::MMTYPE_MODE_RSP;
|
||||
};
|
||||
|
||||
template <> struct MMTYPE<slac::messages::cm_slac_match_req> {
|
||||
static const uint16_t value = slac::defs::MMTYPE_CM_SLAC_MATCH | slac::defs::MMTYPE_MODE_REQ;
|
||||
};
|
||||
|
||||
template <> struct MMTYPE<slac::messages::cm_set_key_req> {
|
||||
static const uint16_t value = slac::defs::MMTYPE_CM_SET_KEY | slac::defs::MMTYPE_MODE_REQ;
|
||||
};
|
||||
|
||||
template <typename SlacMessageType> struct MMV {
|
||||
// this is the default value for homeplug av 2.0 messages, which are
|
||||
// backward compatible with homeplug av 1.1 messages
|
||||
// non-backward (to 1.1) compatible message are CM_CHAN_EST,
|
||||
// CM_AMP_MAP and CM_NW_STATS, these need to use AV_2_0
|
||||
// older av 1.0 message need to use AV_1_0
|
||||
static constexpr auto value = slac::defs::MMV::AV_1_1;
|
||||
};
|
||||
} // namespace _context_detail
|
||||
|
||||
struct ContextCallbacks {
|
||||
std::function<void(slac::messages::HomeplugMessage&)> send_raw_slac{nullptr};
|
||||
std::function<void(const std::string&)> signal_state{nullptr};
|
||||
std::function<void(const std::string&)> log_debug{nullptr};
|
||||
std::function<void(const std::string&)> log_info{nullptr};
|
||||
std::function<void(const std::string&)> log_warn{nullptr};
|
||||
std::function<void(const std::string&)> log_error{nullptr};
|
||||
};
|
||||
|
||||
struct Context {
|
||||
static constexpr std::array<uint8_t, ETH_ALEN> BROADCAST_MAC = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
static constexpr std::array<uint8_t, ETH_ALEN> EV_PLC_MAC = {0x00, 0xB0, 0x52, 0x00, 0x00, 0x01};
|
||||
|
||||
Context(const ContextCallbacks& callbacks_, const std::array<uint8_t, ETH_ALEN>& mac) :
|
||||
callbacks(callbacks_), ev_host_mac(mac) {
|
||||
}
|
||||
|
||||
const std::array<uint8_t, ETH_ALEN> ev_host_mac{};
|
||||
|
||||
// event specific payloads
|
||||
// FIXME (aw): due to the synchroneous nature of the fsm, this could be even a ptr/ref
|
||||
slac::messages::HomeplugMessage slac_message;
|
||||
|
||||
// FIXME (aw): message should be const, but libslac doesn't allow for const ptr - needs changes in libslac
|
||||
template <typename SlacMessageType>
|
||||
void send_slac_message(const uint8_t* dest_mac, SlacMessageType const& message) {
|
||||
slac::messages::HomeplugMessage hp_message;
|
||||
hp_message.setup_ethernet_header(dest_mac);
|
||||
try {
|
||||
hp_message.setup_payload(&message, sizeof(message), _context_detail::MMTYPE<SlacMessageType>::value,
|
||||
_context_detail::MMV<SlacMessageType>::value);
|
||||
} catch (const std::runtime_error& e) {
|
||||
const auto error_message = std::string("Could not setup SLAC payload: ") + std::string(e.what());
|
||||
log_error(error_message);
|
||||
}
|
||||
callbacks.send_raw_slac(hp_message);
|
||||
}
|
||||
|
||||
// signal handlers
|
||||
void signal_state(const std::string& state);
|
||||
|
||||
// logging util
|
||||
void log_debug(const std::string& text);
|
||||
void log_info(const std::string& text);
|
||||
void log_warn(const std::string& text);
|
||||
void log_error(const std::string& text);
|
||||
|
||||
private:
|
||||
const ContextCallbacks& callbacks;
|
||||
};
|
||||
|
||||
struct SessionParamaters {
|
||||
SessionParamaters(const uint8_t* run_id, const uint8_t* evse_mac);
|
||||
uint8_t run_id[slac::defs::RUN_ID_LEN];
|
||||
uint8_t evse_mac[ETH_ALEN];
|
||||
};
|
||||
|
||||
} // namespace slac::fsm::ev
|
||||
|
||||
#endif // EV_SLAC_CONTEXT_HPP
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef EV_SLAC_FSM_HPP
|
||||
#define EV_SLAC_FSM_HPP
|
||||
|
||||
#include <fsm/fsm.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace slac::fsm::ev {
|
||||
|
||||
enum class Event {
|
||||
RESET,
|
||||
TRIGGER_MATCHING,
|
||||
SLAC_MESSAGE,
|
||||
|
||||
// internal events
|
||||
FAILED,
|
||||
};
|
||||
|
||||
using FSMReturnType = int;
|
||||
|
||||
using FSM = ::fsm::FSM<Event, FSMReturnType>;
|
||||
using FSMSimpleState = ::fsm::states::StateWithContext<FSM::SimpleStateType, Context>;
|
||||
using FSMCompoundState = ::fsm::states::StateWithContext<FSM::CompoundStateType, Context>;
|
||||
|
||||
} // namespace slac::fsm::ev
|
||||
|
||||
#endif // EV_SLAC_FSM_HPP
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef EV_SLAC_STATES_OTHERS_HPP
|
||||
#define EV_SLAC_STATES_OTHERS_HPP
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <everest/slac/fsm/ev/fsm.hpp>
|
||||
|
||||
namespace slac::fsm::ev {
|
||||
|
||||
struct ResetState : public FSMSimpleState {
|
||||
using FSMSimpleState::FSMSimpleState;
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
|
||||
void enter() final;
|
||||
CallbackReturnType callback() final;
|
||||
};
|
||||
|
||||
struct InitSlacState : public FSMSimpleState {
|
||||
using FSMSimpleState::FSMSimpleState;
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
|
||||
void enter() final;
|
||||
CallbackReturnType callback() final;
|
||||
|
||||
// only returns true, if valid
|
||||
bool check_for_valid_parm_conf();
|
||||
|
||||
// sends out CM_SLAC_PARM.REQ, increases num of tries and returns the timeout until a response is expected
|
||||
int send_parm_req();
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> next_timeout;
|
||||
|
||||
int num_of_tries{0};
|
||||
|
||||
uint8_t run_id[8];
|
||||
};
|
||||
|
||||
struct MatchRequestState : public FSMSimpleState {
|
||||
MatchRequestState(Context& ctx, SessionParamaters session_parameters);
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
|
||||
void enter() final;
|
||||
CallbackReturnType callback() final;
|
||||
|
||||
// return the pointer to the NMK, if valid
|
||||
const uint8_t* check_for_valid_match_req_conf();
|
||||
|
||||
// sends out CM_SLAC_PARM.REQ, increases num of tries and returns the timeout until a response is expected
|
||||
int send_match_req();
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> next_timeout;
|
||||
|
||||
int num_of_tries{0};
|
||||
|
||||
SessionParamaters session_parameters;
|
||||
};
|
||||
|
||||
struct JoinNetworkState : public FSMSimpleState {
|
||||
static constexpr auto SET_KEY_TIMEOUT_MS = 500;
|
||||
JoinNetworkState(Context& ctx, const uint8_t* nmk);
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
|
||||
void enter() final;
|
||||
CallbackReturnType callback() final;
|
||||
|
||||
// only returns true, if valid
|
||||
bool check_for_valid_set_key_conf();
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> timeout;
|
||||
|
||||
uint8_t nmk[slac::defs::NMK_LEN];
|
||||
};
|
||||
|
||||
struct MatchedState : public FSMSimpleState {
|
||||
using FSMSimpleState::FSMSimpleState;
|
||||
|
||||
void enter() final {
|
||||
ctx.log_info("Entered matched state");
|
||||
ctx.signal_state("MATCHED");
|
||||
}
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
};
|
||||
|
||||
struct FailedState : public FSMSimpleState {
|
||||
using FSMSimpleState::FSMSimpleState;
|
||||
|
||||
void enter() final {
|
||||
ctx.log_info("Entered failed state");
|
||||
}
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
};
|
||||
|
||||
} // namespace slac::fsm::ev
|
||||
|
||||
#endif // EV_SLAC_STATES_OTHERS_HPP
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef EV_SLAC_STATES_SOUNDING_HPP
|
||||
#define EV_SLAC_STATES_SOUNDING_HPP
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <slac/slac.hpp>
|
||||
|
||||
#include <everest/slac/fsm/ev/fsm.hpp>
|
||||
|
||||
namespace slac::fsm::ev {
|
||||
|
||||
struct SoundingState : public FSMSimpleState {
|
||||
SoundingState(Context& ctx, SessionParamaters session_parameters);
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
|
||||
void enter() final;
|
||||
CallbackReturnType callback() final;
|
||||
|
||||
// returns true, if CM_ATTEN_CHAR.IND is expected and fulfills expectations and response has been send
|
||||
bool handle_valid_atten_char_ind();
|
||||
|
||||
// returns true if further message will need to be send
|
||||
bool do_sounding();
|
||||
|
||||
// timepoint until we need to receive the atten_char_ind message
|
||||
std::chrono::time_point<std::chrono::steady_clock> sounding_timeout;
|
||||
|
||||
// timepoint until we internally want to send the next message
|
||||
std::chrono::time_point<std::chrono::steady_clock> next_timeout;
|
||||
|
||||
int count_start_atten_char_sent{0};
|
||||
int count_mnbc_sound_sent{0};
|
||||
|
||||
SessionParamaters session_parameters;
|
||||
};
|
||||
|
||||
} // namespace slac::fsm::ev
|
||||
|
||||
#endif // EV_SLAC_STATES_SOUNDING_HPP
|
||||
44
tools/EVerest-main/lib/everest/slac/fsm/ev/src/context.cpp
Normal file
44
tools/EVerest-main/lib/everest/slac/fsm/ev/src/context.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include <everest/slac/fsm/ev/context.hpp>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace slac::fsm::ev {
|
||||
|
||||
void Context::signal_state(const std::string& state) {
|
||||
if (callbacks.signal_state) {
|
||||
callbacks.signal_state(state);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::log_debug(const std::string& text) {
|
||||
if (callbacks.log_debug) {
|
||||
callbacks.log_debug(text);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::log_info(const std::string& text) {
|
||||
if (callbacks.log_info) {
|
||||
callbacks.log_info(text);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::log_warn(const std::string& text) {
|
||||
if (callbacks.log_warn) {
|
||||
callbacks.log_warn(text);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::log_error(const std::string& text) {
|
||||
if (callbacks.log_error) {
|
||||
callbacks.log_error(text);
|
||||
}
|
||||
}
|
||||
|
||||
SessionParamaters::SessionParamaters(const uint8_t* run_id_, const uint8_t* evse_mac_) {
|
||||
memcpy(run_id, run_id_, sizeof(run_id));
|
||||
memcpy(evse_mac, evse_mac_, sizeof(evse_mac));
|
||||
}
|
||||
|
||||
} // namespace slac::fsm::ev
|
||||
303
tools/EVerest-main/lib/everest/slac/fsm/ev/src/states/others.cpp
Normal file
303
tools/EVerest-main/lib/everest/slac/fsm/ev/src/states/others.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include <everest/slac/fsm/ev/states/others.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#include "timing_helper.hpp"
|
||||
|
||||
#include <everest/slac/fsm/ev/states/sounding.hpp>
|
||||
|
||||
namespace slac::fsm::ev {
|
||||
|
||||
void ResetState::enter() {
|
||||
ctx.signal_state("UNMATCHED");
|
||||
ctx.log_info("Entered Reset state");
|
||||
}
|
||||
|
||||
FSMSimpleState::HandleEventReturnType ResetState::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::TRIGGER_MATCHING) {
|
||||
return sa.create_simple<InitSlacState>(ctx);
|
||||
} else {
|
||||
return sa.PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
FSMSimpleState::CallbackReturnType ResetState::callback() {
|
||||
ctx.log_info("Called callback of ResetState");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FSMSimpleState::HandleEventReturnType InitSlacState::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::SLAC_MESSAGE) {
|
||||
if (check_for_valid_parm_conf()) {
|
||||
ctx.signal_state("MATCHING");
|
||||
return sa.create_simple<SoundingState>(ctx, SessionParamaters{run_id, ctx.slac_message.get_src_mac()});
|
||||
}
|
||||
|
||||
return sa.HANDLED_INTERNALLY;
|
||||
} else if (ev == Event::FAILED) {
|
||||
return sa.create_simple<FailedState>(ctx);
|
||||
} else if (ev == Event::RESET) {
|
||||
return sa.create_simple<ResetState>(ctx);
|
||||
} else {
|
||||
return sa.PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
void InitSlacState::enter() {
|
||||
ctx.log_info("Entered init state");
|
||||
|
||||
// generate random run_id
|
||||
std::random_device rnd_dev;
|
||||
std::mt19937 rng(rnd_dev());
|
||||
std::uniform_int_distribution<std::mt19937::result_type> dist256(0, 255);
|
||||
|
||||
for (auto& id : this->run_id) {
|
||||
id = dist256(rng);
|
||||
}
|
||||
}
|
||||
|
||||
FSMSimpleState::CallbackReturnType InitSlacState::callback() {
|
||||
if (num_of_tries == 0) {
|
||||
return send_parm_req();
|
||||
}
|
||||
|
||||
// did already send a parm req, check for timeout
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto time_left = milliseconds_left(now, next_timeout);
|
||||
|
||||
if (time_left > 0) {
|
||||
// still have time
|
||||
return time_left;
|
||||
}
|
||||
|
||||
// timeout, check if we still have retries
|
||||
if (num_of_tries < 100) {
|
||||
// FIXME (aw): the norm says num_of_tries < slac::defs::C_EV_MATCH_RETRY
|
||||
// but doesn't seem to work in 'real life'
|
||||
return send_parm_req();
|
||||
}
|
||||
|
||||
// no retries left fail
|
||||
return Event::FAILED;
|
||||
}
|
||||
|
||||
int InitSlacState::send_parm_req() {
|
||||
slac::messages::cm_slac_parm_req msg;
|
||||
msg.application_type = 0x0;
|
||||
msg.security_type = 0x0;
|
||||
memcpy(msg.run_id, run_id, sizeof(msg.run_id));
|
||||
|
||||
ctx.send_slac_message(ctx.BROADCAST_MAC.data(), msg);
|
||||
|
||||
num_of_tries++;
|
||||
|
||||
next_timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(slac::defs::TT_MATCH_RESPONSE_MS);
|
||||
|
||||
return slac::defs::TT_MATCH_RESPONSE_MS;
|
||||
}
|
||||
|
||||
bool InitSlacState::check_for_valid_parm_conf() {
|
||||
const auto mmtype = ctx.slac_message.get_mmtype();
|
||||
if (mmtype != (slac::defs::MMTYPE_CM_SLAC_PARAM | slac::defs::MMTYPE_MODE_CNF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// correct message type
|
||||
const auto& parm_cnf = ctx.slac_message.get_payload<slac::messages::cm_slac_parm_cnf>();
|
||||
|
||||
// it is not clear, whether we should use the m-sound target anyhow ... or if we should really validate any of the
|
||||
// fields except the run id
|
||||
const auto same_run_id = (memcmp(parm_cnf.run_id, run_id, sizeof(run_id)) == 0);
|
||||
|
||||
return same_run_id;
|
||||
}
|
||||
|
||||
MatchRequestState::MatchRequestState(Context& ctx, SessionParamaters session_parameters_) :
|
||||
FSMSimpleState(ctx), session_parameters(std::move(session_parameters_)) {
|
||||
}
|
||||
|
||||
FSMSimpleState::HandleEventReturnType MatchRequestState::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::SLAC_MESSAGE) {
|
||||
const auto nmk = check_for_valid_match_req_conf();
|
||||
if (nmk) {
|
||||
return sa.create_simple<JoinNetworkState>(ctx, nmk);
|
||||
}
|
||||
|
||||
return sa.HANDLED_INTERNALLY;
|
||||
} else if (ev == Event::FAILED) {
|
||||
return sa.create_simple<FailedState>(ctx);
|
||||
} else if (ev == Event::RESET) {
|
||||
return sa.create_simple<ResetState>(ctx);
|
||||
} else {
|
||||
return sa.PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
void MatchRequestState::enter() {
|
||||
ctx.log_info("Entered MatchRequestState state");
|
||||
}
|
||||
|
||||
FSMSimpleState::CallbackReturnType MatchRequestState::callback() {
|
||||
if (num_of_tries == 0) {
|
||||
return send_match_req();
|
||||
}
|
||||
|
||||
// did already send a parm req, check for timeout
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto time_left = milliseconds_left(now, next_timeout);
|
||||
|
||||
if (time_left > 0) {
|
||||
// still have time
|
||||
return time_left;
|
||||
}
|
||||
|
||||
// timeout, check if we still have retries
|
||||
if (num_of_tries < slac::defs::C_EV_MATCH_RETRY) {
|
||||
return send_match_req();
|
||||
}
|
||||
|
||||
// no retries left fail
|
||||
return Event::FAILED;
|
||||
}
|
||||
|
||||
const uint8_t* MatchRequestState::check_for_valid_match_req_conf() {
|
||||
if (num_of_tries == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto mmtype = ctx.slac_message.get_mmtype();
|
||||
if (mmtype != (slac::defs::MMTYPE_CM_SLAC_MATCH | slac::defs::MMTYPE_MODE_CNF)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// correct message type
|
||||
const auto& match_cnf = ctx.slac_message.get_payload<slac::messages::cm_slac_match_cnf>();
|
||||
|
||||
const auto run_id_match =
|
||||
(memcmp(session_parameters.run_id, match_cnf.run_id, sizeof(session_parameters.run_id)) == 0);
|
||||
|
||||
if (run_id_match) {
|
||||
return match_cnf.nmk;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int MatchRequestState::send_match_req() {
|
||||
slac::messages::cm_slac_match_req msg;
|
||||
msg.application_type = 0x0;
|
||||
msg.security_type = 0x0;
|
||||
msg.mvf_length = htole16(0x3e); // FIXME (aw) fixed constant
|
||||
memset(msg.pev_id, 0, sizeof(msg.pev_id));
|
||||
memcpy(msg.pev_mac, ctx.ev_host_mac.data(), sizeof(msg.pev_mac));
|
||||
memset(msg.evse_id, 0, sizeof(msg.evse_id));
|
||||
memcpy(msg.evse_mac, session_parameters.evse_mac, sizeof(msg.evse_mac));
|
||||
memcpy(msg.run_id, session_parameters.run_id, sizeof(msg.run_id));
|
||||
memset(msg._reserved, 0, sizeof(msg._reserved));
|
||||
|
||||
ctx.send_slac_message(session_parameters.evse_mac, msg);
|
||||
|
||||
num_of_tries++;
|
||||
|
||||
next_timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(slac::defs::TT_MATCH_RESPONSE_MS);
|
||||
|
||||
return slac::defs::TT_MATCH_RESPONSE_MS;
|
||||
}
|
||||
|
||||
JoinNetworkState::JoinNetworkState(Context& ctx, const uint8_t* nmk_) : FSMSimpleState(ctx) {
|
||||
memcpy(nmk, nmk_, sizeof(nmk));
|
||||
}
|
||||
|
||||
FSMSimpleState::HandleEventReturnType JoinNetworkState::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::SLAC_MESSAGE) {
|
||||
if (check_for_valid_set_key_conf()) {
|
||||
// FIXME (aw): later on, we also need to distinguish between set_key failed
|
||||
return sa.create_simple<MatchedState>(ctx);
|
||||
}
|
||||
|
||||
return sa.HANDLED_INTERNALLY;
|
||||
} else if (ev == Event::FAILED) {
|
||||
return sa.create_simple<FailedState>(ctx);
|
||||
} else if (ev == Event::RESET) {
|
||||
return sa.create_simple<ResetState>(ctx);
|
||||
} else {
|
||||
return sa.PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
void JoinNetworkState::enter() {
|
||||
ctx.log_info("Entered JoinNetwork state");
|
||||
|
||||
slac::messages::cm_set_key_req msg;
|
||||
msg.key_type = slac::defs::CM_SET_KEY_REQ_KEY_TYPE_NMK;
|
||||
msg.my_nonce = 0xAAAAAAAA;
|
||||
msg.your_nonce = 0x00000000;
|
||||
msg.pid = slac::defs::CM_SET_KEY_REQ_PID_HLE;
|
||||
msg.prn = htole16(slac::defs::CM_SET_KEY_REQ_PRN_UNUSED);
|
||||
msg.pmn = slac::defs::CM_SET_KEY_REQ_PMN_UNUSED;
|
||||
msg.cco_capability = slac::defs::CM_SET_KEY_REQ_CCO_CAP_NONE;
|
||||
slac::utils::generate_nid_from_nmk(msg.nid, nmk);
|
||||
msg.new_eks = slac::defs::CM_SET_KEY_REQ_PEKS_NMK_KNOWN_TO_STA;
|
||||
memcpy(msg.new_key, nmk, sizeof(msg.new_key));
|
||||
|
||||
ctx.send_slac_message(ctx.EV_PLC_MAC.data(), msg);
|
||||
|
||||
timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(SET_KEY_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
FSMSimpleState::CallbackReturnType JoinNetworkState::callback() {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto time_left = milliseconds_left(now, timeout);
|
||||
|
||||
if (time_left > 0) {
|
||||
// still have time
|
||||
return time_left;
|
||||
}
|
||||
|
||||
// we reached the set key timeout
|
||||
return Event::FAILED;
|
||||
}
|
||||
|
||||
bool JoinNetworkState::check_for_valid_set_key_conf() {
|
||||
const auto mmtype = ctx.slac_message.get_mmtype();
|
||||
if (mmtype != (slac::defs::MMTYPE_CM_SET_KEY | slac::defs::MMTYPE_MODE_CNF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// correct message type
|
||||
const auto& set_key_cnf = ctx.slac_message.get_payload<slac::messages::cm_set_key_cnf>();
|
||||
|
||||
// FIXME (aw): validation of the message?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FSMSimpleState::HandleEventReturnType MatchedState::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::RESET) {
|
||||
return sa.create_simple<ResetState>(ctx);
|
||||
} else if (ev == Event::SLAC_MESSAGE) {
|
||||
return sa.HANDLED_INTERNALLY;
|
||||
} else {
|
||||
return sa.PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
FSMSimpleState::HandleEventReturnType FailedState::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::RESET) {
|
||||
return sa.create_simple<ResetState>(ctx);
|
||||
} else if (ev == Event::SLAC_MESSAGE) {
|
||||
return sa.HANDLED_INTERNALLY;
|
||||
} else {
|
||||
return sa.PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace slac::fsm::ev
|
||||
@@ -0,0 +1,154 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include <everest/slac/fsm/ev/states/sounding.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
|
||||
#include "timing_helper.hpp"
|
||||
|
||||
#include <everest/slac/fsm/ev/states/others.hpp>
|
||||
|
||||
namespace slac::fsm::ev {
|
||||
|
||||
SoundingState::SoundingState(Context& ctx, SessionParamaters session_parameters_) :
|
||||
FSMSimpleState(ctx), session_parameters(std::move(session_parameters_)) {
|
||||
}
|
||||
|
||||
void SoundingState::enter() {
|
||||
sounding_timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(slac::defs::TT_EV_ATTEN_RESULTS_MS);
|
||||
}
|
||||
|
||||
FSMSimpleState::CallbackReturnType SoundingState::callback() {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto sounding_time_left = milliseconds_left(now, sounding_timeout);
|
||||
|
||||
if (sounding_time_left <= 0) {
|
||||
return Event::FAILED;
|
||||
}
|
||||
|
||||
if (count_mnbc_sound_sent == 10) {
|
||||
// we're already done, return time left until sounding timeout
|
||||
return sounding_time_left;
|
||||
}
|
||||
|
||||
if (count_start_atten_char_sent == 0) {
|
||||
// no sounding messages have been send yet
|
||||
next_timeout = now;
|
||||
}
|
||||
|
||||
const auto next_step_time_left = milliseconds_left(now, next_timeout);
|
||||
|
||||
if (next_step_time_left > 0) {
|
||||
// just idle a bit
|
||||
return std::min(next_step_time_left, sounding_time_left);
|
||||
}
|
||||
|
||||
// need to issue the next step
|
||||
if (do_sounding()) {
|
||||
// FIXME (aw): how to setup the next timeout, we could send the everything right away, but waiting at least a
|
||||
// bit seems to be a good practice
|
||||
const auto next_step_delay_ms = 20; // FIXME (aw): which value to use? TP_EV_BATCH_MSG_INTERVAL_MS
|
||||
next_timeout = now + std::chrono::milliseconds(next_step_delay_ms);
|
||||
return std::min(next_step_delay_ms, static_cast<int>(sounding_time_left));
|
||||
}
|
||||
|
||||
return sounding_time_left;
|
||||
}
|
||||
|
||||
FSMSimpleState::HandleEventReturnType SoundingState::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::SLAC_MESSAGE) {
|
||||
if (handle_valid_atten_char_ind()) {
|
||||
return sa.create_simple<MatchRequestState>(ctx, session_parameters);
|
||||
}
|
||||
return sa.HANDLED_INTERNALLY;
|
||||
} else if (ev == Event::RESET) {
|
||||
return sa.create_simple<ResetState>(ctx);
|
||||
}
|
||||
|
||||
return sa.PASS_ON;
|
||||
}
|
||||
|
||||
bool SoundingState::do_sounding() {
|
||||
if (count_start_atten_char_sent < slac::defs::C_EV_START_ATTEN_CHAR_INDS) {
|
||||
|
||||
slac::messages::cm_start_atten_char_ind msg;
|
||||
msg.application_type = 0x0;
|
||||
msg.security_type = 0x0;
|
||||
msg.num_sounds = slac::defs::C_EV_MATCH_MNBC;
|
||||
msg.timeout = (slac::defs::TT_EVSE_MATCH_MNBC_MS + 99) / 100; // in multiples of 100ms!
|
||||
msg.resp_type = 0x01; // fixed value indicating 'other Green Phy station'
|
||||
memcpy(msg.forwarding_sta, ctx.ev_host_mac.data(), sizeof(msg.forwarding_sta));
|
||||
memcpy(msg.run_id, session_parameters.run_id, sizeof(msg.run_id));
|
||||
|
||||
ctx.send_slac_message(ctx.BROADCAST_MAC.data(), msg);
|
||||
|
||||
count_start_atten_char_sent++;
|
||||
|
||||
} else if (count_mnbc_sound_sent < slac::defs::C_EV_MATCH_MNBC) {
|
||||
count_mnbc_sound_sent++;
|
||||
|
||||
slac::messages::cm_mnbc_sound_ind msg;
|
||||
msg.application_type = 0x0;
|
||||
msg.security_type = 0x0;
|
||||
memset(msg.sender_id, 0, sizeof(msg.sender_id));
|
||||
msg.remaining_sound_count = slac::defs::C_EV_MATCH_MNBC - count_mnbc_sound_sent;
|
||||
memcpy(msg.run_id, session_parameters.run_id, sizeof(msg.run_id));
|
||||
memset(msg._reserved, 0, sizeof(msg._reserved));
|
||||
|
||||
// FIXME (aw): does this have any performance penalties?
|
||||
std::random_device rnd_dev;
|
||||
std::mt19937 rng(rnd_dev());
|
||||
std::uniform_int_distribution<std::mt19937::result_type> dist256(0, 255);
|
||||
|
||||
for (auto& random : msg.random) {
|
||||
random = dist256(rng);
|
||||
}
|
||||
|
||||
ctx.send_slac_message(ctx.BROADCAST_MAC.data(), msg);
|
||||
}
|
||||
|
||||
return (count_mnbc_sound_sent < slac::defs::C_EV_MATCH_MNBC);
|
||||
}
|
||||
|
||||
bool SoundingState::handle_valid_atten_char_ind() {
|
||||
if (count_mnbc_sound_sent < slac::defs::C_EV_MATCH_MNBC) {
|
||||
ctx.log_info("Received unexpected message while SOUNDING");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto mmtype = ctx.slac_message.get_mmtype();
|
||||
|
||||
if (mmtype != (slac::defs::MMTYPE_CM_ATTEN_CHAR | slac::defs::MMTYPE_MODE_IND)) {
|
||||
ctx.log_info("Received unexpected message after SOUNDING");
|
||||
return false;
|
||||
}
|
||||
|
||||
// correct message type
|
||||
const auto& atten_char = ctx.slac_message.get_payload<slac::messages::cm_atten_char_ind>();
|
||||
|
||||
const auto run_id_match =
|
||||
(memcmp(session_parameters.run_id, atten_char.run_id, sizeof(session_parameters.run_id)) == 0);
|
||||
|
||||
if (run_id_match == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// reply
|
||||
slac::messages::cm_atten_char_rsp response;
|
||||
response.application_type = 0x0;
|
||||
response.security_type = 0x0;
|
||||
memcpy(response.source_address, ctx.ev_host_mac.data(), sizeof(response.source_address));
|
||||
memcpy(response.run_id, atten_char.run_id, sizeof(response.run_id));
|
||||
memset(response.source_id, 0, sizeof(response.source_id));
|
||||
memset(response.resp_id, 0, sizeof(response.resp_id));
|
||||
response.result = 0x0;
|
||||
|
||||
ctx.send_slac_message(session_parameters.evse_mac, response);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace slac::fsm::ev
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef EV_SLAC_STATES_TIMING_HELPER_HPP
|
||||
#define EV_SLAC_STATES_TIMING_HELPER_HPP
|
||||
|
||||
#include <chrono>
|
||||
|
||||
template <typename TimepointType> auto milliseconds_left(const TimepointType& from, const TimepointType& to) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(to - from).count();
|
||||
}
|
||||
|
||||
#endif // EV_SLAC_STATES_TIMING_HELPER_HPP
|
||||
Reference in New Issue
Block a user