Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions errors/evse_manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ errors:
description: An Isolation Monitoring Device tripped due to low resistance to the chassis during active charging.
- name: MREC11CableCheckFault
description: Cable check failed. Isolation monitor self test failed before charging.
- name: VoltagePlausibilityFault
description: Voltage plausibility check failed. Standard deviation between voltage measurements from different sources exceeded threshold for configured duration.
3 changes: 2 additions & 1 deletion modules/EVSE/EvseManager/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ IMPLS = [
"token_provider",
"random_delay",
"dc_external_derate",
"over_voltage"
"over_voltage",
"voltage_plausibility"
]

cc_everest_module(
Expand Down
1 change: 1 addition & 0 deletions modules/EVSE/EvseManager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ target_sources(${MODULE_NAME}
"random_delay/uk_random_delayImpl.cpp"
"dc_external_derate/dc_external_derateImpl.cpp"
"over_voltage/OverVoltageMonitor.cpp"
"voltage_plausibility/VoltagePlausibilityMonitor.cpp"
)

# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
Expand Down
17 changes: 17 additions & 0 deletions modules/EVSE/EvseManager/ErrorHandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,4 +345,21 @@ void ErrorHandling::clear_cable_check_fault() {
}
}

void ErrorHandling::raise_voltage_plausibility_fault(const std::string& description) {
// raise externally
// High severity as this indicates a serious measurement inconsistency
Everest::error::Error error_object = p_evse->error_factory->create_error(
"evse_manager/VoltagePlausibilityFault", "", description, Everest::error::Severity::High);
p_evse->raise_error(error_object);
process_error();
}

void ErrorHandling::clear_voltage_plausibility_fault() {
// clear externally
if (p_evse->error_state_monitor->is_error_active("evse_manager/VoltagePlausibilityFault", "")) {
p_evse->clear_error("evse_manager/VoltagePlausibilityFault");
process_error();
}
}

} // namespace module
3 changes: 3 additions & 0 deletions modules/EVSE/EvseManager/ErrorHandling.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ class ErrorHandling {
void raise_cable_check_fault(const std::string& description);
void clear_cable_check_fault();

void raise_voltage_plausibility_fault(const std::string& description);
void clear_voltage_plausibility_fault();

protected:
void raise_inoperative_error(const Everest::error::Error& caused_by);

Expand Down
34 changes: 34 additions & 0 deletions modules/EVSE/EvseManager/EvseManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ void EvseManager::ready() {
charger = std::make_unique<Charger>(bsp, error_handling, r_powermeter_billing(), store,
hw_capabilities.connector_type, config.evse_id);

// Create voltage plausibility monitor for DC charging
voltage_plausibility_monitor = std::make_unique<VoltagePlausibilityMonitor>(
[this](const std::string& description) {
if (this->error_handling) {
this->error_handling->raise_voltage_plausibility_fault(description);
}
},
config.voltage_plausibility_std_deviation_threshold_V,
std::chrono::milliseconds(config.voltage_plausibility_fault_duration_ms),
std::chrono::milliseconds(config.voltage_plausibility_measurement_max_age_ms));

// Now incoming hardware capabilties can be processed
hw_caps_mutex.unlock();

Expand Down Expand Up @@ -471,6 +482,10 @@ void EvseManager::ready() {
internal_over_voltage_monitor->reset();
internal_over_voltage_monitor->start_monitor();
}
if (voltage_plausibility_monitor) {
voltage_plausibility_monitor->reset();
voltage_plausibility_monitor->start_monitor();
}
});

