Skip to content

fix: 完善遥测数据身份、功耗解析及采集生命周期与启动配置 - #3

Merged
massif-01 merged 1 commit into
RMinte:mainfrom
massif-01:bugfix/ws-data-reliability
Oct 10, 2026
Merged

massif-01 merged 1 commit into
RMinte:mainfrom
massif-01:bugfix/ws-data-reliability

Conversation

@massif-01

@massif-01 massif-01 commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

解决的问题与最终行为

这个 PR 完成 WS-DATA-v1.0 的数据可靠性修复及部署前入口收尾。目标是让消费者能区分新输入与重复缓存、正确读取供电功耗,并让采集启动/退出及 CLI 配置真正协作。

以前,同一份缓存被 HTTP 读取或 Socket.IO 重发时,服务会更新顶层 timestamp,客户端无法判断是否有新测量;功耗正则会把 RAM/SWAP 容量误收进 power,又漏掉仓库样例中带 mW 的供电读数。此次还补齐了采集生命周期锁与 CLI 退出信号、显式间隔类型、线程启动失败管道清理和环境配置入口的衔接。

最终行为:重复发送不增加测量序号;逐字相同的新输入仍计作新帧;缺失功耗/TJ不填0或续用旧字段;原正常 temperature.tj 路径、Socket.IO tegrastats_update 事件和默认频率保持。

本 PR 只提交代码、文档和离线证据。未连接 Orin/ESP32、未部署或重启真实服务、未修改天山OS/TJ绑定/风扇控制,也没有创建热模型。 合并不等于设备验证完成。

1. 数据身份、新鲜度与自洽快照

在 /api/status、CPU/内存/温度/功耗分接口及 tegrastats_update 原 JSON 顶层新增 telemetry_meta,不引入 data 外壳、不移动既有字段。

字段 合同
schema_version 整数1,新增元数据版本
stream_id 每次采集生命周期生成的随机UUID;不是WS sid或主机boot_id
sample_seq 无帧0、首帧1;只在接受新的有效输入行时递增
received_at 完整输出行被采集线程读到的UTC时间,无样本为null
sample_age_ms 由同主机单调时钟计算的接收年龄,未知为null
fresh 采集状态可用、有有效样本且年龄小于界限
collector_state starting / running / stopped / error
freshness_timeout_ms max(5000, 3 × 实际采集间隔ms)
time_basis collector_receive
  • 同帧的多次HTTP读取/WS发送保持stream、seq、received_at;发送时间与年龄可以变化。
  • 两条内容完全相同的有效行仍增加序号。空行、日志噪声、完全损坏行不发布,不续龄。
  • 阈值相等即过期;UTC前跳/后跳不参与年龄计算。EOF、异常退出和显式停止及时反映不可用状态。
  • 重启清空旧缓存、换stream并从seq=0开始;旧线程迟到结果/退出不能污染新生命周期。WS重连不生成采集重启。
  • 数值、身份和接收锚点在同一锁下发布/深复制;返回对象被修改不反向污染缓存,序列化/网络发送在锁外。
  • 无首帧时数据接口仍503,错误对象可带meta;health的healthy只说明HTTP响应,不代表采集健康。
  • Parser公开状态方法保留原数据结构、数值时间戳和无数据空对象。服务顶层 timestamp 仍是UTC响应/发送时间字符串。

fresh=true不保证每个字段存在、传感器准确或上游没有积压。独立HTTP请求不天然组成跨接口原子快照。接收年龄不是硬件内部采样年龄。

2. 功耗解析:完整供电名、完整数值令牌

功耗仅接受明确的 VDD... / VIN_... 供电名:两侧同时带 mW,或同时无单位、沿用现有mW语义。输出继续使用完整原名的小写,不迁移别名、不计算整机总功耗。

仓库固定样例的修复结果(不是本轮实机采样):

{
  "vdd_gpu_soc": {"current": 2468, "average": 2468, "unit": "mW"},
  "vdd_cpu_cv": {"current": 246, "average": 246, "unit": "mW"},
  "vin_sys_5v0": {"current": 3383, "average": 3383, "unit": "mW"}
}

RAM/SWAP/lfb/频率/百分比/假供电名不会进入power。明确0有效;负数、小数残片、混合单位、mA/W/MB、尾缀、NC、残缺分隔符等被拒绝,不部分匹配成合法整数。同名冲突项不可用,相同重复只输出一次;坏rail不丢弃该帧真实TJ和其他合法rail。新帧缺项不从旧帧补用。功耗诊断最多每分钟五条通用消息,不保存原始输入行。

3. 采集生命周期与退出清理

