new release
This commit is contained in:
@@ -105,6 +105,7 @@ static void evse_process(void)
|
||||
if (evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGW(TAG, "Charging limit reached → revoking authorization");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ static SemaphoreHandle_t evse_mutex;
|
||||
// ✅ Proteção para flags partilhadas (event handlers vs task)
|
||||
static portMUX_TYPE s_mgr_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
static bool auth_enabled = false;
|
||||
static char s_pending_ocpp_tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
// Estado de pausa controlado pelo Load Balancer
|
||||
static bool lb_paused = false;
|
||||
@@ -41,6 +42,8 @@ static portMUX_TYPE s_sched_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo
|
||||
|
||||
static uint16_t s_sched_current_a = 0;
|
||||
|
||||
static void lb_clear_pause_state(void)
|
||||
{
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
@@ -61,15 +64,15 @@ 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);
|
||||
uint32_t err_bits = evse_get_error(); // inclui holdoff interno
|
||||
bool has_error = (err_bits != 0);
|
||||
|
||||
bool local_auth_enabled;
|
||||
bool local_lb_paused;
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
local_auth_enabled = auth_enabled;
|
||||
local_lb_paused = lb_paused;
|
||||
local_lb_paused = lb_paused;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (local_auth_enabled)
|
||||
@@ -77,6 +80,7 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A)
|
||||
{
|
||||
ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization.");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
@@ -86,6 +90,7 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
ESP_LOGI(TAG,
|
||||
"[AUTH] error active (err=0x%08" PRIx32 ") → revoking authorization.",
|
||||
err_bits);
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
}
|
||||
@@ -93,12 +98,13 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
if (!sched_allowed && evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG, "[SCHED] window closed (auth mode) → revoking authorization.");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool limit_hit = evse_is_limit_reached();
|
||||
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) &&
|
||||
@@ -114,6 +120,7 @@ static void evse_manager_handle_auth_on_tick(void)
|
||||
!has_error && !limit_hit &&
|
||||
!evse_state_get_authorized())
|
||||
{
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(true);
|
||||
ESP_LOGI(TAG, "Authentication disabled → forced authorization (schedule ok, no error/limits).");
|
||||
lb_clear_pause_state();
|
||||
@@ -134,7 +141,8 @@ static void evse_manager_task(void *arg)
|
||||
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;
|
||||
if (base != AUTH_EVENTS || !data)
|
||||
return;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
@@ -142,11 +150,30 @@ 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");
|
||||
if (evt->authorized)
|
||||
{
|
||||
evse_session_set_authorized_tag(evt->tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
evse_session_clear_authorized_tag();
|
||||
}
|
||||
evse_state_set_authorized(evt->authorized);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_EVENT_TAG_VERIFY:
|
||||
{
|
||||
const auth_tag_verify_event_t *evt = (const auth_tag_verify_event_t *)data;
|
||||
ESP_LOGI(TAG, "Tag %s -> pending remote/OCPP verification", evt->tag);
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
strncpy(s_pending_ocpp_tag, evt->tag, AUTH_TAG_MAX_LEN - 1);
|
||||
s_pending_ocpp_tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTH_EVENT_MODE_CHANGED:
|
||||
case AUTH_EVENT_INIT:
|
||||
{
|
||||
@@ -155,8 +182,10 @@ static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
auth_enabled = (evt->mode != AUTH_MODE_OPEN);
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
@@ -170,7 +199,8 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base
|
||||
(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)
|
||||
{
|
||||
@@ -211,7 +241,7 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
was_paused = lb_paused;
|
||||
prev_auth = lb_prev_authorized;
|
||||
prev_auth = lb_prev_authorized;
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (was_paused)
|
||||
@@ -263,27 +293,55 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base
|
||||
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)
|
||||
{
|
||||
case OCPP_EVENT_AUTHORIZED:
|
||||
ESP_LOGI(TAG, "[OCPP] Authorized");
|
||||
{
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
strncpy(tag, s_pending_ocpp_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
|
||||
if (tag[0] != '\0')
|
||||
{
|
||||
evse_session_set_authorized_tag(tag);
|
||||
ESP_LOGI(TAG, "[OCPP] Authorized tag=%s", tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "[OCPP] Authorized");
|
||||
}
|
||||
|
||||
evse_state_set_authorized(true);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
}
|
||||
|
||||
case OCPP_EVENT_AUTH_REJECTED:
|
||||
case OCPP_EVENT_AUTH_TIMEOUT:
|
||||
case OCPP_EVENT_REMOTE_STOP:
|
||||
case OCPP_EVENT_STOP_TX:
|
||||
ESP_LOGW(TAG, "[OCPP] Authorization/Stop");
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
|
||||
case OCPP_EVENT_REMOTE_START:
|
||||
ESP_LOGI(TAG, "[OCPP] RemoteStart");
|
||||
portENTER_CRITICAL(&s_mgr_mux);
|
||||
s_pending_ocpp_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&s_mgr_mux);
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(true);
|
||||
lb_clear_pause_state();
|
||||
break;
|
||||
@@ -317,21 +375,31 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da
|
||||
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;
|
||||
|
||||
portENTER_CRITICAL(&s_sched_mux);
|
||||
s_sched_allowed = ev->allowed_now;
|
||||
s_sched_current_a = ev->current_limit_a;
|
||||
portEXIT_CRITICAL(&s_sched_mux);
|
||||
|
||||
ESP_LOGI(TAG, "[SCHED] allowed_now=%d", (int)ev->allowed_now);
|
||||
ESP_LOGI(TAG, "[SCHED] allowed_now=%d current=%uA",
|
||||
(int)ev->allowed_now, (unsigned)ev->current_limit_a);
|
||||
|
||||
if (!ev->allowed_now && evse_state_get_authorized())
|
||||
{
|
||||
ESP_LOGI(TAG, "[SCHED] window closed → stopping session (authorized=false)");
|
||||
evse_session_clear_authorized_tag();
|
||||
evse_state_set_authorized(false);
|
||||
}
|
||||
|
||||
// ✅ aplica corrente quando permitido
|
||||
if (ev->allowed_now && ev->current_limit_a > 0)
|
||||
{
|
||||
evse_set_runtime_charging_current(ev->current_limit_a);
|
||||
}
|
||||
}
|
||||
|
||||
void evse_manager_init(void)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include "evse_session.h"
|
||||
#include "evse_meter.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -25,6 +26,8 @@ static uint64_t watt_microseconds = 0;
|
||||
static evse_session_t last_session;
|
||||
static bool last_session_valid = false;
|
||||
static uint32_t session_counter = 0;
|
||||
static char authorized_tag[AUTH_TAG_MAX_LEN];
|
||||
static char current_session_tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
static portMUX_TYPE session_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
@@ -52,9 +55,31 @@ void evse_session_init(void)
|
||||
watt_microseconds = 0;
|
||||
last_session_valid = false;
|
||||
session_counter = 0;
|
||||
authorized_tag[0] = '\0';
|
||||
current_session_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
}
|
||||
|
||||
void evse_session_set_authorized_tag(const char *tag)
|
||||
{
|
||||
portENTER_CRITICAL(&session_mux);
|
||||
if (tag && tag[0] != '\0')
|
||||
{
|
||||
strncpy(authorized_tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
authorized_tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
authorized_tag[0] = '\0';
|
||||
}
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
}
|
||||
|
||||
void evse_session_clear_authorized_tag(void)
|
||||
{
|
||||
evse_session_set_authorized_tag(NULL);
|
||||
}
|
||||
|
||||
void evse_session_start(void)
|
||||
{
|
||||
TickType_t tick = xTaskGetTickCount();
|
||||
@@ -67,12 +92,17 @@ void evse_session_start(void)
|
||||
watt_microseconds = 0;
|
||||
session_counter++;
|
||||
uint32_t id = session_counter;
|
||||
strncpy(current_session_tag, authorized_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
current_session_tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
strncpy(tag, current_session_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
|
||||
evse_set_limit_reached(false);
|
||||
|
||||
ESP_LOGI(TAG, "Session started (id=%" PRIu32 ") tick=%u us=%" PRId64,
|
||||
id, (unsigned)tick, now_us);
|
||||
ESP_LOGI(TAG, "Session started (id=%" PRIu32 ", tag=%s) tick=%u us=%" PRId64,
|
||||
id, tag[0] ? tag : "-", (unsigned)tick, now_us);
|
||||
|
||||
evse_session_event_data_t evt = {
|
||||
.type = EVSE_SESSION_EVENT_STARTED,
|
||||
@@ -82,6 +112,8 @@ void evse_session_start(void)
|
||||
.avg_power_w = 0,
|
||||
.is_current = true,
|
||||
};
|
||||
strncpy(evt.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
evt.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
post_session_event(&evt);
|
||||
}
|
||||
|
||||
@@ -91,6 +123,7 @@ void evse_session_end(void)
|
||||
int64_t start_us;
|
||||
uint64_t w_us;
|
||||
uint32_t id;
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
int64_t end_us = esp_timer_get_time();
|
||||
|
||||
@@ -106,11 +139,14 @@ void evse_session_end(void)
|
||||
start_us = session_start_us;
|
||||
w_us = watt_microseconds;
|
||||
id = session_counter;
|
||||
strncpy(tag, current_session_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
|
||||
session_start_tick = 0;
|
||||
session_start_us = 0;
|
||||
last_tick_us = 0;
|
||||
watt_microseconds = 0;
|
||||
current_session_tag[0] = '\0';
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
|
||||
uint32_t duration_s = (end_us > start_us) ? (uint32_t)((end_us - start_us) / 1000000LL) : 0;
|
||||
@@ -120,6 +156,9 @@ void evse_session_end(void)
|
||||
|
||||
portENTER_CRITICAL(&session_mux);
|
||||
last_session.start_tick = start_tick;
|
||||
last_session.session_id = id;
|
||||
strncpy(last_session.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
last_session.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
last_session.duration_s = duration_s;
|
||||
last_session.energy_wh = energy_wh;
|
||||
last_session.avg_power_w = avg_power;
|
||||
@@ -129,8 +168,8 @@ void evse_session_end(void)
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Session ended (id=%" PRIu32 "): duration=%" PRIu32 " s, energy=%" PRIu32
|
||||
" Wh, avg_power=%" PRIu32 " W",
|
||||
id, duration_s, energy_wh, avg_power);
|
||||
" Wh, avg_power=%" PRIu32 " W, tag=%s",
|
||||
id, duration_s, energy_wh, avg_power, tag[0] ? tag : "-");
|
||||
|
||||
evse_session_event_data_t evt = {
|
||||
.type = EVSE_SESSION_EVENT_FINISHED,
|
||||
@@ -140,6 +179,8 @@ void evse_session_end(void)
|
||||
.avg_power_w = avg_power,
|
||||
.is_current = false,
|
||||
};
|
||||
strncpy(evt.tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
evt.tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
post_session_event(&evt);
|
||||
}
|
||||
|
||||
@@ -185,6 +226,8 @@ bool evse_session_get(evse_session_t *out)
|
||||
bool has_current;
|
||||
evse_session_t last_copy;
|
||||
bool last_valid;
|
||||
uint32_t id;
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
|
||||
int64_t now_us = esp_timer_get_time();
|
||||
|
||||
@@ -193,6 +236,9 @@ bool evse_session_get(evse_session_t *out)
|
||||
start_us = session_start_us;
|
||||
w_us = watt_microseconds;
|
||||
has_current = (session_start_tick != 0);
|
||||
id = session_counter;
|
||||
strncpy(tag, current_session_tag, AUTH_TAG_MAX_LEN - 1);
|
||||
tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
last_copy = last_session;
|
||||
last_valid = last_session_valid;
|
||||
portEXIT_CRITICAL(&session_mux);
|
||||
@@ -205,6 +251,9 @@ bool evse_session_get(evse_session_t *out)
|
||||
uint32_t avg_power = (duration_s > 0) ? (uint32_t)(watt_seconds / duration_s) : 0;
|
||||
|
||||
out->start_tick = start_tick;
|
||||
out->session_id = id;
|
||||
strncpy(out->tag, tag, AUTH_TAG_MAX_LEN - 1);
|
||||
out->tag[AUTH_TAG_MAX_LEN - 1] = '\0';
|
||||
out->duration_s = duration_s;
|
||||
out->energy_wh = energy_wh;
|
||||
out->avg_power_w = avg_power;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_event.h"
|
||||
#include "auth_types.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(EVSE_EVENTS);
|
||||
|
||||
@@ -45,6 +46,7 @@ typedef struct {
|
||||
evse_session_event_type_t type; ///< STARTED / FINISHED
|
||||
|
||||
uint32_t session_id;
|
||||
char tag[AUTH_TAG_MAX_LEN];
|
||||
uint32_t duration_s;
|
||||
uint32_t energy_wh;
|
||||
uint32_t avg_power_w;
|
||||
|
||||
@@ -4,19 +4,32 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "auth_types.h"
|
||||
|
||||
/**
|
||||
* @brief Charging session statistics
|
||||
*/
|
||||
typedef struct {
|
||||
TickType_t start_tick; ///< tick when session began (debug/trace)
|
||||
uint32_t duration_s; ///< total duration in seconds (tempo real)
|
||||
uint32_t energy_wh; ///< total energy consumed in Wh (tempo real)
|
||||
uint32_t avg_power_w; ///< average power in W
|
||||
bool is_current; ///< true if session still in progress
|
||||
TickType_t start_tick; ///< tick when session began (debug/trace)
|
||||
uint32_t session_id; ///< monotonically increasing session identifier
|
||||
char tag[AUTH_TAG_MAX_LEN]; ///< RFID/auth tag associated with this session, empty if none
|
||||
uint32_t duration_s; ///< total duration in seconds (tempo real)
|
||||
uint32_t energy_wh; ///< total energy consumed in Wh (tempo real)
|
||||
uint32_t avg_power_w; ///< average power in W
|
||||
bool is_current; ///< true if session still in progress
|
||||
} evse_session_t;
|
||||
|
||||
void evse_session_init(void);
|
||||
|
||||
/**
|
||||
* @brief Stores the tag that authorized the next charging session.
|
||||
*
|
||||
* The tag is copied into the session when evse_session_start() is called.
|
||||
* Passing NULL or an empty string clears the pending tag.
|
||||
*/
|
||||
void evse_session_set_authorized_tag(const char *tag);
|
||||
void evse_session_clear_authorized_tag(void);
|
||||
|
||||
void evse_session_start(void);
|
||||
void evse_session_end(void);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user