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

315 lines
9.0 KiB
C
Raw Permalink 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_runrec.c
* @brief CBR2 31 天运行记录(规格书 14.1 节,DP18 RO124B
* @author cyWu <1917507415@qq.com>
* @date 2026.09.09
* @version V1.0.0,首次发布
*
* @note 什么叫在运行:app_pump_get()==1PC2 与 PA6 **都已导通**),
* 间隔等待/作业态开但泵关不计(14.1)。日期轴:未对时只往 slot[0]
* 加、不旋转;对时后按本地日历左移;掉电再上电先由 app_nvm_load 灌 VM。
*
* ⚠ 上下文分工(勿再踩复位事故):
* - app_runrec_tick20ms 中断):只把泵导通的毫秒折成 pending 秒,
* 不碰 s_rr.rec[]、不写 flash。
* - app_runrec_rotate_if_needed500ms 任务):收下 pending 加到
* slot[0]、每拍灌 DP18、跨天旋转、满 600s / 跨天 / 首次对时才
* 写 VM。拔电没有软关机钩子,接受最多丢不满 10 分钟的秒数。
******************************************************************************/
#include <string.h>
#include "system/includes.h" /* local_irq_disable / local_irq_enable */
#include "app_runrec.h"
#include "app_pump.h"
#include "app_nvm.h"
#include "usr_rtc.h"
#include "jb_product.h"
#include "board_pin.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP_RREC]"
#define LOG_INFO_ENABLE
#include "debug.h"
#if BOARD_RUNREC_ENABLE
/** 运行记录上下文:31 格日历 + ISR/任务两侧的折秒状态 */
typedef struct {
uint32_t rec[BOARD_RUN_RECORD_DAYS]; /**< slot[0]=今天 … slot[30]=前 30 天(任务侧) */
uint32_t ymd; /**< slot[0] 对应本地日期 yyyymmdd0=未对时 */
uint16_t ms; /**< 不足 1 秒的毫秒零头(仅 ISR) */
volatile uint16_t pending_sec; /**< ISR 折秒,任务收下后加到 slot[0] */
uint16_t unsaved; /**< 距上次落盘累计的运行秒数(仅任务) */
} AppRunrecCtx_t;
static AppRunrecCtx_t s_rr;
#endif
/**
* @brief 公历日期 → 天数序号(Howard Hinnant 的 days_from_civil
* @param y 年(如 2026
* @param m 月 1~12
* @param d 日 1~31
* @return 距 1970-01-01 的天数(可为负)
*/
static int32_t runrec_days_from_civil(int y, int m, int d)
{
int era;
int yoe;
int doy;
int doe;
y -= (m <= 2) ? 1 : 0;
era = ((y >= 0) ? y : (y - 399)) / 400;
yoe = y - era * 400; /* [0,399] */
doy = (153 * (m + ((m > 2) ? -3 : 9)) + 2) / 5 + d - 1; /* [0,365] */
doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; /* [0,146096]*/
return (int32_t)(era * 146097L + (long)doe - 719468L);
}
/**
* @brief yyyymmdd 压缩日期 → 天数序号
* @param ymd yyyymmdd(如 20260904
* @return 天数序号
*/
static int32_t runrec_ymd_to_days(uint32_t ymd)
{
return runrec_days_from_civil((int)(ymd / 10000u),
(int)((ymd / 100u) % 100u),
(int)(ymd % 100u));
}
/**
* @brief 初始化运行记录
* @return 无
*/
void app_runrec_init(void)
{
#if !BOARD_RUNREC_ENABLE
log_info("runrec disabled\n");
return;
#else
memset(&s_rr, 0, sizeof(s_rr));
app_runrec_pack(gDevData.run_record); /* DP18 报全 0 */
log_info("runrec init, %u days\n", (unsigned)BOARD_RUN_RECORD_DAYS);
#endif
}
#if BOARD_RUNREC_ENABLE
/**
* @brief 把 ISR 折好的秒数加到 slot[0],满 10 分钟则请求写 VM
* @return 1=需要立刻写运行记录 item
*/
static uint8_t runrec_apply_pending(void)
{
uint16_t sec;
uint8_t need_vm = 0;
local_irq_disable();
sec = s_rr.pending_sec;
s_rr.pending_sec = 0;
local_irq_enable();
if (sec == 0) {
return 0;
}
s_rr.rec[0] += sec;
s_rr.unsaved = (uint16_t)(s_rr.unsaved + sec);
if (s_rr.unsaved >= BOARD_RUN_RECORD_SAVE_SEC) {
s_rr.unsaved = (uint16_t)(s_rr.unsaved % BOARD_RUN_RECORD_SAVE_SEC);
need_vm = 1;
}
if (need_vm) {
log_info("runrec %us reached, save vm\n", (unsigned)BOARD_RUN_RECORD_SAVE_SEC);
}
return need_vm;
}
#endif /* BOARD_RUNREC_ENABLE */
/**
* @brief 20ms 中断上下文节拍:泵实际导通才折秒到 pending,不碰 s_rr.rec
* @param dt 距上次调用的毫秒数
* @return 无
*/
void app_runrec_tick(uint16_t dt)
{
#if !BOARD_RUNREC_ENABLE
(void)dt;
return;
#else
if (!app_pump_get()) {
/* 没在喷:不累加,并丢弃不足 1 秒的零头(14.1:泵未导通不计入) */
s_rr.ms = 0;
return;
}
s_rr.ms = (uint16_t)(s_rr.ms + dt);
while (s_rr.ms >= 1000u) {
s_rr.ms = (uint16_t)(s_rr.ms - 1000u);
s_rr.pending_sec++; /* 任务侧再加到 slot[0],避免与跨天旋转打架 */
}
#endif
}
/**
* @brief 500ms 任务上下文:收下 pending、灌 DP18、跨天旋转、必要时写 VM
* @return 无
*/
void app_runrec_rotate_if_needed(void)
{
#if !BOARD_RUNREC_ENABLE
return;
#else
struct sys_time t;
uint32_t ymd;
int32_t diff;
int i;
uint8_t need_vm = runrec_apply_pending();
if (usr_rtc_get_time(&t)) {
ymd = (uint32_t)t.year * 10000u + (uint32_t)t.month * 100u + (uint32_t)t.day;
if (s_rr.ymd == 0) {
/* 首次对时:slot[0] 已是当天累计值,只登记日期,不旋转不清零 */
s_rr.ymd = ymd;
need_vm = 1;
log_info("runrec rtc synced, ymd=%lu\n", (unsigned long)ymd);
} else if (ymd != s_rr.ymd) {
diff = runrec_ymd_to_days(ymd) - runrec_ymd_to_days(s_rr.ymd);
if (diff <= 0) {
s_rr.ymd = ymd; /* 时钟回拨/异常:只纠正日期,不乱移数据 */
} else {
if (diff > BOARD_RUN_RECORD_DAYS) {
diff = BOARD_RUN_RECORD_DAYS; /* 跨太多天:整表作废 */
}
/* 左移 N 天:old[i] → new[i+N],前面 N 格清零 */
for (i = (int)BOARD_RUN_RECORD_DAYS - 1; i >= diff; i--) {
s_rr.rec[i] = s_rr.rec[i - diff];
}
for (i = 0; i < diff; i++) {
s_rr.rec[i] = 0;
}
s_rr.ymd = ymd;
need_vm = 1;
log_info("runrec rotate %ld day(s), ymd=%lu\n",
(long)diff, (unsigned long)ymd);
}
}
}
/* 每拍灌 DP18:读请求 / 10min 全量能拿到当前秒数;差分上报不带本 DP */
app_runrec_pack(gDevData.run_record);
if (need_vm) {
app_nvm_save_runrec(); /* 满 10 分钟或跨天 / 首次对时才写 flash */
}
#endif
}
/**
* @brief 取某天的运行秒数
* @param day 0=今天 … 30=前 30 天
* @return 秒数
*/
uint32_t app_runrec_get(uint8_t day)
{
#if !BOARD_RUNREC_ENABLE
(void)day;
return 0;
#else
if (day >= BOARD_RUN_RECORD_DAYS) {
return 0;
}
return s_rr.rec[day];
#endif
}
/**
* @brief 打包成 DP18 的 124B 载荷(31 × uint32 大端)
* @param out 输出缓冲
* @note 模块关闭时仍输出全 0(13.1:关闭时报全 0),
* 避免 app_nvm 把未初始化栈内存写进 VM 的 RUNREC item
* @return 无
*/
void app_runrec_pack(uint8_t *out)
{
uint8_t i;
#if !BOARD_RUNREC_ENABLE
memset(out, 0, APP_RUNREC_PACK_LEN);
return;
#else
for (i = 0; i < BOARD_RUN_RECORD_DAYS; i++) {
uint32_t v = s_rr.rec[i];
out[(uint8_t)(i * 4u) + 0u] = (uint8_t)(v >> 24);
out[(uint8_t)(i * 4u) + 1u] = (uint8_t)(v >> 16);
out[(uint8_t)(i * 4u) + 2u] = (uint8_t)(v >> 8);
out[(uint8_t)(i * 4u) + 3u] = (uint8_t)(v & 0xFFu);
}
#endif
}
/**
* @brief 从 VM 的 124B 载荷灌回 RAM
* @param in 输入缓冲
* @return 无
*/
void app_runrec_unpack(const uint8_t *in)
{
uint8_t i;
#if !BOARD_RUNREC_ENABLE
(void)in;
return;
#else
for (i = 0; i < BOARD_RUN_RECORD_DAYS; i++) {
s_rr.rec[i] = ((uint32_t)in[(uint8_t)(i * 4u) + 0u] << 24)
| ((uint32_t)in[(uint8_t)(i * 4u) + 1u] << 16)
| ((uint32_t)in[(uint8_t)(i * 4u) + 2u] << 8)
| (uint32_t)in[(uint8_t)(i * 4u) + 3u];
}
app_runrec_pack(gDevData.run_record); /* 开机立刻灌 DP18,不等 500ms */
#endif
}
/**
* @brief slot[0] 对应的本地日期
* @return yyyymmdd0=未对时
*/
uint32_t app_runrec_get_slot0_date(void)
{
#if !BOARD_RUNREC_ENABLE
return 0;
#else
return s_rr.ymd;
#endif
}
/**
* @brief 设置 slot[0] 对应日期
* @param ymd yyyymmdd0=未对时
* @return 无
*/
void app_runrec_set_slot0_date(uint32_t ymd)
{
#if BOARD_RUNREC_ENABLE
s_rr.ymd = ymd;
#else
(void)ymd;
#endif
}
/**
* @brief 清零 31 格与日期(出厂重置)
* @return 无
*/
void app_runrec_reset(void)
{
#if !BOARD_RUNREC_ENABLE
return;
#else
memset(&s_rr, 0, sizeof(s_rr));
app_runrec_pack(gDevData.run_record);
#endif
}