修复真实CLI→server→parser路径的两类入口问题,并保留生命周期隔离:

  • 退出信号与锁的协作:原CLI信号处理器直接同步stop,会在启动/关闭持锁时重入不可重入锁。现在只登记退出意图;启动和清理期间延后处理,运行中唤醒阻塞服务循环,由正常finally完成清理。没有机械改为RLock或忽略SIGTERM。退出意图防止意外再次启动,stop保留error状态。
  • 读者未启动时的管道归属:Popen成功但线程构造/Thread.start失败时,未接管stdout的失败路径明确关闭管道、回收对应进程并释放失败引用。读者入口和失败路径以同生命周期的接管/取消状态协作,取消后的迟到入口不能再读取旧管道。
  • 已由读者接管的流仍交给读者finally关闭,主线程不抢关正在读取的流;不能仅凭某一瞬间is_alive=false判断从未启动。
  • 进程或管道清理异常有有限诊断,保留最初启动异常;未完成关闭的对象保留供正常stop重试,不靠GC、解释器退出或Thread私有字段清理。
  • 未增加自动重启、线程池、历史消息队列或通用进程监管框架。

4. CLI配置贯通:优先级、单位、布尔和预览

有意的行为变化:以前CLI忽略的环境变量,现在真正参与启动。部署前必须核对服务收到的环境与命令行,不能只看默认值。

每项统一为:显式CLI > 现有主环境变量 > 既有旧别名 > 默认。先选定生效来源再校验:合法显式参数可以覆盖无效低优先级环境;非法主值不会偷偷回退到旧别名。Config()不隐式读取环境;Config.from_env()、CLI run和config预览共享解析规则。

字段 主变量 保留的旧别名 默认/单位
host TEGRASTATS_API_HOST TEGRASTATS_HOST 10.10.99.98
port TEGRASTATS_API_PORT TEGRASTATS_PORT 58090;1..65535整数
debug TEGRASTATS_API_DEBUG TEGRASTATS_DEBUG false
update_interval TEGRASTATS_API_UPDATE_INTERVAL TEGRASTATS_UPDATE_INTERVAL 1.0秒;有限正数
max_connections TEGRASTATS_API_MAX_CONNECTIONS TEGRASTATS_MAX_CONNECTIONS 10;正整数
tegrastats_interval TEGRASTATS_API_TEGRASTATS_INTERVAL TEGRASTATS_INTERVAL 1000毫秒;正整数
cors_origins TEGRASTATS_API_CORS_ORIGINS TEGRASTATS_CORS_ORIGINS *
log_level TEGRASTATS_API_LOG_LEVEL TEGRASTATS_LOG_LEVEL INFO
log_file TEGRASTATS_API_LOG_FILE TEGRASTATS_LOG_FILE app.log,仍未接线
allow_unsafe_werkzeug TEGRASTATS_API_ALLOW_UNSAFE_WERKZEUG TEGRASTATS_ALLOW_UNSAFE_WERKZEUG true

显式 --tegrastats-interval 1000 / 1000.0 均归一化为整数1000,最终命令是 tegrastats --interval 1000,阈值5000ms;3000得到命令3000、阈值9000ms。纠正原“秒”帮助说明,没有将原透传数值乘1000;小数毫秒、零/负数/nan/inf继续拒绝。--update-interval仍为秒,合法小数秒保留。

环境布尔明确支持true/false、1/0、yes/no、on/off,忽略大小写和外侧空白,拼错/空值报错。--debug保持兼容,增加--no-debug;未传保留环境值,同时传时最后一个生效,来源准确显示。

run启动摘要与config预览使用同一配置和共享过期界限规则,显示监听地址/端口、采集ms、推送s、阈值、debug及关键来源。预览支持相同相关CLI选项,明确“本次环境与参数对应的启动配置;未查询运行中的服务”,不构造服务、采集或监听。help/version不受坏环境阻断。

选中非法配置在服务构造前退出,错误说明字段、来源和类型要求,不回显环境原值。监听参数、采集命令、生产推送sleep、连接限制、HTTP CORS/Socket.IO和实际日志级别均核对到消费者。log_file没有文件日志消费者,摘要明确未接线;本PR不新增日志系统、.env自动加载或热更新配置平台。

5. 兼容性与明确保留的限制

正常TJ仍来自实际解析的 temperature.tj,没有改用GPU、平均温度或最大值。Socket.IO事件、连接/心跳方式、默认推送/采集节奏保持,未改成裸WebSocket;模型监控业务及模型状态/日志接口未重写。

旧客户端可以忽略meta继续读取正常TJ。但本PR仍按原方式发送最近缓存,不采取过期停发、清空TJ或强制断开,因此旧客户端可能继续把缓存当更新,不能宣称端到端新鲜度已经修好。

