Files
STM32F4-Base/test/board/net_test_task.c
T
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号)
一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、
采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。

门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏
undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8
reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF;
id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。

test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字,
是本批唯一改变构建产物的文件集合。

为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged
部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、
不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。

实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01%
(基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 /
告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。

基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处
引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使
识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。

⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二
根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有),
今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。

固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0;
App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889
断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿;
check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带
BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。

.cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法:
Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。

同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%,
RW_CCM 57,376 B / 87.9%,阈值 92%。
2026-10-26 12:28:14 +08:00

989 lines
40 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.
/*****************************************************************************
* 模块名称:CH395F NET 层测试(Phase 6~11)
* 模块功能:基于 BSD Socket API(单连接模式)的 NET 层测试,由 testTask
* 任务内的 net_layer_test_main() / net_layer_client_test_main() 事件
* 循环驱动,需 PC 配合。
* 包含 Phase 6(TCP Echo) / 7(TCP Client) / 8(大文件) / 9(Select) /
* 10(边界) / 11(KeepAlive) 及监听循环。
* 作者:王建锋
* 创建日期:2026-07-22
******************************************************************************/
#include <string.h>
#include "cmsis_os.h"
#include "net_socket.h"
#include "net_types.h"
#include "ch395f_test.h"
#include "ch395f_test_task.h"
#include "net_test_task.h"
#include "net_latency_test.h"
#define DBG_TAG "[NET_TEST]"
#include "dbg_log.h"
/*
* 收发缓冲区 —— 全局静态,不在任务栈上。
* s_rx_buf 用于 Phase 6(Echo)/8(大文件)/9(Select)/10(边界)/11(KeepAlive)。
* s_tx_buf 仅在 Phase 8(大文件) 和 Phase 10(边界) 中使用。
* Phase 7(TCP Client) 使用函数内局部小缓冲(64B 递增 + 回显)。
*
* 大小依据:
* - CH395F 单 Socket 接收缓冲按 1KB block 配置(net_socket.c 中
* set_recv_buf(sock, 30, 2) = 2KB),单次 net_recv 至多返回 ~2KB,
* 故 s_rx_buf 实际只需 ≥ 2KB(此处 32KB 留足余量)。
* - Phase 8(大文件) 现改为流式分块:每次仅填一个窗口(≤1024B)即发出,
* 不再一次性填入整文件,故 s_tx_buf 只需 ≥ 单窗口大小(此处 32KB 留足余量)。
* 取 32KB(32768) 同时满足两者并留余量,且为 2 的幂;相比原 64KB
* 在 STM32F4(128KB RAM)上节省一半静态空间。
*/
#define TEST_BUF_SIZE 32768
/*
* 缓冲区声明前置于全部 Phase 代码:声明必须早于任何使用点(Phase 6
* 即首次使用 s_rx_buf)。历史修复:原声明位于文件后段(Phase 8 注释块
* 之后),phase6_run 使用先于声明 → ARMCC #20 identifier undefined,
* 全部 NET 层测试配置不可编译。声明条件按各 Phase 实际用缓冲精确门控:
* s_rx_buf —— Phase 6(Echo)/9(Select) 恒用,
* Phase 8(大文件) 非 SEND 方向用,Phase 10(边界) 用;
* (Phase 11 只用 64B 探针读,2026-10-13 改专用小缓冲——
* 产品 RAM 余量放不下 32KB,勿再把 11 加回本门控)
* s_tx_buf —— Phase 8(大文件) 非 RECV 方向(填充发送窗口)及
* Phase 10(边界) 用。
* PHASE8_DIRECTION 由下方方向配置块定义(仅 ENABLE_PHASE8_TESTS 时);
* #if 中未定义标识符按 0(ECHO) 处理,方向门控结果与配置一致。
*/
/* Phase 8 方向配置:须早于下方缓冲声明,以按方向精确控制 s_tx_buf 等是否参与编译 */
#ifdef ENABLE_PHASE8_TESTS
#define PHASE8_DIR_ECHO 0
#define PHASE8_DIR_SEND 1
#define PHASE8_DIR_RECV 2
#ifndef PHASE8_FILE_SIZE
#define PHASE8_FILE_SIZE 102400 /* 100KB */
#endif
#ifndef PHASE8_DIRECTION
#define PHASE8_DIRECTION PHASE8_DIR_ECHO
#endif
#endif
#if defined(ENABLE_PHASE6_TESTS) || \
(defined(ENABLE_PHASE8_TESTS) && (PHASE8_DIRECTION != PHASE8_DIR_SEND)) || \
defined(ENABLE_PHASE9_TESTS) || defined(ENABLE_PHASE10_TESTS)
static uint8_t s_rx_buf[TEST_BUF_SIZE];
#endif
/* s_tx_buf 仅 SEND/ECHO(及 Phase 10)需要;RECV 不发送,避免未使用变量警告 */
#if (defined(ENABLE_PHASE8_TESTS) && (PHASE8_DIRECTION != PHASE8_DIR_RECV)) || \
defined(ENABLE_PHASE10_TESTS)
static uint8_t s_tx_buf[TEST_BUF_SIZE];
#endif
/* ===================================================================
* Phase 6 — NET 层 TCP Echo(基础收发)
*
* 测试目的:验证 BSD Socket API(net_socket)的单连接 TCP Echo 通路。
* Socket 0 复用监听和数据通道。PC 发任意数据,MCU 原样返回。
* 这是 Phase 7/8/9/10 的基础。
*
* 入口条件:Socket 0 处于 ESTABLISHED(TCP 连接已建立)。
* 通过:数据收发一致。
* 失败:send/recv 数量不匹配。
* =================================================================== */
#ifdef ENABLE_PHASE6_TESTS
static void phase6_run(int32_t sockfd) {
/*
* 单连接 TCP Echo:NET_MSG_DONTWAIT 非阻塞收,原样回显。
* 连接断开由 net 层自动重监听(auto_relisten),应用层不维护生命周期;
* 字节完整性由 PC 侧校验(data == test_data),固件不做每连接自检。
*/
int32_t n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, NET_MSG_DONTWAIT);
if (n > 0) {
int32_t sent = net_send(sockfd, s_rx_buf, n, 0);
if (sent != n) {
DBG_ERROR("echo: send %d != recv %d", sent, n);
}
}
}
#endif
/* ===================================================================
* Phase 7 — NET 层 TCP Client(主动连接 PC 回显服务器)
*
* 测试目的:验证 BSD Socket API 的 TCP Client 路径(net_socket + net_bind
* + net_connect + net_send + net_recv):
* 1. MCU 主动连接 PC 回显服务器(192.168.1.2:8080)
* 2. 64B 递增数据回显后,固件侧逐字节比对 + 32 位累加和校验
* 3. 关闭并重新连接(规避 TIME_WAIT:每轮换本地端口)
* 4. 连接超时负向用例(PC 服务关闭时 connect 不应成功)
*
* 入口条件:PC 运行 `python test/ch395f_socket_test.py tcp_server --port 8081`
* (TC-NET-703 需先关闭该服务)。
* TC 映射:TC-NET-701(连接 + 回显)/ TC-NET-702(关闭重连)/ TC-NET-703(连接超时)。
* 通过:回显逐字节一致且 32 位 checksum 匹配;重连全部成功;超时用例未建连。
* 失败:连接失败 / 回显不一致 / 重连中断 / 超时用例误建连。
* =================================================================== */
#ifdef ENABLE_PHASE7_TESTS
#include "net_select.h"
#ifndef PHASE7_REMOTE_IP
#define PHASE7_REMOTE_IP "192.168.1.2"
#endif
#ifndef PHASE7_REMOTE_PORT
#define PHASE7_REMOTE_PORT 8081
#endif
#ifndef PHASE7_LOCAL_PORT
#define PHASE7_LOCAL_PORT 50000
#endif
#ifndef PHASE7_LOOP_COUNT
#define PHASE7_LOOP_COUNT 3
#endif
#ifndef PHASE7_EXCH_SIZE
#define PHASE7_EXCH_SIZE 64
#endif
#ifndef PHASE7_CONN_TIMEOUT_MS
#define PHASE7_CONN_TIMEOUT_MS 10000
#endif
#ifndef PHASE7_TIMEOUT_TEST_MS
#define PHASE7_TIMEOUT_TEST_MS 30000 /* TC-NET-703 连接超时窗(指南规定 30s) */
#endif
/* 32 位累加和:足以发现丢字节/错位,比 CRC32 便宜 */
static uint32_t phase7_cksum(const uint8_t *p, uint32_t n) {
uint32_t s = 0;
while (n--) s += *p++;
return s;
}
/* 单次 TCP Client 会话:对应 TC-NET-701 主体 / TC-NET-702 单轮。
* 每轮使用不同本地端口 local_port 规避 TIME_WAIT。
* 返回 1 表示全流程通过,0 表示中途失败(已记录 TEST_CHECK)。 */
static int32_t phase7_session(uint16_t local_port)
{
int32_t sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (sockfd < 0) {
TEST_CHECK(0, "TC-NET-701 socket create");
return 0;
}
/* 绑定本地端口(不同轮用不同端口规避 TIME_WAIT) */
{
struct net_sockaddr_in laddr;
memset(&laddr, 0, sizeof(laddr));
laddr.sin_family = NET_AF_INET;
laddr.sin_port = net_htons(local_port);
if (net_bind(sockfd, (struct net_sockaddr *)&laddr, sizeof(laddr)) < 0) {
TEST_CHECK(0, "TC-NET-701 bind port %d", local_port);
net_close(sockfd);
return 0;
}
}
/* 目标地址 */
struct net_sockaddr_in raddr;
memset(&raddr, 0, sizeof(raddr));
raddr.sin_family = NET_AF_INET;
raddr.sin_port = net_htons((uint16_t)PHASE7_REMOTE_PORT);
raddr.sin_addr.s_addr = net_inet_addr(PHASE7_REMOTE_IP);
DBG_INFO("TC-NET-701 connecting %s:%d (local port %d)",
PHASE7_REMOTE_IP, PHASE7_REMOTE_PORT, local_port);
int32_t rc = net_connect(sockfd, (struct net_sockaddr *)&raddr, sizeof(raddr));
TEST_CHECK(rc == 0, "TC-NET-701 connect issued (ret=%d)", rc);
if (rc != 0) { net_close(sockfd); return 0; }
/* 等待连接建立:轮询 socket 状态(ESTABLISHED)。
* 用 net_select 触发 net_poll 并作为 100ms 节拍,状态以 net_get_sock 为准。 */
{
int32_t connected = 0;
uint32_t tick = HAL_GetTick();
while (HAL_GetTick() - tick < PHASE7_CONN_TIMEOUT_MS) {
net_sock_t *sk = net_get_sock(sockfd);
if (sk && sk->state == NET_SOCK_STATE_ESTABLISHED) {
connected = 1; break;
}
if (sk && sk->state == NET_SOCK_STATE_CLOSED) {
break; /* 连接失败 */
}
osDelay(100); /* 仅作等待/让出 CPU;连接状态由 netTask 的 net_poll 每 10ms 刷新 */
}
TEST_CHECK(connected, "TC-NET-701 connected within %dms", PHASE7_CONN_TIMEOUT_MS);
if (!connected) { net_close(sockfd); return 0; }
}
/* 数据交换:64B 递增模式 */
uint8_t tx[PHASE7_EXCH_SIZE];
uint8_t rx[PHASE7_EXCH_SIZE];
for (int32_t i = 0; i < PHASE7_EXCH_SIZE; i++) tx[i] = (uint8_t)i;
int32_t sent = net_send(sockfd, tx, PHASE7_EXCH_SIZE, 0);
TEST_CHECK(sent == PHASE7_EXCH_SIZE, "TC-NET-701 send %dB (sent=%d)", PHASE7_EXCH_SIZE, sent);
if (sent != PHASE7_EXCH_SIZE) { net_close(sockfd); return 0; }
int32_t recvd = 0;
uint32_t tick = HAL_GetTick();
while (recvd < PHASE7_EXCH_SIZE) {
int32_t n = net_recv(sockfd, rx + recvd, PHASE7_EXCH_SIZE - recvd, NET_MSG_DONTWAIT);
if (n > 0) {
recvd += n;
} else if (n == 0) {
break; /* 对端关闭 */
} else if (n < 0 && net_get_errno() == NET_ERR_WOULDBLOCK) {
if (HAL_GetTick() - tick > PHASE7_CONN_TIMEOUT_MS) break;
osDelay(10);
continue;
} else {
break; /* 其它错误 */
}
if (HAL_GetTick() - tick > PHASE7_CONN_TIMEOUT_MS) break;
}
int32_t ok = (recvd == PHASE7_EXCH_SIZE);
if (ok) {
int32_t bad = 0;
for (int32_t i = 0; i < PHASE7_EXCH_SIZE; i++) {
if (rx[i] != (uint8_t)i) { bad = 1; break; }
}
TEST_CHECK(!bad, "TC-NET-701 byte-compare %dB", PHASE7_EXCH_SIZE);
TEST_CHECK(1, "TC-NET-701 recv %dB, cksum=0x%08X", recvd, phase7_cksum(rx, PHASE7_EXCH_SIZE));
} else {
TEST_CHECK(0, "TC-NET-701 recv %dB (expect %d)", recvd, PHASE7_EXCH_SIZE);
}
net_close(sockfd);
return ok;
}
/* TC-NET-703(负向):连接超时。PC 未在 REMOTE_IP:REMOTE_PORT 提供服务时,
* connect 不应成功建立。本用例需先关闭 PC 端 tcp_server(与 TC-NET-701/702 互斥环境:
* 701/702 要求服务端在线,703 要求服务端离线,故 703 应在独立的一轮中运行)。 */
static void phase7_test_timeout(void)
{
int32_t sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (sockfd < 0) { TEST_CHECK(0, "TC-NET-703 socket create"); return; }
struct net_sockaddr_in raddr;
memset(&raddr, 0, sizeof(raddr));
raddr.sin_family = NET_AF_INET;
raddr.sin_port = net_htons((uint16_t)PHASE7_REMOTE_PORT);
raddr.sin_addr.s_addr = net_inet_addr(PHASE7_REMOTE_IP);
DBG_INFO("TC-NET-703 connecting %s:%d (expect TIMEOUT, PC server must be OFF)",
PHASE7_REMOTE_IP, PHASE7_REMOTE_PORT);
int32_t rc = net_connect(sockfd, (struct net_sockaddr *)&raddr, sizeof(raddr));
TEST_CHECK(rc == 0, "TC-NET-703 connect issued (ret=%d)", rc);
if (rc != 0) { net_close(sockfd); return; }
int32_t established = 0;
uint32_t tick = HAL_GetTick();
while (HAL_GetTick() - tick < PHASE7_TIMEOUT_TEST_MS) {
net_sock_t *sk = net_get_sock(sockfd);
if (sk && sk->state == NET_SOCK_STATE_ESTABLISHED) { established = 1; break; }
if (sk && sk->state == NET_SOCK_STATE_CLOSED) break;
osDelay(100); /* 仅作等待/让出 CPU;连接状态由 netTask 的 net_poll 每 10ms 刷新 */
}
if (established) {
TEST_CHECK(0, "TC-NET-703 connected?! stop PC server and retry");
} else {
TEST_CHECK(1, "TC-NET-703 no-connect within %dms (timeout as expected)", PHASE7_TIMEOUT_TEST_MS);
}
net_close(sockfd);
}
/* TC-NET-704:多 Socket 并发 TCP Client。
* 并发上限 = NET_MAX_SOCKETS(8) − 3 个常驻监听(FTP / 对时 / 远显)= 5;
* 本用例同时打开 Socket 0~4,各自连 PC echo server,每路独立收发
* PHASE7_EXCH_SIZE 字节回显并比对,验证 5 个 Socket 均能独立工作。
* 依赖 PC 端 tcp_server 支持并发连接(--max-conn ≥ 5)。
* (2026-10-15:原值 8 超出并发上限,socket create #5 必失败。) */
#ifndef PHASE7_MULTI_COUNT
#define PHASE7_MULTI_COUNT 5
#endif
#ifndef PHASE7_MULTI_BASE_PORT
#define PHASE7_MULTI_BASE_PORT 51000
#endif
static int32_t phase7_session_multi(void)
{
int32_t sockfd[PHASE7_MULTI_COUNT];
uint8_t tx[PHASE7_EXCH_SIZE];
uint8_t rx[PHASE7_EXCH_SIZE];
for (int32_t i = 0; i < PHASE7_EXCH_SIZE; i++) tx[i] = (uint8_t)i;
/* 1) 打开并连接所有 Socket(各自绑定不同本地端口规避 TIME_WAIT) */
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
sockfd[i] = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (sockfd[i] < 0) {
TEST_CHECK(0, "TC-NET-704 socket create #%d", i);
for (int32_t j = 0; j < i; j++) net_close(sockfd[j]);
return 0;
}
struct net_sockaddr_in laddr;
memset(&laddr, 0, sizeof(laddr));
laddr.sin_family = NET_AF_INET;
laddr.sin_port = net_htons((uint16_t)(PHASE7_MULTI_BASE_PORT + i));
if (net_bind(sockfd[i], (struct net_sockaddr *)&laddr, sizeof(laddr)) < 0) {
TEST_CHECK(0, "TC-NET-704 bind #%d port %d", i, PHASE7_MULTI_BASE_PORT + i);
net_close(sockfd[i]);
for (int32_t j = 0; j < i; j++) net_close(sockfd[j]);
return 0;
}
struct net_sockaddr_in raddr;
memset(&raddr, 0, sizeof(raddr));
raddr.sin_family = NET_AF_INET;
raddr.sin_port = net_htons((uint16_t)PHASE7_REMOTE_PORT);
raddr.sin_addr.s_addr = net_inet_addr(PHASE7_REMOTE_IP);
int32_t rc = net_connect(sockfd[i], (struct net_sockaddr *)&raddr, sizeof(raddr));
TEST_CHECK(rc == 0, "TC-NET-704 connect #%d issued (ret=%d)", i, rc);
if (rc != 0) {
net_close(sockfd[i]);
for (int32_t j = 0; j < i; j++) net_close(sockfd[j]);
return 0;
}
}
/* 2) 等待所有连接建立(或超时) */
{
uint32_t tick = HAL_GetTick();
int32_t all_up = 0;
while (HAL_GetTick() - tick < PHASE7_CONN_TIMEOUT_MS) {
all_up = 1;
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
net_sock_t *sk = net_get_sock(sockfd[i]);
if (!sk || sk->state != NET_SOCK_STATE_ESTABLISHED) { all_up = 0; break; }
}
if (all_up) break;
osDelay(100);
}
TEST_CHECK(all_up, "TC-NET-704 all %d sockets established within %dms", PHASE7_MULTI_COUNT, PHASE7_CONN_TIMEOUT_MS);
if (!all_up) {
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
net_sock_t *sk = net_get_sock(sockfd[i]);
if (sk) DBG_ERROR("TC-NET-704 socket #%d state=%d", i, sk->state);
net_close(sockfd[i]);
}
return 0;
}
}
/* 3) 每路独立收发回显 */
int32_t ok = 1;
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
int32_t sent = net_send(sockfd[i], tx, PHASE7_EXCH_SIZE, 0);
if (sent != PHASE7_EXCH_SIZE) {
TEST_CHECK(0, "TC-NET-704 socket #%d send %dB (sent=%d)", i, PHASE7_EXCH_SIZE, sent);
ok = 0;
continue;
}
int32_t recvd = 0;
uint32_t tick = HAL_GetTick();
while (recvd < PHASE7_EXCH_SIZE) {
int32_t n = net_recv(sockfd[i], rx + recvd, PHASE7_EXCH_SIZE - recvd, NET_MSG_DONTWAIT);
if (n > 0) recvd += n;
else if (n == 0) break;
else if (n < 0 && net_get_errno() == NET_ERR_WOULDBLOCK) {
if (HAL_GetTick() - tick > PHASE7_CONN_TIMEOUT_MS) break;
osDelay(10);
continue;
} else break;
if (HAL_GetTick() - tick > PHASE7_CONN_TIMEOUT_MS) break;
}
int32_t sock_ok = (recvd == PHASE7_EXCH_SIZE);
if (sock_ok) {
int32_t bad = 0;
for (int32_t k = 0; k < PHASE7_EXCH_SIZE; k++) if (rx[k] != (uint8_t)k) { bad = 1; break; }
TEST_CHECK(!bad, "TC-NET-704 socket #%d echo %dB OK", i, PHASE7_EXCH_SIZE);
} else {
TEST_CHECK(0, "TC-NET-704 socket #%d recv %dB (expect %d)", i, recvd, PHASE7_EXCH_SIZE);
ok = 0;
}
}
/* 4) 关闭所有 */
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) net_close(sockfd[i]);
return ok;
}
void net_layer_client_test_main(void)
{
DBG_INFO("=== CH395F Phase 7 Tests (NET TCP Client) ===");
DBG_INFO("--- TC-NET-701: connect + %dB echo exchange ---", PHASE7_EXCH_SIZE);
phase7_session((uint16_t)PHASE7_LOCAL_PORT);
DBG_INFO("--- TC-NET-702: close/reconnect x%d ---", PHASE7_LOOP_COUNT);
{
int32_t all_ok = 1;
for (int32_t i = 0; i < PHASE7_LOOP_COUNT; i++) {
if (!phase7_session((uint16_t)(PHASE7_LOCAL_PORT + 1 + i))) all_ok = 0;
}
TEST_CHECK(all_ok, "TC-NET-702 reconnect x%d all passed", PHASE7_LOOP_COUNT);
}
DBG_INFO("--- TC-NET-704: %d-socket concurrent client (PC %s:%d MUST be ON) ---",
PHASE7_MULTI_COUNT, PHASE7_REMOTE_IP, PHASE7_REMOTE_PORT);
phase7_session_multi();
DBG_INFO("--- TC-NET-703: connect timeout (PC %s:%d MUST be OFF) ---",
PHASE7_REMOTE_IP, PHASE7_REMOTE_PORT);
phase7_test_timeout();
TEST_REPORT("Phase 7");
}
#endif
/* ===================================================================
* Phase 8 — 大文件 / 批量传输(默认 100KB,文件收发)
*
* 测试目的:验证超过 Socket 接收缓冲(2KB)的大块数据在 net_send /
* net_recv 层经分块后的正确处理;并支持「文件发送(MCU→PC) /
* 文件接收(PC→MCU) / 回显 round-trip」三种方向。
*
* 数据模式:递增字节,绝对位置 p 处字节 = (p & 0xFF);发送端按窗口
* (offset + i) 生成,接收端按 offset 逐字节比对(自校验)。
* 整包另用 CRC32(与 PC zlib.crc32 一致)做整体校验。
*
* 缓冲策略:s_tx_buf / s_rx_buf 仅 32KB,100KB 须按窗口逐块生成/校验,
* 不整体驻留 RAM(流式分块)。
*
* 方向选择(编译宏 PHASE8_DIRECTION):
* 0 = ECHO 发 100KB 经 PC 回显,MCU 收齐校验(默认)
* 1 = SEND MCU 发 100KB,PC 落盘 rx_100k.bin 校验
* 2 = RECV PC 发 100KB(+4B CRC32 尾),MCU 收齐字节+CRC 校验
* =================================================================== */
#ifdef ENABLE_PHASE8_TESTS
/* CRC32(标准,与 Python zlib.crc32 一致;初始 0xFFFFFFFF,末异或 0xFFFFFFFF)
* 仅 RECV 方向需要:MCU 收齐后比对 PC 附在文件末尾的 4 字节 CRC32。 */
#if PHASE8_DIRECTION == PHASE8_DIR_RECV
static uint32_t phase8_crc32_step(uint32_t crc, const uint8_t *data, uint32_t len) {
for (uint32_t i = 0; i < len; i++) {
crc ^= data[i];
for (int32_t k = 0; k < 8; k++) {
crc = (crc & 1u) ? (crc >> 1) ^ 0xEDB88320u : (crc >> 1);
}
}
return crc;
}
#endif
/* 仅 SEND/ECHO 方向需要:生成递增模式发送窗口 */
#if PHASE8_DIRECTION != PHASE8_DIR_RECV
static void phase8_fill(uint8_t *buf, uint32_t size, uint32_t offset) {
for (uint32_t i = 0; i < size; i++) {
buf[i] = (uint8_t)((offset + i) & 0xFF);
}
}
#endif
/* 仅 ECHO/RECV 方向需要:逐字节校验接收数据 */
#if PHASE8_DIRECTION != PHASE8_DIR_SEND
static int32_t phase8_verify(uint8_t *buf, uint32_t size, uint32_t offset) {
for (uint32_t i = 0; i < size; i++) {
if (buf[i] != (uint8_t)((offset + i) & 0xFF)) {
DBG_ERROR("Phase8: mismatch at %lu (expect 0x%02X, got 0x%02X)",
offset + i, (uint8_t)((offset + i) & 0xFF), buf[i]);
return 0;
}
}
return 1;
}
#endif
/* 用例号:TC-NET-801(SEND 方向)与 TC-NET-802(RECV 方向)——两向由
* PHASE8_DIRECTION 编译期切换,定义见 docs/测试说明/CH395F_Test_Guide.md
* 阶段 8(本函数体从未改过名,只是一直没挂上号,2026-10-21 核对补注)。 */
static void phase8_run(int32_t sockfd) {
#if PHASE8_DIRECTION == PHASE8_DIR_RECV
/* PC 发送文件(尾附 4B CRC32),MCU 接收并逐字节 + CRC32 校验 */
DBG_INFO("Phase8[RECV]: receiving %d-byte file", PHASE8_FILE_SIZE);
uint32_t recvd = 0;
uint32_t crc = 0xFFFFFFFFu;
while (recvd < PHASE8_FILE_SIZE) {
int32_t to_read = (PHASE8_FILE_SIZE - recvd) > (TEST_BUF_SIZE - 4)
? (TEST_BUF_SIZE - 4) : (int)(PHASE8_FILE_SIZE - recvd);
int32_t n = net_recv(sockfd, s_rx_buf, to_read, 0);
if (n <= 0) {
DBG_ERROR("Phase8[RECV]: recv fail at %lu (ret=%d)", recvd, n);
return;
}
int32_t ok = phase8_verify(s_rx_buf, (uint32_t)n, recvd);
TEST_CHECK(ok, "Phase8[RECV]: verify at %lu (%d bytes)", recvd, n);
if (!ok) return;
crc = phase8_crc32_step(crc, s_rx_buf, (uint32_t)n);
recvd += (uint32_t)n;
}
/* 读取 PC 附在文件末尾的 4 字节 CRC32(小端) */
uint8_t foot[4];
uint32_t got = 0;
while (got < 4) {
int32_t n = net_recv(sockfd, foot + got, (int)(4 - got), 0);
if (n <= 0) {
DBG_ERROR("Phase8[RECV]: footer recv fail (ret=%d)", n);
return;
}
got += (uint32_t)n;
}
uint32_t crc_exp = crc ^ 0xFFFFFFFFu;
uint32_t crc_act = ((uint32_t)foot[0]) | ((uint32_t)foot[1] << 8)
| ((uint32_t)foot[2] << 16) | ((uint32_t)foot[3] << 24);
TEST_CHECK(crc_act == crc_exp,
"Phase8[RECV]: CRC32 exp=0x%08X act=0x%08X", crc_exp, crc_act);
DBG_INFO("Phase8[RECV]: done, CRC32=0x%08X", crc_exp);
#elif PHASE8_DIRECTION == PHASE8_DIR_SEND
/* MCU 发送文件,PC 落盘校验(PC 侧自行比对字节/CRC32) */
DBG_INFO("Phase8[SEND]: sending %d-byte file", PHASE8_FILE_SIZE);
uint32_t sent = 0;
while (sent < PHASE8_FILE_SIZE) {
/* 每次填充一个滑动窗口(≤ s_tx_buf 容量);net_send 内部按硬件发送 FIFO
* (NET_SEND_CHUNK_MAX=1KB,见 TRAP-NET-16) 自动拆分写入,无需此处逐 1KB 分块 */
uint32_t chunk = (PHASE8_FILE_SIZE - sent) > (uint32_t)TEST_BUF_SIZE
? (uint32_t)TEST_BUF_SIZE : (PHASE8_FILE_SIZE - sent);
phase8_fill(s_tx_buf, chunk, sent);
int32_t n = net_send(sockfd, s_tx_buf, (int)chunk, 0);
if (n <= 0) {
DBG_ERROR("Phase8[SEND]: send fail at %lu (ret=%d)", sent, n);
return;
}
sent += (uint32_t)n;
}
DBG_INFO("Phase8[SEND]: sent %d bytes", PHASE8_FILE_SIZE);
TEST_CHECK(1, "Phase8[SEND]: %d-byte file sent", PHASE8_FILE_SIZE);
#else /* PHASE8_DIR_ECHO */
/* 发 100KB 经 PC 回显(tcp_server --max-conn 1),MCU 收齐逐字节校验 */
DBG_INFO("Phase8[ECHO]: transferring %d-byte file", PHASE8_FILE_SIZE);
uint32_t sent = 0;
while (sent < PHASE8_FILE_SIZE) {
/* 同 SEND:填充滑动窗口(≤ s_tx_buf),由 net_send 内部按硬件 FIFO 自动拆分 */
uint32_t chunk = (PHASE8_FILE_SIZE - sent) > (uint32_t)TEST_BUF_SIZE
? (uint32_t)TEST_BUF_SIZE : (PHASE8_FILE_SIZE - sent);
phase8_fill(s_tx_buf, chunk, sent);
int32_t n = net_send(sockfd, s_tx_buf, (int)chunk, 0);
if (n <= 0) {
DBG_ERROR("Phase8[ECHO]: send fail at %lu (ret=%d)", sent, n);
return;
}
sent += (uint32_t)n;
}
uint32_t recvd = 0;
while (recvd < PHASE8_FILE_SIZE) {
int32_t n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, 0);
if (n <= 0) {
DBG_ERROR("Phase8[ECHO]: recv fail at %lu (ret=%d)", recvd, n);
return;
}
int32_t ok = phase8_verify(s_rx_buf, (uint32_t)n, recvd);
TEST_CHECK(ok, "Phase8[ECHO]: verify at %lu (%d bytes)", recvd, n);
if (!ok) return;
recvd += (uint32_t)n;
}
TEST_CHECK(1, "Phase8[ECHO]: %d-byte file transfer complete", PHASE8_FILE_SIZE);
#endif
/* 一次性传输完成:主动关闭数据连接,触发 net 层自动重监听。
* 否则 accept 循环会在“仍打开的同一条连接”上重复执行本 Phase——
* SEND/RECV 由 PC 端先关闭尚可终止,ECHO 方向 PC 回显客户端不会主动
* 关闭,会导致 MCU 无限重复、PC 端 tcp_file --op echo 一直挂起。 */
net_close(sockfd);
}
#endif
/* ===================================================================
* Phase 9 — Select/Poll I/O 多路复用
*
* 测试目的:验证 net_select() API 在单 Socket 模式下正确工作:
* 1. Socket 0 有数据到达时 select 返回正数
* 2. NET_FD_ISSET 正确指示就绪的 Socket
* 3. 超时(50ms 无数据)时返回 0
*
* 入口条件:Socket 0 处于 ESTABLISHED。
* 通过:select 检测到数据并成功回显。
* 失败:select 未检测到数据或回显失败。
* 用例号:TC-NET-901(单 Socket 就绪检测)与 TC-NET-902(超时返回 0),
* 定义见 docs/测试说明/CH395F_Test_Guide.md 阶段 9——本测试一直在,
* 只是从未挂上这两个号(2026-10-21 核对补注)。
* =================================================================== */
#ifdef ENABLE_PHASE9_TESTS
#include "net_select.h"
static uint32_t s_phase9_count = 0;
static void phase9_run(int32_t sockfd) {
net_fd_set_t readfds;
net_timeval_t tv;
/*
* 配置 select:只监测 Socket 0 的可读状态,超时 50ms。
* NET_FD_ZERO 清空集合,NET_FD_SET 添加 Socket 0。
* 50ms 超时确保不阻塞任务循环太久。
*/
NET_FD_ZERO(&readfds);
NET_FD_SET(sockfd, &readfds);
tv.tv_sec = 0;
tv.tv_usec = 50000;
int32_t nfds = net_select(sockfd + 1, &readfds, NULL, NULL, &tv);
if (nfds <= 0) return;
/* Socket 0 可读:非阻塞接收并回显 */
if (NET_FD_ISSET(sockfd, &readfds)) {
int32_t n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, NET_MSG_DONTWAIT);
if (n > 0) {
int32_t sent = net_send(sockfd, s_rx_buf, n, 0);
if (sent == n) {
s_phase9_count++;
TEST_CHECK(1, "Phase9: select echo #%lu (%d bytes)", s_phase9_count, n);
}
}
}
}
#endif
/* ===================================================================
* Phase 10 — 边界条件测试
*
* 测试目的:验证以下 DMA 和协议栈边界情况:
* 1. TC-NET-1001: 4095B(SPI 最大载荷 -1,单次 DMA 容纳)
* 2. TC-NET-1002: 4096B(= SPI 最大载荷,填满单次 DMA)
* 3. TC-NET-1003: 4097B(超过 SPI 最大载荷,拆 2 次 DMA)
* 4. TC-NET-1004: 零长度发送(返回 -1)
* 5. TC-NET-1005: 20 × 100B 连续快速发送
*
* 各用例通过状态机(s_phase10_stage)串行执行,每次 phase10_run 推进一个
* 步骤(发送→等待→接收→验证→下一阶段)。发送数据为递增模式字节
* (buf[i] = seed + i),接收端按相同模式验证。
*
* 入口条件:Socket 0 处于 ESTABLISHED。
* 通过:各用例自身判定。
* =================================================================== */
#ifdef ENABLE_PHASE10_TESTS
static int32_t s_phase10_stage = 0;
static uint32_t s_phase10_recv = 0;
static void phase10_fill(uint8_t *buf, uint32_t size, uint8_t seed) {
for (uint32_t i = 0; i < size; i++) {
buf[i] = (uint8_t)(seed + i);
}
}
static int32_t phase10_verify(uint8_t *buf, uint32_t size, uint8_t seed) {
for (uint32_t i = 0; i < size; i++) {
if (buf[i] != (uint8_t)(seed + i)) return 0;
}
return 1;
}
static void phase10_run(int32_t sockfd) {
/* 阶段 0 → 1:发送 4095B */
if (s_phase10_stage == 0) {
DBG_INFO("Phase10: 4095B (DMA-1)");
phase10_fill(s_tx_buf, 4095, 0x10);
int32_t sent = net_send(sockfd, s_tx_buf, 4095, 0);
TEST_CHECK(sent == 4095, "Phase10: send 4095B (sent=%d)", sent);
s_phase10_recv = 0;
s_phase10_stage = 1;
return;
}
/* 阶段 1:接收并验证 4095B 回显 */
if (s_phase10_stage == 1) {
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4095 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 4095) {
TEST_CHECK(phase10_verify(s_rx_buf, 4095, 0x10), "Phase10: verify 4095B");
s_phase10_stage = 2;
}
}
return;
}
/* 阶段 2 → 3:发送 4096B(DMA 边界精确值) */
if (s_phase10_stage == 2) {
DBG_INFO("Phase10: 4096B (DMA exact)");
phase10_fill(s_tx_buf, 4096, 0x20);
int32_t sent = net_send(sockfd, s_tx_buf, 4096, 0);
TEST_CHECK(sent == 4096, "Phase10: send 4096B (sent=%d)", sent);
s_phase10_recv = 0;
s_phase10_stage = 3;
return;
}
/* 阶段 3:接收并验证 4096B 回显 */
if (s_phase10_stage == 3) {
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4096 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 4096) {
TEST_CHECK(phase10_verify(s_rx_buf, 4096, 0x20), "Phase10: verify 4096B");
s_phase10_stage = 4;
}
}
return;
}
/* 阶段 4 → 5:发送 4097B(超过 DMA 上限,自动拆 4096+1) */
if (s_phase10_stage == 4) {
DBG_INFO("Phase10: 4097B (DMA+1, 2 SPI xacts)");
phase10_fill(s_tx_buf, 4097, 0x30);
int32_t sent = net_send(sockfd, s_tx_buf, 4097, 0);
TEST_CHECK(sent == 4097, "Phase10: send 4097B (sent=%d)", sent);
s_phase10_recv = 0;
s_phase10_stage = 5;
return;
}
/* 阶段 5:接收并验证 4097B 回显 */
if (s_phase10_stage == 5) {
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4097 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 4097) {
TEST_CHECK(phase10_verify(s_rx_buf, 4097, 0x30), "Phase10: verify 4097B");
s_phase10_stage = 6;
}
}
return;
}
/* 阶段 6:零长度发送 —— 期望 net_send 返回 -1(NET_ERR_INVAL) */
if (s_phase10_stage == 6) {
DBG_INFO("Phase10: zero-length send");
int32_t sent = net_send(sockfd, s_tx_buf, 0, 0);
TEST_CHECK(sent < 0, "Phase10: send 0B returns %d (expect <0)", sent);
s_phase10_stage = 7;
return;
}
/* 阶段 7:20 × 100B 快速发送 */
if (s_phase10_stage == 7) {
DBG_INFO("Phase10: 20x rapid sends (100B each)");
phase10_fill(s_tx_buf, 100, 0x40);
int32_t all_ok = 1;
for (int32_t i = 0; i < 20; i++) {
if (net_send(sockfd, s_tx_buf, 100, 0) != 100) {
TEST_CHECK(0, "Phase10: rapid #%d fail", i + 1);
all_ok = 0; break;
}
osDelay(1);
}
if (all_ok) {
TEST_CHECK(1, "Phase10: 20x rapid sends OK");
s_phase10_recv = 0;
s_phase10_stage = 8;
} else {
s_phase10_stage = 0;
}
return;
}
/* 阶段 8:接收 2000B 回显总和并验证 */
if (s_phase10_stage == 8) {
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(2000 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 2000) {
TEST_CHECK(1, "Phase10: all echo received");
TEST_REPORT("Phase 10");
s_phase10_stage = 0;
}
}
return;
}
}
#endif
/* ===================================================================
* Phase 11 — TCP KeepAlive 超时断开
*
* 测试目的:验证 CH395F 的 TCP KeepAlive 机制(服务器侧开关已由 net 层
* 在 CONNECT 事件后按 §5.56 转正开启,E11 2026-10-13):
* 1. 连接空闲超过 IDLE(60s)后芯片开始发送探测包
* 2. CNT × INTVL(3 × 5s)无 ACK → SINT_STAT_TIM_OUT
* 3. 本层 QUARANTINE → pump 重建 → 回 LISTEN
*
* 入口条件:Socket 处于 ESTABLISHED 且对端为**死对端**——PC 连 8080 后
* 禁用网卡(存活对端会 ACK 探测、TIMEOUT 永不触发,本测试将
* 一直等待;防火墙规则对已建立连接无效,E11 实证)。
* 通过:从连接建立到 recv 返回 0 的间隔落在 10s~120s 有界窗
* (芯片定时语义与手册公式 75s 不符,实测判死 18~31s——
* 见《网络自愈架构设计》§5.2 E11 与 phase11_run 内判据注释)。
* 用例号:TC-NET-1101(docs/测试说明/CH395F_Test_Guide.md 阶段 11 KeepAlive 触发断开)。
* =================================================================== */
#ifdef ENABLE_PHASE11_TESTS
static int32_t s_phase11_armed = 0;
static uint32_t s_phase11_start = 0;
static uint8_t s_phase11_buf[64]; /* 专用小缓冲:不占 32KB s_rx_buf(RAM 余量) */
/* 返回值:0 = 测试进行中;1 = 已报告完成(主循环据此释放 phase_active,
* 等待**新连接**再测。勿删本机制——报告后连接已死,recv 恒返 0,
* 继续 arm 会以 10ms 节奏刷出数百条假 FAIL(2026-10-13 实跑实证) */
static int phase11_run(int32_t sockfd) {
/*
* 第一次调用:记录开始时间并 arm。
* 后续调用:非阻塞 recv,若返回 0 表示连接已断开(KeepAlive 触发)。
* 计算耗时与有界窗判据比较。
*/
if (!s_phase11_armed) {
s_phase11_armed = 1;
s_phase11_start = HAL_GetTick();
DBG_INFO("Phase11: idle wait for KeepAlive timeout (60s)");
return 0;
}
int32_t n = net_recv(sockfd, s_phase11_buf, 64, NET_MSG_DONTWAIT);
if (n == 0) {
uint32_t elapsed = HAL_GetTick() - s_phase11_start;
/* 判据(E11 实测修正版):芯片定时语义与手册公式(75s)不符——
* 生产参数实测判死窗口 18~31s(产品验证 连接+30.5s、E11 T5
* 对端失联+30s、T4 短参数 连接+~10s)。本 socket 无在途 TX、
* 对端静默且已失联 → 唯一开枪源是 keepalive,故判据为
* **有界窗**:>=10s(存活空闲会话 T2 实测 90s 不被杀,10s 下限
* 区分"误杀"与"keepalive 判死")且 <=120s(判死必须发生)。
* 勿改回 >=60000——那是对着手册公式写的,会在正确固件上假 FAIL */
TEST_CHECK(elapsed >= 10000U && elapsed <= 120000U,
"Phase11: KeepAlive triggered after %lums (expect 10000..120000ms)",
elapsed);
TEST_REPORT("Phase 11");
s_phase11_armed = 0;
return 1;
}
return 0;
}
#endif
void net_layer_test_main(void)
{
int32_t sockfd = -1;
int32_t phase_active = 0;
uint32_t accept_retry = 0;
uint32_t gen = 0; /* 建立监听时的网络代次(代次变化 = 网络已重建) */
struct net_sockaddr_in client_addr;
int32_t addrlen = sizeof(client_addr);
sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (sockfd < 0) {
DBG_ERROR("Test task: net_socket failed");
return;
}
DBG_INFO("Test task: socket %d created", sockfd);
{
struct net_sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = NET_AF_INET;
addr.sin_port = net_htons(8080);
if (net_bind(sockfd, (struct net_sockaddr *)&addr, sizeof(addr)) < 0) {
DBG_ERROR("Test task: net_bind failed");
return;
}
DBG_INFO("Test task: bound to port 8080");
}
if (net_listen(sockfd) < 0) {
DBG_ERROR("Test task: net_listen failed");
return;
}
DBG_INFO("Test task: listening on 8080 (single-socket mode)");
gen = net_get_generation(); /* 记录本监听对应的网络代次 */
/* ==================== 事件驱动阶段 ====================
* Phase 6/8~11(NET 层)在 TCP 监听循环中运行,需要 PC 客户端连接。
* (Phase 7 TCP Client 为独立入口 net_layer_client_test_main,不在本循环。)
* 没有连接时循环等待;连接建立后执行对应 Phase。
* 断开后自动重新 listen。
*/
for (;;) {
if (!phase_active
#ifdef ENABLE_PHASE13_TESTS
&& net_latency_phase13_busy() == 0
#endif
#ifdef ENABLE_PHASE14_TESTS
&& net_latency_phase14_busy() == 0
#endif
) {
int32_t acc = net_accept(sockfd, (struct net_sockaddr *)&client_addr, &addrlen);
if (acc < 0) {
/* 网络重建(代次变化)→ 旧监听随芯片复位失效,重建 8080 监听 */
if (net_get_generation() != gen) {
struct net_sockaddr_in addr;
DBG_INFO("Test task: network rebuilt, relisten 8080");
net_close(sockfd);
sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
memset(&addr, 0, sizeof(addr));
addr.sin_family = NET_AF_INET;
addr.sin_port = net_htons(8080);
if (sockfd < 0 ||
net_bind(sockfd, (struct net_sockaddr *)&addr, sizeof(addr)) < 0 ||
net_listen(sockfd) < 0) {
DBG_ERROR("Test task: relisten failed");
return;
}
gen = net_get_generation();
accept_retry = 0;
continue;
}
if (accept_retry % 100 == 0) {
DBG_INFO("Test task: waiting for connection...");
}
accept_retry++;
osDelay(100);
continue;
}
accept_retry = 0;
phase_active = 1;
DBG_INFO("Test task: client accepted (fd=%d)", acc);
/* 连接建立,重置各 Phase 内部状态 */
#ifdef ENABLE_PHASE10_TESTS
s_phase10_stage = 0;
#endif
#ifdef ENABLE_PHASE11_TESTS
s_phase11_armed = 0;
#endif
}
/* 每 10ms 依次执行各已启用 Phase */
#ifdef ENABLE_PHASE6_TESTS
phase6_run(sockfd);
#endif
#ifdef ENABLE_PHASE8_TESTS
phase8_run(sockfd);
phase_active = 0; /* Phase 8 一次性完成,发完即止 */
#endif
#ifdef ENABLE_PHASE9_TESTS
phase9_run(sockfd);
#endif
#ifdef ENABLE_PHASE10_TESTS
phase10_run(sockfd);
#endif
#ifdef ENABLE_PHASE11_TESTS
if (phase11_run(sockfd)) {
phase_active = 0; /* 已报告:释放主循环去 accept 新连接(net 层已 auto_relisten) */
}
#endif
#ifdef ENABLE_PHASE13_TESTS
/* Phase 13(链路时延 L3~L7/L10,M2):独立运行,勿与其他 Phase 混选;
* 注意此处**不**重置 phase_active —— 保持 10ms 节拍(时延测量对
* 循环周期敏感,Phase 8 式 accept 重入的 100ms 退避会污染样本) */
net_latency_phase13_run(sockfd);
#endif
#ifdef ENABLE_PHASE14_TESTS
/* Phase 14(生产路径时延 L8~L11,M3):不占测试 socket —— 测量真实
* uiTask + ui_net_sink(:7003) 生产路径,由 peer --mode remo 驱动;
* 本调用只是探针窗口状态机(arm/disarm/report),同样保持 10ms 节拍 */
net_latency_phase14_run();
#endif
/*
* 单连接模式:连接断开由 net 层自动重监听(auto_relisten),
* 应用层不巡检/不重监听,回显循环持续服务新连接。
*/
osDelay(10);
}
}