diff --git a/dependencies.yaml b/dependencies.yaml index f7f207832..671596e2b 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -7,10 +7,6 @@ nlohmann_json_schema_validator: git: https://github.com/pboettch/json-schema-validator git_tag: 2.1.0 options: ["BUILD_TESTS OFF", "BUILD_EXAMPLES OFF"] -libfsm: - git: https://github.com/EVerest/libfsm.git - git_tag: v0.1.0 - options: ["BUILD_EXAMPLES OFF"] liblog: git: https://github.com/EVerest/liblog.git git_tag: v0.1.0 diff --git a/doc/state_chart_1_6.drawio b/doc/state_chart_1_6.drawio new file mode 100644 index 000000000..03794f678 --- /dev/null +++ b/doc/state_chart_1_6.drawio @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/include/ocpp/v16/charge_point_state_machine.hpp b/include/ocpp/v16/charge_point_state_machine.hpp index 4146bc357..8d525ddf2 100644 --- a/include/ocpp/v16/charge_point_state_machine.hpp +++ b/include/ocpp/v16/charge_point_state_machine.hpp @@ -7,22 +7,15 @@ #include #include #include -#include #include -#include #include -#include -#include -#include -#include - #include namespace ocpp { namespace v16 { -enum class ChargePointStatusTransition { +enum class FSMEvent { BecomeAvailable, UsageInitiated, StartCharging, @@ -31,7 +24,7 @@ enum class ChargePointStatusTransition { ReserveConnector, TransactionStoppedAndUserActionRequired, ChangeAvailabilityToUnavailable, - FaultDetected, + // FaultDetected - note: this event is handled via a separate function I1_ReturnToAvailable, I2_ReturnToPreparing, I3_ReturnToCharging, @@ -42,111 +35,45 @@ enum class ChargePointStatusTransition { I8_ReturnToUnavailable, }; -using EventTypeFactory = fsm::utils::IdentifiableTypeFactory; - -using Event_UsageInitiated = EventTypeFactory::Derived; -using Event_StartCharging = EventTypeFactory::Derived; -using Event_PauseChargingEV = EventTypeFactory::Derived; -using Event_PauseChargingEVSE = EventTypeFactory::Derived; -using Event_ReserveConnector = EventTypeFactory::Derived; -using Event_ChangeAvailabilityToUnavailable = - EventTypeFactory::Derived; -using Event_FaultDetected = EventTypeFactory::Derived; -using Event_BecomeAvailable = EventTypeFactory::Derived; -using Event_TransactionStoppedAndUserActionRequired = - EventTypeFactory::Derived; -using Event_I1_ReturnToAvailable = EventTypeFactory::Derived; -using Event_I2_ReturnToPreparing = EventTypeFactory::Derived; -using Event_I3_ReturnToCharging = EventTypeFactory::Derived; -using Event_I4_ReturnToSuspendedEV = EventTypeFactory::Derived; -using Event_I5_ReturnToSuspendedEVSE = EventTypeFactory::Derived; -using Event_I6_ReturnToFinishing = EventTypeFactory::Derived; -using Event_I7_ReturnToReserved = EventTypeFactory::Derived; -using Event_I8_ReturnToUnavailable = EventTypeFactory::Derived; - -using EventBufferType = std::aligned_union_t<0, Event_FaultDetected>; -using EventBaseType = EventTypeFactory::Base; - -enum class State { - Available, - Preparing, - Charging, - SuspendedEV, - SuspendedEVSE, - Finishing, - Reserved, - Unavailable, - Faulted, -}; +using FSMState = ChargePointStatus; -struct StateIdType { - const State id; - const std::string name; -}; +using FSMStateTransitions = std::map; -struct ChargePointStateMachine { - using EventInfoType = fsm::EventInfo; - using StateHandleType = fsm::async::StateHandle; - using TransitionType = StateHandleType::TransitionWrapperType; - using FSMContextType = StateHandleType::FSMContextType; - - // define state descriptors - StateHandleType sd_available{{State::Available, "Available"}}; - StateHandleType sd_preparing{{State::Preparing, "Preparing"}}; - StateHandleType sd_charging{{State::Charging, "Charging"}}; - StateHandleType sd_suspended_ev{{State::SuspendedEV, "SuspendedEV"}}; - StateHandleType sd_suspended_evse{{State::SuspendedEVSE, "SuspendedEVSE"}}; - StateHandleType sd_finishing{{State::Finishing, "Finishing"}}; - StateHandleType sd_reserved{{State::Reserved, "Reserved"}}; - StateHandleType sd_unavailable{{State::Unavailable, "Unavailable"}}; - - struct FaultedStateType : public StateHandleType { - using StateHandleType::StateHandleType; - ChargePointErrorCode error_code{ChargePointErrorCode::NoError}; - } sd_faulted{{State::Faulted, "Faulted"}}; - - StateHandleType& t_faulted(const Event_FaultDetected& fault_ev); - - // track current state - ChargePointStatus state; +using FSMDefinition = std::map; +class ChargePointFSM { +public: using StatusNotificationCallback = std::function; - StatusNotificationCallback status_notification_callback; - - explicit ChargePointStateMachine(const StatusNotificationCallback& status_notification_callback); - - ChargePointStatus get_state(); -}; + explicit ChargePointFSM(const StatusNotificationCallback& status_notification_callback, FSMState initial_state); -using ChargePointStateMachineController = fsm::async::PThreadController; + bool handle_event(FSMEvent event); + bool handle_fault(const ChargePointErrorCode& error_code); -struct ChargePointStateMachineWithController { - std::shared_ptr state_machine; - std::shared_ptr controller; + FSMState get_state() const; - ChargePointStateMachineWithController(std::shared_ptr state_machine, - std::shared_ptr controller) { - this->state_machine = state_machine; - this->controller = controller; - } +private: + StatusNotificationCallback status_notification_callback; + // track current state + FSMState state; }; class ChargePointStates { -private: - std::vector> state_machines; - std::mutex state_machines_mutex; - std::function - status_notification_callback; - public: - ChargePointStates(int32_t number_of_connectors, - const std::function& status_notification_callback); - void run(std::map connector_availability); + using ConnectorStatusCallback = + std::function; + ChargePointStates(const ConnectorStatusCallback& connector_status_callback); + void reset(std::map connector_availability); + + void submit_event(int connector_id, FSMEvent event); + void submit_error(int connector_id, const ChargePointErrorCode& error_code); - void submit_event(int32_t connector, const EventBaseType& event); + ChargePointStatus get_state(int connector_id); - ChargePointStatus get_state(int32_t connector); +private: + ConnectorStatusCallback connector_status_callback; + + std::vector state_machines; + std::mutex state_machines_mutex; }; } // namespace v16 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 64d892e78..16654f284 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -42,7 +42,6 @@ target_link_libraries(ocpp PUBLIC everest::log everest::timer - fsm::fsm websocketpp::websocketpp PRIVATE Boost::filesystem diff --git a/lib/ocpp/v16/charge_point.cpp b/lib/ocpp/v16/charge_point.cpp index 7015486dc..26d229cdb 100644 --- a/lib/ocpp/v16/charge_point.cpp +++ b/lib/ocpp/v16/charge_point.cpp @@ -93,7 +93,6 @@ ChargePoint::ChargePoint(const json& config, const std::string& share_path, cons }); this->status = std::make_unique( - this->configuration->getNumberOfConnectors(), [this](int32_t connector, ChargePointErrorCode errorCode, ChargePointStatus status) { this->status_notification_timers.at(connector)->stop(); this->status_notification_timers.at(connector)->timeout( @@ -690,7 +689,10 @@ bool ChargePoint::start() { connector_availability[0] = AvailabilityType::Operative; // FIXME(kai): fix internal representation in charge // point states, we need a different kind of state // machine for connector 0 anyway (with reduced states) - this->status->run(connector_availability); + if (this->configuration->getNumberOfConnectors() + 1 != connector_availability.size()) { + throw std::runtime_error("Number of configured connectors doesn't match number of connectors in the database."); + } + this->status->reset(connector_availability); this->init_websocket(this->configuration->getSecurityProfile()); this->websocket->connect(this->configuration->getSecurityProfile()); @@ -1087,13 +1089,13 @@ void ChargePoint::handleChangeAvailabilityRequest(ocpp::Callenable_evse_callback(connector); } - this->status->submit_event(connector, Event_BecomeAvailable()); + this->status->submit_event(connector, FSMEvent::BecomeAvailable); } else { if (this->disable_evse_callback != nullptr) { // TODO(kai): check return value this->disable_evse_callback(connector); } - this->status->submit_event(connector, Event_ChangeAvailabilityToUnavailable()); + this->status->submit_event(connector, FSMEvent::ChangeAvailabilityToUnavailable); } } @@ -1562,13 +1564,13 @@ void ChargePoint::handleStopTransactionResponse(ocpp::CallResultenable_evse_callback(connector); } - this->status->submit_event(connector, Event_BecomeAvailable()); + this->status->submit_event(connector, FSMEvent::BecomeAvailable); } else { if (this->disable_evse_callback != nullptr) { // TODO(kai): check return value this->disable_evse_callback(connector); } - this->status->submit_event(connector, Event_ChangeAvailabilityToUnavailable()); + this->status->submit_event(connector, FSMEvent::ChangeAvailabilityToUnavailable); } } this->database_handler->update_transaction_csms_ack(transaction->get_session_id()); @@ -2874,17 +2876,17 @@ void ChargePoint::on_session_started(int32_t connector, const std::string& sessi if ((this->status->get_state(connector) == ChargePointStatus::Reserved && session_started_reason == SessionStartedReason::Authorized) || this->status->get_state(connector) != ChargePointStatus::Reserved) { - this->status->submit_event(connector, Event_UsageInitiated()); + this->status->submit_event(connector, FSMEvent::UsageInitiated); } } void ChargePoint::on_session_stopped(const int32_t connector, const std::string& session_id) { // TODO(piet) fix this when evse manager signals clearance of an error if (this->status->get_state(connector) == ChargePointStatus::Faulted) { - this->status->submit_event(connector, Event_I1_ReturnToAvailable()); + this->status->submit_event(connector, FSMEvent::I1_ReturnToAvailable); } else if (this->status->get_state(connector) != ChargePointStatus::Reserved && this->status->get_state(connector) != ChargePointStatus::Unavailable) { - this->status->submit_event(connector, Event_BecomeAvailable()); + this->status->submit_event(connector, FSMEvent::BecomeAvailable); } if (this->logging->session_logging_active()) { @@ -2897,7 +2899,7 @@ void ChargePoint::on_transaction_started(const int32_t& connector, const std::st boost::optional reservation_id, const ocpp::DateTime& timestamp, boost::optional signed_meter_value) { if (this->status->get_state(connector) == ChargePointStatus::Reserved) { - this->status->submit_event(connector, Event_UsageInitiated()); + this->status->submit_event(connector, FSMEvent::UsageInitiated); } auto meter_values_sample_timer = std::make_unique(&this->io_service, [this, connector]() { @@ -2906,7 +2908,7 @@ void ChargePoint::on_transaction_started(const int32_t& connector, const std::st if (meter_value.has_value()) { this->transaction_handler->add_meter_value(connector, meter_value.value()); this->send_meter_value(connector, meter_value.value()); - + // this updates the last meter value in the database const auto transaction = this->transaction_handler->get_transaction(connector); if (transaction != nullptr) { @@ -2960,7 +2962,7 @@ void ChargePoint::on_transaction_stopped(const int32_t connector, const std::str const auto stop_energy_wh = std::make_shared(timestamp, energy_wh_import); this->transaction_handler->get_transaction(connector)->add_stop_energy_wh(stop_energy_wh); - this->status->submit_event(connector, Event_TransactionStoppedAndUserActionRequired()); + this->status->submit_event(connector, FSMEvent::TransactionStoppedAndUserActionRequired); this->stop_transaction(connector, reason, id_tag_end); this->database_handler->update_transaction(session_id, energy_wh_import, timestamp.to_rfc3339(), id_tag_end, reason); @@ -3054,19 +3056,19 @@ ChargePoint::get_filtered_transaction_data(const std::shared_ptr& t } void ChargePoint::on_suspend_charging_ev(int32_t connector) { - this->status->submit_event(connector, Event_PauseChargingEV()); + this->status->submit_event(connector, FSMEvent::PauseChargingEV); } void ChargePoint::on_suspend_charging_evse(int32_t connector) { - this->status->submit_event(connector, Event_PauseChargingEVSE()); + this->status->submit_event(connector, FSMEvent::PauseChargingEVSE); } void ChargePoint::on_resume_charging(int32_t connector) { - this->status->submit_event(connector, Event_StartCharging()); + this->status->submit_event(connector, FSMEvent::StartCharging); } void ChargePoint::on_error(int32_t connector, const ChargePointErrorCode& error) { - this->status->submit_event(connector, Event_FaultDetected(ChargePointErrorCode(error))); + this->status->submit_error(connector, error); } void ChargePoint::on_log_status_notification(int32_t request_id, std::string log_status) { @@ -3209,19 +3211,19 @@ void ChargePoint::register_get_15118_ev_certificate_response_callback( } void ChargePoint::on_reservation_start(int32_t connector) { - this->status->submit_event(connector, Event_ReserveConnector()); + this->status->submit_event(connector, FSMEvent::ReserveConnector); } void ChargePoint::on_reservation_end(int32_t connector) { - this->status->submit_event(connector, Event_BecomeAvailable()); + this->status->submit_event(connector, FSMEvent::BecomeAvailable); } void ChargePoint::on_enabled(int32_t connector) { - this->status->submit_event(connector, Event_BecomeAvailable()); + this->status->submit_event(connector, FSMEvent::BecomeAvailable); } void ChargePoint::on_disabled(int32_t connector) { - this->status->submit_event(connector, Event_ChangeAvailabilityToUnavailable()); + this->status->submit_event(connector, FSMEvent::ChangeAvailabilityToUnavailable); } } // namespace v16 diff --git a/lib/ocpp/v16/charge_point_state_machine.cpp b/lib/ocpp/v16/charge_point_state_machine.cpp index 95148275b..ed376afbe 100644 --- a/lib/ocpp/v16/charge_point_state_machine.cpp +++ b/lib/ocpp/v16/charge_point_state_machine.cpp @@ -3,293 +3,165 @@ #include #include -#include +#include #include #include #include -#include - -#include namespace ocpp { namespace v16 { -ChargePointStateMachine::ChargePointStateMachine(const StatusNotificationCallback& status_notification_callback) : - status_notification_callback(status_notification_callback) { - this->sd_available.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::UsageInitiated: - return trans.set(this->sd_preparing); - case ChargePointStatusTransition::StartCharging: - return trans.set(this->sd_charging); - case ChargePointStatusTransition::PauseChargingEV: - return trans.set(this->sd_suspended_ev); - case ChargePointStatusTransition::PauseChargingEVSE: - return trans.set(this->sd_suspended_evse); - case ChargePointStatusTransition::ReserveConnector: - return trans.set(this->sd_reserved); - case ChargePointStatusTransition::ChangeAvailabilityToUnavailable: - return trans.set(this->sd_unavailable); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_available.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::Available; - this->status_notification_callback(ChargePointStatus::Available, ChargePointErrorCode::NoError); - }; - - this->sd_preparing.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::BecomeAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::StartCharging: - return trans.set(this->sd_charging); - case ChargePointStatusTransition::PauseChargingEV: - return trans.set(this->sd_suspended_ev); - case ChargePointStatusTransition::PauseChargingEVSE: - return trans.set(this->sd_suspended_evse); - case ChargePointStatusTransition::TransactionStoppedAndUserActionRequired: - return trans.set(this->sd_finishing); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_preparing.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::Preparing; - this->status_notification_callback(ChargePointStatus::Preparing, ChargePointErrorCode::NoError); - }; - - this->sd_charging.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::BecomeAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::PauseChargingEV: - return trans.set(this->sd_suspended_ev); - case ChargePointStatusTransition::PauseChargingEVSE: - return trans.set(this->sd_suspended_evse); - case ChargePointStatusTransition::TransactionStoppedAndUserActionRequired: - return trans.set(this->sd_finishing); - case ChargePointStatusTransition::ChangeAvailabilityToUnavailable: - return trans.set(this->sd_unavailable); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_charging.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::Charging; - this->status_notification_callback(ChargePointStatus::Charging, ChargePointErrorCode::NoError); - }; +static const FSMDefinition FSM_DEF = { + {FSMState::Available, + { + {FSMEvent::UsageInitiated, FSMState::Preparing}, + {FSMEvent::StartCharging, FSMState::Charging}, + {FSMEvent::PauseChargingEV, FSMState::SuspendedEV}, + {FSMEvent::PauseChargingEVSE, FSMState::SuspendedEVSE}, + {FSMEvent::ReserveConnector, FSMState::Reserved}, + {FSMEvent::ChangeAvailabilityToUnavailable, FSMState::Unavailable}, + }}, + {FSMState::Preparing, + { + {FSMEvent::BecomeAvailable, FSMState::Available}, + {FSMEvent::StartCharging, FSMState::Charging}, + {FSMEvent::PauseChargingEV, FSMState::SuspendedEV}, + {FSMEvent::PauseChargingEVSE, FSMState::SuspendedEVSE}, + {FSMEvent::TransactionStoppedAndUserActionRequired, FSMState::Finishing}, + }}, + {FSMState::Charging, + { + {FSMEvent::BecomeAvailable, FSMState::Available}, + {FSMEvent::PauseChargingEV, FSMState::SuspendedEV}, + {FSMEvent::PauseChargingEVSE, FSMState::SuspendedEVSE}, + {FSMEvent::TransactionStoppedAndUserActionRequired, FSMState::Finishing}, + {FSMEvent::ChangeAvailabilityToUnavailable, FSMState::Unavailable}, + }}, + {FSMState::SuspendedEV, + { + {FSMEvent::BecomeAvailable, FSMState::Available}, + {FSMEvent::StartCharging, FSMState::Charging}, + {FSMEvent::PauseChargingEVSE, FSMState::SuspendedEVSE}, + {FSMEvent::TransactionStoppedAndUserActionRequired, FSMState::Finishing}, + {FSMEvent::ChangeAvailabilityToUnavailable, FSMState::Unavailable}, + }}, + {FSMState::SuspendedEVSE, + { + {FSMEvent::BecomeAvailable, FSMState::Available}, + {FSMEvent::StartCharging, FSMState::Charging}, + {FSMEvent::PauseChargingEV, FSMState::SuspendedEV}, + {FSMEvent::TransactionStoppedAndUserActionRequired, FSMState::Finishing}, + {FSMEvent::ChangeAvailabilityToUnavailable, FSMState::Unavailable}, + }}, + {FSMState::Finishing, + { + {FSMEvent::BecomeAvailable, FSMState::Available}, + {FSMEvent::UsageInitiated, FSMState::Preparing}, + {FSMEvent::ChangeAvailabilityToUnavailable, FSMState::Unavailable}, + }}, + {FSMState::Reserved, + { + {FSMEvent::BecomeAvailable, FSMState::Available}, + {FSMEvent::UsageInitiated, FSMState::Preparing}, + {FSMEvent::ChangeAvailabilityToUnavailable, FSMState::Unavailable}, + }}, + {FSMState::Unavailable, + { + {FSMEvent::BecomeAvailable, FSMState::Available}, + {FSMEvent::UsageInitiated, FSMState::Preparing}, + {FSMEvent::StartCharging, FSMState::Charging}, + {FSMEvent::PauseChargingEV, FSMState::SuspendedEV}, + {FSMEvent::PauseChargingEVSE, FSMState::SuspendedEVSE}, + }}, + {FSMState::Faulted, + { + {FSMEvent::I1_ReturnToAvailable, FSMState::Available}, + {FSMEvent::I2_ReturnToPreparing, FSMState::Preparing}, + {FSMEvent::I3_ReturnToCharging, FSMState::Charging}, + {FSMEvent::I4_ReturnToSuspendedEV, FSMState::SuspendedEV}, + {FSMEvent::I5_ReturnToSuspendedEVSE, FSMState::SuspendedEVSE}, + {FSMEvent::I6_ReturnToFinishing, FSMState::Finishing}, + {FSMEvent::I7_ReturnToReserved, FSMState::Reserved}, + {FSMEvent::I8_ReturnToUnavailable, FSMState::Unavailable}, + }}, +}; + +ChargePointFSM::ChargePointFSM(const StatusNotificationCallback& status_notification_callback_, + FSMState initial_state) : + status_notification_callback(status_notification_callback_), state(initial_state) { + // FIXME (aw): do we need to call the callback here already? +} - this->sd_suspended_ev.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::BecomeAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::StartCharging: - return trans.set(this->sd_charging); - case ChargePointStatusTransition::PauseChargingEVSE: - return trans.set(this->sd_suspended_evse); - case ChargePointStatusTransition::TransactionStoppedAndUserActionRequired: - return trans.set(this->sd_finishing); - case ChargePointStatusTransition::ChangeAvailabilityToUnavailable: - return trans.set(this->sd_unavailable); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_suspended_ev.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::SuspendedEV; - this->status_notification_callback(ChargePointStatus::SuspendedEV, ChargePointErrorCode::NoError); - }; +FSMState ChargePointFSM::get_state() const { + return state; +} - this->sd_suspended_evse.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::BecomeAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::StartCharging: - return trans.set(this->sd_charging); - case ChargePointStatusTransition::PauseChargingEV: - return trans.set(this->sd_suspended_ev); - case ChargePointStatusTransition::TransactionStoppedAndUserActionRequired: - return trans.set(this->sd_finishing); - case ChargePointStatusTransition::ChangeAvailabilityToUnavailable: - return trans.set(this->sd_unavailable); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_suspended_evse.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::SuspendedEVSE; - this->status_notification_callback(ChargePointStatus::SuspendedEVSE, ChargePointErrorCode::NoError); - }; +bool ChargePointFSM::handle_event(FSMEvent event) { + const auto& transitions = FSM_DEF.at(state); + const auto dest_state_it = transitions.find(event); - this->sd_finishing.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::BecomeAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::UsageInitiated: // user restarts charging session - return trans.set(this->sd_preparing); - case ChargePointStatusTransition::ChangeAvailabilityToUnavailable: - return trans.set(this->sd_unavailable); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_finishing.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::Finishing; - this->status_notification_callback(ChargePointStatus::Finishing, ChargePointErrorCode::NoError); - }; + if (dest_state_it == transitions.end()) { + // no transition defined for this event / should this be logged? + return false; + } - this->sd_reserved.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::BecomeAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::UsageInitiated: - return trans.set(this->sd_preparing); - case ChargePointStatusTransition::ChangeAvailabilityToUnavailable: - return trans.set(this->sd_unavailable); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_reserved.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::Reserved; - this->status_notification_callback(ChargePointStatus::Reserved, ChargePointErrorCode::NoError); - }; + // fall through: transition found + state = dest_state_it->second; - this->sd_unavailable.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::BecomeAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::UsageInitiated: - return trans.set(this->sd_preparing); - case ChargePointStatusTransition::StartCharging: - return trans.set(this->sd_charging); - case ChargePointStatusTransition::PauseChargingEV: - return trans.set(this->sd_suspended_ev); - case ChargePointStatusTransition::PauseChargingEVSE: - return trans.set(this->sd_suspended_evse); - case ChargePointStatusTransition::FaultDetected: - return trans.set(ev, - this); - default: - return; - } - }; - this->sd_unavailable.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::Unavailable; - this->status_notification_callback(ChargePointStatus::Unavailable, ChargePointErrorCode::NoError); - }; + status_notification_callback(state, ChargePointErrorCode::NoError); - this->sd_faulted.transitions = [this](const EventBaseType& ev, TransitionType& trans) { - switch (ev.id) { - case ChargePointStatusTransition::I1_ReturnToAvailable: - return trans.set(this->sd_available); - case ChargePointStatusTransition::I2_ReturnToPreparing: - return trans.set(this->sd_preparing); - case ChargePointStatusTransition::I3_ReturnToCharging: - return trans.set(this->sd_charging); - case ChargePointStatusTransition::I4_ReturnToSuspendedEV: - return trans.set(this->sd_suspended_ev); - case ChargePointStatusTransition::I5_ReturnToSuspendedEVSE: - return trans.set(this->sd_suspended_evse); - case ChargePointStatusTransition::I6_ReturnToFinishing: - return trans.set(this->sd_finishing); - case ChargePointStatusTransition::I7_ReturnToReserved: - return trans.set(this->sd_reserved); - case ChargePointStatusTransition::I8_ReturnToUnavailable: - return trans.set(this->sd_unavailable); - default: - return; - } - }; - this->sd_faulted.state_fun = [this](FSMContextType& ctx) { - this->state = ChargePointStatus::Faulted; - this->status_notification_callback(ChargePointStatus::Faulted, sd_faulted.error_code); - }; + return true; } -ChargePointStatus ChargePointStateMachine::get_state() { - return this->state; +bool ChargePointFSM::handle_fault(const ChargePointErrorCode& error_code) { + state = FSMState::Faulted; + status_notification_callback(state, error_code); + return true; } -// custom transitions -ChargePointStateMachine::StateHandleType& ChargePointStateMachine::t_faulted(const Event_FaultDetected& fault_ev) { - sd_faulted.error_code = fault_ev.data; - return sd_faulted; +ChargePointStates::ChargePointStates(const ConnectorStatusCallback& callback) : connector_status_callback(callback) { + // TODO special state machine for connector 0 } -ChargePointStates::ChargePointStates( - int32_t number_of_connectors, - const std::function& - status_notification_callback) : - status_notification_callback(status_notification_callback) { - for (int32_t connector = 0; connector < number_of_connectors + 1; connector++) { - // TODO special state machine for connector 0 - auto state_machine = std::make_shared( - [this, connector](ChargePointStatus status, ChargePointErrorCode error_code) { - this->status_notification_callback(connector, error_code, status); - }); - auto controller = std::make_shared(); - this->state_machines.push_back( - std::make_shared(state_machine, controller)); +void ChargePointStates::reset(std::map connector_availability) { + const std::lock_guard lck(state_machines_mutex); + state_machines.clear(); + + for (size_t connector_id = 0; connector_id < connector_availability.size(); ++connector_id) { + const auto availability = connector_availability.at(connector_id); + const auto initial_state = + (availability == AvailabilityType::Operative) ? FSMState::Available : FSMState::Unavailable; + state_machines.emplace_back( + [this, connector_id](ChargePointStatus status, ChargePointErrorCode error_code) { + this->connector_status_callback(connector_id, error_code, status); + }, + initial_state); } } -void ChargePointStates::run(std::map connector_availability) { - if (this->state_machines.size() != connector_availability.size()) { - throw std::runtime_error("Could not initialize charge point state machine, number of initial states given does " - "not match number of state machines."); - } - size_t count = 0; - for (auto& state_machine : this->state_machines) { - if (connector_availability.at(count) == AvailabilityType::Operative) { - state_machine->controller->run(state_machine->state_machine->sd_available); - } else { - state_machine->controller->run(state_machine->state_machine->sd_unavailable); - } - count += 1; +void ChargePointStates::submit_event(int connector_id, FSMEvent event) { + const std::lock_guard lck(state_machines_mutex); + if (connector_id > 0 && connector_id < this->state_machines.size()) { + this->state_machines.at(connector_id).handle_event(event); } } -void ChargePointStates::submit_event(int32_t connector, const EventBaseType& event) { - - if (connector > 0 && connector < static_cast(this->state_machines.size())) { - try { - this->state_machines.at(connector)->controller->submit_event(event); - } catch (const std::exception &e) { - EVLOG_warning << "Could not submit event to state machine at connector# " << connector; - } +void ChargePointStates::submit_error(int connector_id, const ChargePointErrorCode& error_code) { + const std::lock_guard lck(state_machines_mutex); + if (connector_id > 0 && connector_id < state_machines.size()) { + state_machines.at(connector_id).handle_fault(error_code); } } -ChargePointStatus ChargePointStates::get_state(int32_t connector) { - if (connector > 0 && connector < static_cast(this->state_machines.size())) { - return this->state_machines.at(connector)->state_machine->get_state(); - } else if (connector == 0) { +ChargePointStatus ChargePointStates::get_state(int connector_id) { + const std::lock_guard lck(state_machines_mutex); + if (connector_id > 0 && connector_id < this->state_machines.size()) { + return state_machines.at(connector_id).get_state(); + } else if (connector_id == 0) { return ChargePointStatus::Available; } + + // fall through on invalid id return ChargePointStatus::Unavailable; }