Files
wandering1 b7c0889cc7 refactor(编号·批次二): 代码与测试侧 215 处保护功能号改成功能名,零行为改动
用户裁定「不要用字母加数字的编号,语义标识是最好的」后的批次二代码半(道 ui-param 执行,逐文件站点数与
自证见其交付)。唯一口径 = 映射表 ci-artifacts/lprot_map.tsv 的功能名列原文,**零自造同义词**;L2.5 与
L-TEST 经实测各 0 处(不是本族),TC 族一字未动。

固件 22 文件 128 处(adc_task.c 21、phasor.c 22、phasor.h 13…),主机测试 13 文件 87 处(pha
sor_test.c
57…);设计文档指针新增 19 行,另 16 只文件本来就写着规划文档 ⇒ 不重复加。

**两条只有真改过 215 处才会踩到的坑,都写进注释规范实践**:
· 指针**不能接在原句尾部**——那会替原文一个未闭合的半括号封口,实测咬坏 App/algo/sample_ring.h 这类
  跨行句 ⇒ 指针一律**单独成行**;
· 号删掉会留下**叠词**(「自检失败闭锁 闭锁判定」「定值校验和 定值巡检」)⇒ 复扫并收口 4 处,措辞只用
  映射名本身;具体对象需要留住时写成「自检失败闭锁(采样故障闭锁)」。

自证(不靠嘴说):改前把 35 只文件原文备份到仓外 temp,逐文件比 `/*`、`*/` 计数、三类括号计数、行数增量
恰等于指针行数、全角括号增删相等、残留号 0 ⇒ **35/35 通过**;Lead 复核后跑权威 @build **0 错 0 警**,
Program Size `Code=233900 RO=26104 RW=1536 ZI=136808` —— **与三条道各自改前改后测的四数逐
字节同数**,
这就是「只动注释与文本」的机器证据;八套主机单测 exit 0,cppcheck 按钩子参数零发现,coding-standard 0。

文件头「修改记录」里的号**一并换名、不还原**(Lead 裁定):这些号是全仓退役的,留一个查无此号的引用比
留一条改过名的条目更坏——「冻结历史不回改」保护的是**别替历史改口**,不是保护一个再也查不到的字符串;
id-check 对变更记录行本就豁免,两种选择都不影响门禁。
2026-10-27 08:59:26 +08:00

