删除条件配置功能,修改成联动配置DP,并且增加31天运行记录,修改程序需求

This commit is contained in:
2026-09-03 10:21:11 +08:00
parent 5377186cfb
commit d56d8204e1
31 changed files with 1609 additions and 1430 deletions
+284
View File
@@ -0,0 +1,284 @@
/******************************************************************************
* @file app_linkage.c
* @brief CBR3 联动引擎实现(规格书第 10 章 + 第 12 章仲裁 + 第 8.2 闪烁)
* @author cyWu <1917507415@qq.com>
* @date 2026.09.02
* @version V3.0.0,随规格书 V3.0.0 新建(整模块替换 app_cond
*
* @note 判定跑在 usr_timer_loop(500ms,任务上下文),与定时引擎同侧。
* 每拍做两件独立的事:
* 1) 第 8.2 节闪烁:有有效配置就按「是否落在 [min,max] 闭区间」判,
* 与风扇是否在转、联动是否被手动作废、仲裁谁占着都无关。
* 2) 第 10.3 节开停:回差 + 超最高优先,只写 fan_gear,不改 mode。
* 仲裁(12 章):手动 > 定时 > 联动。定时占用(ARB_OWNER_TIMER)时
* 联动完全不插嘴;手动作废标志作废本轮直到走到「关」条件。
* ⚠ 本模块不进 20ms 中断上下文(没有 tick 接口)。
******************************************************************************/
#include <string.h>
#include "app_linkage.h"
#include "app_arbiter.h"
#include "app_disp.h"
#include "app_fan.h"
#include "app_mode.h"
#include "app_nvm.h"
#include "app_th.h"
#include "board_pin.h"
#include "jb_product.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP_LKG]"
#define LOG_INFO_ENABLE
#include "debug.h"
/** 20B 配置解析结果(规格书 10.2;版本/预留只存不用) */
typedef struct {
uint16_t tank_id; /**< Byte0-1 大端,0=无效 */
int8_t tmin; /**< Byte2 ℃ */
int8_t tmax; /**< Byte3 ℃ */
uint8_t hmin; /**< Byte4 % */
uint8_t hmax; /**< Byte5 % */
} LinkageCfg_t;
/** 联动运行时(RAM,不作废标志不落 VM) */
typedef struct {
uint8_t voided; /**< 1=被手动作废,须走到「关」条件才再武装 */
} LinkageCtx_t;
static LinkageCtx_t s_lk;
/**
* @brief 解析 DP6 20B 并判定是否有效(第 10.1 / 10.2 节)
* @param c 20B 配置
* @param out 解析结果
* @return 1=有效(缸体号≠0 且 Tmin≤Tmax 且 Hmin≤Hmax
*/
static uint8_t lk_parse(const uint8_t *c, LinkageCfg_t *out)
{
out->tank_id = (uint16_t)(((uint16_t)c[0] << 8) | c[1]);
out->tmin = (int8_t)c[2];
out->tmax = (int8_t)c[3];
out->hmin = c[4];
out->hmax = c[5];
if (out->tank_id == 0) {
return 0;
}
if (out->tmin > out->tmax) {
return 0;
}
if (out->hmin > out->hmax) {
return 0;
}
return 1;
}
/**
* @brief 联动让出风扇:仅当联动正占着时才关(手动/定时占着不动)
* @return 无
*/
static void lk_stop(void)
{
if (app_arbiter_linkage_active()) {
app_arbiter_linkage_release();
app_fan_set_gear(0);
log_info("linkage stop, fan=0\n");
}
}
/**
* @brief 第 8.2 节:喂数码管告警闪烁(出 [min,max] 就闪,与被作废无关)
* @param cfg 已解析的配置
* @param valid 配置是否有效
* @return 无
*/
static void lk_feed_blink(const LinkageCfg_t *cfg, uint8_t valid)
{
/* 传感器故障 "--" 优先于闪烁(8.2):故障期不闪 */
if (!valid || !app_th_get_valid() || app_th_is_fault()) {
app_disp_set_temp_alarm(0);
app_disp_set_humi_alarm(0);
return;
}
{
int16_t tx10 = app_th_get_temp_x10();
uint8_t h = app_th_get_humi();
int16_t tmax = (int16_t)cfg->tmax * 10;
int16_t tmin = (int16_t)cfg->tmin * 10;
/* 没有等于比较:T==Tmax 不算超最高,T==Tmin 不算低于最低 */
app_disp_set_temp_alarm(((tx10 > tmax) || (tx10 < tmin)) ? 1 : 0);
app_disp_set_humi_alarm(((h > cfg->hmax) || (h < cfg->hmin)) ? 1 : 0);
}
}
/**
* @brief 初始化联动引擎
* @return 无
*/
void app_linkage_init(void)
{
#if !BOARD_LINKAGE_ENABLE
log_info("linkage engine disabled\n");
return;
#endif
memset(&s_lk, 0, sizeof(s_lk));
app_disp_set_temp_alarm(0);
app_disp_set_humi_alarm(0);
log_info("linkage init\n");
}
/**
* @brief 立刻按第 10.3 节重判一次(核心判定,幂等)
* @note 步骤:8.4 故障 → 关;8.2 喂闪烁;10.1 无效配置 → 关;
* 12 章再武装(走到关条件才清作废);仲裁;10.3 开/关/保持。
* @return 无
*/
void app_linkage_reeval(void)
{
#if !BOARD_LINKAGE_ENABLE
return;
#endif
LinkageCfg_t cfg;
uint8_t valid = lk_parse(gDevData.linkage_config, &cfg);
uint8_t over_max;
uint8_t under_min;
/* ── 8.4:采样失败 "--" 期间联动视为不成立;若联动占着 → 关掉,避免盲吹 ── */
if (app_th_is_fault()) {
app_disp_set_temp_alarm(0);
app_disp_set_humi_alarm(0);
lk_stop();
return;
}
/* ── 8.2:闪烁与开停解耦,有有效配置就判 ── */
lk_feed_blink(&cfg, valid);
/* ── 10.1:无效配置 → 不自动开;原先联动占着则关掉 ── */
if (!valid) {
lk_stop();
return;
}
if (!app_th_get_valid()) {
/* 从未采到过有效值:无可比数据,联动不成立 */
lk_stop();
return;
}
{
int16_t tx10 = app_th_get_temp_x10();
uint8_t h = app_th_get_humi();
over_max = ((tx10 > (int16_t)cfg.tmax * 10) || (h > cfg.hmax)) ? 1 : 0;
under_min = ((tx10 < (int16_t)cfg.tmin * 10) || (h < cfg.hmin)) ? 1 : 0;
}
/* ── 12 章再武装:必须先走到「关」条件(没有一侧超最高,且至少一侧 < min)
* 才清手动作废;之后重新出现超最高才允许再开 1 档 ── */
if (!over_max && under_min) {
if (s_lk.voided) {
log_info("linkage rearmed (close condition reached)\n");
}
s_lk.voided = 0;
}
if (s_lk.voided) {
return; /* 作废期间只闪不动风扇 */
}
/* ── 仲裁:模式引擎每 20ms 强制档位(通风/强力),联动不与之抢 ── */
if (app_mode_get_active() != 0) {
return;
}
if (over_max) {
/* 10.3 开:任一超最高 → 1 档,不改 mode。定时窗占用则 take 失败 */
if (!app_arbiter_linkage_take()) {
return; /* 12 章:定时窗 vs 联动 → 定时胜,窗内联动不插嘴 */
}
if (app_fan_get_gear() != BOARD_LINKAGE_OPEN_GEAR) {
app_fan_set_gear(BOARD_LINKAGE_OPEN_GEAR);
log_info("linkage open, gear=%d\n", BOARD_LINKAGE_OPEN_GEAR);
}
} else if (under_min) {
/* 10.3 关:没有一侧超最高且至少一侧 < min → 关 */
lk_stop();
}
/* 否则:两侧都在 [min,max] 闭区间(含等于)→ 保持上一态(回差) */
}
/**
* @brief 推进联动引擎(500ms,任务上下文)
* @param dt 距上次调用的毫秒数
* @return 无
*/
void app_linkage_run(uint16_t dt)
{
#if !BOARD_LINKAGE_ENABLE
(void)dt;
return;
#endif
(void)dt; /* 本模块不计时:每拍完整重判,2s 采样周期由 app_th 决定 */
app_linkage_reeval();
}
/**
* @brief DP5 linkage_delete 写 1:清配置并让出风扇
* @return 无
*/
void app_linkage_delete(void)
{
/* 引擎关掉也要落盘(与定时一致:P1 只存不跑) */
memset(gDevData.linkage_config, 0, BOARD_LINKAGE_CFG_LEN);
app_nvm_mark_dirty();
#if BOARD_LINKAGE_ENABLE
s_lk.voided = 0;
lk_stop(); /* 10.4:联动若占着风扇 → fan_gear=0 */
#endif
log_info("linkage config deleted\n");
}
/**
* @brief DP6 重写完成:存 VM + 清手动作废 + 立刻按新 min/max 重判
* @return 无
*/
void app_linkage_set_config(void)
{
/* 引擎关掉也要落盘;开停判定仅在 BOARD_LINKAGE_ENABLE=1 时跑 */
app_nvm_mark_dirty();
#if BOARD_LINKAGE_ENABLE
s_lk.voided = 0; /* 10.5:清手动作废标志 */
app_linkage_reeval(); /* 该开开、该关关 */
#endif
log_info("linkage_config stored\n");
}
/**
* @brief 手动作废本轮联动
* @return 无
*/
void app_linkage_void(void)
{
#if !BOARD_LINKAGE_ENABLE
return;
#endif
if (!s_lk.voided) {
s_lk.voided = 1;
log_info("linkage voided by manual\n");
}
}
/**
* @brief 当前配置是否有效(第 10.1 节)
* @return 1=有效
*/
uint8_t app_linkage_is_valid(void)
{
#if !BOARD_LINKAGE_ENABLE
return 0;
#endif
LinkageCfg_t cfg;
return lk_parse(gDevData.linkage_config, &cfg);
}