new upgrade
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
// === Início de: components/evse/evse_manager.c ===
|
||||
#include "evse_manager.h"
|
||||
#include "evse_state.h"
|
||||
#include "evse_error.h"
|
||||
@@ -11,10 +10,10 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
@@ -27,27 +26,29 @@
|
||||
static const char *TAG = "EVSE_Manager";
|
||||
|
||||
static SemaphoreHandle_t evse_mutex;
|
||||
static volatile bool auth_enabled = false;
|
||||
|
||||
// ✅ Proteção para flags partilhadas (event handlers vs task)
|
||||
static portMUX_TYPE s_mgr_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
static bool auth_enabled = false;
|
||||
|
||||
// Estado de pausa controlado pelo Load Balancer
|
||||
static volatile bool lb_paused = false;
|
||||
static volatile bool lb_prev_authorized = false;
|
||||
static bool lb_paused = false;
|
||||
static bool lb_prev_authorized = false;
|
||||
|
||||
// Estado de janela do scheduler
|
||||
static volatile bool s_sched_allowed = true;
|
||||
static bool s_sched_allowed = true;
|
||||
static portMUX_TYPE s_sched_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
||||
|
||||
// ================= Helpers internos =================
|
||||
|
||||
static void lb_clear_pause_state(void)
|
||||
{
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
lb_paused = false;
|
||||
lb_prev_authorized = false;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
}
|
||||
|
||||
// Exposto para outros módulos (se quiserem saber se o scheduler permite)
|
||||
bool evse_sched_is_allowed(void)
|
||||
{
|
||||
bool v;
|
||||
@@ -60,19 +61,35 @@ bool evse_sched_is_allowed(void)
|
||||
static void evse_manager_handle_auth_on_tick(void)
|
||||
{
|
||||
bool sched_allowed = evse_sched_is_allowed();
|
||||
uint32_t err_bits = evse_get_error(); // inclui holdoff interno
|
||||
bool has_error = (err_bits != 0);
|
||||
|
||||
if (auth_enabled)
|
||||
bool local_auth_enabled;
|
||||
bool local_lb_paused;
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
local_auth_enabled = auth_enabled;
|
||||
local_lb_paused = lb_paused;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (local_auth_enabled)
|
||||
{
|
||||
// Se o carro foi desconectado, revoga autorização
|
||||
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A)
|
||||
{
|
||||
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
|
||||
evse_state_set_authorized(false);
|
||||
// Desconexão física invalida qualquer pausa pendente do LB
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
|
||||
// Em modos RFID/OCPP, o scheduler pode também forçar paragem
|
||||
if (has_error && evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG,
|
||||
"[AUTH] error active (err=0x%08" PRIx32 ") → revoking authorization.",
|
||||
err_bits);
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
|
||||
if (!sched_allowed && evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG, "[SCHED] window closed (auth mode) → revoking authorization.");
|
||||
@@ -81,28 +98,32 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Modo OPEN: só autoriza se LB e Scheduler permitirem
|
||||
if (!lb_paused && sched_allowed && !evse_state_get_authorized())
|
||||
bool limit_hit = evse_is_limit_reached();
|
||||
bool can_operate = evse_config_is_available() && evse_config_is_enabled();
|
||||
|
||||
if ((has_error || limit_hit || !sched_allowed || !can_operate || local_lb_paused) &&
|
||||
evse_state_get_authorized())
|
||||
{
|
||||
evse_state_set_authorized(true);
|
||||
ESP_LOGI(TAG, "Authentication disabled → forced authorization (within schedule).");
|
||||
lb_clear_pause_state();
|
||||
ESP_LOGI(TAG,
|
||||
"[OPEN] blocking (err=%d limit=%d sched=%d operate=%d lb_paused=%d) → revoking authorization.",
|
||||
(int)has_error, (int)limit_hit, (int)sched_allowed, (int)can_operate, (int)local_lb_paused);
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
|
||||
// Fora da janela, garantir que não fica autorizado
|
||||
if (!sched_allowed && evse_state_get_authorized())
|
||||
if (!local_lb_paused && sched_allowed && can_operate &&
|
||||
!has_error && !limit_hit &&
|
||||
!evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG, "[SCHED] window closed (OPEN mode) → revoking authorization.");
|
||||
evse_state_set_authorized(false);
|
||||
evse_state_set_authorized(true);
|
||||
ESP_LOGI(TAG, "Authentication disabled → forced authorization (schedule ok, no error/limits).");
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Task de ciclo principal =====
|
||||
static void evse_manager_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
while (true)
|
||||
{
|
||||
evse_manager_tick();
|
||||
@@ -110,15 +131,10 @@ static void evse_manager_task(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Tratador de eventos de AUTH =====
|
||||
static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
if (base != AUTH_EVENTS || !data)
|
||||
return;
|
||||
|
||||
auth_mode_t g_mode = AUTH_MODE_OPEN;
|
||||
if (base != AUTH_EVENTS || !data) return;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
@@ -127,8 +143,6 @@ static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)data;
|
||||
ESP_LOGI(TAG, "Tag %s -> %s", evt->tag, evt->authorized ? "AUTHORIZED" : "DENIED");
|
||||
evse_state_set_authorized(evt->authorized);
|
||||
|
||||
// Qualquer alteração explícita de auth invalida pausa do LB
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
}
|
||||
@@ -137,37 +151,26 @@ static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
case AUTH_EVENT_INIT:
|
||||
{
|
||||
const auth_mode_event_data_t *evt = (const auth_mode_event_data_t *)data;
|
||||
g_mode = evt->mode;
|
||||
ESP_LOGI(TAG, "Auth mode = %s", auth_mode_to_str(g_mode));
|
||||
if (g_mode == AUTH_MODE_OPEN)
|
||||
{
|
||||
// Em OPEN, a autorização passa a ser gerida por evse_manager_handle_auth_on_tick(),
|
||||
// que também respeita o scheduler.
|
||||
evse_state_set_authorized(false); // vai ser forçado no próximo tick se permitido
|
||||
auth_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_state_set_authorized(false);
|
||||
auth_enabled = true;
|
||||
}
|
||||
ESP_LOGI(TAG, "Auth mode = %s", auth_mode_to_str(evt->mode));
|
||||
|
||||
// Modo mudou -> qualquer pausa antiga deixa de fazer sentido
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
auth_enabled = (evt->mode != AUTH_MODE_OPEN);
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Tratador de eventos de Load Balancer =====
|
||||
static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
(void)handler_arg;
|
||||
(void)event_base;
|
||||
|
||||
if (!event_data)
|
||||
return;
|
||||
if (!event_data) return;
|
||||
|
||||
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED)
|
||||
{
|
||||
@@ -181,98 +184,86 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base
|
||||
const loadbalancer_master_limit_event_t *evt =
|
||||
(const loadbalancer_master_limit_event_t *)event_data;
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Novo limite de corrente (master): %u A (ts: %lld)",
|
||||
ESP_LOGI(TAG, "Novo limite de corrente (master): %u A (ts: %lld)",
|
||||
evt->max_current, (long long)evt->timestamp_us);
|
||||
|
||||
if (evt->max_current == 0)
|
||||
{
|
||||
// Suspensão por LB (não interessa se é OPEN ou RFID/OCPP)
|
||||
lb_paused = true;
|
||||
lb_prev_authorized = evse_state_get_authorized();
|
||||
bool prev_auth = evse_state_get_authorized();
|
||||
|
||||
if (lb_prev_authorized)
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
lb_paused = true;
|
||||
lb_prev_authorized = prev_auth;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (prev_auth)
|
||||
{
|
||||
ESP_LOGI(TAG, "[LB] limit=0A → pausando sessão (authorized=false)");
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGD(TAG, "[LB] limit=0A → já não estava autorizado");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ajusta corrente em runtime
|
||||
evse_set_runtime_charging_current(evt->max_current);
|
||||
|
||||
if (lb_paused)
|
||||
{
|
||||
lb_paused = false;
|
||||
bool was_paused;
|
||||
bool prev_auth;
|
||||
|
||||
// Só retomamos se EVSE estiver operacional e scheduler permitir
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
was_paused = lb_paused;
|
||||
prev_auth = lb_prev_authorized;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (was_paused)
|
||||
{
|
||||
bool can_resume =
|
||||
(evse_get_error() == 0) &&
|
||||
evse_config_is_available() &&
|
||||
evse_config_is_enabled() &&
|
||||
evse_sched_is_allowed();
|
||||
evse_sched_is_allowed() &&
|
||||
!evse_is_limit_reached();
|
||||
|
||||
if (!can_resume)
|
||||
{
|
||||
ESP_LOGW(TAG,
|
||||
"[LB] limit=%uA → não retoma automaticamente (erro/indisponível/desabilitado/fora de horário)",
|
||||
"[LB] limit=%uA → não retoma automaticamente (erro/indisp/desab/fora de horário/limite)",
|
||||
evt->max_current);
|
||||
lb_clear_pause_state();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!auth_enabled)
|
||||
bool local_auth_enabled;
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
local_auth_enabled = auth_enabled;
|
||||
lb_paused = false; // já vai tentar retomar
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (!local_auth_enabled)
|
||||
{
|
||||
// Modo OPEN: retoma sempre (se dentro da janela do scheduler)
|
||||
ESP_LOGI(TAG,
|
||||
"[LB] limit=%uA → modo OPEN, reautorizando (authorized=true)",
|
||||
evt->max_current);
|
||||
ESP_LOGI(TAG, "[LB] limit=%uA → modo OPEN, reautorizando", evt->max_current);
|
||||
evse_state_set_authorized(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// RFID/OCPP: só retoma se havia autorização antes da pausa
|
||||
if (lb_prev_authorized)
|
||||
if (prev_auth)
|
||||
{
|
||||
ESP_LOGI(TAG,
|
||||
"[LB] limit=%uA → RFID/OCPP, retomando autorização anterior (auto-resume)",
|
||||
evt->max_current);
|
||||
ESP_LOGI(TAG, "[LB] limit=%uA → RFID/OCPP, retomando autorização anterior", evt->max_current);
|
||||
evse_state_set_authorized(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG,
|
||||
"[LB] limit=%uA → RFID/OCPP, sem autorização prévia, mantendo estado atual",
|
||||
evt->max_current);
|
||||
}
|
||||
}
|
||||
|
||||
// Limpa estado prévio (não reaplicar em pausas futuras)
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
lb_prev_authorized = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Caso normal: apenas ajuste de corrente, sem mexer em auth
|
||||
ESP_LOGD(TAG,
|
||||
"[LB] limit=%uA → ajustando corrente runtime (sem mudança de autorização)",
|
||||
evt->max_current);
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Tratador de eventos de OCPP =====
|
||||
static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
if (base != OCPP_EVENTS)
|
||||
return;
|
||||
if (base != OCPP_EVENTS) return;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
@@ -283,13 +274,10 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_AUTH_REJECTED:
|
||||
ESP_LOGW(TAG, "[OCPP] Authorization rejected");
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_AUTH_TIMEOUT:
|
||||
ESP_LOGW(TAG, "[OCPP] Authorization timeout");
|
||||
case OCPP_EVENT_REMOTE_STOP:
|
||||
case OCPP_EVENT_STOP_TX:
|
||||
ESP_LOGW(TAG, "[OCPP] Authorization/Stop");
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
@@ -300,24 +288,11 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_REMOTE_STOP:
|
||||
ESP_LOGI(TAG, "[OCPP] RemoteStop");
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_START_TX:
|
||||
ESP_LOGI(TAG, "[OCPP] StartTx");
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_STOP_TX:
|
||||
ESP_LOGI(TAG, "[OCPP] StopTx");
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
|
||||
// ChangeAvailability remoto (operative/inoperative)
|
||||
case OCPP_EVENT_OPERATIVE_UPDATED:
|
||||
{
|
||||
if (!data)
|
||||
@@ -329,7 +304,6 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
ESP_LOGI(TAG, "[OCPP] OperativeUpdated: operative=%d ts=%lld",
|
||||
(int)ev->operative, (long long)ev->timestamp_us);
|
||||
|
||||
// Mapear operative → enabled local (persiste e emite EVSE_EVENT_ENABLE_UPDATED)
|
||||
evse_config_set_enabled(ev->operative);
|
||||
break;
|
||||
}
|
||||
@@ -340,16 +314,10 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Tratador de eventos de Scheduler =====
|
||||
static void on_sched_event(void *arg,
|
||||
esp_event_base_t base,
|
||||
int32_t id,
|
||||
void *data)
|
||||
static void on_sched_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
if (base != SCHED_EVENTS || data == NULL)
|
||||
return;
|
||||
if (base != SCHED_EVENTS || data == NULL) return;
|
||||
|
||||
const sched_event_state_t *ev = (const sched_event_state_t *)data;
|
||||
|
||||
@@ -357,29 +325,26 @@ static void on_sched_event(void *arg,
|
||||
s_sched_allowed = ev->allowed_now;
|
||||
portEXIT_CRITICAL(&s_sched_mux);
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"[SCHED] event id=%" PRIi32 " allowed_now=%d",
|
||||
id, (int)ev->allowed_now);
|
||||
ESP_LOGI(TAG, "[SCHED] allowed_now=%d", (int)ev->allowed_now);
|
||||
|
||||
// Se a janela fechou, parar sessão (revogar autorização)
|
||||
if (!ev->allowed_now && evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG, "[SCHED] window closed → stopping session (authorized=false)");
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
|
||||
// Se a janela abriu de novo, não auto-reautorizamos aqui.
|
||||
// Deixamos que o utilizador / OCPP decida iniciar nova sessão.
|
||||
// (Em modo OPEN, o tick trata disso respeitando o scheduler.)
|
||||
}
|
||||
|
||||
// ===== Inicialização =====
|
||||
void evse_manager_init(void)
|
||||
{
|
||||
evse_mutex = xSemaphoreCreateMutex();
|
||||
configASSERT(evse_mutex != NULL);
|
||||
|
||||
evse_config_init();
|
||||
esp_err_t err = evse_config_init();
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to init EVSE config NVS: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
evse_error_init();
|
||||
evse_hardware_init();
|
||||
evse_state_init();
|
||||
@@ -393,11 +358,10 @@ void evse_manager_init(void)
|
||||
|
||||
ESP_LOGI(TAG, "EVSE Manager inicializado.");
|
||||
|
||||
BaseType_t rc = xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
|
||||
BaseType_t rc = xTaskCreate(evse_manager_task, "evse_manager_task", 8192, NULL, 4, NULL);
|
||||
configASSERT(rc == pdPASS);
|
||||
}
|
||||
|
||||
// ===== Main Tick =====
|
||||
void evse_manager_tick(void)
|
||||
{
|
||||
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
||||
@@ -412,5 +376,3 @@ void evse_manager_tick(void)
|
||||
|
||||
xSemaphoreGive(evse_mutex);
|
||||
}
|
||||
|
||||
// === Fim de: components/evse/evse_manager.c ===
|
||||
|
||||
Reference in New Issue
Block a user