指定TianshanOS提交的真实C字段读取和JSONPath仅作为主机只读参考。原CPU/内存结构、功耗别名不匹配与缺失零填充问题仍列台账,不扩展协议迁移。内置监控上报CPU与运行配置选择的TJ绑定是不同路径,本PR不改变现场绑定。

6. 验证:实际执行的范围

本地主机macOS arm64 / Python3.12.3;直接依赖遵循requirements.txt固定版本,完整锁文件随附。原56项+新增54项,共110项离线测试通过,并从最终暂存内容导出的干净目录安装wheel后再次通过,不依赖未提交输入。

覆盖包括:

  • 正常/缺项/畸形/噪声功耗与温度输入、同内容新帧、重复缓存、过期边界、UTC变化、EOF/停止/异常、重启迟到。
  • 三个并发读者与2000帧更新,身份/数值同帧;修改返回对象不污染缓存;慢发送不阻塞采集。
  • 生产Flask路由与Socket.IO内存客户端、多客户端/重连、模型状态/日志正常合同。
  • 启动前/创建中/运行中/关闭中/重复退出/启动失败后退出/等待超时,真实CLI/server/parser路径;SIGTERM只发送给自建测试子进程。
  • 线程构造/启动失败使用受控Python假输出子进程和真实stdout管道,保留外部强引用、不调用GC;已接管读者不被抢关;清理异常不覆盖原异常。
  • 全十项主变量/旧别名、来源优先级、坏低优先级覆盖、选中非法值、布尔关闭/双开关、真实消费者、预览零副作用、重复调用、help/version。
  • 真实Socket.IO Packet编码、客户端核心C读取/JSONPath、有界资源、wheel与7份源码/标记逐字匹配及实际导入路径。

安全重跑(先在隔离环境安装所需依赖,并准备主机C编译器):

python -m pytest -q tests/offline
python scripts/verify_ws_data_offline.py --output /tmp/ws-data-review-results

不要收集整个tests目录:旧live脚本包含固定局域网连接。离线套件禁止网络/监听、真实tegrastats和真实ModelMonitor;明确例外只有测试自己创建/回收的受控Python子进程及主机C编译/读取程序。未执行systemctl/journalctl或安装脚本。已有39条UTC API弃用警告不是本次行为失败;夹具设置错误及其修正保留在证据包。

资源/容量结果只属于本地主机:固定样例约从780B到1.1KB。自动化重组缓冲8192B(含终止符),约8160B可重组,约8220B按旧规则会截断;内置4096B路径没有同样重组,不能保证超单块设备接收。近容量C读取通过也不等于ESP32真实传输通过。有限帧内存/耗时结果不是Orin长期运行证明。

Python3.8仅检查语法,未声称该版本运行通过。原CI的build任务按github.ref == refs/heads/main或版本tag判断,PR ref不满足。本PR远端CI / build实际已完成且结论为SKIPPED;这不是远端110项测试通过。本PR未重写CI。

7. 证据与提交布局

生产代码仅改parser/server/cli/config;其余差异是离线测试、必要基线反例、客户端只读C引用/stub、文档、wheel和最终证据。保存基线源码用于走真实旧函数复现,不用一套理想化解析器代替;C引用注明提交/来源/SHA并保留许可证头,没有天山OS构建产物。

候选仍为未发布的1.0.1,必须按wheel SHA区分:

b5f3520d1509a43601eed56fd00a81607581d3f41748d7a7e5c5352c0fd9677c

报告/包保存的是发布前本地验收快照,其中“未提交推送”描述该开发阶段;本PR进行Git发布,不改写历史证据、不代表已部署。完整历史旧包/临时输出留在本地,仓库直接保留重跑所需输入与最终摘要,详细记录通过证据包交付。

8. 部署与回退边界

独立复审通过后由用户在受控待机窗口手动部署。本PR不执行部署,也不发布Release或自动提升版本。

部署前备份实际旧安装包/源目录与包元数据、启动命令和服务环境,确定唯一回退对象;检查此前被忽略而现在生效的环境。安装同版本候选时显式强制重装、不顺带升级依赖;在服务真实解释器/工作目录/导入环境核对模块路径和SHA,从真实启动摘要确认最终配置,不能仅看1.0.1或终端config预览。

部署后待确认:实际TJ/WS恢复、采集身份/序号/年龄/状态连续性、合法功耗、实际最大报文与分片、基本资源及旧客户端供温连续性。持续无温度、启动失败或配置意外变化时按备份恢复旧包和原环境/命令;不在高负载制造中断、不随意关闭风扇。热模型与设备验证另行进行。

@massif-01
massif-01 merged commit ca690bd into RMinte:main Oct 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant