238 行
8.7 KiB
C
238 行
8.7 KiB
C
/* 0.1.14:Hub 按需启停(hubctl.h)。
|
||
|
||
口径同其它测试:判据用 `hub_decide()` 直接喂输入;引脚与事件用 `hubctl_tick()`
|
||
打在假 GPIO / 事件缓冲上。R716 的上拉用 `sim_gpio_drive(HUB_RST#, 1)` 模拟 ——
|
||
放成输入时读回 1 = Hub 在工作,配成输出拉低时读回 0 = 按住复位。 */
|
||
#include <string.h>
|
||
|
||
#include "board_pins.h"
|
||
#include "evlog.h"
|
||
#include "hal_time.h"
|
||
#include "hubctl.h"
|
||
#include "netcfg.h"
|
||
#include "sim_hal.h"
|
||
#include "unity.h"
|
||
|
||
#define PIN BOARD_PIN_HUB_RST_N
|
||
|
||
static hub_mode_t s_saved;
|
||
static int s_saves;
|
||
|
||
static void save(void *ctx, hub_mode_t m)
|
||
{
|
||
(void)ctx;
|
||
s_saved = m;
|
||
s_saves++;
|
||
}
|
||
|
||
static void link(bool up)
|
||
{
|
||
net_status_t st;
|
||
memset(&st, 0, sizeof(st));
|
||
st.mqtt = up ? NET_UP : NET_CONNECTING;
|
||
net_status_set(&st);
|
||
}
|
||
|
||
static uint32_t now(void) { return hal_time_now_ms(); }
|
||
|
||
/* 每 100 ms 一拍(执行器空闲时的节奏),推 ms 毫秒。 */
|
||
static void run_ms(uint32_t ms)
|
||
{
|
||
for (uint32_t t = 0; t < ms; t += 100) {
|
||
sim_time_advance_ms(100);
|
||
hubctl_tick(now());
|
||
}
|
||
}
|
||
|
||
static int hub_events(uint8_t arg)
|
||
{
|
||
(void)evlog_flush(0);
|
||
evlog_rec_t r[16];
|
||
const uint32_t n = evlog_read(0, r, 16);
|
||
int k = 0;
|
||
for (uint32_t i = 0; i < n; i++) {
|
||
k += (r[i].type == LOG_EV_HUB && r[i].arg == arg) ? 1 : 0;
|
||
}
|
||
return k;
|
||
}
|
||
|
||
void setUp(void)
|
||
{
|
||
sim_hal_reset();
|
||
TEST_ASSERT_EQUAL_INT(HAL_OK, evlog_init());
|
||
sim_gpio_drive(PIN, 1); /* R716 */
|
||
s_saves = 0;
|
||
link(true);
|
||
hubctl_init(HUB_MODE_AUTO, save, NULL, now());
|
||
}
|
||
|
||
void tearDown(void) {}
|
||
|
||
static const hub_in_t k_idle = { .link_up = true };
|
||
|
||
/* ---- 判据 ---------------------------------------------------------------- */
|
||
|
||
/* 启动算一次需要:HUB_IDLE_MS 之内开着(idle),到点就关。 */
|
||
static void test_boot_window_then_off(void)
|
||
{
|
||
TEST_ASSERT_TRUE(hubctl_is_on());
|
||
TEST_ASSERT_EQUAL_INT(1, sim_gpio_level(PIN));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&k_idle, now() + HUB_IDLE_MS - 1));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, now() + HUB_IDLE_MS));
|
||
}
|
||
|
||
/* 优先级:mode > vbus > flash > act > hold > link。任一条都刷新「最后一次需要」。 */
|
||
static void test_reasons_and_their_order(void)
|
||
{
|
||
hub_in_t in = k_idle;
|
||
in.vbus_any = in.flash_any = in.act_any = true;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_VBUS, hub_decide(&in, now()));
|
||
in.vbus_any = false;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_FLASH, hub_decide(&in, now()));
|
||
in.flash_any = false;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_ACT, hub_decide(&in, now()));
|
||
in.act_any = false;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, now()));
|
||
|
||
/* VBUS 一直开着 30 分钟,关掉之后还要再开 HUB_IDLE_MS。 */
|
||
const uint32_t t0 = now() + 30u * 60000u;
|
||
in.vbus_any = true;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_VBUS, hub_decide(&in, t0));
|
||
in.vbus_any = false;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t0 + HUB_IDLE_MS - 1));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + HUB_IDLE_MS));
|
||
|
||
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_set_mode(HUB_MODE_ON));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_MODE, hub_decide(&in, t0 + 10u * HUB_IDLE_MS));
|
||
}
|
||
|
||
/* MQTT 断满 HUB_LINK_LOST_MS 才算(WiFi 抖一下不开);恢复后再给一个 idle 窗口。 */
|
||
static void test_link_lost_forces_on(void)
|
||
{
|
||
const uint32_t t0 = now() + HUB_IDLE_MS; /* 启动窗口已过 */
|
||
hub_in_t in = k_idle;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0));
|
||
in.link_up = false;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + HUB_LINK_LOST_MS - 1));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_LINK, hub_decide(&in, t0 + HUB_LINK_LOST_MS));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_LINK, hub_decide(&in, t0 + 5u * HUB_IDLE_MS));
|
||
in.link_up = true;
|
||
const uint32_t t1 = t0 + 5u * HUB_IDLE_MS + 1;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t1));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS));
|
||
|
||
/* 断了不到一分钟又连上:计时重来,不累计。 */
|
||
in.link_up = false;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS + 1));
|
||
in.link_up = true;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS + 30000u));
|
||
in.link_up = false;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS + 30001u));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF,
|
||
hub_decide(&in, t1 + HUB_IDLE_MS + 30001u + HUB_LINK_LOST_MS - 1));
|
||
}
|
||
|
||
/* 电脑插上(UP_VBUS_DET 上升沿)与 USB 命令都算一次需要;电平一直高不算。 */
|
||
static void test_host_edge_and_usb_touch(void)
|
||
{
|
||
const uint32_t t0 = now() + HUB_IDLE_MS;
|
||
hub_in_t in = k_idle;
|
||
in.host = true;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t0)); /* 上升沿 */
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + HUB_IDLE_MS)); /* 电平不算 */
|
||
|
||
hubctl_touch(t0 + 2u * HUB_IDLE_MS);
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t0 + 3u * HUB_IDLE_MS - 1));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + 3u * HUB_IDLE_MS));
|
||
}
|
||
|
||
/* 保持窗口;min=0 取消保持并结束 idle 窗口(下一拍就关)。 */
|
||
static void test_hold(void)
|
||
{
|
||
const uint32_t t0 = now();
|
||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, hubctl_hold(HUB_HOLD_MAX_MIN + 1, t0));
|
||
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_hold(30, t0));
|
||
TEST_ASSERT_EQUAL_UINT32(1800, hubctl_hold_left_s(t0));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_HOLD, hub_decide(&k_idle, t0 + 30u * 60000u - 1));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&k_idle, t0 + 30u * 60000u));
|
||
TEST_ASSERT_EQUAL_UINT32(0, hubctl_hold_left_s(t0 + 30u * 60000u));
|
||
|
||
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_hold(0, t0 + 30u * 60000u));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0 + 30u * 60000u + 1));
|
||
}
|
||
|
||
/* 关着 49.7 天,32 位时钟回绕之后 idle 窗口不许「复活」。 */
|
||
static void test_idle_window_does_not_revive_after_wrap(void)
|
||
{
|
||
const uint32_t t0 = now() + HUB_IDLE_MS;
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0 + 0x80000000u));
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0 + 0xFFFFFFF0u));
|
||
}
|
||
|
||
/* ---- 引脚与事件 ---------------------------------------------------------- */
|
||
|
||
/* 关 = 配成输出拉低;开 = 放成输入(R716 拉高),**从不主动写 1**。每次翻转一条事件。 */
|
||
static void test_tick_drives_the_pin_and_logs(void)
|
||
{
|
||
run_ms(HUB_IDLE_MS + 200);
|
||
TEST_ASSERT_FALSE(hubctl_is_on());
|
||
TEST_ASSERT_EQUAL_INT(0, sim_gpio_level(PIN));
|
||
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_OFF));
|
||
TEST_ASSERT_EQUAL_UINT64(0, sim_gpio_rise_us(PIN)); /* 固件没写过 1 */
|
||
|
||
/* 断网一分钟 ⇒ 自己回来。 */
|
||
link(false);
|
||
run_ms(HUB_LINK_LOST_MS + 200);
|
||
TEST_ASSERT_TRUE(hubctl_is_on());
|
||
TEST_ASSERT_EQUAL_INT(HUB_WHY_LINK, hubctl_why());
|
||
TEST_ASSERT_EQUAL_INT(1, sim_gpio_level(PIN));
|
||
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_ON));
|
||
TEST_ASSERT_EQUAL_UINT64(0, sim_gpio_rise_us(PIN));
|
||
|
||
/* 一直开着不重复记。 */
|
||
run_ms(5000);
|
||
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_ON));
|
||
}
|
||
|
||
static void test_mode_is_saved_and_logged(void)
|
||
{
|
||
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, hubctl_set_mode(HUB_MODE_COUNT));
|
||
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_set_mode(HUB_MODE_ON));
|
||
TEST_ASSERT_EQUAL_INT(1, s_saves);
|
||
TEST_ASSERT_EQUAL_INT(HUB_MODE_ON, s_saved);
|
||
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_MODE | HUB_MODE_ON));
|
||
run_ms(HUB_IDLE_MS + 200);
|
||
TEST_ASSERT_TRUE(hubctl_is_on()); /* 常开 */
|
||
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_set_mode(HUB_MODE_ON));
|
||
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_MODE | HUB_MODE_ON)); /* 没变不记 */
|
||
|
||
hub_mode_t m = HUB_MODE_ON;
|
||
TEST_ASSERT_TRUE(hub_mode_parse("auto", &m));
|
||
TEST_ASSERT_EQUAL_INT(HUB_MODE_AUTO, m);
|
||
TEST_ASSERT_FALSE(hub_mode_parse("off", &m));
|
||
}
|
||
|
||
/* 从 NVS 读到的是垃圾 ⇒ 按 AUTO。 */
|
||
static void test_bad_mode_falls_back_to_auto(void)
|
||
{
|
||
hubctl_init((hub_mode_t)7, NULL, NULL, now());
|
||
TEST_ASSERT_EQUAL_INT(HUB_MODE_AUTO, hubctl_mode());
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
UNITY_BEGIN();
|
||
RUN_TEST(test_boot_window_then_off);
|
||
RUN_TEST(test_reasons_and_their_order);
|
||
RUN_TEST(test_link_lost_forces_on);
|
||
RUN_TEST(test_host_edge_and_usb_touch);
|
||
RUN_TEST(test_hold);
|
||
RUN_TEST(test_idle_window_does_not_revive_after_wrap);
|
||
RUN_TEST(test_tick_drives_the_pin_and_logs);
|
||
RUN_TEST(test_mode_is_saved_and_logged);
|
||
RUN_TEST(test_bad_mode_falls_back_to_auto);
|
||
return UNITY_END();
|
||
}
|