大疆上云API-1.10.0版本-设备位置数据上报处理源码解析

这一章节主要是解析机场和无人机的OSD位置数据上报处理流程,废话不多说,上源码!!!!有了上一章节的准备,本地环境请自行搭建。

源码解析(设备上报OSD位置数据处理)

1、OSD数据路由点

处理逻辑如下:

/**
 *
 * @author sean.zhou
 * @date 2021/11/17
 * @version 0.1
 */
@Configuration
public class OsdRouter {

    /**
     * 配置OSD路由器流程,该流程从指定的输入通道接收消息,进行处理,并根据处理结果将消息路由到不同的通道。
     *
     * @return 配置好的IntegrationFlow实例。
     */
    @Bean
    public IntegrationFlow osdRouterFlow() {
        return IntegrationFlows
                // 从名为INBOUND_OSD的通道接收消息
                .from(ChannelName.INBOUND_OSD)
                // 对接收到的消息进行转换,提取并修改其负载
                .transform(Message.class, source -> {
                    try {
                        // 使用Jackson的ObjectMapper将字节数组负载反序列化为TopicOsdRequest对象
                        TopicOsdRequest response = Common.getObjectMapper().readValue((byte[]) source.getPayload(), new TypeReference<TopicOsdRequest>() {});
                        // 从消息头中获取MQTT接收到的主题,并解析出特定的部分作为新的来源信息
                        String topic = String.valueOf(source.getHeaders().get(MqttHeaders.RECEIVED_TOPIC));
                        // 修改来源信息,基于预定义的前缀和后缀截取主题中的特定部分
                        return response.setFrom(topic.substring((THING_MODEL_PRE + PRODUCT).length(), topic.indexOf(OSD_SUF)));
                    } catch (IOException e) {
                        // 如果反序列化失败,则抛出自定义的异常
                        throw new CloudSDKException(e);
                    }
                }, null)
                // 对转换后的TopicOsdRequest对象进行处理
                .<TopicOsdRequest>handle((response, headers) -> {
                    // 根据响应中的网关ID获取对应的网关管理器
                    GatewayManager gateway = SDKManager.getDeviceSDK(response.getGateway());
                    // 根据网关类型和是否为网关本身确定设备类型枚举
                    OsdDeviceTypeEnum typeEnum = OsdDeviceTypeEnum.find(gateway.getType(), response.getFrom().equals(response.getGateway()));
                    // 处理响应中的数据
                    Map<String, Object> data = (Map<String, Object>) response.getData();
                    // 如果不是网关类型,则处理负载数据,添加额外的索引位置信息
                    if (!typeEnum.isGateway()) {
                        List payloadData = (List) data.getOrDefault(PayloadModelConst.PAYLOAD_KEY, new ArrayList<>());
                        // 遍历所有索引位置,如果数据中包含这些键,则将其值添加到payloadData中
                        PayloadModelConst.getAllIndexWithPosition().stream().filter(data::containsKey)
                                .map(data::get).forEach(payloadData::add);
                        data.put(PayloadModelConst.PAYLOAD_KEY, payloadData);
                    }
                    // 使用ObjectMapper将处理后的数据转换回相应的类型,并更新响应对象
                    return response.setData(Common.getObjectMapper().convertValue(data, typeEnum.getClassType()));
                })
                // 根据处理后的响应数据类型将消息路由到不同的通道
                .<TopicOsdRequest, OsdDeviceTypeEnum>route(response -> OsdDeviceTypeEnum.find(response.getData().getClass()),
                        mapping -> Arrays.stream(OsdDeviceTypeEnum.values()).forEach(key -> mapping.channelMapping(key, key.getChannelName())))
                .get(); // 完成并返回配置好的IntegrationFlow实例
    }

}

2、数据处理业务逻辑入口

/**
 * @author sean
 * @version 1.7
 * @date 2023/6/30
 */
public class AbstractDeviceService {

