/* 刷机模式的窗口表。契约与线程约定在 chgmode.h。 */ #include "chgmode.h" #include "phonerail.h" typedef struct { volatile uint32_t until; /* 窗口截止时刻(ms,回绕安全比较) */ volatile uint8_t set; /* 1 = 窗口或宽限期还没被 tick 清掉 */ uint8_t by_cmd; /* 1 = 命令关掉的(已记过事件,tick 不再报「到期」);只有执行器任务读写 */ } chg_t; static chg_t s_chg[PHONERAIL_RAIL_COUNT]; static bool rail_ok(int rail) { return rail >= 1 && rail <= PHONERAIL_RAIL_COUNT; } /* `a` 在 `b` 之前(回绕安全)。 */ static bool before(uint32_t a, uint32_t b) { return (int32_t)(a - b) < 0; } bool chgmode_active(int rail, uint32_t now) { return rail_ok(rail) && s_chg[rail - 1].set && before(now, s_chg[rail - 1].until); } bool chgmode_relaxed(int rail, uint32_t now) { return rail_ok(rail) && s_chg[rail - 1].set && before(now, s_chg[rail - 1].until + CHG_MODE_GRACE_MS); } uint32_t chgmode_left_s(int rail, uint32_t now) { if (!chgmode_active(rail, now)) { return 0; } return (s_chg[rail - 1].until - now + 999u) / 1000u; } int chgmode_count(uint32_t now) { int n = 0; for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) { n += chgmode_active(r, now) ? 1 : 0; } return n; } hal_err_t chgmode_set(int rail, uint32_t minutes, uint32_t now) { if (!rail_ok(rail) || minutes > CHG_MODE_MAX_MIN) { return HAL_ERR_ARG; } chg_t *c = &s_chg[rail - 1]; if (minutes == 0) { if (chgmode_active(rail, now)) { c->until = now; /* 立刻结束窗口,宽限期从现在算 */ c->by_cmd = 1; } return HAL_OK; } if (!chgmode_active(rail, now) && chgmode_count(now) >= CHG_MODE_MAX_RAILS) { return HAL_ERR_STATE; } /* 先写时刻、后置标志:巡检任务读到 set=1 时 until 一定是新的。 */ c->until = now + minutes * 60000u; c->by_cmd = 0; c->set = 1; return HAL_OK; } hal_err_t chgmode_extend(int rail, uint32_t minutes, uint32_t now) { if (!rail_ok(rail) || minutes < 1 || minutes > CHG_MODE_MAX_MIN) { return HAL_ERR_ARG; } if (!chgmode_active(rail, now)) { return HAL_ERR_STATE; } chg_t *c = &s_chg[rail - 1]; const uint32_t cap = CHG_MODE_MAX_MIN * 60000u; uint32_t left = c->until - now + minutes * 60000u; /* 窗口内 until − now 为正 */ if (left > cap) { left = cap; } c->until = now + left; /* set 已是 1;单字写,巡检看到的要么旧要么新 */ return HAL_OK; } uint32_t chgmode_tick(uint32_t now) { uint32_t ended = 0; for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) { chg_t *c = &s_chg[r - 1]; if (c->set && !before(now, c->until + CHG_MODE_GRACE_MS)) { c->set = 0; if (!c->by_cmd) { ended |= 1u << (r - 1); } } } return ended; } void chgmode_reset(void) { for (int r = 0; r < PHONERAIL_RAIL_COUNT; r++) { s_chg[r].set = 0; s_chg[r].until = 0; s_chg[r].by_cmd = 0; } }