0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(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%。
159 lines
5.5 KiB
Python
159 lines
5.5 KiB
Python
# relisten 沉降段原语化受阻:芯片级复用时序台架主机端(与 test/board/ch395f_reuse_bench.c 共用变体表)
|
||
# 用法: python ch395f_reuse_host.py [--host IP] [--cycles N] [--mode legacy|e4]
|
||
# legacy: 按 idx 轮转连接 5000+idx 端口,记录收发结果与时刻。
|
||
# e4: E4 sock_reset A/B/C 矩阵配套(2026-10-13):每轮两次连接——
|
||
# ①污染:连上发 3000B 后 RST(SO_LINGER 0,TRAP-NET-18 中止形态);
|
||
# ②载荷:立即重连发 4096B 图案(pat[i]=(seed+i)&0xFF, seed=(rg*97+13)&0xFF)后 FIN。
|
||
# 端口 5100+rg,rg=0..(3*rounds-1),变体顺序 A/B/C(与固件 bench 表一致)。
|
||
import argparse
|
||
import socket
|
||
import struct
|
||
import sys
|
||
import time
|
||
|
||
# {idx: (tx_len, close_mode)} 与固件表一致
|
||
VARS = {
|
||
0: (100, 0), # 0ms control
|
||
1: (100, 0), # 300ms control
|
||
2: (2000, 0), # 2KB control
|
||
3: (100, 0), # 0ms + ghost-wait fix
|
||
4: (100, 0), # 300ms + ghost-wait fix
|
||
5: (2000, 0), # 2KB + ghost-wait fix
|
||
}
|
||
|
||
|
||
def ts():
|
||
return time.strftime("%H:%M:%S") + ".%03d" % (int(time.time() * 1000) % 1000)
|
||
|
||
|
||
def run_round(host, idx, tx_len, close_mode):
|
||
port = 5000 + idx
|
||
# 等端口监听(芯片还没 open 时连接被拒,重试)
|
||
s = None
|
||
t0 = time.time()
|
||
while time.time() - t0 < 12.0:
|
||
try:
|
||
s = socket.create_connection((host, port), timeout=5)
|
||
break
|
||
except OSError:
|
||
time.sleep(0.2)
|
||
if s is None:
|
||
print("%s r%u CONNECT timeout" % (ts(), idx))
|
||
return False
|
||
|
||
ok = True
|
||
try:
|
||
s.settimeout(5)
|
||
got = 0
|
||
while got < tx_len:
|
||
chunk = s.recv(min(1024, tx_len - got))
|
||
if not chunk:
|
||
break
|
||
got += len(chunk)
|
||
note = ""
|
||
if got != tx_len:
|
||
note = " <<<< SWALLOWED (want %d)" % tx_len
|
||
ok = False
|
||
if close_mode == 1:
|
||
# 等芯片 FIN(mode1 芯片先关)
|
||
try:
|
||
fin = s.recv(1)
|
||
if fin:
|
||
note += " unexpected data after tx: %r" % fin
|
||
except socket.timeout:
|
||
note += " <<<< no FIN from chip (mode1)"
|
||
ok = False
|
||
print("%s r%u connected got=%d/%d%s" % (ts(), idx, got, tx_len, note))
|
||
except socket.timeout:
|
||
print("%s r%u recv timeout <<<< STALL" % (ts(), idx))
|
||
ok = False
|
||
except OSError as e:
|
||
print("%s r%u sock error %r" % (ts(), idx, e))
|
||
ok = False
|
||
finally:
|
||
s.close() # 主机关闭(mode0/2 主机先关)
|
||
return ok
|
||
|
||
|
||
def connect_retry(host, port, budget_s):
|
||
"""connect with retry until budget_s elapses; None on give-up"""
|
||
t0 = time.time()
|
||
while time.time() - t0 < budget_s:
|
||
try:
|
||
return socket.create_connection((host, port), timeout=5)
|
||
except OSError:
|
||
time.sleep(0.2)
|
||
return None
|
||
|
||
|
||
def e4_round(host, rg, rounds_per_variant, first_budget):
|
||
"""one E4 round: contaminate (send 3000B then RST) + payload (4096B pattern then FIN)"""
|
||
port = 5100 + rg
|
||
seed = (rg * 97 + 13) & 0xFF
|
||
budget = first_budget if rg == 0 else 30
|
||
s = connect_retry(host, port, budget)
|
||
if s is None:
|
||
print("%s E4 r%2d port%5d <<<< contam CONNECT timeout" % (ts(), rg, port))
|
||
return False
|
||
try:
|
||
s.sendall(b"\x33" * 3000)
|
||
except OSError as e:
|
||
print("%s E4 r%2d contam send err %r" % (ts(), rg, e))
|
||
try:
|
||
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0))
|
||
except OSError:
|
||
pass
|
||
s.close() # RST: abort mid-session like ftp_abort_probe (TRAP-NET-18 form)
|
||
time.sleep(0.05)
|
||
|
||
s = connect_retry(host, port, 20)
|
||
if s is None:
|
||
print("%s E4 r%2d port%5d <<<< payload CONNECT timeout" % (ts(), rg, port))
|
||
return False
|
||
payload = bytes(((seed + i) & 0xFF) for i in range(4096))
|
||
try:
|
||
s.sendall(payload)
|
||
except OSError as e:
|
||
print("%s E4 r%2d payload send err %r" % (ts(), rg, e))
|
||
s.close()
|
||
return False
|
||
s.close() # FIN: full payload delivered, chip drains
|
||
print("%s E4 r%2d var=%s port%5d contam+payload sent (seed=%02X)"
|
||
% (ts(), rg, "ABC"[rg // rounds_per_variant], port, seed))
|
||
return True
|
||
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--host", default="192.168.1.100")
|
||
ap.add_argument("--cycles", type=int, default=3)
|
||
ap.add_argument("--mode", choices=["legacy", "e4"], default="legacy")
|
||
ap.add_argument("--rounds", type=int, default=10, help="e4: rounds per variant")
|
||
ap.add_argument("--first-budget", type=int, default=300,
|
||
help="e4: connect budget for the first round (bench runs E2/E3 first)")
|
||
args = ap.parse_args()
|
||
|
||
if args.mode == "e4":
|
||
fails = []
|
||
total = 3 * args.rounds
|
||
for rg in range(total):
|
||
if not e4_round(args.host, rg, args.rounds, args.first_budget):
|
||
fails.append(rg)
|
||
print("E4 RESULT: %d/%d host-side ok, failures %s" % (total - len(fails), total, fails))
|
||
return 1 if fails else 0
|
||
|
||
fails = []
|
||
for c in range(args.cycles):
|
||
for idx in sorted(VARS):
|
||
tx_len, mode = VARS[idx]
|
||
print("%s === cycle%u r%u (tx=%d mode=%d) ===" % (ts(), c, idx, tx_len, mode))
|
||
if not run_round(args.host, idx, tx_len, mode):
|
||
fails.append((c, idx))
|
||
time.sleep(0.3)
|
||
print("RESULT: %d failures %s" % (len(fails), fails))
|
||
return 1 if fails else 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|