    /**
     * 机场osd数据入口
     * @param request  data
     * @param headers   The headers for a {@link Message}.
     * @return status_reply
     */
    @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_DOCK)
    public void osdDock(TopicOsdRequest<OsdDock> request, MessageHeaders headers) {
        throw new UnsupportedOperationException("osdDock not implemented");
    }

    /**
     * 机场无人机osd数据入口
     * @param request  data
     * @param headers   The headers for a {@link Message}.
     * @return status_reply
     */
    @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_DOCK_DRONE)
    public void osdDockDrone(TopicOsdRequest<OsdDockDrone> request, MessageHeaders headers) {
        throw new UnsupportedOperationException("osdDockDrone not implemented");
    }

    /**
     * 遥控器osd数据入口
     * @param request  data
     * @param headers   The headers for a {@link Message}.
     * @return status_reply
     */
    @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_RC)
    public void osdRemoteControl(TopicOsdRequest<OsdRemoteControl> request, MessageHeaders headers) {
        throw new UnsupportedOperationException("osdRemoteControl not implemented");
    }

    /**
     * 遥控无人机osd数据入口
     * @param request  data
     * @param headers   The headers for a {@link Message}.
     * @return status_reply
     */
    @ServiceActivator(inputChannel = ChannelName.INBOUND_OSD_RC_DRONE)
    public void osdRcDrone(TopicOsdRequest<OsdRcDrone> request, MessageHeaders headers) {
        throw new UnsupportedOperationException("osdRcDrone not implemented");
    }
 }

3、实际处理业务逻辑代码解析

(1)机场OSD数据

上报json如下:

{
    "tid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
    "bid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
    "timestamp": 1667220916697,
    "data": {
        "network_state": {
            "type": 2,
            "quality": 0,
            "rate": 5.0970001220703125
        },
        "drone_charge_state": {
            "state": 0,
            "capacity_percent": 100
        },
        "drone_in_dock": 1,
        "rainfall": 0,
        "wind_speed": 0,
        "environment_temperature": 24,
        "temperature": 24.9,
        "humidity": 62,
        "latitude": 22.907809968,
        "longitude": 113.703482143,
        "height": 34.174125671386719,
        "alternate_land_point": {
            "latitude": 22.907898319908661,
            "longitude": 113.70347329676635,
            "safe_land_height": 0,
            "is_configured": 1
        },
        "first_power_on": 1631945855969,
        "position_state": {
            "is_calibration": 1,
            "is_fixed": 2,
            "quality": 5,
            "gps_number": 6,
            "rtk_number": 25
        },
        "storage": {
            "total": 82045336,
            "used": 51772
        },
        "mode_code": 1,
        "cover_state": 0,
        "supplement_light_state": 0,
        "emergency_stop_state": 0,
        "air_conditioner_mode": 0,
        "battery_store_mode": 1,
        "alarm_state": 0,
        "putter_state": 0,
        "sub_device": {
            "device_sn": "1581F5BKD225D00BP891",
            "device_model_key": "0-67-0",
            "device_online_status": 0,
            "device_paired": 1
        }
    },
    "gateway": "dock_sn"
}

实际代码如下:

@Override
    public void osdDock(TopicOsdRequest<OsdDock> request, MessageHeaders headers) {
        String from = request.getFrom();
        //查询当前osd数据对应的设备是否在线
        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(from);
        //设备不在线或工作空间不存在,不处理
        if (deviceOpt.isEmpty() || !StringUtils.hasText(deviceOpt.get().getWorkspaceId())) {
            deviceOpt = deviceService.getDeviceBySn(from);
            if (deviceOpt.isEmpty()) {
                log.error("Please restart the drone.");
                return;
            }
        }

        DeviceDTO device = deviceOpt.get();
        //机场未绑定
        if (!StringUtils.hasText(device.getWorkspaceId())) {
            log.error("Please bind the dock first.");
        }
        //如果设备有子设备-飞机,查询飞机的状态并设置到机场信息中
        if (StringUtils.hasText(device.getChildDeviceSn())) {
            deviceService.getDeviceBySn(device.getChildDeviceSn()).ifPresent(device::setChildren);
        }
        //保存机场在线情况到redis
        deviceRedisService.setDeviceOnline(device);
        //覆盖/保存机场osd信息到redis
        fillDockOsd(from, request.getData());
        //推送机场位置信息到连接了websoket的web端
        deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.DOCK_OSD, from, request.getData());
    }

 	//判断覆盖redis里面存储的机场osd信息
    private void fillDockOsd(String dockSn, OsdDock dock) {
        Optional<OsdDock> oldDockOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDock.class);
        //机场的任务编码不为空不处理数据 说明机场状态不变
        if (Objects.nonNull(dock.getJobNumber())) {
            return;
        }
        //不存在机场状态信息数据
        if (oldDockOpt.isEmpty()) {
            deviceRedisService.setDeviceOsd(dockSn, dock);
            return;
        }
        OsdDock oldDock = oldDockOpt.get();
        //判断机场模式不为空   使用旧数据机场连接状态覆盖新的数据连接状态 修改redis并返回
        if (Objects.nonNull(dock.getModeCode())) {
            dock.setDrcState(oldDock.getDrcState());
            deviceRedisService.setDeviceOsd(dockSn, dock);
            return;
        }
        //判断连接状态不为空   使用新数据机场连接状态覆盖老的数据连接状态 修改redis
        if (Objects.nonNull(dock.getDrcState()) ) {
            oldDock.setDrcState(dock.getDrcState());
            deviceRedisService.setDeviceOsd(dockSn, oldDock);
        }
    }

