#include // for PRIu32 #include "evse_state.h" #include "evse_api.h" #include "evse_limits.h" #include "evse_meter.h" #include "evse_session.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" // ======================== // External state references // ======================== //extern evse_state_t current_state; // Current EVSE FSM state //extern TickType_t session_start_tick; // Timestamp of charging session start // ======================== // Concurrency protection // ======================== static portMUX_TYPE evse_mux = portMUX_INITIALIZER_UNLOCKED; // ======================== // Runtime state (volatile) // ======================== static bool limit_reached = false; static uint32_t consumption_limit = 0; // Energy limit in Wh static uint32_t charging_time_limit = 0; // Time limit in seconds static uint16_t under_power_limit = 0; // Minimum acceptable power in W // ======================== // Default (persistent) limits // ======================== static uint32_t default_consumption_limit = 0; static uint32_t default_charging_time_limit = 0; static uint16_t default_under_power_limit = 0; // ======================== // Limit status flag // ======================== bool evse_get_limit_reached(void) { bool val; portENTER_CRITICAL(&evse_mux); val = limit_reached; portEXIT_CRITICAL(&evse_mux); return val; } void evse_set_limit_reached(bool v) { portENTER_CRITICAL(&evse_mux); limit_reached = v; portEXIT_CRITICAL(&evse_mux); } // ======================== // Runtime limit accessors // ======================== uint32_t evse_get_consumption_limit(void) { uint32_t val; portENTER_CRITICAL(&evse_mux); val = consumption_limit; portEXIT_CRITICAL(&evse_mux); return val; } void evse_set_consumption_limit(uint32_t value) { portENTER_CRITICAL(&evse_mux); consumption_limit = value; portEXIT_CRITICAL(&evse_mux); } uint32_t evse_get_charging_time_limit(void) { uint32_t val; portENTER_CRITICAL(&evse_mux); val = charging_time_limit; portEXIT_CRITICAL(&evse_mux); return val; } void evse_set_charging_time_limit(uint32_t value) { portENTER_CRITICAL(&evse_mux); charging_time_limit = value; portEXIT_CRITICAL(&evse_mux); } uint16_t evse_get_under_power_limit(void) { uint16_t val; portENTER_CRITICAL(&evse_mux); val = under_power_limit; portEXIT_CRITICAL(&evse_mux); return val; } void evse_set_under_power_limit(uint16_t value) { portENTER_CRITICAL(&evse_mux); under_power_limit = value; portEXIT_CRITICAL(&evse_mux); } // ======================== // Default (persistent) limit accessors // These values can be stored/restored via NVS // ======================== uint32_t evse_get_default_consumption_limit(void) { return default_consumption_limit; } void evse_set_default_consumption_limit(uint32_t value) { default_consumption_limit = value; } uint32_t evse_get_default_charging_time_limit(void) { return default_charging_time_limit; } void evse_set_default_charging_time_limit(uint32_t value) { default_charging_time_limit = value; } uint16_t evse_get_default_under_power_limit(void) { return default_under_power_limit; } void evse_set_default_under_power_limit(uint16_t value) { default_under_power_limit = value; } bool evse_is_limit_reached(void) { return evse_get_limit_reached(); } // ======================== // Limit checking logic // This function must be called periodically while charging. // It will flag the session as "limit reached" when thresholds are violated. // ======================== void evse_limits_check(void) { // Only check during an active charging session if (!evse_state_is_charging(evse_get_state())) { return; } evse_session_t sess; // Retrieve accumulated data for the current session if (!evse_session_get(&sess) || !sess.is_current) { // If there's no active session, abort return; } bool reached = false; // 1) Energy consumption limit (Wh) if (consumption_limit > 0 && sess.energy_wh >= consumption_limit) { ESP_LOGW("EVSE_LIMITS", "Energy limit reached: %" PRIu32 " Wh ≥ %" PRIu32 " Wh", sess.energy_wh, consumption_limit); reached = true; } // 2) Charging time limit (seconds) if (charging_time_limit > 0 && sess.duration_s >= charging_time_limit) { ESP_LOGW("EVSE_LIMITS", "Charging time limit reached: %" PRIu32 " s ≥ %" PRIu32 " s", sess.duration_s, charging_time_limit); reached = true; } // 3) Under-power limit (instantaneous power) uint32_t inst_power = evse_meter_get_instant_power(); if (under_power_limit > 0 && inst_power < under_power_limit) { ESP_LOGW("EVSE_LIMITS", "Under-power limit reached: %" PRIu32 " W < %" PRIu32 " W", (uint32_t)inst_power, (uint32_t)under_power_limit); reached = true; } if (reached) { evse_set_limit_reached(true); } }