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 模式构建航点/建图航线
本 SDK 为单模块结构,协议定义层与航线工具套件在同一 JAR 中,一次引入即获得全部能力:
<dependency>
<groupId>ltd.cdmi.autel</groupId>
<artifactId>autel-cloud-api-sdk</artifactId>
<version>1.3.0.0</version>
</dependency>非 Maven 项目可直接下载预构建 JAR(包含源码包):
| 平台 | 下载地址 |
|---|---|
| GitHub | releases/latest |
| Gitee | 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,其余是类型安全业务代码:
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:
var msg = MessageCodec.parse(payload, FlighttaskProgressData.class);
msg.data().output().status(); // 编译期类型安全
sendEventReply(msg.tid(), 0); // tid 与原始 event 一致机场上云场景中,Dock 机场通过 MQTT 与云平台交互,覆盖 services/drc/events/requests/status 全部 5 个通道。SDK 不绑定 MQTT 客户端实现,调用方自行接入任意 MQTT 客户端,按 extractMethod + parse 模式处理消息。以下按 Autel Cloud API 业务功能划分,列出每个功能涉及的 SDK 类与调用方式,全部基于 MQTT 协议。
通道:requests(绑定流程)+ status(上线拓扑)
// 绑定流程: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
通道:services(下发)+ events(进度)
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 个无参数方法共用)
通道:services + events + drc
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 个无参数方法共用)
通道:services(进入 DRC)+ drc(摇杆控制)
// 进入 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
通道:services
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
通道:services
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
通道:requests(STS 凭证)+ events(上传回调)
// 获取 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
通道:services(无参方法)+ events(进度)
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
通道:services(无参数指令)+ events(进度上报)
// 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
通道:services + events
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 个无参数方法共用)
通道:thing/product/{sn}/osd(周期推送)+ thing/product/{sn}/state(变化推送)
// 机场 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 个枚举类
通道:events
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 个错误码常量)
通道:thing/product/{sn}/property/set
// 属性设置请求(扁平 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 实时推送。SDK 提供 HTTP 路径常量(HttpApiPath)、HTTP 响应信封(HttpResponseEnvelope<T>)、WebSocket biz_code 枚举(WsBizCode)和推送信封(WsPushMessage<T>),不绑定具体 HTTP/WS 客户端实现。
用 HttpApiPath 常量拼接路径(替换 {workspace_id} / {wayline_id} 等占位符),用任意 HTTP 客户端发请求。Autel HTTP API 响应统一信封为 {"code":0,"message":"...","data":{...}},用 HttpResponseEnvelope.parse 一步完成信封解析 + data 反序列化为类型安全 POJO。
// 获取 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());
}用 WsPushMessage.extractBizCode 提取消息类型,再按 WsBizCode 路由,每个 case 调 WsPushMessage.parse 一步完成信封解析 + data 反序列化为类型安全 POJO。
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);
}
}| 业务域 | 路径前缀 | 端点常量 |
|---|---|---|
| 设备拓扑 | /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<T>、WsBizCode(8 个 biz_code)、WsPushMessage<T>、推送 data POJO(DeviceOsdPushData、MapElementPushData、MapGroupRefreshData、WsEmptyData)
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 定义一致。
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();WpmlCodec 提供 XML 序列化、KMZ 打包/解包、POJO 反序列化三类静态方法:
// 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<Folder> template = WpmlCodec.parseTemplateKml(kml);
Kml<ExecuteFolder> waylines = WpmlCodec.parseWaylinesWpml(wpml);
// 4. 一站式:KMZ 字节流 → POJO 容器
ParsedKmz parsed = WpmlCodec.parseKmz(kmz);
Kml<Folder> t = parsed.template();
Kml<ExecuteFolder> w = parsed.waylines();- 模板入口:
WaypointTemplate、Mapping2dTemplate、Mapping3dTemplate、MappingStripTemplate - Builder 链:
WaypointBuilder、ActionGroupBuilder、ActionBuilder - 编解码:
WpmlCodec(静态工具类)、WpmlStreamWriter/WpmlOutputFactory - 动作参数:
ActionActuatorFunc(枚举)、ActionActuatorFuncParam(密封接口)及实现类 - 枚举:
enumtype/包下共 23 个枚举(HeightMode、ActionActuatorFunc、FinishAction等)
SDK 提供以下诊断类,供调用者在运行时排查协议对接问题、校验设备配置、追溯协议来源。
Autel 协议错误码为 6 位数字(如 314001),裸数字无法理解含义。AutelErrorCode 收录 209 个错误码常量,提供运行时查表:
// 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)从 MQTT topic 字符串中解析出设备 SN、通道类型、消息方向,用于日志分类、消息路由、问题定位。
TopicResolver 仅解析 topic 字符串本身的路由信息,不涉及 payload。method 名称是
MQTT 消息的另一独立维度,应通过 MessageCodec.extractMethod(payload) 从 payload 单独提取,
与 topic 解析解耦:
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);
}
}在配置设备组合前校验机场-飞行器或遥控器-飞行器是否兼容,避免下发不支持的指令组合:
// 校验 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 系列");
}三个注解均为 RUNTIME 保留策略,可通过反射在运行时检查任何 POJO 或枚举的验证状态和文档来源:
// 检查某个 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<T> |
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<T> |
websocket/ |
WebSocket biz_code+推送 | WsBizCode、WsPushMessage<T> + 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 产品支持页。
@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) 三元组歧义,待官方澄清
- 架构设计文档 — 模块设计、包结构、协议覆盖、类说明、已知缺陷
- TDD 测试用例文档 — Given-When-Then 测试用例规格
- AI 编程约定 — 修改流程、硬约束、文档更新策略、命名规范
- Autel Cloud API 官方文档 — 协议真相源
- 左侧:扫码添加好友
- 右侧:扫码加入技术交流群
Apache License 2.0 (详见 LICENSE)