- 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
296 lines
12 KiB
C++
296 lines
12 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright Pionix GmbH and Contributors to EVerest
|
|
#ifndef SCOPED_LOCK_TIMEOUT
|
|
#define SCOPED_LOCK_TIMEOUT
|
|
|
|
#include "everest/exceptions.hpp"
|
|
#include "everest/logging.hpp"
|
|
#include <mutex>
|
|
#include <signal.h>
|
|
|
|
#include "backtrace.hpp"
|
|
|
|
/*
|
|
Simple helper class for scoped lock with timeout
|
|
*/
|
|
namespace Everest {
|
|
|
|
enum class MutexDescription {
|
|
Undefined,
|
|
Charger_signal_loop,
|
|
Charger_signal_error,
|
|
Charger_signal_error_cleared,
|
|
Charger_mainloop,
|
|
Charger_process_event,
|
|
Charger_waiting_for_power,
|
|
Charger_cancel_transaction,
|
|
Charger_setup,
|
|
Charger_get_current_state,
|
|
Charger_get_authorized_pnc,
|
|
Charger_get_authorized_eim,
|
|
Charger_get_authorized_pnc_ready_for_hlc,
|
|
Charger_get_authorized_eim_ready_for_hlc,
|
|
Charger_authorize,
|
|
Charger_deauthorize,
|
|
Charger_disable,
|
|
Charger_enable,
|
|
Charger_set_faulted,
|
|
Charger_get_max_current,
|
|
Charger_set_current_drawn_by_vehicle,
|
|
Charger_request_error_sequence,
|
|
Charger_set_matching_started,
|
|
Charger_notify_currentdemand_started,
|
|
Charger_inform_new_evse_max_hlc_limits,
|
|
Charger_get_evse_max_hlc_limits,
|
|
Charger_inform_new_evse_min_hlc_limits,
|
|
Charger_get_evse_min_hlc_limits,
|
|
Charger_dlink_pause,
|
|
Charger_dlink_terminate,
|
|
Charger_dlink_error,
|
|
Charger_set_hlc_charging_active,
|
|
Charger_set_hlc_allow_close_contactor,
|
|
Charger_errors_prevent_charging,
|
|
Charger_set_max_current,
|
|
Charger_switch_three_phases_while_charging,
|
|
Charger_get_last_stop_transaction,
|
|
Charger_set_hlc_d20_active,
|
|
IEC_process_bsp_event,
|
|
IEC_state_machine,
|
|
IEC_set_pwm,
|
|
IEC_set_cp_state_X1,
|
|
IEC_set_cp_state_F,
|
|
IEC_allow_power_on,
|
|
IEC_force_unlock,
|
|
EVSE_charger_ready,
|
|
EVSE_set_ev_info,
|
|
EVSE_publish_ev_info,
|
|
EVSE_subscribe_dc_ev_maximum_limits,
|
|
EVSE_subscribe_departure_time,
|
|
EVSE_subscribe_ac_eamount,
|
|
EVSE_subscribe_ac_ev_max_voltage,
|
|
EVSE_subscribe_ac_ev_max_current,
|
|
EVSE_subscribe_ac_ev_min_current,
|
|
EVSE_subscribe_ac_ev_power_limits,
|
|
EVSE_subscribe_ac_ev_present_powers,
|
|
EVSE_subscribe_ac_ev_dynamic_control_mode,
|
|
EVSE_subscribe_dc_ev_energy_capacity,
|
|
EVSE_subscribe_dc_ev_energy_request,
|
|
EVSE_subscribe_dc_full_soc,
|
|
EVSE_subscribe_dc_bulk_soc,
|
|
EVSE_subscribe_dc_ev_remaining_time,
|
|
EVSE_subscribe_dc_ev_status,
|
|
EVSE_subscribe_evcc_id,
|
|
EVSE_subscribe_powermeter,
|
|
EVSE_get_latest_powermeter_data_billing,
|
|
EVSE_get_reservation_id,
|
|
EVSE_reserve,
|
|
EVSE_cancel_reservation,
|
|
EVSE_is_reserved,
|
|
EVSE_get_ev_info
|
|
};
|
|
|
|
static std::string to_string(MutexDescription d) {
|
|
switch (d) {
|
|
case MutexDescription::Undefined:
|
|
return "Undefined";
|
|
case MutexDescription::Charger_signal_loop:
|
|
return "Charger.cpp: error_handling->signal_loop";
|
|
case MutexDescription::Charger_signal_error:
|
|
return "Charger.cpp: error_handling->signal_error";
|
|
case MutexDescription::Charger_signal_error_cleared:
|
|
return "Charger.cpp: error_handling->signal_all_errors_cleared";
|
|
case MutexDescription::Charger_mainloop:
|
|
return "Charger.cpp: mainloop";
|
|
case MutexDescription::Charger_process_event:
|
|
return "Charger.cpp: process_event";
|
|
case MutexDescription::Charger_waiting_for_power:
|
|
return "Charger.cpp: pause_charging_wait_for_power";
|
|
case MutexDescription::Charger_cancel_transaction:
|
|
return "Charger.cpp: cancel_transaction";
|
|
case MutexDescription::Charger_setup:
|
|
return "Charger.cpp: setup";
|
|
case MutexDescription::Charger_get_current_state:
|
|
return "Charger.cpp: get_current_state";
|
|
case MutexDescription::Charger_get_authorized_pnc:
|
|
return "Charger.cpp: get_authorized_pnc";
|
|
case MutexDescription::Charger_get_authorized_eim:
|
|
return "Charger.cpp: get_authorized_eim";
|
|
case MutexDescription::Charger_get_authorized_pnc_ready_for_hlc:
|
|
return "Charger.cpp: get_authorized_pnc_ready_for_hlc";
|
|
case MutexDescription::Charger_get_authorized_eim_ready_for_hlc:
|
|
return "Charger.cpp: get_authorized_eim_ready_for_hlc";
|
|
case MutexDescription::Charger_authorize:
|
|
return "Charger.cpp: authorize";
|
|
case MutexDescription::Charger_deauthorize:
|
|
return "Charger.cpp: deauthorize";
|
|
case MutexDescription::Charger_disable:
|
|
return "Charger.cpp: disable";
|
|
case MutexDescription::Charger_enable:
|
|
return "Charger.cpp: enable";
|
|
case MutexDescription::Charger_set_faulted:
|
|
return "Charger.cpp: set_faulted";
|
|
case MutexDescription::Charger_get_max_current:
|
|
return "Charger.cpp: get_max_current";
|
|
case MutexDescription::Charger_set_current_drawn_by_vehicle:
|
|
return "Charger.cpp: set_current_drawn_by_vehicle";
|
|
case MutexDescription::Charger_request_error_sequence:
|
|
return "Charger.cpp: request_error_sequence";
|
|
case MutexDescription::Charger_set_matching_started:
|
|
return "Charger.cpp: set_matching_started";
|
|
case MutexDescription::Charger_notify_currentdemand_started:
|
|
return "Charger.cpp: notify_currentdemand_started";
|
|
case MutexDescription::Charger_inform_new_evse_max_hlc_limits:
|
|
return "Charger.cpp: inform_new_evse_max_hlc_limits";
|
|
case MutexDescription::Charger_get_evse_max_hlc_limits:
|
|
return "Charger.cpp: get_evse_max_hlc_limits";
|
|
case MutexDescription::Charger_inform_new_evse_min_hlc_limits:
|
|
return "Charger.cpp: inform_new_evse_min_hlc_limits";
|
|
case MutexDescription::Charger_get_evse_min_hlc_limits:
|
|
return "Charger.cpp: get_evse_min_hlc_limits";
|
|
case MutexDescription::Charger_dlink_pause:
|
|
return "Charger.cpp: dlink_pause";
|
|
case MutexDescription::Charger_dlink_terminate:
|
|
return "Charger.cpp: dlink_dlink_terminate";
|
|
case MutexDescription::Charger_dlink_error:
|
|
return "Charger.cpp: dlink_error";
|
|
case MutexDescription::Charger_set_hlc_charging_active:
|
|
return "Charger.cpp: set_hlc_charging_active";
|
|
case MutexDescription::Charger_set_hlc_allow_close_contactor:
|
|
return "Charger.cpp: set_hlc_allow_close_contactor";
|
|
case MutexDescription::Charger_errors_prevent_charging:
|
|
return "Charger.cpp: errors_prevent_charging";
|
|
case MutexDescription::Charger_set_max_current:
|
|
return "Charger.cpp: set max current";
|
|
case MutexDescription::Charger_switch_three_phases_while_charging:
|
|
return "Charger.cpp switch_three_phases_while_charging";
|
|
case MutexDescription::Charger_get_last_stop_transaction:
|
|
return "Charger.cpp get_last_stop_transaction";
|
|
case MutexDescription::Charger_set_hlc_d20_active:
|
|
return "Charger.cpp set_hlc_d20_active";
|
|
case MutexDescription::IEC_process_bsp_event:
|
|
return "IECStateMachine::process_bsp_event";
|
|
case MutexDescription::IEC_state_machine:
|
|
return "IECStateMachine::state_machine";
|
|
case MutexDescription::IEC_set_pwm:
|
|
return "IECStateMachine::set_pwm";
|
|
case MutexDescription::IEC_set_cp_state_X1:
|
|
return "IECStateMachine::set_cp_state_X1";
|
|
case MutexDescription::IEC_set_cp_state_F:
|
|
return "IECStateMachine::set_cp_state_F";
|
|
case MutexDescription::IEC_allow_power_on:
|
|
return "IECStateMachine::allow_power_on";
|
|
case MutexDescription::IEC_force_unlock:
|
|
return "IECStateMachine::force_unlock";
|
|
case MutexDescription::EVSE_charger_ready:
|
|
return "EvseManager.cpp: charger_ready";
|
|
case MutexDescription::EVSE_set_ev_info:
|
|
return "EvseManager.cpp: set ev_info present_voltage/current";
|
|
case MutexDescription::EVSE_publish_ev_info:
|
|
return "EvseManager.cpp: publish_ev_info";
|
|
case MutexDescription::EVSE_subscribe_dc_ev_maximum_limits:
|
|
return "EvseManager.cpp: subscribe_dc_ev_maximum_limits";
|
|
case MutexDescription::EVSE_subscribe_departure_time:
|
|
return "EvseManager.cpp: subscribe_departure_time";
|
|
case MutexDescription::EVSE_subscribe_ac_eamount:
|
|
return "EvseManager.cpp: subscribe_ac_eamount";
|
|
case MutexDescription::EVSE_subscribe_ac_ev_max_voltage:
|
|
return "EvseManager.cpp: subscribe_ac_ev_max_voltage";
|
|
case MutexDescription::EVSE_subscribe_ac_ev_max_current:
|
|
return "EvseManager.cpp: subscribe_ac_ev_max_current";
|
|
case MutexDescription::EVSE_subscribe_ac_ev_min_current:
|
|
return "EvseManager.cpp: subscribe_ac_ev_min_current";
|
|
case MutexDescription::EVSE_subscribe_ac_ev_power_limits:
|
|
return "EvseManager.cpp: subscribe_ac_ev_power_limits";
|
|
case MutexDescription::EVSE_subscribe_ac_ev_present_powers:
|
|
return "EvseManager.cpp: subscribe_ac_ev_present_powers";
|
|
case MutexDescription::EVSE_subscribe_ac_ev_dynamic_control_mode:
|
|
return "EvseManager.cpp: subscribe_ac_ev_dynamic_control_mode";
|
|
case MutexDescription::EVSE_subscribe_dc_ev_energy_capacity:
|
|
return "EvseManager.cpp: subscribe_dc_ev_energy_capacity";
|
|
case MutexDescription::EVSE_subscribe_dc_ev_energy_request:
|
|
return "EvseManager.cpp: subscribe_dc_ev_energy_request";
|
|
case MutexDescription::EVSE_subscribe_dc_full_soc:
|
|
return "EvseManager.cpp: subscribe_dc_full_soc";
|
|
case MutexDescription::EVSE_subscribe_dc_bulk_soc:
|
|
return "EvseManager.cpp: subscribe_dc_bulk_soc";
|
|
case MutexDescription::EVSE_subscribe_dc_ev_remaining_time:
|
|
return "EvseManager.cpp: subscribe_dc_ev_remaining_time";
|
|
case MutexDescription::EVSE_subscribe_dc_ev_status:
|
|
return "EvseManager.cpp subscribe_dc_ev_status";
|
|
case MutexDescription::EVSE_subscribe_evcc_id:
|
|
return "EvseManager.cpp: subscribe_evcc_id";
|
|
case MutexDescription::EVSE_subscribe_powermeter:
|
|
return "EvseManager.cpp: subscribe_powermeter";
|
|
case MutexDescription::EVSE_get_latest_powermeter_data_billing:
|
|
return "EvseManager.cpp: get_latest_powermeter_data_billing";
|
|
case MutexDescription::EVSE_get_reservation_id:
|
|
return "EvseManager.cpp: get_reservation_id";
|
|
case MutexDescription::EVSE_reserve:
|
|
return "EvseManager.cpp: reserve";
|
|
case MutexDescription::EVSE_cancel_reservation:
|
|
return "EvseManager.cpp: cancel_reservation";
|
|
case MutexDescription::EVSE_is_reserved:
|
|
return "EvseManager.cpp: is_reserved";
|
|
case MutexDescription::EVSE_get_ev_info:
|
|
return "EvseManager.cpp: get_ev_info";
|
|
}
|
|
return "Undefined";
|
|
}
|
|
|
|
class timed_mutex_traceable : public std::timed_mutex {
|
|
#ifdef EVEREST_USE_BACKTRACES
|
|
public:
|
|
MutexDescription description;
|
|
pthread_t p_id;
|
|
#endif
|
|
};
|
|
|
|
template <typename mutex_type> class scoped_lock_timeout {
|
|
public:
|
|
explicit scoped_lock_timeout(mutex_type& __m, MutexDescription description) : mutex(__m) {
|
|
if (not mutex.try_lock_for(deadlock_timeout)) {
|
|
#ifdef EVEREST_USE_BACKTRACES
|
|
request_backtrace(pthread_self());
|
|
request_backtrace(mutex.p_id);
|
|
// Give some time for other timeouts to report their state and backtraces
|
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
|
|
|
std::string different_thread;
|
|
if (mutex.p_id not_eq pthread_self()) {
|
|
different_thread = " from a different thread.";
|
|
} else {
|
|
different_thread = " from the same thread";
|
|
}
|
|
|
|
EVLOG_AND_THROW(EverestTimeoutError("Mutex deadlock detected: Failed to lock " + to_string(description) +
|
|
", mutex held by " + to_string(mutex.description) + different_thread));
|
|
#endif
|
|
} else {
|
|
locked = true;
|
|
#ifdef EVEREST_USE_BACKTRACES
|
|
mutex.description = description;
|
|
mutex.p_id = pthread_self();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
~scoped_lock_timeout() {
|
|
if (locked) {
|
|
mutex.unlock();
|
|
}
|
|
}
|
|
|
|
scoped_lock_timeout(const scoped_lock_timeout&) = delete;
|
|
scoped_lock_timeout& operator=(const scoped_lock_timeout&) = delete;
|
|
|
|
private:
|
|
bool locked{false};
|
|
mutex_type& mutex;
|
|
|
|
// This should be lower then command timeouts from framework (by default 300s)
|
|
static constexpr auto deadlock_timeout = std::chrono::seconds(120);
|
|
};
|
|
} // namespace Everest
|
|
|
|
#endif
|