Skip to content

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. 验证基线(先确认“不是没启动”)

  1. UNET 启动

powershell cd C:\Users\80428\Downloads\unet-community-3.4.4\unet-3.4.4 bin\unet samples\2-node-network.groovy

  1. 端口检查(不要只相信 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

推荐的“最小可复现”验证链路

  1. Probe powershell python scripts\unet_probe_gateway.py --scenario scenarios\demo_2node_handbook.yaml --all

  2. RX 监听(Node B) powershell python scripts\unet_subscribe_rx.py --scenario scenarios\demo_2node_handbook.yaml --node-id 2 --seconds 30 --debug

  3. TX 发送(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

  4. 跑一次 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