335 lines
10 KiB
C
Executable File
335 lines
10 KiB
C
Executable File
// === Início de: components/evse/evse_manager.c ===
|
|
#include "evse_manager.h"
|
|
#include "evse_state.h"
|
|
#include "evse_error.h"
|
|
#include "evse_hardware.h"
|
|
#include "evse_config.h"
|
|
#include "evse_api.h"
|
|
#include "evse_meter.h"
|
|
#include "evse_session.h"
|
|
#include "evse_config.h"
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/semphr.h"
|
|
#include "freertos/queue.h"
|
|
#include "esp_log.h"
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "auth_events.h"
|
|
#include "loadbalancer_events.h"
|
|
#include "ocpp_events.h"
|
|
#include "esp_event.h"
|
|
|
|
static const char *TAG = "EVSE_Manager";
|
|
|
|
static SemaphoreHandle_t evse_mutex;
|
|
static bool auth_enabled = false;
|
|
|
|
// Estado de pausa controlado pelo Load Balancer
|
|
static bool lb_paused = false;
|
|
static bool lb_prev_authorized = false;
|
|
|
|
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
|
|
|
// ================= Helpers internos =================
|
|
|
|
static void lb_clear_pause_state(void)
|
|
{
|
|
lb_paused = false;
|
|
lb_prev_authorized = false;
|
|
}
|
|
|
|
static void evse_manager_handle_auth_on_tick(void)
|
|
{
|
|
if (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();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Se autenticação está desativada, garante autorização sempre ativa
|
|
if (!evse_state_get_authorized())
|
|
{
|
|
evse_state_set_authorized(true);
|
|
ESP_LOGI(TAG, "Authentication disabled → forced authorization.");
|
|
// Em modo OPEN, pausa do LB não é tão relevante, mas limpamos mesmo assim
|
|
lb_clear_pause_state();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===== Task de ciclo principal =====
|
|
static void evse_manager_task(void *arg)
|
|
{
|
|
while (true)
|
|
{
|
|
evse_manager_tick();
|
|
vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS));
|
|
}
|
|
}
|
|
|
|
static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
|
{
|
|
if (base != AUTH_EVENTS || !data)
|
|
return;
|
|
|
|
auth_mode_t g_mode = AUTH_MODE_OPEN;
|
|
|
|
switch (id)
|
|
{
|
|
case AUTH_EVENT_TAG_PROCESSED:
|
|
{
|
|
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;
|
|
}
|
|
|
|
case AUTH_EVENT_MODE_CHANGED:
|
|
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)
|
|
{
|
|
evse_state_set_authorized(true);
|
|
auth_enabled = false;
|
|
}
|
|
else
|
|
{
|
|
evse_state_set_authorized(false);
|
|
auth_enabled = true;
|
|
}
|
|
|
|
// Modo mudou -> qualquer pausa antiga deixa de fazer sentido
|
|
lb_clear_pause_state();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===== Tratador de eventos de loadbalancer =====
|
|
static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base,
|
|
int32_t event_id, void *event_data)
|
|
{
|
|
if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED)
|
|
{
|
|
const loadbalancer_state_event_t *evt = (const loadbalancer_state_event_t *)event_data;
|
|
ESP_LOGI(TAG, "Loadbalancer %s (ts: %lld)",
|
|
evt->enabled ? "ENABLED" : "DISABLED",
|
|
(long long)evt->timestamp_us);
|
|
// Ações adicionais podem ser adicionadas aqui conforme necessário
|
|
}
|
|
else if (event_id == LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT)
|
|
{
|
|
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)",
|
|
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();
|
|
|
|
if (lb_prev_authorized)
|
|
{
|
|
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;
|
|
|
|
// Só retomamos se EVSE estiver operacional
|
|
bool can_resume =
|
|
(evse_get_error() == 0) &&
|
|
evse_config_is_available() &&
|
|
evse_config_is_enabled();
|
|
|
|
if (!can_resume)
|
|
{
|
|
ESP_LOGW(TAG,
|
|
"[LB] limit=%uA → não retoma automaticamente (erro/indisponível/desabilitado)",
|
|
evt->max_current);
|
|
lb_clear_pause_state();
|
|
return;
|
|
}
|
|
|
|
if (!auth_enabled)
|
|
{
|
|
// Modo OPEN: retoma sempre
|
|
ESP_LOGI(TAG,
|
|
"[LB] limit=%uA → modo OPEN, reautorizando (authorized=true)",
|
|
evt->max_current);
|
|
evse_state_set_authorized(true);
|
|
}
|
|
else
|
|
{
|
|
// RFID/OCPP: só retoma se havia autorização antes da pausa
|
|
if (lb_prev_authorized)
|
|
{
|
|
ESP_LOGI(TAG,
|
|
"[LB] limit=%uA → RFID/OCPP, retomando autorização anterior (auto-resume)",
|
|
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)
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
|
{
|
|
if (base != OCPP_EVENTS)
|
|
return;
|
|
|
|
switch (id)
|
|
{
|
|
case OCPP_EVENT_AUTHORIZED:
|
|
ESP_LOGI(TAG, "[OCPP] Authorized");
|
|
evse_state_set_authorized(true);
|
|
lb_clear_pause_state();
|
|
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");
|
|
evse_state_set_authorized(false);
|
|
lb_clear_pause_state();
|
|
break;
|
|
|
|
case OCPP_EVENT_REMOTE_START:
|
|
ESP_LOGI(TAG, "[OCPP] RemoteStart");
|
|
evse_state_set_authorized(true);
|
|
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");
|
|
// StartTx em si não precisa mexer em auth, mas limpamos estado de pausa por segurança
|
|
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)
|
|
{
|
|
ESP_LOGW(TAG, "[OCPP] OperativeUpdated sem payload — ignorado");
|
|
break;
|
|
}
|
|
const ocpp_operative_event_t *ev = (const ocpp_operative_event_t *)data;
|
|
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);
|
|
// Opcional: poderias também limpar a pausa aqui, dependendo da política
|
|
// lb_clear_pause_state();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ESP_LOGD(TAG, "[OCPP] Unhandled event id=%" PRId32, id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ===== Inicialização =====
|
|
void evse_manager_init(void)
|
|
{
|
|
evse_mutex = xSemaphoreCreateMutex();
|
|
|
|
evse_config_init();
|
|
evse_error_init();
|
|
evse_hardware_init();
|
|
evse_state_init();
|
|
evse_meter_init();
|
|
evse_session_init();
|
|
|
|
ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, ESP_EVENT_ANY_ID, &on_auth_event, NULL));
|
|
ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL));
|
|
ESP_ERROR_CHECK(esp_event_handler_register(OCPP_EVENTS, ESP_EVENT_ANY_ID, &on_ocpp_event, NULL));
|
|
|
|
ESP_LOGI(TAG, "EVSE Manager inicializado.");
|
|
xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL);
|
|
}
|
|
|
|
// ===== Main Tick =====
|
|
void evse_manager_tick(void)
|
|
{
|
|
xSemaphoreTake(evse_mutex, portMAX_DELAY);
|
|
|
|
evse_hardware_tick();
|
|
evse_error_tick();
|
|
evse_state_tick();
|
|
evse_temperature_check();
|
|
evse_session_tick();
|
|
|
|
evse_manager_handle_auth_on_tick();
|
|
|
|
xSemaphoreGive(evse_mutex);
|
|
}
|
|
// === Fim de: components/evse/evse_manager.c ===
|