Files
STM32F4-Base/App/task/net_task.c
T
wandering1 5a9988b557 chore(注释): 清掉源码里不可检索的过程编码
清掉的(读者无从解析、且所指文档已不存在)
- 已删任务文档的编号:Task #32、其内部 §5 / §4.3、P1-5、P0-2、P1-4a、
  "方案 a"、"Lead 批准 2026-10-12"
- 正文里当代词用的文件版本号:"(v2.7 起为 AboveNormal)""已随 v2.0 事件
  驱动改造删除""(v2.0:DMA 消费)""见文件头 v1.3 说明"等 → 改写成陈述现状
- 测试与脚本同类:adc_input 桩头"v2.0:DMA 模型"、stub/adc.h 的 v2.0、
  check_cubemx_survival.py 段头的"2026-10-10 v2.8"、run_all_suites.py 的"方案 A"

刻意保留
- 可检索登记簿锚点:P-AUDIT 真条目(P0-1/P1-8/P2-1/P2-9/P3-11)、S6/S11、
  E-31/E-44、N-18/N-23/N-32、B1/B5/B7/B8、L1-x~L5-x 功能号
- 文件头修改记录块内的 vN.M(规范 V1.0 §2.1/§2.2 要求保留该块;块内编号
  在本文件内自洽可读)

顺带纠正注释与代码已经不一致的过时事实
- 仍把 TIM6 写成现行节拍源:Src/main.c 的 include 注释与回调注释、
  Src/freertos.c 的 adcTask 块 → 改 TIM3;adc_input.h 修订条目里"ADC1 只认
  TIM2/TIM3/TIM8 的 TRGO"不准确 → 换成指向判据(ADC采样节拍设计说明 §3.2)
- App/algo/phasor.c 双区注释称"唯一消费者是 adcTask、为后续保护任务预留",
  实际 protectTask 已跨上下文读(prot_elm_run -> phasor_get)→ 改为"读者跨
  adcTask 心跳与 protectTask 判据两个上下文,勿改加锁、勿取消双区",并同步
  修正 docs/算法/采集与算法层设计说明.md 同一句
- adc_tick_irq_notify() 的"拒绝 ISR 内计算"理由仍写"内含 2 ms 忙等"(DMA 化
  后已无忙等)→ 换成三条真实理由:任务级共享数据 / 计算量 / 生成区回调
- 裸 §5(指代不明)与"规划 §1.6 / 规划 §4"昵称式 → 写成完整路径紧邻式

字节卫生(Edit 会静默剥 BOM)
- App/task/ui_task.c 与 Drivers/BSP/UI/ui_net_sink.c 被剥掉 BOM 两次,均按
  3 字节 EF BB BF 前缀补回并复验;全部 20 个文件 CR=0、U+FFFD=0、无行尾空白

门禁
- @build 0 错误 0 警告,且 Program Size 与清理前逐项相同
  (Code=213356 RO=18580 RW=1184 ZI=131480)→ 纯注释改动、中文未被误重编码
- ui-golden 首跑红:新工作树缺被 gitignore 的黄金参考 simulator/tests/ref/
  (不入库、只在主工作树生成过);从主工作树复制该基线后复跑通过 → 注释改动
  零像素影响
- 全套 pre-commit 在这 20 个文件上 Passed(coding-standard / cppcheck /
  6 个主机单测 / doc-links / task-table / mem-numbers / cubemx-survival /
  registration-check / mem-budget / font-charset / ui-golden / id-check)
- id-check 可判定比例 82.71% -> 82.88%(删掉的是"形似 P-AUDIT 但查无定义"的
  假锚点),悬空引用 863 -> 854
2026-10-21 11:39:53 +08:00

281 lines
13 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.
/*
* 模块名称: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(配置引擎属主,P1-3);
* 本任务只读 RAM 副本
* v1.6 2026-09-15 王建锋 P1-6:入口加 CH395F 硬件层测试模式自挂起守卫
* (基础线程无条件创建,该模式下不运行);
* freertos.c USER CODE 区重复创建已删除
* v1.7 2026-10-02 王建锋 P1-8:初始组网失败/重建失败 → 自检记录 + 30s 周期重试
* (原实现:初始失败 = netTask 无限驻留;重建失败 = 静默丢配置)
* v1.8 2026-10-10 王建锋 任务耗时测量埋点:net_poll() 单独计时——
* 本任务每 ~1ms 分派一次消息队列,若驱动轮询被拉长,
* 其它任务(尤其 UI 的 net_accept 往返)的等待随之拉长
*/
#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 不得直接操作)
*/
/* P1-8(2026-10-02):重建失败延迟重试的截止 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(F-NET-7,用户裁定 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;
}
/*
* 函数功能:网络任务初始化
* 入口参数:无
* 返 回 值:0 - 成功,-1 - 失败
* 限定条件:CH395F 协议栈已初始化
* 函数说明:创建 TCP Server 监听 Socket,注册回调等
*/
int net_task_init(void) {
DBG_INFO("net_task_init");
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 位长期缺失 →
* 必需掩码不可达 → 复位循环,E-31 的 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 超时:工厂默认组网,不阻塞网络启动 */
}
}
}
/* P1-8(2026-10-02):初始组网失败不自驻留——自检台账留痕 + 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 服务据此重建监听;期间连接会断开(预期)。
* P2-11(2026-10-04):用 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 {
/* P1-8(2026-10-02):重建失败 → 自检留痕 + 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)) {
/* P1-8: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);
}
}