(2)机场对应无人机OSD数据

对应上报的json如下:

{
    "bid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
    "data": {
        "52-0-0": {
            "measure_target_altitude": 0,
            "measure_target_distance": 591.70001220703125,
            "measure_target_error_state": 1,
            "measure_target_latitude": 0,
            "measure_target_longitude": 0,
            "payload_index": "52-0-0",
            "version": 1
        },
        "activation_time": 1667935211,
        "attitude_head": 41.7,
        "attitude_pitch": 2.7,
        "attitude_roll": 0,
        "battery": {
            "batteries": [
                {
                    "capacity_percent": 95,
                    "firmware_version": "02.00.20.44",
                    "high_voltage_storage_days": 16,
                    "index": 0,
                    "loop_times": 137,
                    "sn": "4BUPJ99DAD009W",
                    "sub_type": 0,
                    "temperature": 33.3,
                    "type": 0,
                    "voltage": 24303
                },
                {
                    "capacity_percent": 85,
                    "firmware_version": "02.00.20.44",
                    "high_voltage_storage_days": 5,
                    "index": 1,
                    "loop_times": 82,
                    "sn": "4BUPJ9EDAD01CE",
                    "sub_type": 0,
                    "temperature": 32,
                    "type": 0,
                    "voltage": 24311
                }
            ],
            "capacity_percent": 90,
            "landing_power": 0,
            "remain_flight_time": 0,
            "return_home_power": 0
        },
        "distance_limit_status": {
            "distance_limit": 5000,
            "state": 0
        },
        "elevation": 0,
        "firmware_version": "05.01.0214",
        "gear": 1,
        "height": 38.417465209960938,
        "height_limit": 120,
        "home_distance": 0,
        "horizontal_speed": 0,
        "latitude": 0,
        "longitude": 0,
        "maintain_status": {
            "maintain_status_array": [
                {
                    "last_maintain_flight_time": 0,
                    "last_maintain_time": 0,
                    "last_maintain_type": 1,
                    "state": 0
                },
                {
                    "last_maintain_flight_time": 0,
                    "last_maintain_time": 0,
                    "last_maintain_type": 2,
                    "state": 0
                },
                {
                    "last_maintain_flight_time": 0,
                    "last_maintain_time": 0,
                    "last_maintain_type": 3,
                    "state": 0
                }
            ]
        },
        "mode_code": 0,
        "night_lights_state": 0,
        "obstacle_avoidance": {
            "downside": 1,
            "horizon": 1,
            "upside": 1
        },
        "position_state": {
            "gps_number": 0,
            "is_fixed": 0,
            "quality": 0,
            "rtk_number": 0
        },
        "storage": {
            "total": 0,
            "used": 0
        },
        "total_flight_distance": 0,
        "total_flight_sorties": 0,
        "total_flight_time": 0,
        "track_id": "",
        "vertical_speed": 0,
        "wind_direction": 0,
        "wind_speed": 0
    },
    "tid": "2d2040eb-23b0-43dc-b7ac-64838276c4ac",
    "timestamp": 1670422793916,
    "gateway": "dock_sn"
}

