#include "meters_settings_api.h" #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 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_LOGD(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) { ESP_LOGD(TAG, "Received GET request for /api/v1/config/meters"); httpd_resp_set_type(req, "application/json"); cJSON *config = cJSON_CreateObject(); // Recuperando as configurações dos contadores meter_type_t gridmeterType = meter_manager_grid_get_model(); meter_type_t evsemeterType = meter_manager_evse_get_model(); ESP_LOGD(TAG, "Grid meter type: %s", meter_type_to_str(gridmeterType)); ESP_LOGD(TAG, "EVSE meter type: %s", meter_type_to_str(evsemeterType)); // Adicionando os tipos de contadores ao objeto JSON cJSON_AddStringToObject(config, "gridmeter", meter_type_to_str(gridmeterType)); cJSON_AddStringToObject(config, "evsemeter", meter_type_to_str(evsemeterType)); // Convertendo o objeto JSON para uma string const char *json_str = cJSON_Print(config); ESP_LOGD(TAG, "Returning meters config: %s", json_str); httpd_resp_sendstr(req, json_str); // Liberação da memória free((void *)json_str); cJSON_Delete(config); return ESP_OK; } // Função para atualizar as configurações dos contadores static esp_err_t meters_config_post_handler(httpd_req_t *req) { ESP_LOGD(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) { 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 ESP_LOGD(TAG, "Received POST data: %s", buf); cJSON *json = cJSON_Parse(buf); 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"); return ESP_FAIL; } // 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 ESP_LOGD(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 ESP_LOGD(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring); meter_manager_evse_set_model(evseType); } cJSON_Delete(json); httpd_resp_sendstr(req, "Meters updated successfully"); ESP_LOGD(TAG, "Meters configuration updated successfully"); return ESP_OK; } // Registrando os manipuladores de URI para os contadores 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 httpd_uri_t meters_get_uri = { .uri = "/api/v1/config/meters", .method = HTTP_GET, .handler = meters_config_get_handler, .user_ctx = ctx}; ESP_LOGD(TAG, "Registering GET handler for /api/v1/config/meters"); httpd_register_uri_handler(server, &meters_get_uri); // URI para o método POST httpd_uri_t meters_post_uri = { .uri = "/api/v1/config/meters", .method = HTTP_POST, .handler = meters_config_post_handler, .user_ctx = ctx}; ESP_LOGD(TAG, "Registering POST handler for /api/v1/config/meters"); httpd_register_uri_handler(server, &meters_post_uri); }