new module
This commit is contained in:
7
components/scheduler/CMakeLists.txt
Executable file
7
components/scheduler/CMakeLists.txt
Executable file
@@ -0,0 +1,7 @@
|
||||
set(srcs "src/scheduler_types.c" "src/scheduler.c" "src/scheduler_events.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "src"
|
||||
PRIV_REQUIRES nvs_flash esp_timer
|
||||
REQUIRES esp_event evse)
|
||||
2
components/scheduler/idf_component.yml
Executable file
2
components/scheduler/idf_component.yml
Executable file
@@ -0,0 +1,2 @@
|
||||
version: "2.6.0"
|
||||
description: Authentication component
|
||||
18
components/scheduler/include/scheduler.h
Executable file
18
components/scheduler/include/scheduler.h
Executable file
@@ -0,0 +1,18 @@
|
||||
// components/scheduler/include/scheduler.h
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "scheduler_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void scheduler_init(void);
|
||||
void scheduler_set_config(const sched_config_t *cfg);
|
||||
sched_config_t scheduler_get_config(void);
|
||||
bool scheduler_is_allowed_now(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
17
components/scheduler/include/scheduler_events.h
Normal file
17
components/scheduler/include/scheduler_events.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// scheduler_events.h
|
||||
#pragma once
|
||||
#include "esp_event.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(SCHED_EVENTS);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SCHED_EVENT_INIT = 0, // envia estado inicial
|
||||
SCHED_EVENT_WINDOW_CHANGED, // allowed_now mudou
|
||||
} sched_event_id_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool allowed_now;
|
||||
} sched_event_state_t;
|
||||
31
components/scheduler/include/scheduler_types.h
Normal file
31
components/scheduler/include/scheduler_types.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// components/scheduler/include/scheduler_types.h
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SCHED_MODE_DISABLED = 0, // não faz gating
|
||||
SCHED_MODE_SIMPLE, // por ex: janela diária única
|
||||
SCHED_MODE_WEEKLY // por ex: janelas por dia da semana
|
||||
} sched_mode_t;
|
||||
|
||||
typedef struct {
|
||||
bool enabled; // gating ativo?
|
||||
sched_mode_t mode;
|
||||
|
||||
// exemplo bem simples: uma janela diária [start_min..end_min[
|
||||
// minutos desde meia-noite, 0..1439
|
||||
uint16_t start_min;
|
||||
uint16_t end_min;
|
||||
} sched_config_t;
|
||||
|
||||
const char *sched_mode_to_str(sched_mode_t mode);
|
||||
bool sched_mode_from_str(const char *s, sched_mode_t *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
251
components/scheduler/src/scheduler.c
Executable file
251
components/scheduler/src/scheduler.c
Executable file
@@ -0,0 +1,251 @@
|
||||
// components/scheduler/src/scheduler.c
|
||||
#include "scheduler.h"
|
||||
#include "scheduler_types.h"
|
||||
#include "scheduler_events.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "scheduler";
|
||||
|
||||
/* ===== Estado interno ===== */
|
||||
static sched_config_t s_cfg = {
|
||||
.enabled = false,
|
||||
.mode = SCHED_MODE_DISABLED,
|
||||
.start_min = 0,
|
||||
.end_min = 24 * 60};
|
||||
|
||||
static bool s_allowed_now = true;
|
||||
static TaskHandle_t s_task_handle = NULL;
|
||||
|
||||
/* ===== NVS ===== */
|
||||
#define NVS_NAMESPACE "scheduler"
|
||||
#define NVS_KEY_ENABLED "enabled"
|
||||
#define NVS_KEY_MODE "mode"
|
||||
#define NVS_KEY_START_MIN "start_min"
|
||||
#define NVS_KEY_END_MIN "end_min"
|
||||
|
||||
static void load_config_from_nvs(sched_config_t *out)
|
||||
{
|
||||
if (!out)
|
||||
return;
|
||||
|
||||
*out = s_cfg; // defaults
|
||||
|
||||
nvs_handle_t h;
|
||||
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &h);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "No scheduler namespace in NVS (%s), using defaults",
|
||||
esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t u8;
|
||||
uint16_t u16;
|
||||
|
||||
if (nvs_get_u8(h, NVS_KEY_ENABLED, &u8) == ESP_OK)
|
||||
{
|
||||
out->enabled = (u8 != 0);
|
||||
}
|
||||
if (nvs_get_u8(h, NVS_KEY_MODE, &u8) == ESP_OK)
|
||||
{
|
||||
if (u8 <= (uint8_t)SCHED_MODE_SIMPLE)
|
||||
{
|
||||
out->mode = (sched_mode_t)u8;
|
||||
}
|
||||
}
|
||||
if (nvs_get_u16(h, NVS_KEY_START_MIN, &u16) == ESP_OK)
|
||||
{
|
||||
out->start_min = u16;
|
||||
}
|
||||
if (nvs_get_u16(h, NVS_KEY_END_MIN, &u16) == ESP_OK)
|
||||
{
|
||||
out->end_min = u16;
|
||||
}
|
||||
|
||||
nvs_close(h);
|
||||
|
||||
ESP_LOGI(TAG, "Loaded from NVS: enabled=%d mode=%d start=%u end=%u",
|
||||
out->enabled, out->mode,
|
||||
(unsigned)out->start_min, (unsigned)out->end_min);
|
||||
}
|
||||
|
||||
static void save_config_to_nvs(const sched_config_t *cfg)
|
||||
{
|
||||
if (!cfg)
|
||||
return;
|
||||
|
||||
nvs_handle_t h;
|
||||
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
err = nvs_set_u8(h, NVS_KEY_ENABLED, cfg->enabled ? 1 : 0);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "nvs_set_u8(enabled) failed: %s", esp_err_to_name(err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nvs_set_u8(h, NVS_KEY_MODE, (uint8_t)cfg->mode);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "nvs_set_u8(mode) failed: %s", esp_err_to_name(err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nvs_set_u16(h, NVS_KEY_START_MIN, cfg->start_min);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "nvs_set_u16(start_min) failed: %s", esp_err_to_name(err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nvs_set_u16(h, NVS_KEY_END_MIN, cfg->end_min);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "nvs_set_u16(end_min) failed: %s", esp_err_to_name(err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nvs_commit(h);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "nvs_commit failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Scheduler config saved to NVS");
|
||||
}
|
||||
|
||||
out:
|
||||
nvs_close(h);
|
||||
}
|
||||
|
||||
/* ===== Lógica de janelas ===== */
|
||||
static bool compute_allowed_now(const sched_config_t *cfg)
|
||||
{
|
||||
if (!cfg->enabled || cfg->mode == SCHED_MODE_DISABLED)
|
||||
{
|
||||
// se desativado, não limita nada
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm tm_now = {0};
|
||||
localtime_r(&now, &tm_now);
|
||||
|
||||
uint16_t now_min = (uint16_t)(tm_now.tm_hour * 60 + tm_now.tm_min);
|
||||
|
||||
// janela [start_min, end_min)
|
||||
if (cfg->start_min <= cfg->end_min)
|
||||
{
|
||||
// Ex: 08:00–18:00
|
||||
return (now_min >= cfg->start_min && now_min < cfg->end_min);
|
||||
}
|
||||
else
|
||||
{
|
||||
// janela que passa pela meia-noite, ex: 22:00–06:00
|
||||
return (now_min >= cfg->start_min || now_min < cfg->end_min);
|
||||
}
|
||||
}
|
||||
|
||||
static void scheduler_recompute_and_emit(void)
|
||||
{
|
||||
bool new_allowed = compute_allowed_now(&s_cfg);
|
||||
|
||||
if (new_allowed != s_allowed_now)
|
||||
{
|
||||
s_allowed_now = new_allowed;
|
||||
ESP_LOGI(TAG, "allowed_now changed -> %d", s_allowed_now);
|
||||
|
||||
sched_event_state_t ev = {
|
||||
.allowed_now = s_allowed_now};
|
||||
esp_event_post(SCHED_EVENTS,
|
||||
SCHED_EVENT_WINDOW_CHANGED,
|
||||
&ev,
|
||||
sizeof(ev),
|
||||
portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Task do scheduler ===== */
|
||||
static void scheduler_task(void *arg)
|
||||
{
|
||||
const TickType_t period = pdMS_TO_TICKS(60000); // 60s
|
||||
|
||||
ESP_LOGI(TAG, "Scheduler task started");
|
||||
|
||||
for (;;)
|
||||
{
|
||||
scheduler_recompute_and_emit();
|
||||
vTaskDelay(period);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== API pública ===== */
|
||||
void scheduler_init(void)
|
||||
{
|
||||
// 1) carregar config
|
||||
load_config_from_nvs(&s_cfg);
|
||||
|
||||
// 2) calcular estado inicial
|
||||
s_allowed_now = compute_allowed_now(&s_cfg);
|
||||
|
||||
// 3) enviar evento INIT
|
||||
sched_event_state_t ev = {
|
||||
.allowed_now = s_allowed_now};
|
||||
esp_event_post(SCHED_EVENTS,
|
||||
SCHED_EVENT_INIT,
|
||||
&ev,
|
||||
sizeof(ev),
|
||||
portMAX_DELAY);
|
||||
|
||||
ESP_LOGI(TAG, "Init: allowed_now=%d", s_allowed_now);
|
||||
|
||||
// 4) criar a task
|
||||
if (s_task_handle == NULL)
|
||||
{
|
||||
xTaskCreate(
|
||||
scheduler_task,
|
||||
"scheduler_task",
|
||||
4 * 1024,
|
||||
NULL,
|
||||
3, // prioridade razoável
|
||||
&s_task_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void scheduler_set_config(const sched_config_t *cfg)
|
||||
{
|
||||
if (!cfg)
|
||||
return;
|
||||
|
||||
s_cfg = *cfg;
|
||||
save_config_to_nvs(&s_cfg);
|
||||
|
||||
// recomputa imediatamente para refletir mudança
|
||||
scheduler_recompute_and_emit();
|
||||
}
|
||||
|
||||
sched_config_t scheduler_get_config(void)
|
||||
{
|
||||
return s_cfg;
|
||||
}
|
||||
|
||||
bool scheduler_is_allowed_now(void)
|
||||
{
|
||||
return s_allowed_now;
|
||||
}
|
||||
4
components/scheduler/src/scheduler_events.c
Normal file
4
components/scheduler/src/scheduler_events.c
Normal file
@@ -0,0 +1,4 @@
|
||||
// scheduler_events.c
|
||||
#include "scheduler_events.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(SCHED_EVENTS);
|
||||
22
components/scheduler/src/scheduler_types.c
Executable file
22
components/scheduler/src/scheduler_types.c
Executable file
@@ -0,0 +1,22 @@
|
||||
// components/scheduler/src/scheduler_types.c
|
||||
#include "scheduler_types.h"
|
||||
#include <strings.h>
|
||||
|
||||
const char *sched_mode_to_str(sched_mode_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case SCHED_MODE_DISABLED: return "disabled";
|
||||
case SCHED_MODE_SIMPLE: return "simple";
|
||||
case SCHED_MODE_WEEKLY: return "weekly";
|
||||
default: return "disabled";
|
||||
}
|
||||
}
|
||||
|
||||
bool sched_mode_from_str(const char *s, sched_mode_t *out)
|
||||
{
|
||||
if (!s || !out) return false;
|
||||
if (!strcasecmp(s, "disabled")) { *out = SCHED_MODE_DISABLED; return true; }
|
||||
if (!strcasecmp(s, "simple")) { *out = SCHED_MODE_SIMPLE; return true; }
|
||||
if (!strcasecmp(s, "weekly")) { *out = SCHED_MODE_WEEKLY; return true; }
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user