Files
code-test-drone/utils/tests/task_runner/log_test.py
T
keita ded17d7995 实现预定流程全链路: 任务状态机 + 编排器 + 地面站 + 真机/SITL入口
- utils/task_runner: 6态状态机(WAIT→ARM_TAKEOFF→GOTO(L2)→TRACK(L1)→APPROACH→RTL)
  + 编排器(快照采集/状态动作) + 双层日志(事件JSONL/遥测CSV) + 吊舱控制指令(0x07/0x0F/0x0E/0x08)
- pod_bridge: ground_link 地面坐标UDP注入(超龄判无效,无激光替代表4);
  set_heading_servo_enabled() 运行时L1/L2切换(切换即reset清积分防输出跳变);
  pod_locked 提为公共门控; config 新增 ground_inject 段
- virtual_pod 模拟吊舱(无电机语义) + verify_px4 增量④仅飞控验证 +
  pod_sitl_test L1 SITL全流程(飞行编排不塞进 run_bridge, 保持纯offboard闭环入口)
- 入口: task_runner_sitl(VirtualPod+虚拟目标, SITL端到端跑通约1m24s) /
  task_runner_real(真串口+UDP注入+吊舱控制)
- 地面站 ground_station: 坐标注入 + 状态可视化(Flask)
- S1标定回填(2026-10-09): beta_sign=+1 / alpha_sign=-1, 加标定回归测试守护
- 新增测试: task_runner x4 + ground_link/virtual_pod*/bridge/px4_gateway 增量; 全量 483 passed
- 文档: 预定流程(需求) + 预定流程-操作指南(操作/待办) + L1方案补S1结论与实现状态
2026-10-13 15:50:32 +08:00

124 lines
4.1 KiB
Python
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.
"""log_test.py —— 任务日志模块单元测试。
覆盖:open 建目录/写表头、事件 JSONL、遥测 CSV、telemetry_every_n 节流、
disabled 时零副作用、close 收尾。
"""
import csv
import json
import pytest
from task_runner.log import TaskLogConfig, TaskLogger, TelemetryRecord
def _cfg(tmp_path, **kw):
c = TaskLogConfig(dir=str(tmp_path), **kw)
return c
def test_open_creates_dir_and_csv_header(tmp_path):
logger = TaskLogger(_cfg(tmp_path))
logger.open()
logger.close()
assert (tmp_path / "task_events.jsonl").exists()
assert (tmp_path / "task_telemetry.csv").exists()
with open(tmp_path / "task_telemetry.csv", encoding="utf-8") as f:
header = next(csv.reader(f))
assert header[0] == "t_s"
assert "state" in header
assert "distance_to_target_m" in header
def test_log_event_writes_jsonl(tmp_path):
logger = TaskLogger(_cfg(tmp_path))
logger.open()
logger.log_event(1000, "state", from_state="wait",
to_state="arm_takeoff", reason="收到地面坐标")
logger.log_event(2000, "emergency", reason="RC 接管")
logger.close()
with open(tmp_path / "task_events.jsonl", encoding="utf-8") as f:
lines = [json.loads(x) for x in f if x.strip()]
assert len(lines) == 2
assert lines[0]["type"] == "state"
assert lines[0]["from_state"] == "wait"
assert lines[0]["to_state"] == "arm_takeoff"
assert lines[0]["ts_ms"] == 1000
assert lines[0]["t_s"] == 0.0 # 首个事件 t_s = 0
assert lines[1]["t_s"] == 1.0 # 第二个事件 t_s = 1.0s
def test_log_telemetry_writes_csv_rows(tmp_path):
logger = TaskLogger(_cfg(tmp_path))
logger.open()
rec = TelemetryRecord(state="goto", pos_valid=True, latitude_deg=47.39,
distance_to_target_m=33.5, mode="VELOCITY_HEADING",
yaw_rate_dps=-30.0, vx_body_mps=1.7)
logger.log_telemetry(1000, rec)
logger.log_telemetry(1100, rec)
logger.close()
with open(tmp_path / "task_telemetry.csv", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
assert len(rows) == 2
assert rows[0]["state"] == "goto"
assert rows[0]["pos_valid"] == "True"
assert rows[0]["distance_to_target_m"] == "33.5"
assert rows[0]["t_s"] == "0.0"
assert rows[1]["t_s"] == "0.1"
def test_telemetry_throttled_by_every_n(tmp_path):
logger = TaskLogger(_cfg(tmp_path, telemetry_every_n=5))
logger.open()
rec = TelemetryRecord(state="goto")
for i in range(12):
logger.log_telemetry(1000 + i * 100, rec)
logger.close()
with open(tmp_path / "task_telemetry.csv", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
assert len(rows) == 2 # 第 5、10 次各记一次
def test_disabled_logs_nothing(tmp_path):
logger = TaskLogger(_cfg(tmp_path, enabled=False))
logger.open()
logger.log_event(1000, "state", from_state="wait", to_state="arm_takeoff")
logger.log_telemetry(1000, TelemetryRecord(state="goto"))
logger.close()
# 未 open(disabled)→ 不建目录、不写文件
assert not (tmp_path / "task_events.jsonl").exists()
assert not (tmp_path / "task_telemetry.csv").exists()
def test_log_before_open_is_noop(tmp_path):
logger = TaskLogger(_cfg(tmp_path))
logger.log_event(1000, "state", from_state="wait", to_state="arm_takeoff")
logger.log_telemetry(1000, TelemetryRecord(state="goto"))
logger.close()
assert not (tmp_path / "task_events.jsonl").exists()
def test_append_keeps_existing_rows(tmp_path):
"""重复 open(追加模式)不清空既有内容,表头不重复写。"""
logger = TaskLogger(_cfg(tmp_path))
logger.open()
logger.log_telemetry(1000, TelemetryRecord(state="wait"))
logger.close()
logger2 = TaskLogger(_cfg(tmp_path))
logger2.open()
logger2.log_telemetry(2000, TelemetryRecord(state="goto"))
logger2.close()
with open(tmp_path / "task_telemetry.csv", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
assert len(rows) == 2
assert rows[0]["state"] == "wait"
assert rows[1]["state"] == "goto"