# autel-cloud-api-sdk **Repository Path**: alpeai/autel-cloud-api-sdk ## Basic Information - **Project Name**: autel-cloud-api-sdk - **Description**: Autel Cloud API SDK 是将 Autel 上云协议的常量与数据结构以纯 Java 类型(record + enum)表达的类型安全 POJO 库,作为第三方平台对接 Autel 机场与遥控器的协议定义层 + 航线工具套件单一真相源,消除协议定义的重复维护。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-08-17 - **Last Updated**: 2026-08-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Autel Cloud API SDK ## 简介 Autel Cloud API SDK 是 Autel 上云协议的 Java 类型安全 POJO 库,覆盖机场上云(Dock ↔ 平台,MQTT 5 通道 services/drc/events/requests/status)与遥控器上云(Controller ↔ 平台,HTTP + WebSocket)场景,包含协议定义层(topic/method/envelope/error/command/model/telemetry)与航线工具套件(wayline Builder/Template/Codec),共 83 个 method、15 个 property、13 款设备型号。 **为什么用这个 SDK?** - **编译期类型安全**——Autel JSON 字段名映射为 Java record 字段,拼写错误编译即报,不再运行时静默失败 - **协议知识在 JAR 不在人脑**——83 个 method 全部编码为类型安全的 record,IDE 自动补全即可发现全部能力,无需逐字阅读 Autel 文档 - **专人跟踪协议变更**——Autel 更新协议时由 SDK 维护者同步更新,消费方升级 JAR 即获取最新定义,无需自己盯文档 - **100% 接口覆盖不随人员流动退化**——核心成员离职不会导致代码无人敢改、平台兼容性永久降级;协议知识固化在代码中,团队能力不依赖个人记忆 - **协议权威解释,持续验证完善**——字段必填/选填、类型、回复结构等歧义由 SDK 一锤定音,消除团队内解读争议;当前已通过 Autel 官方文档核实的协议元素标注 `@Verified`,推断项标注 `@Inferred` 待真机验证,详见下方「验证状态说明」 作为模拟器与平台的共享"单一真相源",本 SDK 消除两个系统间协议定义的重复维护,升级一处即双方同步。 ## 版本说明 本 SDK 跟随 Autel Cloud API 官方协议版本,当前最新协议版本为 **1.3.0**。 版本号格式:`1.3.0.X` - 前三段(`1.3.0`)对齐 Autel Cloud API 协议版本,协议升级时同步递增 - 末段(`X`)为 SDK 问题修复版本号,协议不变情况下随 SDK 缺陷修复递增 ## 特性 - **全通道覆盖**:services(40) / drc(1) / events(22) / requests(4) / status(1) = 68 个 method 枚举 + property/set(15) + 航线工具套件 - **类型安全**:每个 method 有对应的 Request/Reply/Data record,字段 camelCase 自动映射 Autel snake_case JSON - **文档追踪**:`@DocUrl` 标注 Autel 文档链接,`@Verified`/`@Inferred` 标注验证状态 - **零 MQTT 耦合**:纯 POJO 定义层,不依赖 MQTT 客户端 - **Jackson SNAKE_CASE**:`MessageCodec` 统一配置,snake_case ↔ camelCase 双向映射 - **航线工具一体化**:WPML 模板生成/解析工具与协议 POJO 共处一模块,Builder 模式构建航点/建图航线 ## 快速开始 ### Maven 依赖 本 SDK 为单模块结构,协议定义层与航线工具套件在同一 JAR 中,一次引入即获得全部能力: ```xml ltd.cdmi.autel autel-cloud-api-sdk 1.3.0.0 ``` ### 直接下载 JAR 非 Maven 项目可直接下载预构建 JAR(包含源码包): | 平台 | 下载地址 | |---|---| | GitHub | [releases/latest](https://github.com/cdmiltd/autel-cloud-api-sdk/releases/latest) | | Gitee | [releases](https://gitee.com/alpeai/autel-cloud-api-sdk/releases) | > JAR 文件名格式:`autel-cloud-api-sdk-{version}.jar` ### 通用调用模式 SDK 提供三个对称入口,分别对应三类协议通道,调用方式完全一致(先提取消息类型 → switch 路由 → parse 反序列化为类型安全 POJO): | 协议通道 | 提取消息类型 | 反序列化入口 | 适用场景 | |---|---|---|---| | MQTT | `MessageCodec.extractMethod(payload)` | `MessageCodec.parse(payload, Class)` | 机场上云(services/drc/events/requests/status 5 通道) | | WebSocket | `WsPushMessage.extractBizCode(payload)` | `WsPushMessage.parse(payload, Class)` | 平台推送(平台 → 前端) | | HTTP | — | `HttpResponseEnvelope.parse(body, Class)` | HTTP API 请求(前端 → 平台) | > 每条通道的 parse 工厂方法归位到产出类型所在的包:MQTT 信封 `AutelMessage` 位于 codec 包,WS 信封 `WsPushMessage` 位于 websocket 包,HTTP 信封 `HttpResponseEnvelope` 位于 http 包。避免 codec 基础设施层反向依赖上层业务类型。 **机场上云(MQTT)示例**:5 个 MQTT 通道信封结构一致 `{method, tid, bid?, data}`,用 `switch + parse` 模式,每个 case 1 行 `parse`,其余是类型安全业务代码: ```java String method = MessageCodec.extractMethod(payload); switch (method) { case "fly_to_point" -> { var msg = MessageCodec.parse(payload, FlyToPointRequest.class); msg.data().flyToId(); // 编译期类型安全,无需 cast String reply = MessageCodec.toJson(new NoOutputReply()); sendReply(msg.tid(), reply); // 发到 thing/product/{sn}/services_reply } default -> log.warn("未处理: {}", method); } ``` **事件通道**同样使用 `parse`,回复用 `events_reply`: ```java var msg = MessageCodec.parse(payload, FlighttaskProgressData.class); msg.data().output().status(); // 编译期类型安全 sendEventReply(msg.tid(), 0); // tid 与原始 event 一致 ``` ## 场景一:机场上云(Dock ↔ 平台,MQTT 5 通道) 机场上云场景中,Dock 机场通过 MQTT 与云平台交互,覆盖 services/drc/events/requests/status 全部 5 个通道。SDK 不绑定 MQTT 客户端实现,调用方自行接入任意 MQTT 客户端,按 `extractMethod + parse` 模式处理消息。以下按 Autel Cloud API 业务功能划分,列出每个功能涉及的 SDK 类与调用方式,全部基于 MQTT 协议。 ### 1. 设备注册与上线 **通道**:requests(绑定流程)+ status(上线拓扑) ```java // 绑定流程:airport_bind_status → airport_organization_get → airport_organization_bind case "airport_bind_status" -> { var msg = MessageCodec.parse(payload, AirportBindStatusRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new AirportBindStatusReply(0, 1))); } case "airport_organization_get" -> { var msg = MessageCodec.parse(payload, AirportOrganizationGetRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new AirportOrganizationGetReply(0, ...))); } case "airport_organization_bind" -> { var msg = MessageCodec.parse(payload, AirportOrganizationBindRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new AirportOrganizationBindReply(0))); } // 上线拓扑:设备主动上报 update_topo(status 通道) var topo = MessageCodec.parse(payload, UpdateTopoData.class); topo.data().subDevice(); // 子设备列表(含 domain/type/sub_type) ``` **涉及 SDK 类**:`AirportBindStatusRequest/Reply`、`AirportOrganizationGetRequest/Reply`、`AirportOrganizationBindRequest/Reply`、`UpdateTopoData`、`UpdateTopoReplyData` ### 2. 航线任务 **通道**:services(下发)+ events(进度) ```java case "flighttask_prepare" -> { var msg = MessageCodec.parse(payload, FlighttaskPrepareRequest.class); msg.data().flightId(); // 航线 ID sendReply(msg.tid(), MessageCodec.toJson(new FlighttaskPrepareReply(0))); } case "flighttask_execute" -> { var msg = MessageCodec.parse(payload, FlighttaskExecuteRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new FlighttaskExecuteReply(0))); } case "flighttask_undo" -> { var msg = MessageCodec.parse(payload, FlighttaskUndoRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // flighttask_pause / flighttask_recovery / return_home / return_home_cancel // 这 4 个无参数方法复用 NoParameterRequest: case "flighttask_pause" -> { var msg = MessageCodec.parse(payload, NoParameterRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // 航线进度事件(events 通道) case "flighttask_progress" -> { var msg = MessageCodec.parse(payload, FlighttaskProgressData.class); msg.data().output().status(); // executing / success / failed sendEventReply(msg.tid(), 0); } ``` **涉及 SDK 类**:`FlighttaskPrepareRequest/Reply`、`FlighttaskExecuteRequest/Reply`、`FlighttaskUndoRequest`、`ReturnHomeReply`、`FlighttaskProgressData`、`NoParameterRequest`(4 个无参数方法共用) ### 3. 指令飞行 **通道**:services + events + drc ```java case "fly_to_point" -> { var msg = MessageCodec.parse(payload, FlyToPointRequest.class); msg.data().flyToId(); msg.data().height(); // 相对起飞点高度 sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "takeoff_to_point" -> { var msg = MessageCodec.parse(payload, TakeoffToPointRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "payload_authority_grab" -> { var msg = MessageCodec.parse(payload, PayloadAuthorityGrabRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // fly_to_point_stop / flight_authority_grab / drc_mode_exit // 这 3 个无参数方法复用 NoParameterRequest // 飞行进度事件 case "fly_to_point_progress" -> { var msg = MessageCodec.parse(payload, FlyToPointProgressData.class); msg.data().status(); // in_progress / success / failed sendEventReply(msg.tid(), 0); } case "takeoff_to_point_progress" -> { var msg = MessageCodec.parse(payload, TakeoffToPointProgressData.class); msg.data().status(); sendEventReply(msg.tid(), 0); } ``` **涉及 SDK 类**:`FlyToPointRequest`、`TakeoffToPointRequest`、`PayloadAuthorityGrabRequest`、`FlyToPointProgressData`、`TakeoffToPointProgressData`、`NoParameterRequest`(3 个无参数方法共用) ### 4. 远程控制(DRC 通道) **通道**:services(进入 DRC)+ drc(摇杆控制) ```java // 进入 DRC 模式(services 通道) case "drc_mode_enter" -> { var msg = MessageCodec.parse(payload, DrcModeEnterRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // DRC 通道消息(topic: thing/product/{sn}/drc/down) case "drone_control" -> { var msg = MessageCodec.parse(payload, DroneControlRequest.class); msg.data().joyStickX(); // 横滚 x msg.data().joyStickY(); // 俯仰 y msg.data().joyStickH(); // 高度 h msg.data().joyStickW(); // 偏航 w msg.data().seq(); // 序列号 } ``` **涉及 SDK 类**:`DrcModeEnterRequest`、`DroneControlRequest/Reply` ### 5. 相机与负载管理 **通道**:services ```java case "camera_photo_take" -> { var msg = MessageCodec.parse(payload, CameraPhotoTakeRequest.class); msg.data().payloadIndex(); // 负载索引 sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "camera_recording_start" -> { var msg = MessageCodec.parse(payload, CameraRecordingStartRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "camera_recording_stop" -> { var msg = MessageCodec.parse(payload, CameraRecordingStopRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } ``` **涉及 SDK 类**:`CameraPhotoTakeRequest`、`CameraRecordingStartRequest`、`CameraRecordingStopRequest` ### 6. 直播管理 **通道**:services ```java case "live_start_push" -> { var msg = MessageCodec.parse(payload, LiveStartPushRequest.class); msg.data().url(); // RTMP/RTSP 推流地址 msg.data().videoIndex(); // 视频流索引 sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "live_stop_push" -> { var msg = MessageCodec.parse(payload, LiveStopPushRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "live_set_quality" -> { var msg = MessageCodec.parse(payload, LiveSetQualityRequest.class); msg.data().quality(); // 清晰度 sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "live_lens_change" -> { var msg = MessageCodec.parse(payload, LiveLensChangeRequest.class); msg.data().lens(); // 镜头类型 sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } ``` **涉及 SDK 类**:`LiveStartPushRequest`、`LiveStopPushRequest`、`LiveSetQualityRequest`、`LiveLensChangeRequest` ### 7. 媒体管理 **通道**:requests(STS 凭证)+ events(上传回调) ```java // 获取 STS 临时凭证(requests 通道) case "storage_config_get" -> { var msg = MessageCodec.parse(payload, StorageConfigGetRequest.class); var reply = new StorageConfigGetReply(0, new StorageConfigGetReply.Output( "bucket", "endpoint", "object-key-prefix/", credentials)); sendReply(msg.tid(), MessageCodec.toJson(reply)); } // 媒体上传回调(events 通道) case "file_upload_callback" -> { var msg = MessageCodec.parse(payload, FileUploadCallbackData.class); msg.data().file().objectKey(); // 对象存储键 sendEventReply(msg.tid(), 0); } ``` **涉及 SDK 类**:`StorageConfigGetRequest/Reply`(含 `Output` 内嵌 STS 凭证结构)、`FileUploadCallbackData` ### 8. 固件升级与日志管理 **通道**:services(无参方法)+ events(进度) ```java case "ota_create" -> { var msg = MessageCodec.parse(payload, OtaCreateRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new OtaCreateReply(0))); } // 固件升级进度(events 通道) case "ota_progress" -> { var msg = MessageCodec.parse(payload, OtaProgressData.class); msg.data().progress(); // 0-100 sendEventReply(msg.tid(), 0); } // 远程日志(services 通道) case "fileupload_list" -> { var msg = MessageCodec.parse(payload, FileUploadListRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new FileUploadListReply(0, List.of()))); } case "fileupload_start" -> { var msg = MessageCodec.parse(payload, FileUploadStartRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "fileupload_update" -> { var msg = MessageCodec.parse(payload, FileUploadUpdateRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // 日志上传进度(events 通道) case "fileupload_progress" -> { var msg = MessageCodec.parse(payload, FileuploadProgressData.class); msg.data().progress(); sendEventReply(msg.tid(), 0); } ``` **涉及 SDK 类**:`OtaCreateRequest/Reply`、`OtaProgressData`、`FileUploadListRequest/Reply`、`FileUploadStartRequest`、`FileUploadUpdateRequest`、`FileuploadProgressData` ### 9. 远程调试 **通道**:services(无参数指令)+ events(进度上报) ```java // 11 个无参数远程调试指令复用 NoParameterRequest: case "drone_open", "drone_close", "device_reboot", "cover_open", "cover_close", "charge_open", "charge_close", "putter_open", "putter_close", "drone_format", "device_format" -> { var msg = MessageCodec.parse(payload, NoParameterRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // 远程调试进度上报(events 通道,与指令同名) case "drone_open" -> { var msg = MessageCodec.parse(payload, DebugProgressData.class); msg.data().status(); sendEventReply(msg.tid(), 0); } ``` **涉及 SDK 类**:`NoParameterRequest`(11 个无参数指令共用)、`DebugProgressData`、`DebugProgressStatus` ### 10. AI 目标识别 **通道**:services + events ```java case "target_detect_open" -> { var msg = MessageCodec.parse(payload, TargetDetectOpenRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // target_detect_close / target_tracking_close 复用 NoParameterRequest case "target_detect_close" -> { var msg = MessageCodec.parse(payload, NoParameterRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } case "select_tracking_target" -> { var msg = MessageCodec.parse(payload, SelectTrackingTargetRequest.class); sendReply(msg.tid(), MessageCodec.toJson(new NoOutputReply())); } // AI 事件(events 通道) case "ai_detect_shoot_notify" -> { var msg = MessageCodec.parse(payload, AiDetectShootNotifyData.class); msg.data().targets(); sendEventReply(msg.tid(), 0); } ``` **涉及 SDK 类**:`TargetDetectOpenRequest`、`SelectTrackingTargetRequest`、`AiDetectShootNotifyData`、`EvidenceCollectionStatusData`、`AiserviceIllegalParkingInfoData`、`NoParameterRequest`(2 个无参数方法共用) ### 11. 设备状态与遥测 **通道**:thing/product/{sn}/osd(周期推送)+ thing/product/{sn}/state(变化推送) ```java // 机场 OSD(topic: thing/product/{sn}/osd) DockOsd dockOsd = MessageCodec.fromJson(payload, DockOsd.class); dockOsd.modeCode(); // 机巢模式码 dockOsd.networkState(); // 网络状态 // 飞行器 OSD DroneOsd droneOsd = MessageCodec.fromJson(payload, DroneOsd.class); droneOsd.modeCode(); // 飞行器模式码 droneOsd.latitude(); // 纬度 droneOsd.height(); // 高度(相对起飞点) ``` **涉及 SDK 类**:`DockOsd`、`DroneOsd`、`RcOsd`、`OsdField`(82 个字段枚举)、`StateField`(7 个字段枚举)、`telemetry/enumtype/` 下 24 个枚举类 ### 12. HMS 告警 **通道**:events ```java case "hms" -> { var msg = MessageCodec.parse(payload, HmsData.class); msg.data().list().forEach(hms -> { hms.code(); // HMS 错误码(参见 AutelErrorCode) hms.level(); // 告警级别 }); sendEventReply(msg.tid(), 0); } ``` **涉及 SDK 类**:`HmsData`、`AutelErrorCode`(209 个错误码常量) ### 13. 属性设置 **通道**:thing/product/{sn}/property/set ```java // 属性设置请求(扁平 map 结构,无 properties 包裹) var request = PropertySetRequest.of(Map.of( "distance_limit_status", Map.of("state", 1), "takeoff_and_rth_altitude", Map.of("rth_altitude", 100, "takeoff_altitude", 50) )); String payload = MessageCodec.toJson(request); // 发到 thing/product/{sn}/property/set // 属性设置回复(扁平 map 结构) case "property/set" -> { var msg = MessageCodec.parse(payload, PropertySetRequest.class); var reply = PropertySetReply.of(Map.of( "distance_limit_status", 0 // 0=成功 )); sendReply(msg.tid(), MessageCodec.toJson(reply)); } ``` **涉及 SDK 类**:`PropertySetRequest`、`PropertySetReply`、`PropertySetMethod`(15 个属性枚举) ## 场景二:HTTP API + WebSocket 推送 本场景覆盖 HTTP API 调用与 WebSocket 实时推送。SDK 提供 HTTP 路径常量(`HttpApiPath`)、HTTP 响应信封(`HttpResponseEnvelope`)、WebSocket biz_code 枚举(`WsBizCode`)和推送信封(`WsPushMessage`),不绑定具体 HTTP/WS 客户端实现。 ### HTTP 调用(前端 → 平台) 用 `HttpApiPath` 常量拼接路径(替换 `{workspace_id}` / `{wayline_id}` 等占位符),用任意 HTTP 客户端发请求。Autel HTTP API 响应统一信封为 `{"code":0,"message":"...","data":{...}}`,用 `HttpResponseEnvelope.parse` 一步完成信封解析 + `data` 反序列化为类型安全 POJO。 ```java // 获取 STS 临时凭证 String path = HttpApiPath.STS.replace("{workspace_id}", workspaceId); HttpResponse resp = httpClient.post(server + path, body); var envelope = HttpResponseEnvelope.parse(resp.body(), StorageConfigGetReply.Output.class); if (envelope.code() == 0) { envelope.data().bucket(); // 对象存储桶名 envelope.data().objectKeyPrefix(); // 上传 key 前缀 } else { log.error("STS 获取失败: {} - {}", envelope.code(), envelope.message()); } ``` ### WebSocket 推送(平台 → 前端) 用 `WsPushMessage.extractBizCode` 提取消息类型,再按 `WsBizCode` 路由,每个 case 调 `WsPushMessage.parse` 一步完成信封解析 + `data` 反序列化为类型安全 POJO。 ```java void onWsMessage(String payload) { String bizCode = WsPushMessage.extractBizCode(payload); switch (WsBizCode.fromCode(bizCode).orElse(null)) { case DEVICE_OSD -> { var msg = WsPushMessage.parse(payload, DeviceOsdPushData.class); msg.data().host().latitude(); // 类型安全,无 cast } case DEVICE_ONLINE, DEVICE_OFFLINE, DEVICE_UPDATE_TOPO -> { var msg = WsPushMessage.parse(payload, WsEmptyData.class); // 触发 HTTP 拓扑刷新 String path = HttpApiPath.DEVICES_TOPOLOGIES.replace("{workspace_id}", workspaceId); httpClient.get(server + path); } case MAP_ELEMENT_CREATE, MAP_ELEMENT_UPDATE, MAP_ELEMENT_DELETE -> { var msg = WsPushMessage.parse(payload, MapElementPushData.class); // 类型安全访问地图元素 } case MAP_GROUP_REFRESH -> { var msg = WsPushMessage.parse(payload, MapGroupRefreshData.class); // msg.data().ids() → 按 group_id 调 HTTP 拉取元素列表 } case null -> log.warn("未知 biz_code: {}", bizCode); } } ``` ### HTTP 端点按业务域 | 业务域 | 路径前缀 | 端点常量 | |---|---|---| | 设备拓扑 | `/manage/api/v1/workspaces` | `DEVICES_TOPOLOGIES` | | 地图元素 | `/map/api/v1/workspaces` | `ELEMENT_GROUPS`、`CREATE_ELEMENT`、`UPDATE_ELEMENT`、`DELETE_ELEMENT` | | 媒体管理 | `/media/api/v1/workspaces` | `FAST_UPLOAD`、`TINY_FINGERPRINTS`、`UPLOAD_CALLBACK` | | 存储服务 | `/storage/api/v1/workspaces` | `STS` | | 航线管理 | `/wayline/api/v1/workspaces` | `WAYLINES`、`WAYLINE_URL`、`UPLOAD_CALLBACK` | **涉及 SDK 类**:`HttpApiPath`(5 业务域 13 个端点)、`HttpResponseEnvelope`、`WsBizCode`(8 个 biz_code)、`WsPushMessage`、推送 data POJO(`DeviceOsdPushData`、`MapElementPushData`、`MapGroupRefreshData`、`WsEmptyData`) ## 航线模板(WPML 生成与解析) WPML(Waypoint Mission Language)是 Autel 机场使用的航线文件格式,本质是一组 XML 文件打包成的 `.kmz`(ZIP)。本模块是**本地文件工具**,不涉及 MQTT/HTTP/WS 协议,生成的 `.kmz` 可通过「场景一 → 2. 航线任务」上传到云平台再推送给机场执行。 ### 四种模板 | 模板类 | templateType | 测区/航点 | 典型场景 | |---|---|---|---| | `WaypointTemplate` | `waypoint` | 航点列表 | 航点飞行(巡检/拍照/录像) | | `Mapping2dTemplate` | `mapping2d` | `Polygon` 测区 | 二维正射建图 | | `Mapping3dTemplate` | `mapping3d` | `Polygon` 测区 | 三维倾斜摄影 | | `MappingStripTemplate` | `mappingStrip` | `LineString` 航带 | 航带飞行(带状测区) | 四个模板均提供 `toXml()`(生成 `template.kml`)、`toWpml()`(派生 `waylines.wpml`)、`toKmz()`(打包 KMZ)三种输出方式。**高度语义**:所有 `height` 参数均为相对起飞点高度,与 Autel WPML `wpml:height` 定义一致。 ### 航点飞行模板 ```java String kml = WaypointTemplate.builder() .author("John") .createTime(System.currentTimeMillis()) .finishAction(FinishAction.GO_HOME) .takeOffSecurityHeight(20) .globalTransitionalSpeed(8) .globalRTHHeight(100) .droneInfo(DroneModel.EVO_MAX_4T) // EVO Max 4T .payloadInfo(CameraModel.FUSION_4T) // Fusion 4T 相机 .templateId(0) .coordinateMode(CoordinateMode.WGS84) .heightMode(HeightMode.EGM96) .autoFlightSpeed(7) .globalHeight(100) .globalWaypointTurnMode(WaypointTurnMode.TO_POINT_AND_STOP_WITH_DISCONTINUITY_CURVATURE) .addWaypoint(w -> w.longitude(113.98057).latitude(22.987663).height(100) .addActionGroup(ag -> ag .actionGroupId(0) .actionGroupStartIndex(0) .actionGroupEndIndex(0) .actionTriggerType(ActionTriggerType.REACH_POINT) .addAction(a -> a.actionId(0) .actionActuatorFunc(ActionActuatorFunc.TAKE_PHOTO) .actionActuatorFuncParam(new TakePhotoParam(0, "point1", "wide", 1))))) .addWaypoint(w -> w.longitude(113.99000).latitude(22.987663).height(100)) .toXml(); // 打包为 .kmz byte[] kmz = WaypointTemplate.builder() /* ... 同上 ... */ .toKmz(); ``` ### KMZ 打包与解析 `WpmlCodec` 提供 XML 序列化、KMZ 打包/解包、POJO 反序列化三类静态方法: ```java // 1. 打包:XML 字符串 → KMZ 字节流 byte[] kmz = WpmlCodec.toKmz(kml, wpml); // 2. 解包:KMZ 字节流 → 原始 XML 字符串 KmzContent content = WpmlCodec.fromKmz(kmz); String kml = content.templateKml(); String wpml = content.waylinesWpml(); // 3. 解析:XML 字符串 → POJO Kml template = WpmlCodec.parseTemplateKml(kml); Kml waylines = WpmlCodec.parseWaylinesWpml(wpml); // 4. 一站式:KMZ 字节流 → POJO 容器 ParsedKmz parsed = WpmlCodec.parseKmz(kmz); Kml t = parsed.template(); Kml w = parsed.waylines(); ``` ### 涉及 SDK 类 - 模板入口:`WaypointTemplate`、`Mapping2dTemplate`、`Mapping3dTemplate`、`MappingStripTemplate` - Builder 链:`WaypointBuilder`、`ActionGroupBuilder`、`ActionBuilder` - 编解码:`WpmlCodec`(静态工具类)、`WpmlStreamWriter`/`WpmlOutputFactory` - 动作参数:`ActionActuatorFunc`(枚举)、`ActionActuatorFuncParam`(密封接口)及实现类 - 枚举:`enumtype/` 包下共 23 个枚举(`HeightMode`、`ActionActuatorFunc`、`FinishAction` 等) ## 诊断工具 SDK 提供以下诊断类,供调用者在运行时排查协议对接问题、校验设备配置、追溯协议来源。 ### 错误码查表:`AutelErrorCode` Autel 协议错误码为 6 位数字(如 `314001`),裸数字无法理解含义。`AutelErrorCode` 收录 209 个错误码常量,提供运行时查表: ```java // services_reply 返回 result=314001,查表获取官方描述 var msg = MessageCodec.parse(payload, FlighttaskPrepareReply.class); int result = msg.data().result(); AutelErrorCode.describe(result).ifPresentOrElse( info -> log.error("任务失败: {} ({})", info.description(), info.code()), () -> log.error("未知错误码: {}", result) ); // 输出: 任务失败: 飞行任务下发失败,请稍后重试 (314001) ``` ### Topic 路由解析:`TopicResolver` 从 MQTT topic 字符串中解析出设备 SN、通道类型、消息方向,用于日志分类、消息路由、问题定位。 `TopicResolver` 仅解析 topic 字符串本身的路由信息,不涉及 payload。method 名称是 MQTT 消息的另一独立维度,应通过 `MessageCodec.extractMethod(payload)` 从 payload 单独提取, 与 topic 解析解耦: ```java void onMessage(String topic, String payload) { TopicInfo info = TopicResolver.resolve(topic); String method = MessageCodec.extractMethod(payload); // 独立维度,单独提取 log.info("[{}] {} {} → {}", info.direction(), // DOWN(云→设备)/ UP(设备→云) info.channel(), // SERVICES / EVENTS / DRC_UP / ... info.deviceSn(), // 设备 SN method); // fly_to_point switch (info.channel()) { case SERVICES -> handleServices(info.deviceSn(), method, payload); case EVENTS -> handleEvents(info.deviceSn(), method, payload); case DRC_UP -> handleDrcUp(info.deviceSn(), method, payload); case null -> log.warn("未知通道: {}", topic); } } ``` ### 设备兼容性校验:`DeviceCompatibility` 在配置设备组合前校验机场-飞行器或遥控器-飞行器是否兼容,避免下发不支持的指令组合: ```java // 校验 EVO Nest + EVO Max 4T 是否兼容(true) boolean ok = DeviceCompatibility.isCompatible(DockModel.EVO_NEST, DroneModel.EVO_MAX_4T); // 校验 Autel Dragonfish Nest + EVO Max 4T 是否兼容(false,Dragonfish Nest 配套 Dragonfish 飞行器) boolean ok = DeviceCompatibility.isCompatible(DockModel.AUTEL_DRAGONFISH_NEST, DroneModel.EVO_MAX_4T); if (!ok) { log.warn("Dragonfish Nest 不支持 EVO Max 系列"); } ``` ### 协议溯源:`@Verified` / `@Inferred` / `@DocUrl` 注解 三个注解均为 `RUNTIME` 保留策略,可通过反射在运行时检查任何 POJO 或枚举的验证状态和文档来源: ```java // 检查某个 POJO 的验证状态 var clazz = FlighttaskPrepareRequest.class; boolean verified = clazz.isAnnotationPresent(Verified.class); boolean inferred = clazz.isAnnotationPresent(Inferred.class); if (verified) { String basis = clazz.getAnnotation(Verified.class).basis(); log.info("{} 已验证: {}", clazz.getSimpleName(), basis); } if (inferred) { String reason = clazz.getAnnotation(Inferred.class).reason(); String verifyPoint = clazz.getAnnotation(Inferred.class).verifyPoint(); log.warn("{} 推断项: {} (待验证: {})", clazz.getSimpleName(), reason, verifyPoint); } // 获取 Autel 文档链接 if (clazz.isAnnotationPresent(DocUrl.class)) { String url = clazz.getAnnotation(DocUrl.class).value(); log.info("文档: {}", url); } ``` ## 模块概览 | 包 | 职责 | 核心类 | |---|---|---| | `annotation/` | 文档追溯注解 | `@DocUrl`、`@Verified`、`@Inferred` | | `codec/` | JSON 编解码 + 类型安全信封解析 | `MessageCodec`、`AutelMessage` | | `protocol/` | 协议层:topic/method/envelope/error | `TopicBuilder`、`TopicResolver`、`TopicChannel`、`ServiceMethod`/`EventMethod`/`DrcMethod`、`AutelErrorCode` | | `command/` | 指令 POJO:5 通道 | `service/`、`drc/`、`event/`、`property/`、`request/`、`status/` | | `model/` | 设备型号/兼容性 | `DeviceModel`、`DroneModel`、`DockModel`、`ControllerModel`、`CameraModel`、`PayloadModel`、`DeviceCompatibility` | | `telemetry/` | OSD/State 遥测 | `DockOsd`、`DroneOsd`、`RcOsd`、`OsdField`、`StateField` + 24 个枚举类 | | `wayline/` | 航线模板(WPML 生成/解析) | `WaypointTemplate`、`Mapping2d/3d/StripTemplate`、`WpmlCodec` + 23 个枚举 | | `http/` | HTTP 路径常量+响应信封 | `HttpApiPath`、`HttpResponseEnvelope` | | `websocket/` | WebSocket biz_code+推送 | `WsBizCode`、`WsPushMessage` + data POJO | ## 设备型号支持 SDK 覆盖 Autel Cloud API 全部 13 款设备型号: | 设备大类 | 枚举 | 型号 | |---|---|---| | 无人机(domain=0) | `DroneModel` | EVO Max 4T (0-11000-0)、EVO Max 4N (0-11000-1) | | 相机(domain=1, camera) | `CameraModel` | Fusion 4T (1-10052-0)、Fusion 4N (1-10053-0)、EVO Nest Camera (1-10165-0) | | 功能负载(domain=1, payload) | `PayloadModel` | 抛投器 (1-10301-0)、Tracer (1-10301-0)、喊话器 (1-10305-0) | | 遥控器(domain=2) | `ControllerModel` | Smart Controller V3 (2-20119-0)、Ground Control Station (2-20120-0) | | 机巢(domain=3) | `DockModel` | Autel Virtual Nest (3-30000-0)、EVO Nest (3-30001-0)、Autel Dragonfish Nest (3-30002-0) | > 设备型号三元组 (domain, type, sub_type) 对齐 [Autel Cloud API 产品支持页](https://doc.autelrobotics.com/cloud_api/cn/10/30)。 ## 验证状态说明 - **`@Verified`**:已通过 Autel 官方文档核实 - **`@Inferred`**:基于 Autel 文档推断或协议惯例推断,待真机验证 - 详见各 POJO 文件注解 当前 `@Inferred` 待验证项(完整清单见各源文件 `@Inferred` 注解): - `WsBizCode.DEVICE_OSD` — Autel 文档未直接列出 device_osd biz_code,参考 DJI 推断 - `PayloadModel.PAYLOAD_DROP_SYSTEM` / `AIRBORNE_PILOT_POSITIONING` — Autel 官方产品表格 (1,10301,0) 三元组歧义,待官方澄清 ## 文档索引 - [架构设计文档](docs/architecture-design.md) — 模块设计、包结构、协议覆盖、类说明、已知缺陷 - [TDD 测试用例文档](docs/tdd-test-cases.md) — Given-When-Then 测试用例规格 - [AI 编程约定](AGENTS.md) — 修改流程、硬约束、文档更新策略、命名规范 - [Autel Cloud API 官方文档](https://doc.autelrobotics.com/cloud_api/cn/00/1/) — 协议真相源 ## 交流沟通

微信二维码      技术交流群

- **左侧**:扫码添加好友 - **右侧**:扫码加入技术交流群 ## License Apache License 2.0 (详见 [LICENSE](LICENSE))