new release
This commit is contained in:
@@ -15,7 +15,7 @@ idf_component_register(
|
||||
SRCS ${srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "src"
|
||||
PRIV_REQUIRES nvs_flash esp_http_server esp_netif vfs spiffs json evse meter_manager loadbalancer evse_link
|
||||
PRIV_REQUIRES nvs_flash esp_http_server esp_netif vfs spiffs json evse meter_manager protocols loadbalancer evse_link
|
||||
)
|
||||
|
||||
# SPIFFS image (opcional)
|
||||
|
||||
@@ -11,4 +11,12 @@
|
||||
// Função para registrar os manipuladores de URI para as configurações dos contadores
|
||||
void register_meters_settings_handlers(httpd_handle_t server, void *ctx);
|
||||
|
||||
/**
|
||||
* Exponha dados do meter via REST.
|
||||
* Endpoints:
|
||||
* GET /api/v1/meters/live -> dados mais recentes (GRID e/ou EVSE)
|
||||
* GET /api/v1/meters/live?source=GRID|EVSE
|
||||
*/
|
||||
void register_meters_data_handlers(httpd_handle_t server, void *ctx);
|
||||
|
||||
#endif // METERS_SETTINGS_API_H
|
||||
|
||||
@@ -24,11 +24,11 @@ static esp_err_t dashboard_get_handler(httpd_req_t *req) {
|
||||
cJSON *charger1 = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(charger1, "id", 1);
|
||||
cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state));
|
||||
cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current());
|
||||
cJSON_AddNumberToObject(charger1, "current", evse_get_runtime_charging_current());
|
||||
cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_max_charging_current());
|
||||
|
||||
// Calcular a potência com base na corrente (considerando 230V)
|
||||
int power = (evse_get_charging_current()) * 230;
|
||||
int power = (evse_get_runtime_charging_current()) * 230;
|
||||
cJSON_AddNumberToObject(charger1, "power", power);
|
||||
|
||||
cJSON_AddItemToArray(chargers, charger1);
|
||||
|
||||
@@ -1,12 +1,169 @@
|
||||
#include "meters_settings_api.h"
|
||||
#include "meter_manager.h" // Atualizado para usar o novo manager
|
||||
#include "meter_manager.h" // Atualizado para usar o novo manager
|
||||
#include "meter_events.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "cJSON.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_timer.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "meters_settings_api";
|
||||
|
||||
typedef struct
|
||||
{
|
||||
meter_event_data_t last_grid;
|
||||
meter_event_data_t last_evse;
|
||||
bool has_grid;
|
||||
bool has_evse;
|
||||
int64_t ts_grid_us;
|
||||
int64_t ts_evse_us;
|
||||
SemaphoreHandle_t mtx;
|
||||
} meters_cache_t;
|
||||
|
||||
static meters_cache_t g_cache = {0};
|
||||
|
||||
static void cache_init_once(void)
|
||||
{
|
||||
if (!g_cache.mtx)
|
||||
{
|
||||
g_cache.mtx = xSemaphoreCreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_event_locked(const meter_event_data_t *src)
|
||||
{
|
||||
if (!src)
|
||||
return;
|
||||
if (strcmp(src->source ? src->source : "", "EVSE") == 0)
|
||||
{
|
||||
g_cache.last_evse = *src;
|
||||
g_cache.has_evse = true;
|
||||
g_cache.ts_evse_us = esp_timer_get_time();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default trate como GRID
|
||||
g_cache.last_grid = *src;
|
||||
g_cache.has_grid = true;
|
||||
g_cache.ts_grid_us = esp_timer_get_time();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Event handler (atualiza cache) ----------
|
||||
static void meters_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
if (base != METER_EVENT || id != METER_EVENT_DATA_READY || data == NULL)
|
||||
return;
|
||||
|
||||
cache_init_once();
|
||||
if (xSemaphoreTake(g_cache.mtx, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
copy_event_locked((const meter_event_data_t *)data);
|
||||
xSemaphoreGive(g_cache.mtx);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Helpers JSON ----------
|
||||
static cJSON *meter_event_to_json(const meter_event_data_t *m, int64_t ts_us)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "source", m->source ? m->source : "GRID");
|
||||
cJSON_AddNumberToObject(root, "frequency", m->frequency);
|
||||
cJSON_AddNumberToObject(root, "powerFactor", m->power_factor);
|
||||
cJSON_AddNumberToObject(root, "totalEnergy", m->total_energy);
|
||||
|
||||
// timestamp relativo (segundos desde boot) e em micros
|
||||
cJSON_AddNumberToObject(root, "timestampUs", (double)ts_us);
|
||||
|
||||
// arrays
|
||||
cJSON *vr = cJSON_CreateArray();
|
||||
for (int i = 0; i < 3; i++)
|
||||
cJSON_AddItemToArray(vr, cJSON_CreateNumber(m->vrms[i]));
|
||||
cJSON *ir = cJSON_CreateArray();
|
||||
for (int i = 0; i < 3; i++)
|
||||
cJSON_AddItemToArray(ir, cJSON_CreateNumber(m->irms[i]));
|
||||
cJSON *pw = cJSON_CreateArray();
|
||||
for (int i = 0; i < 3; i++)
|
||||
cJSON_AddItemToArray(pw, cJSON_CreateNumber(m->watt[i]));
|
||||
cJSON_AddItemToObject(root, "vrms", vr);
|
||||
cJSON_AddItemToObject(root, "irms", ir);
|
||||
cJSON_AddItemToObject(root, "watt", pw);
|
||||
return root;
|
||||
}
|
||||
|
||||
// ---------- HTTP GET /api/v1/meters/live ----------
|
||||
static esp_err_t meters_live_get_handler(httpd_req_t *req)
|
||||
{
|
||||
cache_init_once();
|
||||
|
||||
char query[64] = {0};
|
||||
char source[16] = {0};
|
||||
bool want_grid = true, want_evse = true;
|
||||
|
||||
if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK)
|
||||
{
|
||||
if (httpd_query_key_value(query, "source", source, sizeof(source)) == ESP_OK)
|
||||
{
|
||||
if (strcasecmp(source, "GRID") == 0)
|
||||
{
|
||||
want_grid = true;
|
||||
want_evse = false;
|
||||
}
|
||||
else if (strcasecmp(source, "EVSE") == 0)
|
||||
{
|
||||
want_grid = false;
|
||||
want_evse = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *out = cJSON_CreateObject();
|
||||
cJSON *arr = cJSON_CreateArray();
|
||||
|
||||
if (xSemaphoreTake(g_cache.mtx, pdMS_TO_TICKS(50)) == pdTRUE)
|
||||
{
|
||||
if (want_grid && g_cache.has_grid)
|
||||
{
|
||||
cJSON_AddItemToArray(arr, meter_event_to_json(&g_cache.last_grid, g_cache.ts_grid_us));
|
||||
}
|
||||
if (want_evse && g_cache.has_evse)
|
||||
{
|
||||
cJSON_AddItemToArray(arr, meter_event_to_json(&g_cache.last_evse, g_cache.ts_evse_us));
|
||||
}
|
||||
xSemaphoreGive(g_cache.mtx);
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(out, "meters", arr);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
char *s = cJSON_PrintUnformatted(out);
|
||||
httpd_resp_sendstr(req, s ? s : "{\"meters\":[]}");
|
||||
if (s)
|
||||
free(s);
|
||||
cJSON_Delete(out);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ---------- Registro ----------
|
||||
void register_meters_data_handlers(httpd_handle_t server, void *ctx)
|
||||
{
|
||||
// registra event handler para receber amostras
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(METER_EVENT, METER_EVENT_DATA_READY, meters_event_handler, NULL));
|
||||
|
||||
// endpoint GET
|
||||
httpd_uri_t live = {
|
||||
.uri = "/api/v1/meters/live",
|
||||
.method = HTTP_GET,
|
||||
.handler = meters_live_get_handler,
|
||||
.user_ctx = ctx};
|
||||
httpd_register_uri_handler(server, &live);
|
||||
|
||||
ESP_LOGI(TAG, "REST /api/v1/meters/live registrado");
|
||||
}
|
||||
|
||||
// Função para recuperar as configurações dos contadores
|
||||
static esp_err_t meters_config_get_handler(httpd_req_t *req) {
|
||||
static esp_err_t meters_config_get_handler(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGI(TAG, "Received GET request for /api/v1/config/meters");
|
||||
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
@@ -38,24 +195,27 @@ static esp_err_t meters_config_get_handler(httpd_req_t *req) {
|
||||
}
|
||||
|
||||
// Função para atualizar as configurações dos contadores
|
||||
static esp_err_t meters_config_post_handler(httpd_req_t *req) {
|
||||
static esp_err_t meters_config_post_handler(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGI(TAG, "Received POST request for /api/v1/config/meters");
|
||||
|
||||
char buf[512];
|
||||
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
||||
|
||||
if (len <= 0) {
|
||||
if (len <= 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Received empty body in POST request");
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
buf[len] = '\0'; // Garantir que a string está terminada
|
||||
buf[len] = '\0'; // Garantir que a string está terminada
|
||||
|
||||
ESP_LOGI(TAG, "Received POST data: %s", buf);
|
||||
|
||||
cJSON *json = cJSON_Parse(buf);
|
||||
if (!json) {
|
||||
if (!json)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to parse JSON data");
|
||||
// Resposta detalhada de erro
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format");
|
||||
@@ -64,15 +224,17 @@ static esp_err_t meters_config_post_handler(httpd_req_t *req) {
|
||||
|
||||
// Atualizando os contadores
|
||||
cJSON *gridmeter = cJSON_GetObjectItem(json, "gridmeter");
|
||||
if (gridmeter) {
|
||||
meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type
|
||||
if (gridmeter)
|
||||
{
|
||||
meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type
|
||||
ESP_LOGI(TAG, "Updating grid meter type to: %s", gridmeter->valuestring);
|
||||
meter_manager_grid_set_model(gridType);
|
||||
}
|
||||
|
||||
cJSON *evsemeter = cJSON_GetObjectItem(json, "evsemeter");
|
||||
if (evsemeter) {
|
||||
meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type
|
||||
if (evsemeter)
|
||||
{
|
||||
meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type
|
||||
ESP_LOGI(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring);
|
||||
meter_manager_evse_set_model(evseType);
|
||||
}
|
||||
@@ -86,7 +248,8 @@ static esp_err_t meters_config_post_handler(httpd_req_t *req) {
|
||||
}
|
||||
|
||||
// Registrando os manipuladores de URI para os contadores
|
||||
void register_meters_settings_handlers(httpd_handle_t server, void *ctx) {
|
||||
void register_meters_settings_handlers(httpd_handle_t server, void *ctx)
|
||||
{
|
||||
ESP_LOGD(TAG, "Registering URI handlers for meters settings");
|
||||
|
||||
// URI para o método GET
|
||||
@@ -94,8 +257,7 @@ void register_meters_settings_handlers(httpd_handle_t server, void *ctx) {
|
||||
.uri = "/api/v1/config/meters",
|
||||
.method = HTTP_GET,
|
||||
.handler = meters_config_get_handler,
|
||||
.user_ctx = ctx
|
||||
};
|
||||
.user_ctx = ctx};
|
||||
ESP_LOGD(TAG, "Registering GET handler for /api/v1/config/meters");
|
||||
httpd_register_uri_handler(server, &meters_get_uri);
|
||||
|
||||
@@ -104,8 +266,7 @@ void register_meters_settings_handlers(httpd_handle_t server, void *ctx) {
|
||||
.uri = "/api/v1/config/meters",
|
||||
.method = HTTP_POST,
|
||||
.handler = meters_config_post_handler,
|
||||
.user_ctx = ctx
|
||||
};
|
||||
.user_ctx = ctx};
|
||||
ESP_LOGD(TAG, "Registering POST handler for /api/v1/config/meters");
|
||||
httpd_register_uri_handler(server, &meters_post_uri);
|
||||
}
|
||||
|
||||
@@ -10,14 +10,15 @@
|
||||
#include "static_file_api.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
static const char *TAG = "rest_main";
|
||||
|
||||
esp_err_t rest_server_init(const char *base_path) {
|
||||
esp_err_t rest_server_init(const char *base_path)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing REST API with base path: %s", base_path);
|
||||
|
||||
rest_server_context_t *ctx = calloc(1, sizeof(rest_server_context_t));
|
||||
if (!ctx) {
|
||||
if (!ctx)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for REST context");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
@@ -30,7 +31,8 @@ esp_err_t rest_server_init(const char *base_path) {
|
||||
|
||||
httpd_handle_t server = NULL;
|
||||
esp_err_t err = httpd_start(&server, &config);
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err));
|
||||
free(ctx);
|
||||
return err;
|
||||
@@ -39,16 +41,18 @@ esp_err_t rest_server_init(const char *base_path) {
|
||||
ESP_LOGI(TAG, "HTTP server started successfully");
|
||||
|
||||
// Register endpoint groups
|
||||
register_evse_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_network_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_ocpp_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_auth_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_dashboard_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_meters_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_evse_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_network_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_ocpp_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_auth_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_dashboard_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_meters_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_loadbalancing_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_link_config_handlers(server,ctx);
|
||||
register_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
register_link_config_handlers(server, ctx);
|
||||
|
||||
register_meters_data_handlers(server, ctx);
|
||||
|
||||
register_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação
|
||||
|
||||
ESP_LOGI(TAG, "All REST API endpoint groups registered successfully");
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -12,9 +12,9 @@
|
||||
font-family: 'Arial', 'Helvetica', 'sans-serif'; /* Usando fontes genéricas do sistema */
|
||||
}
|
||||
</style>
|
||||
<title>Vite + React</title>
|
||||
<script type="module" crossorigin src="/assets/index-CmjuW5AW.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Bc9ibDeR.css">
|
||||
<title>ChargeFlow</title>
|
||||
<script type="module" crossorigin src="/assets/index-BcUN2CM9.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-NSBcyqXj.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Reference in New Issue
Block a user