# AGENTS.md 本项目是**摩天云 MQTT–MAVLink 串口桥**(`motian-mavlink-bridge`)。它连接飞控串口与摩天云 MQTT Broker:被动解析串口上的 MAVLink 帧,按平台 Base JSON 协议上报遥测,并把平台下行指令翻译成 VKFly MAVLink 帧写回串口。详细协议说明见 `README.md`,实施方案见 `doc/实施方案.md`。 ## 技术栈与运行环境 - Python **>= 3.10**(代码使用 `X | None` 等语法),Linux 部署。 - 运行依赖:`paho-mqtt>=2,<3`、`pyserial>=3.5,<4`、`pymavlink>=2.4.50,<3`(见 `pyproject.toml`)。 - MAVLink 方言 `VKFly` 由 `msg_definitions` Git 子模块的 XML 生成。 - 测试用标准库 `unittest`,不使用 pytest。 ## 目录结构 ``` motian_bridge/ cli.py 生产入口,创建 paho-mqtt / pyserial 实例并注入 Bridge bridge.py Bridge 核心:MQTT 收发、串口读写线程、Base JSON 编解码、handler 注册、参数表自动同步 config.py Config 冻结 dataclass,含校验;密钥从环境变量读取 aircraft.py Aircraft:按飞控聚合身份、最新观测、电池与参数表(ParamTable) telemetry.py MAVLink 帧解析与上报节流,内部持有一个 Aircraft(仅串口线程使用) commands.py 平台业务指令 -> VKFly MAVLink 帧的 handler dialects/v20/VKFly.py ⚠️ 生成文件,禁止手工修改 tests/ unittest 用例(依赖 FakeMqtt/FakeSerial,test_commands 复用 test_bridge 的辅助函数) tools/ local_mqtt_broker.py,本地 aMQTT 测试 Broker(非生产依赖) doc/ 平台接口规范、实施方案、qa.md(待平台澄清问题) config.example.json 配置模板 ``` ## 环境搭建与常用命令 ```bash python3 -m venv .venv .venv/bin/python -m pip install -e . # 安装(可编辑模式) cp config.example.json config.local.json # 再按需修改真实参数 # 跑全部测试(必须是 -s tests,用例间有相互导入) .venv/bin/python -m unittest discover -s tests -v # 启动桥接服务 export MOTIAN_MQTT_PASSWORD='由部署环境提供' .venv/bin/motian-mavlink-bridge --config config.local.json --debug # 本地联调:先起本地 Broker(需先 pip install "amqtt>=0.12,<0.13") .venv/bin/python tools/local_mqtt_broker.py ``` ## 架构与代码约定 - **依赖注入**:`Bridge` 只接收抽象的 MQTT client 与 `serial_factory`,具体实现由 `cli.py` 提供,便于测试用 fake 替换。保持这一模式。 - **线程边界**:串口读取线程(`_serial_loop`)独占 `Telemetry` 解析器与其内部的 `Aircraft` 状态;MQTT 回调线程负责下发。跨线程共享用 `threading.Lock` / `threading.Event`,不要引入全局可变状态。`Aircraft` 的 `latest`/`batteries`/`params` 都只允许串口线程写入,`Telemetry` 保留 `source`/`latest`/`params`/`batteries` 兼容属性。 - **配置**:所有配置项加到 `Config` 冻结 dataclass,并在 `__post_init__` 里做校验(非法值直接抛 `ValueError`)。**密钥、密码只从环境变量读取**(`mqtt_password_env`),绝不写进 JSON 或代码。 - **风格**:4 空格缩进、双引号、类型标注、模块级 `LOG = logging.getLogger(__name__)`。判断类型用 `type(x) is int` 这类精确判断(避免 `bool` 被当作 `int` 接受)。 - **扩展新平台指令**:在 `FlightCommandHandlers.install()` 里 `register_handler(data_type, handler)`,handler 返回时通过 `bridge.publish_report(data_type, data)` 上报。**未实现/未确认的指令只记日志并丢弃,绝不伪造成功 ACK**。 - 文档、注释用中文;代码标识符和 **git 提交信息用英文祈使句**(如 `Add platform command forwarding callbacks`)。 ## 协议约定与红线 - 上行原始透传固定为 `DataType=526` + 大写 HEX `Data`,发到 `/PUBLISH/`;结构化上报发到 `/RECEIVE/`。字段名是 `DataType`(**没有尾随空格**)。 - MQTT 使用 **QoS 0、不 retain**;收到的 retained 下行一律丢弃;断线不缓存、不重放。 - 下行报文必须有 `DataType`/`UAVType`/`Data`/`SystemID`/`TimeStamp`,且设备标识须与本机配置一致;`Data` 必须是非空、偶数长度 HEX,不超过 `max_command_bytes`。 - 业务指令在**尚未收到飞控 HEARTBEAT、未锁定目标 system/component 前禁止下发**(`_target()` 会抛错保护)。 - **不要修改** `motian_bridge/dialects/v20/VKFly.py`——它是生成文件。XML 变更后重新生成: ```bash .venv/bin/python -m pymavlink.tools.mavgen --lang Python --wire-protocol 2.0 \ --output motian_bridge/dialects/v20/VKFly.py \ msg_definitions/msg_definitions/VKFly.xml ``` - `Telemetry` 中 `VKFly.MAVLINK_IGNORE_CRC = 0` 是安全底线,**不要**为调试方便关闭 CRC 校验。 - 不要提交 `config.local.json`、`.venv/`、`*.pdf`(见 `.gitignore`); - 不要降低 TLS 强度(`mqtt_ca_file` 校验、证书配置等保持开启)。 ## 测试要求 - 新增行为必须补 `tests/` 下的 `unittest` 用例;提交前必须通过: `.venv/bin/python -m unittest discover -s tests -v` - 涉及串口/MQTT 的用例用 `FakeMqtt` / `FakeSerial`(见 `tests/test_bridge.py`);涉及真实帧编解码时用 `VKFly.MAVLink` 构造帧,参照 `tests/test_telemetry.py`。 - 涉及配置校验的改动,同步更新 `ValidationTests`。 ## 常见陷阱 - 串口**重连会重置 `Telemetry`**(可能换了一架飞控),旧状态必须丢弃,不要跨重连复用遥测。 - `Telemetry` 逐字节喂给 `parse_char`,每次只返回一条消息;分块读取不影响帧边界,不要改成整块喂入。 - `STATUSTEXT` 的文本需绕过生成的 Python 解码器、手动按 `char[50]` 读 UTF-8(生成代码按 ASCII 处理会丢字符),改动这里前先看 `telemetry.py` 注释。 - 除**锁定 HEARTBEAT 后主动同步一次参数表**外,桥接程序只被动接收,不主动向飞控请求消息流;缺字段时先确认飞控是否在广播对应消息(`--debug` 可看到 missing/stale 日志)。参数同步由 `request_params_on_start`(默认开)控制,超时/重试见 `param_request_timeout`、`param_request_max_attempts`;`#281` 与自动同步共用 `commands.py` 的 `request_params()`,改动需同步更新 `test_commands.ParameterSyncTests`。 - `#274` 的 `true` 表示**解锁**,不是物理离地;`DataType 258` 的 `IsFlying` 也按解锁解释。 - 部分平台参数映射尚未由飞控协议确认(`commands.py` 的 `TODO_PARAMS`)以及 `#2050 Data=true` 的“接管”语义,均保留 TODO,**不要擅自映射或发送**;需先澄清可记入 `doc/qa.md`。