274 lines
13 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.
/*
* 模块名称:Network Application Task
* 模块功能:CH395F 协议栈初始化 + 轮询 + TCP/UDP 业务处理
* 与 net_socket.h 配合,在 FreeRTOS 独立任务中运行
* 适用平台:STM32F407ZGTx + CH395F
* 作者:王建锋
* 创建日期:2026-07-19
* 修改记录:
* v1.0 2026-07-19 王建锋 创建初始版本
* v1.1 2026-07-19 王建锋 migrate from freertos.c StartNetTask
* v1.2 2026-07-21 王建锋 网络初始化移入本任务(net_init + TCP 监听)
* v1.3 2026-07-22 王建锋 单连接模式重构,增加大文件/边界/KeepAlive 测试
* v1.4 2026-09-14 王建锋 新增自检埋点:PHY 链路 3s 未建立 → sys_log "comms fault"
* v1.5 2026-09-15 王建锋 配置加载移交 defaultTask(配置引擎属主);
* 本任务只读 RAM 副本
* v1.6 2026-09-15 王建锋 入口加 CH395F 硬件层测试模式自挂起守卫
* (基础线程无条件创建,该模式下不运行);
* freertos.c USER CODE 区重复创建已删除
* v1.7 2026-10-02 王建锋 初始组网失败/重建失败 → 自检记录 + 30s 周期重试
* (原实现:初始失败 = netTask 无限驻留;重建失败 = 静默丢配置)
* v1.8 2026-10-10 王建锋 任务耗时测量埋点:net_poll() 单独计时——
* 本任务每 ~1ms 分派一次消息队列,若驱动轮询被拉长,
* 其它任务(尤其 UI 的 net_accept 往返)的等待随之拉长
* v1.9 2026-10-21 王建锋 删除 net_task_init():空壳入口(体内只有一条
* DBG_INFO),全库无调用点,而函数头却写"创建 TCP
* Server 监听 Socket,注册回调等"——描述与实际不符,
* 监听实际在 net_stack_bring_up() 与各服务里做
*/
#include <string.h>
#include "net_task.h"
#include "cmsis_os.h"
#include "net_socket.h"
#include "net_select.h"
#include "net_cfg.h"
#include "ch395f.h"
#include "sys_events.h"
#include "sys_log.h"
#include "test_config.h" /* 测试模式宏(ENABLE_HW_LAYER_TESTS);产品构建全未定义,无作用 */
#include "task_watchdog.h"
#include "task_prof.h" /* 探针:net_poll 耗时 */
#define DBG_TAG "[NET_TASK]"
#include "dbg_log.h"
/*
* 函数功能:"立即应用网口配置"平台回调(注册给 net_cfg)
* 入口参数:无
* 返回值:无
* 限定条件:保存写盘成功后经 net_cfg_request_apply() 调用(MCU 上由
* defaultTask 上下文调用;任意任务上下文均安全)
* 函数说明:只置事件位;真正的重建在主循环内完成(CH395F SPI 归 netTask 独占,
* UI 不得直接操作)
*/
/* 重建失败延迟重试的截止 tick;0 = 无待重试 */
static uint32_t s_reinit_retry_at;
static void net_reconfig_request_cb(void) {
if (sys_evtHandle != NULL) {
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_RECONFIG);
}
}
/*
* 函数功能:CH395F 协议栈一次性初始化(两种构建模式共用)
* 入口参数:无
* 返 回 值:0 - 成功,-1 - 失败
* 函数说明:net_cfg 已由 defaultTask 在就绪屏障前加载进 RAM 副本(配置引擎
* 属主 = defaultTask,本任务不触碰配置文件引擎)→ 只读副本组网 →
* net_init → MAC 回写
* 配置模块 → MAC 打印 → PHY 强制 100M 全双工 → 广播网络就绪事件。
* NET 层模式由 netTask 调用;硬件层模式(阶段 1~5)netTask 不创建,
* 由 testTask 调用后独占 CH395F。
*/
int net_stack_bring_up(void) {
const net_cfg_t *cfg;
char ip[16];
char mask[16];
char gw[16];
DBG_INFO("=== Network Init ===");
/* 网络配置:defaultTask 已在 SYS_EVT_STORAGE_READY 屏障前置入 RAM 副本
* (首启/文件缺失/损坏 = 工厂默认,与既有硬编码组网值一致);
* 此处只读副本,不触碰配置文件引擎 */
cfg = net_cfg_get();
net_cfg_fmt_ipv4(cfg->ip, ip, sizeof(ip));
net_cfg_fmt_ipv4(cfg->mask, mask, sizeof(mask));
net_cfg_fmt_ipv4(cfg->gw, gw, sizeof(gw));
if (net_init(ip, mask, gw, cfg->mac) != 0) {
DBG_ERROR("net init: FAIL");
return -1;
}
DBG_INFO("net init: OK");
uint8_t mac[6];
ch395f_get_mac_addr(mac);
DBG_INFO("MAC: %02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
/* 回写芯片实际 MAC 到配置模块(UI "网口设置"页只读影子,
* 不直接触 CH395F SPI——SPI 归 netTask 集中处理) */
net_cfg_set_active_mac(mac);
uint8_t ipinf[20];
ch395f_get_ip_inf(ipinf);
DBG_INFO("IP_INF: IP=%d.%d.%d.%d GW=%d.%d.%d.%d MASK=%d.%d.%d.%d",
ipinf[0], ipinf[1], ipinf[2], ipinf[3],
ipinf[4], ipinf[5], ipinf[6], ipinf[7],
ipinf[8], ipinf[9], ipinf[10], ipinf[11]);
ch395f_set_phy(CH395F_PHY_100M_FULL);
/* 强制 PHY 模式后链路会短暂掉线再重建,CH395F 会锁存 GINT_STAT_PHY_CHANGE(0x04) 全局中断标志。
* 这里只轮询 ch395f_get_phy_status() 确认链路是否已真正建立——**不读全局中断寄存器**:
* 它读即清,唯一读者 = net_poll(唯一读者红线,docs/网络/网络自愈架构设计.md §4.10;
* 用户裁定 2026-10-13 测试代码同样禁自行读取)。
* 锁存的 PHY_CHANGE 会在 net_poll 首轮被正常消费(喂驱动链路态缓存,无害)。
* 链路一就绪(phy != DISCONN)即跳出;最多等约 3s(60 × 50ms),超时仍按实际状态继续(由下方日志暴露问题)。 */
uint8_t phy = CH395F_PHY_DISCONN;
for (unsigned int i = 0U; i < 60U; i++) {
phy = ch395f_get_phy_status();
if (phy != CH395F_PHY_DISCONN) {
break;
}
/* 心跳埋点:本循环单次最长约 1.1s(含 PHY 仍未就绪的 60 次迭代里
* 每次 50ms 延时叠加),远超看门狗 100ms 抽样窗——必须在延时段补心跳,
* 否则 PHY 未插网线时本任务会被误判为卡死(见设计说明 §4.4 长耗时白名单) */
task_wd_beat(TASK_WD_NET);
osDelay(50);
}
DBG_INFO("PHY: %s",
(phy == CH395F_PHY_100M_FULL) ? "100M FULL" :
(phy == CH395F_PHY_100M_HALF) ? "100M HALF" :
(phy == CH395F_PHY_10M_FULL) ? "10M FULL" :
(phy == CH395F_PHY_10M_HALF) ? "10M HALF" :
(phy == CH395F_PHY_DISCONN) ? "DISCONNECT" : "UNKNOWN");
/* 自检埋点:链路超时未建立 → 记入自检记录(仅故障才写) */
if (phy == CH395F_PHY_DISCONN) {
syslog_post(SYSLOG_SELFTEST, "comms fault");
}
/* 网络已重建完成、对外可用:递增代次号,通知各 TCP 服务(FTP/对时/远程显示)
* 旧 Socket 已随芯片复位失效、应重建监听 */
net_generation_inc();
return 0;
}
/*
* 函数功能:网络任务主函数
* 入口参数:arg - FreeRTOS 传入参数(未使用)
* 返 回 值:无
* 函数说明:等待存储就绪(就绪屏障 = 卷挂载 + 网络配置加载完成,10s 超时
* 回退工厂默认,存储故障不阻塞组网)→ net_stack_bring_up() 初始化
* CH395F 协议栈,
* 再进入轮询循环处理网络事件。
* 本任务 = 基础线程:CubeMX 生成区无条件创建(全构建唯一实例);
* CH395F 硬件层测试模式(阶段 1~5)下入口自挂起(不 bring_up、
* 不轮询、不广播 NET_READY),由 testTask 独占 CH395F。
*/
void net_task_func(void const *arg) {
(void)arg;
task_wd_bind(TASK_WD_NET); /* FTL 心跳路由:登记本任务句柄(入口一次) */
/* 硬件层测试模式:testTask 独占 CH395F,本任务自挂起(基础线程恒创建,
* 该模式下"不运行"):不 bring_up、不 net_poll、不广播 NET_READY;
* ftpTask/timeSyncTask 将永久阻塞于屏障 = 预期行为(见《项目启动顺序管理》) */
#if defined(ENABLE_HW_LAYER_TESTS)
DBG_INFO("netTask parked: HW-layer test mode, CH395F owned by testTask");
for (;;) {
/* 该模式下本任务已被 task_wd_required_mask() 剔除,喂心跳无副作用;
* 保留埋点以免掩码配置变更时意外误杀 */
task_wd_beat(TASK_WD_NET);
osDelay(1000);
}
#endif
/* 配置在 NAND 的 FatFS 卷上:等存储就绪再读(取代原 2s 魔法延时);
* 超时(存储异常)继续以工厂默认组网,不卡网络启动 */
if (sys_evtHandle != NULL) {
/* osFlagsNoClear:就绪位是状态,不能被本任务消费(ftpTask 等仍要 WaitAll 两位)。
* 2026-10-02 台架修复:10s 超时等待改 100ms 分片 + 每片喂心跳——原实现
* 在等待期无心跳:若存储 bring-up 失败,本等待跑满 10s 超时,而 IWDG
* 此时已启动(defaultTask 在 bring-up 后即启动),NET 位长期缺失 →
* 必需掩码不可达 → 复位循环,存储自愈的 2s 重试根本没有机会。
* 分片喂位保证"存储未就绪"降级态下 NET 位持续新鲜。 */
uint32_t rounds_left = 100u; /* 100 × 100ms = 原 10s 超时语义 */
for (;;) {
uint32_t r = osEventFlagsWait(sys_evtHandle, SYS_EVT_STORAGE_READY,
osFlagsWaitAny | osFlagsNoClear, 100u);
if ((r & osFlagsError) == 0u) {
break; /* 存储就绪 */
}
task_wd_beat(TASK_WD_NET); /* 看门狗:分片喂心跳(屏障等待态) */
if ((--rounds_left) == 0u) {
break; /* 10s 超时:工厂默认组网,不阻塞网络启动 */
}
}
}
/* 初始组网失败不自驻留——自检台账留痕 + 30s 周期重试
* (1s 分片、每片喂心跳):线缆/芯片恢复即可当场完成组网;原 for(;;)osDelay
* 会令看门狗 NET 位持续陈旧,并阻塞全部下游就绪屏障 */
for (;;) {
if (net_stack_bring_up() == 0) {
break;
}
DBG_ERROR("netTask: init FAIL, retry in 30s");
(void)syslog_post(SYSLOG_SELFTEST, "net init fail");
for (uint32_t i = 0U; i < 30U; i++) {
osDelay(1000U);
task_wd_beat(TASK_WD_NET);
}
}
/* 广播"网络就绪",唤醒等待它的 ftpTask / start_test_task / timeSyncTask 等 */
if (sys_evtHandle != NULL) {
osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_READY);
}
/* 注册"立即应用网口配置"回调:UI 保存成功后经 net_cfg_request_apply() 触发 */
net_cfg_set_apply_cb(net_reconfig_request_cb);
DBG_INFO("netTask: started (single-socket mode)");
/* ==================== 主轮询循环 ==================== */
for (;;) {
/* 心跳:本任务在监视掩码内(ENABLE_HW_LAYER_TESTS 下由 task_wd_required_mask
* 剔除,本埋点无副作用) */
task_wd_beat(TASK_WD_NET);
/* UI 保存网口配置后请求立即应用:在此重建网络(新 IP/掩码/网关/MAC)。
* 重建完成后代次号已递增,各 TCP 服务据此重建监听;期间连接会断开(预期)。
* 用 osEventFlagsWait(timeout=0) 原子"取走并清除"RECONFIG
* 位,替换原 Get+Clear 两步;原写法在 Get 与 Clear 之间若有生产者在同一位上
* Set,该次请求会被 Clear 一并吞掉(丢事件窗口)。 */
if (sys_evtHandle != NULL) {
uint32_t evt = osEventFlagsWait(sys_evtHandle, SYS_EVT_NET_RECONFIG,
osFlagsWaitAny, 0U);
if ((evt & osFlagsError) == 0u) {
DBG_INFO("netTask: apply new net config (re-init)");
if (net_stack_bring_up() == 0) {
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_READY);
s_reinit_retry_at = 0U;
} else {
/* 重建失败 → 自检留痕 + 30s 延迟重试(重事件位),
* 新配置不再静默丢失(原实现仅一条 DBG_ERROR 后丢弃) */
DBG_ERROR("netTask: re-init FAIL, retry in 30s");
(void)syslog_post(SYSLOG_SELFTEST, "net reinit fail");
s_reinit_retry_at = (uint32_t)HAL_GetTick() + 30000U;
}
} else if ((s_reinit_retry_at != 0U)
&& ((int32_t)((uint32_t)HAL_GetTick() - s_reinit_retry_at) >= 0)) {
/* 30s 倒计时到 → 重新触发重建(符号差处理 tick 回绕) */
s_reinit_retry_at = 0U;
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_RECONFIG);
}
}
/* P2 探针:驱动轮询单独计时。本循环每 ~1ms 一次,是 netTask 回执
* (UI 的 net_accept 在等它)的节奏来源;此值变大 = UI 心跳被拖长 */
task_prof_enter(TASK_PROF_NET_POLL);
net_poll();
task_prof_exit(TASK_PROF_NET_POLL);
net_process_messages();
osDelay(1);
}
}