Appearance
Demo-1 踩坑与排查纪要(UNET 3.4.4 + fjagepy, Windows)
本文是过程性记录:把从 Demo-0 推进到 Demo-1(UNET Gateway Tx/Rx + 真实 traces 落盘)过程中遇到的典型坑、现象、定位思路与对应修复沉淀下来,便于答辩复现与后续扩展。
适用环境(复现基线):
- UNET: unet-community-3.4.4 / unet-3.4.4
- Windows: PowerShell
- Sample: samples/2-node-network.groovy
- Node A: tcp://localhost:1101
- Node B: tcp://localhost:1102
- Python: >=3.10
- Gateway Python 侧:fjagepy 2.x(arlpy 可能安装但不一定能用 gateway)
0. 验证基线(先确认“不是没启动”)
- UNET 启动
powershell cd C:\Users\80428\Downloads\unet-community-3.4.4\unet-3.4.4 bin\unet samples\2-node-network.groovy
- 端口检查(不要只相信 UNET 控制台输出)
powershell Test-NetConnection localhost -Port 1101 Test-NetConnection localhost -Port 1102
只要 TcpTestSucceeded : True,就说明“端口可达”。后续若 Python probe 还报 not connected,多半是 Python 侧后端/协议栈问题。
1) 坑:装了 arlpy 但它不具备 gateway 能力,导致 probe 误判为“连不上”
现象
- python scripts\unet_probe_gateway.py ... 输出 Backend : arlpy,但 Status : error,并提示 not connected。
- 同时你用 Test-NetConnection 已经确认 1101/1102 端口是通的。
根因
我们的后端探测顺序是 rlpy -> fjagepy -> logonly。
但是在一些环境里:
- rlpy 只是“离线工具集”(例如日志解析)
- 并不包含 rlpy.unet.Gateway / rlpy.unet.gateway 这类 fjage gateway 绑定
于是出现:明明 fjagepy 能连,但脚本先选了 arlpy,然后 arlpy 又不能连。
解决
- 后端探测改成:只有当 rlpy.unet 里确实存在 Gateway/gateway 时才选 arlpy,否则自动降级选 fjagepy。
验证
powershell python scripts\unet_probe_gateway.py --scenario scenarios\demo_2node_handbook.yaml --all 预期:Backend : fjagepy 且 Status : connected。
2) 坑:fjagepy 2.x 没有 jagepy.unet 模块,导致 TxFrameReq not available
现象
运行:
powershell python scripts\unet_send_ping_or_tx.py --scenario scenarios\demo_2node_handbook.yaml --node-id 1 --to 2 --payload-bytes 32
报错:
- TxFrameReq not available in fjagepy
根因
fjagepy 2.x 作为 fjage 的 JSON gateway 客户端:
- 能连接、收发 fjage Message
- 但不自带 UNET 的“消息类绑定包”(即你熟悉的 TxFrameReq/RxFrameNtf 等)
解决
改用 fjagepy 内置的 MessageClass('org.arl.unet.DatagramReq') 来发 datagram。
同时注意:
- fjage JSON 传输不能直接序列化 Python ytes;需要 list[int]
验证
使用 --json 输出应能看到:
esult.request: DatagramReq
esult.response.perf: AGREE
3) 坑:--to 2 很多时候不是 UNET 的真实地址(address 是动态分配的)
现象
- 发送端返回 AGREE,但接收端就是收不到对应的 payload。
- 或者只能收到 RxFrameStartNtf(表示“检测到信号开始”)但没有 payload-bearing 的 DatagramNtf。
根因
2-node-network.groovy 的节点 address 通常是 动态分配。 例如我们实际观测到:
- A address 可能是 232
- B address 可能是 31
因此:
- “场景 node_id=2” != “UNET runtime address=2”
解决
- 当使用 --scenario 时,把 --to 视为“场景 node_id”,并通过 ARP 在运行时解析:
- 对 rp agent 发送 org.arl.unet.addr.AddressResolutionReq(name='B')
- 得到 AddressResolutionRsp.address
脚本会在 --json 输出里给出:
- dst_unet_addr
runner 侧也会构建 ddr_by_node_id,发送时用 runtime address,但 header 里保持 dst=逻辑 node_id(保证指标稳定)。
验证
powershell python scripts\unet_send_ping_or_tx.py --scenario scenarios\demo_2node_handbook.yaml --node-id 1 --to 2 --payload-bytes 32 --json 预期:输出包含 dst_unet_addr,且不等于 2 也完全正常。
4) 坑:MTU 限制(header 24 bytes 后,user payload 常见只能到 32 bytes)
现象
- Tx 有时会返回 REFUSE Data length exceeds frame capacity
- 或者 Tx 返回 AGREE 但接收侧没有 payload-bearing 通知(被底层丢弃/截断)
根因
UNET sample 常见 DatagramParam.MTU = 56。 而我们的 DT header 固定 24 bytes:
- 最大 user payload = 56 - 24 = 32 bytes
解决
- runner 在 gateway 模式自动检测 MTU 并裁剪 payload_bytes 到 MTU-24。
- 检测失败时用保守默认 32。
验证
发送脚本侧:建议 --payload-bytes <= 32。
5) 坑:订阅不到消息(services 为空导致 best_effort_subscribe 失效)
现象
- unet_subscribe_rx.py 连接成功,但 30 秒内没有任何 x 输出。
根因
fjagepy 2.x 的 gateway services() 常常返回空列表(或该 API 不暴露 services)。 而我们早期的 est_effort_subscribe() 依赖 services 才会 subscribe。
解决
当 services 为空时:
- 自动对 gw.agents() 全量订阅:gw.subscribe(gw.topic(agent))
这不写死任何 agent 名称,仍符合“不硬编码 UNET 名称”的约束。
6) 坑:timeout 单位不一致(秒 vs 毫秒),导致 RX 循环收不到
现象
- RX loop 看起来在跑,但实际上 gw.receive(timeout=...) 参数类型/单位不匹配。
- 典型:project 传 float seconds,但 fjagepy 期望 int ms。
解决
eceive_message() 改成:
- 先尝试 seconds-style
- 再尝试 ms-style(把秒换成 int(ms))
7) 坑:RX 线程和 TX request() 共享一个 Gateway,会“抢响应”
现象
- 发送偶发失败 / 超时
- 日志里很难解释:明明发出 request(),却拿不到 response
根因
fjagepy 的 Gateway 本质上是一个消息队列:
- 如果 RX 线程无差别 eceive(),可能把 TX request 的 response 也取走
解决
- RX 使用“独立 Gateway 连接”专门接收通知
- TX request 走原 gateway,互不干扰
这是保证“可复现/可稳定”的关键。
8) 坑:真实链路时延较大,run 结束太快会被判为丢包
现象
unner --steps 10 --dt 1 时,traces 里常见只有第 1 包 received=True,其余都 False。
- 但并不是链路真的 80% 丢包,而是“包还在路上”。
根因
RealTimePlatform + half duplex modem + routing/transport 堆栈下:
- 端到端延迟可能达到数秒到十几秒(我们观测到 ~3s、~10s 都出现过)
而 runner 结束后立即 finalize,就会把“还没到达的包”全部判为 loss。
解决
runner gateway 模式增加 --rx-grace:
- 在最后一步结束后额外等待 N 秒继续接收
建议答辩默认:
- --rx-grace 15 起步,必要时调到 20-30。
9) 坑:接收侧 payload 可能是 signed byte list(-128..127),解析会报错
现象
- ytes(list) 报 ValueError: bytes must be in range(0, 256)
根因
部分 UNET/fjage 组合会把 Java byte[] decode 成 Python list[int],其中元素可能是 signed(负数)。
解决
RX parse 做兼容:
- 允许 [-128..255]
- 转换时统一 = item & 0xFF
推荐的“最小可复现”验证链路
Probe
powershell python scripts\unet_probe_gateway.py --scenario scenarios\demo_2node_handbook.yaml --allRX 监听(Node B)
powershell python scripts\unet_subscribe_rx.py --scenario scenarios\demo_2node_handbook.yaml --node-id 2 --seconds 30 --debugTX 发送(Node A)
powershell python scripts\unet_send_ping_or_tx.py --scenario scenarios\demo_2node_handbook.yaml --node-id 1 --to 2 --payload-bytes 32 --json跑一次 gateway mode(建议带 rx-grace)
powershell ='src' python -m unet_dt.orchestrator.runner --scenario scenarios/demo_2node_handbook.yaml --mode gateway --strategy round_robin --steps 10 --dt 1 --seed 42 --rx-grace 15
预期:
uns/<run_id>/traces.csv 至少有 1 行 eceived=True
- 如果 eceived=True 太少:优先加大 --rx-grace 或加大 --steps