Files
JBao_Sprayer_FW/apps/usr_app/app_linkage.c
T
wuchuyuan 0dd65e1552 1、喷淋器 P1/P2功能实现
2、红灯按连接态慢闪
2026-09-09 19:57:00 +08:00

310 lines
8.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @file app_linkage.c
* @brief CBR2 联动引擎(规格书第 11 章 + 第 13 章仲裁):只看湿度回差
* @author cyWu <1917507415@qq.com>
* @date 2026.09.09
* @version V1.0.0, 首次发布
*
* @note 开停只看湿度(11.3,uint8 %;与加热垫温度回差方向相反——过干开):
* humi < Hmin → 联动循环喷:空闲则用 DP7 参数开一轮,一轮正常
* 结束后(仲裁收口 reeval)再判再开,无额外间隔
* humi > Hmax → 联动占着则立刻停轮(req_stop,与谁开的无关)
* [Hmin,Hmax](含等于)→ 保持上一态
* 湿度源(8 章):BLE 已连接 = 服务器 DP6;断连已配对 = 扫描
* JBao-{tank};丢包保持上一态。无任何有效湿度 → 联动不改泵。
*
* 手动作废(13 章,RAM 不落 VM):PB1 停 / DP1=0 / DP7 抢权触发;
* 须先走到「关」条件(H>Hmax)再重新出现 H<Hmin 才允许再开;
* 重写 DP9 清手动作废并立刻重判(11.5)。
*
* ⚠ 判定只在 usr_timer_loop500ms 任务上下文)与 DP6/9 写路径
* 执行,不提供 20ms 中断接口。req_* 只置字节,与 ISR 无竞争。
******************************************************************************/
#include <string.h>
#include "system/includes.h"
#include "app_linkage.h"
#include "app_spray.h"
#include "app_timer_task.h"
#include "app_nvm.h"
#include "jb_product.h"
#include "board_pin.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP_LKG]"
#define LOG_INFO_ENABLE
#include "debug.h"
/** 20B 配置解析结果(规格书 11.2;温度/版本/预留只存不用) */
typedef struct {
uint16_t tank_id; /**< Byte0-1 大端,0=无效 */
uint8_t hmin; /**< Byte4 % */
uint8_t hmax; /**< Byte5 % */
} LinkageCfg_t;
/** 联动运行时(RAM;湿度与作废标志均不落 VM) */
typedef struct {
uint8_t humi; /**< 联动用湿度 % */
uint8_t humi_valid; /**< 1=收到过任意来源的有效湿度 */
uint8_t voided; /**< 1=被手动作废,须先到「关」条件再武装 */
} LinkageCtx_t;
static LinkageCtx_t s_lk;
/**
* @brief 解析 20B 配置并判定有效性(11.1:缸体≠0 且 Hmin≤Hmax
* @param c 20B 配置
* @param out 解析结果
* @return 1=有效
*/
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->hmin = c[4];
out->hmax = c[5];
if (out->tank_id == 0) {
return 0;
}
if (out->hmin > out->hmax) {
return 0;
}
return 1;
}
/**
* @brief 联动让出泵:仅当联动正占着作业时才停(手动/计划占着不动)
* @return 无
*/
static void lk_stop(void)
{
if (app_spray_is_running() && (app_spray_owner() == APP_SPRAY_OWNER_LINKAGE)) {
app_spray_req_stop();
log_info("linkage stop\n");
}
}
/**
* @brief 初始化联动引擎
* @return 无
*/
void app_linkage_init(void)
{
#if !BOARD_LINKAGE_ENABLE
log_info("linkage disabled\n");
return;
#endif
memset(&s_lk, 0, sizeof(s_lk));
log_info("linkage init\n");
}
/**
* @brief 核心判定(幂等,11.3 + 13 章)
* @note 步骤:无效配置 → 联动占着则停;无有效湿度 → 不动;
* 走到「关」条件清作废(再武装);作废期间不动作;
* 计划占用中或队列待跑时不插嘴;否则按回差循环开/停。
* @return 无
*/
static void lk_reeval(void)
{
LinkageCfg_t cfg;
uint8_t valid = lk_parse(gDevData.linkage_config, &cfg);
uint8_t dry; /* H < Hmin:过干,开条件 */
uint8_t wet; /* H > Hmax:过湿,关条件 */
/* 11.1:无效配置(缸体 0 / min>max)→ 不自动开;联动占着则停掉 */
if (!valid) {
lk_stop();
return;
}
/* 11.2:尚无任何有效湿度 → 联动不改泵(保持上电关/上一态) */
if (!s_lk.humi_valid) {
return;
}
dry = (s_lk.humi < cfg.hmin) ? 1u : 0u;
wet = (s_lk.humi > cfg.hmax) ? 1u : 0u;
/* 13 章再武装:先走到「关」条件(H>Hmax)→ 清作废;
* 之后重新出现 H<Hmin 才允许再开。等于 Hmin/Hmax 都不算越界 */
if (wet) {
if (s_lk.voided) {
log_info("linkage rearmed (H>Hmax reached)\n");
}
s_lk.voided = 0;
}
if (s_lk.voided) {
return; /* 作废期间保持上一态,不动作 */
}
/* 13 章仲裁:计划轮占着(owner==PLAN)或队列待跑时联动不开不关,
* 计划收口后由 app_arbiter 调 reeval 补判 */
if (app_spray_owner() == APP_SPRAY_OWNER_PLAN) {
return;
}
if (app_timer_has_pending()) {
return;
}
if (dry) {
/* 11.3 开:H<Hmin 过干 → 空闲则用 DP7 参数开一轮(循环由收口续) */
if (app_spray_owner() == APP_SPRAY_OWNER_NONE) {
app_spray_req_start_link();
log_info("linkage open (dry, H=%u<%u)\n", s_lk.humi, cfg.hmin);
}
} else if (wet) {
/* 11.3 过湿停:走到 H>Hmax 一律停联动的轮;手动/计划占着不插嘴 */
if (app_spray_is_running() && (app_spray_owner() == APP_SPRAY_OWNER_LINKAGE)) {
app_spray_req_stop();
log_info("linkage stop (wet, H=%u>%u)\n", s_lk.humi, cfg.hmax);
}
}
/* 否则 [Hmin,Hmax](含等于):保持上一态(回差) */
}
/**
* @brief 立刻按第 11.3 节重判一次(DP9 重写 / 计划轮收口后调用)
* @return 无
*/
void app_linkage_reeval(void)
{
#if !BOARD_LINKAGE_ENABLE
return;
#endif
lk_reeval();
}
/**
* @brief 推进联动引擎(500ms,任务上下文)
* @param dt 距上次调用的毫秒数
* @return 无
*/
void app_linkage_run(uint16_t dt)
{
#if !BOARD_LINKAGE_ENABLE
(void)dt;
return;
#endif
(void)dt; /* 本模块不计时:每拍完整重判 */
lk_reeval();
}
/**
* @brief DP8 联动删除写 1(脉冲):清 RAM+VM 20B;联动占着则停
* @note gDevData.linkage_delete 由调用方清回 0(脉冲)
* @return 无
*/
void app_linkage_delete(void)
{
memset(gDevData.linkage_config, 0, 20);
app_nvm_mark_dirty();
#if BOARD_LINKAGE_ENABLE
s_lk.voided = 0;
lk_stop(); /* 11.4:若联动占着作业 → 停 */
#endif
log_info("linkage config deleted\n");
}
/**
* @brief APP 重写 DP9memcpy 已完成):存 VM + 清手动作废 + 立刻重判
* @return 无
*/
void app_linkage_set_config(void)
{
app_nvm_mark_dirty();
#if BOARD_LINKAGE_ENABLE
s_lk.voided = 0; /* 11.5:清手动作废标志 */
lk_reeval(); /* 立刻按新阈值重判(有有效湿度时) */
#endif
log_info("linkage_config stored\n");
}
/**
* @brief 手动作废联动轮(PB1 开/停、DP1、DP7 抢权;RAM 不写 VM
* @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 配置是否有效(11.1;扫描门控也用)
* @return 1=有效
*/
uint8_t app_linkage_is_valid(void)
{
LinkageCfg_t cfg;
return lk_parse(gDevData.linkage_config, &cfg);
}
/**
* @brief 缸体编号(Byte0-1 大端;引擎关掉也可读,供扫描匹配用)
* @return 缸体编号,0=未配
*/
uint16_t app_linkage_get_tank_id(void)
{
return (uint16_t)(((uint16_t)gDevData.linkage_config[0] << 8) |
gDevData.linkage_config[1]);
}
/**
* @brief 联动配置版本(Byte6;扫描匹配用)
* @return 配置版本
*/
uint8_t app_linkage_get_cfg_ver(void)
{
return gDevData.linkage_config[6];
}
/**
* @brief BLE 已连接:服务器 DP6 湿度(uint8 %)→ 联动湿度
* @return 无
*/
void app_linkage_set_connected_humi(uint8_t humi)
{
if (s_lk.humi_valid && (s_lk.humi == humi)) {
return;
}
s_lk.humi = humi;
s_lk.humi_valid = 1;
log_info("linkage humi from dp6: %u%%\n", (unsigned)humi);
#if BOARD_LINKAGE_ENABLE
lk_reeval(); /* 湿度边沿立刻判,不等下一个 500ms */
#endif
}
/**
* @brief 断连扫描:匹配到 JBao-{tank} 广播 → 联动湿度
* @return 无
*/
void app_linkage_set_scan_humi(uint8_t humi)
{
if (s_lk.humi_valid && (s_lk.humi == humi)) {
return;
}
s_lk.humi = humi;
s_lk.humi_valid = 1;
log_info("linkage humi from scan: %u%%\n", (unsigned)humi);
#if BOARD_LINKAGE_ENABLE
lk_reeval();
#endif
}
/**
* @brief 是否已有任何有效湿度
* @return 1=有
*/
uint8_t app_linkage_has_humi(void)
{
return s_lk.humi_valid;
}