r_hlc[0]->subscribe_current_demand_finished([this] {
Expand All @@ -479,6 +494,9 @@ void EvseManager::ready() {
if (not r_over_voltage_monitor.empty()) {
r_over_voltage_monitor[0]->call_stop();
}
if (voltage_plausibility_monitor) {
voltage_plausibility_monitor->stop_monitor();
}
if (internal_over_voltage_monitor) {
internal_over_voltage_monitor->stop_monitor();
}
Expand All @@ -501,6 +519,9 @@ void EvseManager::ready() {

r_imd[0]->subscribe_isolation_measurement([this](types::isolation_monitor::IsolationMeasurement m) {
// new DC isolation monitoring measurement received
if (voltage_plausibility_monitor && m.voltage_V.has_value()) {
voltage_plausibility_monitor->update_isolation_monitor_voltage(m.voltage_V.value());
}

// Check for isolation errors
if (charger->get_current_state() == Charger::EvseState::Charging and
Expand Down Expand Up @@ -568,6 +589,9 @@ void EvseManager::ready() {
if (not r_powersupply_DC.empty()) {
r_powersupply_DC[0]->subscribe_voltage_current([this](types::power_supply_DC::VoltageCurrent m) {
powersupply_measurement = m;
if (voltage_plausibility_monitor) {
voltage_plausibility_monitor->update_power_supply_voltage(m.voltage_V);
}
types::iso15118::DcEvsePresentVoltageCurrent present_values;
present_values.evse_present_voltage = (m.voltage_V > 0 ? m.voltage_V : 0.0);
present_values.evse_present_current = m.current_A;
Expand Down Expand Up @@ -794,6 +818,12 @@ void EvseManager::ready() {
if (not r_over_voltage_monitor.empty()) {
r_over_voltage_monitor[0]->call_set_limits(get_emergency_over_voltage_threshold(),
get_error_over_voltage_threshold());
// Subscribe to voltage measurements from over_voltage_monitor for plausibility check
r_over_voltage_monitor[0]->subscribe_voltage_measurement_V([this](float voltage_V) {
if (voltage_plausibility_monitor) {
voltage_plausibility_monitor->update_over_voltage_monitor_voltage(voltage_V);
}
});
}
if (internal_over_voltage_monitor) {
internal_over_voltage_monitor->set_limits(get_emergency_over_voltage_threshold(),
Expand Down Expand Up @@ -1072,6 +1102,10 @@ void EvseManager::ready() {

if (r_powermeter_billing().size() > 0) {
r_powermeter_billing()[0]->subscribe_powermeter([this](types::powermeter::Powermeter p) {
// Update voltage plausibility monitor with powermeter voltage
if (voltage_plausibility_monitor && p.voltage_V.has_value() && p.voltage_V.value().DC.has_value()) {
voltage_plausibility_monitor->update_powermeter_voltage(p.voltage_V.value().DC.value());
}
// Inform charger about current charging current. This is used for slow OC detection.
if (p.current_A and p.current_A.value().L1 and p.current_A.value().L2 and p.current_A.value().L3) {
charger->set_current_drawn_by_vehicle(p.current_A.value().L1.value(), p.current_A.value().L2.value(),
Expand Down
7 changes: 7 additions & 0 deletions modules/EVSE/EvseManager/EvseManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "VarContainer.hpp"
#include "over_voltage/OverVoltageMonitor.hpp"
#include "scoped_lock_timeout.hpp"
#include "voltage_plausibility/VoltagePlausibilityMonitor.hpp"
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1

namespace module {
Expand Down Expand Up @@ -119,6 +120,9 @@ struct Conf {
std::string bpt_channel;
std::string bpt_generator_mode;
std::string bpt_grid_code_island_method;
double voltage_plausibility_std_deviation_threshold_V;
int voltage_plausibility_fault_duration_ms;
int voltage_plausibility_measurement_max_age_ms;
};

class EvseManager : public Everest::ModuleBase {
Expand Down Expand Up @@ -346,6 +350,9 @@ class EvseManager : public Everest::ModuleBase {
int32_t reservation_id;
Everest::timed_mutex_traceable reservation_mutex;

// Voltage plausibility monitor
std::unique_ptr<VoltagePlausibilityMonitor> voltage_plausibility_monitor;

void setup_AC_mode();
void setup_fake_DC_mode();

Expand Down
22 changes: 22 additions & 0 deletions modules/EVSE/EvseManager/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,28 @@ config:
When raising evse_manager/Inoperative use the vendor ID from the original cause
type: boolean
default: false
voltage_plausibility_std_deviation_threshold_V:
description: >-
Maximum allowed standard deviation in volts between voltage measurements from different
sources (power supply, powermeter, isolation monitor, over voltage monitor) before a
voltage plausibility fault is triggered. During DC charging, if the standard deviation
exceeds this threshold for the configured duration, a fault is raised.
type: number
default: 50.0
voltage_plausibility_fault_duration_ms:
description: >-
Duration in milliseconds for which the standard deviation must exceed the threshold
before a voltage plausibility fault is raised. A duration of 0 ms means that a fault
will be raised immediately when the threshold is exceeded.
type: integer
default: 10000
voltage_plausibility_measurement_max_age_ms:
description: >-
Maximum age in milliseconds of a voltage measurement before it is considered stale
and excluded from standard deviation calculation. Measurements older than this value
are not used in the plausibility check.
type: integer
default: 3000
session_id_type:
description: >-
Type to use for generation of session ids.
Expand Down
Loading