-
Notifications
You must be signed in to change notification settings - Fork 123
feat: Over-voltage monitor implementation in EVSEManager #1595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
19043bd
80ca888
ea7ba5f
fbf6453
76f67b2
dba0bba
b66990c
249a70f
12c0db7
ad5a711
0a4b722
c5690f2
288460a
aaa0d78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // Copyright Pionix GmbH and Contributors to EVerest | ||
|
|
||
| #include "over_voltage/OverVoltageMonitor.hpp" | ||
|
|
||
| #include <algorithm> | ||
|
Check warning on line 6 in modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp
|
||
| #include <chrono> | ||
|
Check warning on line 7 in modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp
|
||
| #include <condition_variable> | ||
|
Check warning on line 8 in modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp
|
||
| #include <fmt/core.h> | ||
|
Check warning on line 9 in modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp
|
||
| #include <thread> | ||
|
Check warning on line 10 in modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp
|
||
|
|
||
| namespace module { | ||
|
|
||
| OverVoltageMonitor::OverVoltageMonitor(ErrorCallback callback, std::chrono::milliseconds duration) : | ||
| error_callback_(std::move(callback)), duration_(duration) { | ||
| timer_thread_ = std::thread(&OverVoltageMonitor::timer_thread_func, this); | ||
| } | ||
|
|
||
| OverVoltageMonitor::~OverVoltageMonitor() { | ||
| { | ||
| std::lock_guard<std::mutex> lock(timer_mutex_); | ||
| timer_thread_exit_ = true; | ||
| timer_armed_ = false; | ||
| } | ||
| timer_cv_.notify_one(); | ||
| if (timer_thread_.joinable()) { | ||
| timer_thread_.join(); | ||
| } | ||
| } | ||
|
|
||
| void OverVoltageMonitor::set_limits(double emergency_limit, double error_limit) { | ||
| emergency_limit_ = emergency_limit; | ||
| error_limit_ = error_limit; | ||
| limits_valid_ = true; | ||
| } | ||
|
|
||
| void OverVoltageMonitor::start_monitor() { | ||
| fault_latched_ = false; | ||
| cancel_error_timer(); | ||
| running_ = true; | ||
| } | ||
|
|
||
| void OverVoltageMonitor::stop_monitor() { | ||
| running_ = false; | ||
| cancel_error_timer(); | ||
| } | ||
|
|
||
| void OverVoltageMonitor::reset() { | ||
| fault_latched_ = false; | ||
| cancel_error_timer(); | ||
| } | ||
|
|
||
| void OverVoltageMonitor::update_voltage(double voltage_v) { | ||
| if (!running_ || fault_latched_ || !limits_valid_) { | ||
| return; | ||
| } | ||
|
|
||
| if (voltage_v >= emergency_limit_) { | ||
| cancel_error_timer(); | ||
| trigger_fault(FaultType::Emergency, | ||
| fmt::format("Voltage {:.2f} V exceeded emergency limit {:.2f} V.", voltage_v, emergency_limit_)); | ||
| return; | ||
| } | ||
|
|
||
| if (voltage_v >= error_limit_) { | ||
| arm_error_timer(voltage_v); | ||
| } else { | ||
| cancel_error_timer(); | ||
| } | ||
| } | ||
|
|
||
| void OverVoltageMonitor::trigger_fault(FaultType type, const std::string& reason) { | ||
| fault_latched_ = true; | ||
| running_ = false; | ||
| cancel_error_timer(); | ||
| if (error_callback_) { | ||
| error_callback_(type, reason); | ||
| } | ||
| } | ||
|
|
||
| void OverVoltageMonitor::arm_error_timer(double voltage_v) { | ||
| if (duration_.count() == 0) { | ||
| trigger_fault(FaultType::Error, fmt::format("Voltage {:.2f} V exceeded limit {:.2f} V for at least {} ms.", | ||
| voltage_v, error_limit_, duration_.count())); | ||
| return; | ||
| } | ||
|
|
||
| { | ||
| std::lock_guard<std::mutex> lock(timer_mutex_); | ||
| if (timer_armed_) { | ||
| timer_voltage_snapshot_ = std::max(timer_voltage_snapshot_, voltage_v); | ||
| return; | ||
| } | ||
| timer_armed_ = true; | ||
| timer_voltage_snapshot_ = voltage_v; | ||
| timer_deadline_ = std::chrono::steady_clock::now() + duration_; | ||
| } | ||
| timer_cv_.notify_one(); | ||
| } | ||
|
|
||
| void OverVoltageMonitor::cancel_error_timer() { | ||
| { | ||
| std::lock_guard<std::mutex> lock(timer_mutex_); | ||
| if (!timer_armed_) { | ||
| return; | ||
| } | ||
| timer_armed_ = false; | ||
| } | ||
| timer_cv_.notify_one(); | ||
| } | ||
|
|
||
| void OverVoltageMonitor::timer_thread_func() { | ||
| std::unique_lock<std::mutex> lock(timer_mutex_); | ||
|
|
||
| while (!timer_thread_exit_) { | ||
| // Wait until a timer is armed or exit is requested | ||
| timer_cv_.wait(lock, [this] { return timer_thread_exit_ || timer_armed_; }); | ||
| if (timer_thread_exit_) { | ||
| break; | ||
| } | ||
|
|
||
| // Capture the current deadline and wait until it expires or is cancelled/updated | ||
| auto deadline = timer_deadline_; | ||
| while (!timer_thread_exit_ && timer_armed_) { | ||
| if (timer_cv_.wait_until(lock, deadline) == std::cv_status::timeout) { | ||
| break; | ||
| } | ||
| // Woken up: check for exit, cancellation or re-arming with a new deadline | ||
| if (timer_thread_exit_ || !timer_armed_ || timer_deadline_ != deadline) { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (timer_thread_exit_) { | ||
| break; | ||
| } | ||
| if (!timer_armed_ || timer_deadline_ != deadline) { | ||
| // Timer was cancelled or re-armed; go back to waiting | ||
| continue; | ||
| } | ||
|
|
||
| // Timer expired with this deadline and is still armed | ||
| const double voltage = timer_voltage_snapshot_; | ||
| timer_armed_ = false; | ||
|
|
||
| // Release the lock while invoking the callback path | ||
| lock.unlock(); | ||
| trigger_fault(FaultType::Error, fmt::format("Voltage {:.2f} V exceeded limit {:.2f} V for at least {} ms.", | ||
| voltage, error_limit_, duration_.count())); | ||
| lock.lock(); | ||
| } | ||
| } | ||
|
|
||
| } // namespace module | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is not verified if limits are plausible
limits_validis not neededThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theoretically update_voltage is not called before set_limits but to avoid potential false errors I would argue to leave it to be on the safe side. Not sure how in the future we need to change the flow and shielding checking against invalid limits is not necessary a bad thing.