对应代码如下

 @Override
    public void osdDockDrone(TopicOsdRequest<OsdDockDrone> request, MessageHeaders headers) {
        String from = request.getFrom();
        //查询当前osd数据对应的设备是否在线
        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(from);
        //设备不在线或工作空间不存在,不处理
        if (deviceOpt.isEmpty()) {
            deviceOpt = deviceService.getDeviceBySn(from);
            if (deviceOpt.isEmpty()) {
                log.error("Please restart the drone.");
                return;
            }
        }
        //如果无人机不存在工作空间打印日志
        if (!StringUtils.hasText(deviceOpt.get().getWorkspaceId())) {
            log.error("Please restart the drone.");
        }

        DeviceDTO device = deviceOpt.get();
        //保存设备到redis设置在线
        deviceRedisService.setDeviceOnline(device);
        //保存无人机osd数据到redis
        deviceRedisService.setDeviceOsd(from, request.getData());
        //推送机无人机位置信息到连接了websoket的web端
        deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.DEVICE_OSD, from, request.getData());
    }

(3)遥控器OSD数据

遥控器相关的json数据自行拼装一下:在这里插入图片描述

 @Override
    public void osdRemoteControl(TopicOsdRequest<OsdRemoteControl> request, MessageHeaders headers) {
        String from = request.getFrom();
        //查询当前osd数据对应的设备是否在线
        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(from);
        //设备不在线或工作空间不存在,不处理
        if (deviceOpt.isEmpty()) {
            deviceOpt = deviceService.getDeviceBySn(from);
            if (deviceOpt.isEmpty()) {
                log.error("Please restart the drone.");
                return;
            }
        }
        //如果设备有子设备-飞机,查询飞机的状态并设置到遥控器信息中
        DeviceDTO device = deviceOpt.get();
        if (StringUtils.hasText(device.getChildDeviceSn())) {
            deviceService.getDeviceBySn(device.getChildDeviceSn()).ifPresent(device::setChildren);
        }
        //设置遥控器在线
        deviceRedisService.setDeviceOnline(device);

        OsdRemoteControl data = request.getData();
        //推送当前遥控器位置信息 到 相同空间所有其他连接了websoket的遥控器
        deviceService.pushOsdDataToPilot(device.getWorkspaceId(), from,
                new DeviceOsdHost()
                        .setLatitude(data.getLatitude())
                        .setLongitude(data.getLongitude())
                        .setHeight(data.getHeight()));
        //推送当前遥控器信息到连接了websoket的web端
        deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.RC_OSD, from, data);

    }

(4)遥控器对应无人机OSD数据

遥控无人机对应的json,全部被包含在机场无人机数据字段内如下,请自行拼装:在这里插入图片描述

@Override
    public void osdRcDrone(TopicOsdRequest<OsdRcDrone> request, MessageHeaders headers) {
        String from = request.getFrom();
        //查询当前osd数据对应的设备是否在线
        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(from);
        //设备不在线或工作空间不存在,不处理
        if (deviceOpt.isEmpty()) {
            deviceOpt = deviceService.getDeviceBySn(from);
            if (deviceOpt.isEmpty()) {
                log.error("Please restart the drone.");
                return;
            }
        }
        DeviceDTO device = deviceOpt.get();
        //如果无人机不存在工作空间打印日志
        if (!StringUtils.hasText(device.getWorkspaceId())) {
            log.error("Please bind the drone first.");
        }
        //设置无人机在线 --添加在线时间
        deviceRedisService.setDeviceOnline(device);

        OsdRcDrone data = request.getData();
        //推送当前无人机位置信息 到 相同空间所有其他连接了websoket的遥控器
        deviceService.pushOsdDataToPilot(device.getWorkspaceId(), from,
                new DeviceOsdHost()
                        .setLatitude(data.getLatitude())
                        .setLongitude(data.getLongitude())
                        .setElevation(data.getElevation())
                        .setHeight(data.getHeight())
                        .setAttitudeHead(data.getAttitudeHead())
                        .setElevation(data.getElevation())
                        .setHorizontalSpeed(data.getHorizontalSpeed())
                        .setVerticalSpeed(data.getVerticalSpeed()));
        //推送当前遥控器信息到连接了websoket的web端
        deviceService.pushOsdDataToWeb(device.getWorkspaceId(), BizCodeEnum.DEVICE_OSD, from, data);
    }

具体的本地测试,我这里就不再做了,大家自行使用mqtt去做测试。

总结

无人机位置和姿态信息等数据的处理比较简单,看下来不外乎几个动作。存储redis,和相同工作空间设备共享信息,推送数据的web端供地图显示。今天的代码解析就到这里,有不懂的请留言大家一起讨论~~ 举个例子:为什么机场OSD数据处理的时候fillDockOsd方法里面覆盖redis数据的时候几个判断逻辑是这样的?机场模式和机场连接状态的关系?

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值