|  |  | 
 |  |  | package com.moral.api.service.impl; | 
 |  |  |  | 
 |  |  | import com.alibaba.fastjson.JSON; | 
 |  |  | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | 
 |  |  | import com.moral.api.entity.HistorySecondUav; | 
 |  |  | import com.moral.api.entity.Organization; | 
 |  |  | import com.moral.api.entity.*; | 
 |  |  | import com.moral.api.mapper.HistorySecondUavMapper; | 
 |  |  | import com.moral.api.pojo.dto.uav.UAVQueryTimeSlotDTO; | 
 |  |  | import com.moral.api.pojo.form.uav.UAVQueryTimeSlotForm; | 
 |  |  | 
 |  |  | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | 
 |  |  | import com.moral.api.service.OrganizationService; | 
 |  |  | import com.moral.api.service.SpecialDeviceService; | 
 |  |  | import com.moral.api.utils.UnitConvertUtils; | 
 |  |  | import com.moral.constant.RedisConstants; | 
 |  |  | import com.moral.util.DateUtils; | 
 |  |  | import com.moral.util.GeodesyUtils; | 
 |  |  | import com.moral.util.MathUtils; | 
 |  |  | import org.springframework.beans.factory.annotation.Autowired; | 
 |  |  | import org.springframework.data.redis.core.RedisTemplate; | 
 |  |  | import org.springframework.stereotype.Service; | 
 |  |  | import org.springframework.util.ObjectUtils; | 
 |  |  |  | 
 |  |  | import java.math.BigDecimal; | 
 |  |  | import java.util.*; | 
 |  |  | import java.util.concurrent.ConcurrentHashMap; | 
 |  |  | import java.util.function.Predicate; | 
 |  |  |  | 
 |  |  | /** | 
 |  |  | 
 |  |  | @Service | 
 |  |  | public class HistorySecondUavServiceImpl extends ServiceImpl<HistorySecondUavMapper, HistorySecondUav> implements HistorySecondUavService { | 
 |  |  |  | 
 |  |  |     /* | 
 |  |  |      * 筛选数据举例 | 
 |  |  |      * */ | 
 |  |  |     private Double filterDistance = 2d; | 
 |  |  |  | 
 |  |  |     @Autowired | 
 |  |  |     HistorySecondUavMapper historySecondUavMapper; | 
 |  |  |     @Autowired | 
 |  |  |     OrganizationService organizationService; | 
 |  |  |     @Autowired | 
 |  |  |     SpecialDeviceService specialDeviceService; | 
 |  |  |     @Autowired | 
 |  |  |     RedisTemplate redisTemplate; | 
 |  |  |  | 
 |  |  |     @Override | 
 |  |  |     public List<Date> queryDate(Integer organizationId) { | 
 |  |  | 
 |  |  |             childrenId.add(child.getId()); | 
 |  |  |         } | 
 |  |  |         childrenId.add(organizationId); | 
 |  |  |         queryWrapper.in("organization_id",childrenId); | 
 |  |  |         queryWrapper.in("organization_id", childrenId); | 
 |  |  |         //设置查询时间范围为180天 | 
 |  |  |         Date endDate = new Date(); | 
 |  |  |         Date startDate = DateUtils.addDays(endDate, -180); | 
 |  |  |         queryWrapper.between("batch",startDate,endDate); | 
 |  |  |         queryWrapper.between("batch", startDate, endDate); | 
 |  |  |         //设置查询字段 | 
 |  |  |         queryWrapper.select("DISTINCT batch"); | 
 |  |  |         queryWrapper.orderByDesc("batch"); | 
 |  |  |         //查询结果 | 
 |  |  |         List<HistorySecondUav> historySecondUavs = historySecondUavMapper.selectList(queryWrapper); | 
 |  |  |         //结果转为Date集合 | 
 |  |  | 
 |  |  |             childrenId.add(child.getId()); | 
 |  |  |         } | 
 |  |  |         childrenId.add(organizationId); | 
 |  |  |         wrapper.in("organization_id",childrenId); | 
 |  |  |         wrapper.in("organization_id", childrenId); | 
 |  |  |         //查询根据batch查,因为可能会有跨天飞行的情况。 | 
 |  |  |         wrapper.between("batch",startDate,endDate); | 
 |  |  |         wrapper.between("batch", startDate, endDate); | 
 |  |  |         //设置查询字段 | 
 |  |  |         wrapper.select("mac,time,batch"); | 
 |  |  |         //查询结果 | 
 |  |  |         List<HistorySecondUav> historySecondUavs = historySecondUavMapper.selectList(wrapper); | 
 |  |  |         //根据batch进行分批 | 
 |  |  |         Map<String,List<HistorySecondUav>> batchMap = new LinkedHashMap<>();//key为batch的string | 
 |  |  |         Map<String, List<HistorySecondUav>> batchMap = new LinkedHashMap<>();//key为batch的string | 
 |  |  |         for (HistorySecondUav historySecondUav : historySecondUavs) { | 
 |  |  |             //获取batch对应的数据集合 | 
 |  |  |             List<HistorySecondUav> list = batchMap.get(historySecondUav.getBatch().toString()); | 
 |  |  |             if(list!=null){ | 
 |  |  |             List<HistorySecondUav> list = batchMap.get(DateUtils.dateToDateString(historySecondUav.getBatch(), "yyyy-MM-dd HH:mm:ss")); | 
 |  |  |             if (list != null) { | 
 |  |  |                 list.add(historySecondUav); | 
 |  |  |             }else{ | 
 |  |  |             } else { | 
 |  |  |                 ArrayList<HistorySecondUav> newList = new ArrayList<>(); | 
 |  |  |                 newList.add(historySecondUav); | 
 |  |  |                 batchMap.put(historySecondUav.getBatch().toString(),newList); | 
 |  |  |                 batchMap.put(DateUtils.dateToDateString(historySecondUav.getBatch(), "yyyy-MM-dd HH:mm:ss"), newList); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |         //去除少于30条数据的集合 | 
 |  |  |         batchMap.values().removeIf(new Predicate<List<HistorySecondUav>>() { | 
 |  |  |             @Override | 
 |  |  |             public boolean test(List<HistorySecondUav> historySecondUavs) { | 
 |  |  |                 if(historySecondUavs.size()<=30) | 
 |  |  |                 if (historySecondUavs.size() <= 30) | 
 |  |  |                     return true; | 
 |  |  |                 return false; | 
 |  |  |             } | 
 |  |  |         }); | 
 |  |  |         //根据mac进行分类 | 
 |  |  |         Map<String,List<Map<String,List<HistorySecondUav>>>> macBatchMap = new LinkedHashMap<>();//key为mac | 
 |  |  |         Map<String, List<Map<String, List<HistorySecondUav>>>> macBatchMap = new LinkedHashMap<>();//key为mac | 
 |  |  |         //遍历batchMap将mac提取出来,作为macBatchMap的key,batch和数据map的数据集合放入value | 
 |  |  |         batchMap.forEach((key,value)->{ | 
 |  |  |         batchMap.forEach((key, value) -> { | 
 |  |  |             String mac = value.get(0).getMac(); | 
 |  |  |             List<Map<String, List<HistorySecondUav>>> maps = macBatchMap.get(mac); | 
 |  |  |             if(maps!=null){ | 
 |  |  |                 Map<String,List<HistorySecondUav>> map = new LinkedHashMap<>(); | 
 |  |  |                 map.put(key,value); | 
 |  |  |             if (maps != null) { | 
 |  |  |                 Map<String, List<HistorySecondUav>> map = new LinkedHashMap<>(); | 
 |  |  |                 map.put(key, value); | 
 |  |  |                 maps.add(map); | 
 |  |  |             }else{ | 
 |  |  |                 List<Map<String,List<HistorySecondUav>>> list = new ArrayList<>(); | 
 |  |  |                 Map<String,List<HistorySecondUav>> map = new LinkedHashMap<>(); | 
 |  |  |                 map.put(key,value); | 
 |  |  |             } else { | 
 |  |  |                 List<Map<String, List<HistorySecondUav>>> list = new ArrayList<>(); | 
 |  |  |                 Map<String, List<HistorySecondUav>> map = new LinkedHashMap<>(); | 
 |  |  |                 map.put(key, value); | 
 |  |  |                 list.add(map); | 
 |  |  |                 macBatchMap.put(value.get(0).getMac(),list); | 
 |  |  |                 macBatchMap.put(value.get(0).getMac(), list); | 
 |  |  |             } | 
 |  |  |         }); | 
 |  |  |         //封装返回数据 | 
 |  |  |         List<UAVQueryTimeSlotDTO> dtos = new ArrayList<>(); | 
 |  |  |         macBatchMap.forEach((key,value)->{ | 
 |  |  |         macBatchMap.forEach((key, value) -> { | 
 |  |  |             UAVQueryTimeSlotDTO dto = new UAVQueryTimeSlotDTO(); | 
 |  |  |             List<Map<String,Date>> timeSlots = new ArrayList<>(); | 
 |  |  |             List<Map<String, Object>> timeSlots = new ArrayList<>(); | 
 |  |  |             dto.setMac(key); | 
 |  |  |             //根据mac查询设备名称 | 
 |  |  |             dto.setName((String) specialDeviceService.getSpecialDeviceMapByMac(key).get("name")); | 
 |  |  |             //获取时间段 | 
 |  |  |             value.forEach(listValue->{ | 
 |  |  |                 listValue.forEach((mKey,mValue)->{ | 
 |  |  |             SpecialDevice specialDevice = specialDeviceService.getSpecialDeviceMapByMac(key); | 
 |  |  |             //如果设备不存在则退出循环 | 
 |  |  |             if (specialDevice == null) | 
 |  |  |                 return; | 
 |  |  |             dto.setName(specialDevice.getName()); | 
 |  |  |             //获取时间段与batch | 
 |  |  |             value.forEach(listValue -> { | 
 |  |  |                 listValue.forEach((mKey, mValue) -> { | 
 |  |  |                     Date slotStartDate = mValue.get(0).getTime(); | 
 |  |  |                     Date slotEndDate = mValue.get(mValue.size()-1).getTime(); | 
 |  |  |                     Map<String,Date> dateMap = new HashMap<>(); | 
 |  |  |                     dateMap.put("startTime",slotStartDate); | 
 |  |  |                     dateMap.put("endTime",slotEndDate); | 
 |  |  |                     Date slotEndDate = mValue.get(mValue.size() - 1).getTime(); | 
 |  |  |                     Map<String, Object> dateMap = new HashMap<>(); | 
 |  |  |                     dateMap.put("startTime", slotStartDate); | 
 |  |  |                     dateMap.put("endTime", slotEndDate); | 
 |  |  |                     dateMap.put("batch", mKey); | 
 |  |  |                     dateMap.put("total",mValue.size()); | 
 |  |  |                     timeSlots.add(dateMap); | 
 |  |  |                 }); | 
 |  |  |             }); | 
 |  |  |             dto.setTimeSlot(timeSlots); | 
 |  |  |             dtos.add(dto); | 
 |  |  |         }); | 
 |  |  |       return dtos; | 
 |  |  |         return dtos; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     @Override | 
 |  |  |     public List<HistorySecondUav> queryDataByBatch(String batch) { | 
 |  |  |         //batch转换类型 | 
 |  |  |         Date batchDate = DateUtils.getDate(batch, "yyyy-MM-dd HH:mm:ss"); | 
 |  |  |         //查询数据 | 
 |  |  |         QueryWrapper<HistorySecondUav> wrapper = new QueryWrapper<>(); | 
 |  |  |         wrapper.eq("batch", batchDate); | 
 |  |  |         wrapper.select("value,mac,time"); | 
 |  |  |         List<HistorySecondUav> datas = historySecondUavMapper.selectList(wrapper); | 
 |  |  |         //获取无人机飞行高度最低值 | 
 |  |  |         Double lowestHeight = 0d; | 
 |  |  |         for (HistorySecondUav data : datas) { | 
 |  |  |             String value = data.getValue(); | 
 |  |  |             Map<String, Object> valueMap = JSON.parseObject(value, Map.class); | 
 |  |  |             //判断value里面有没有高度 | 
 |  |  |             if (!valueMap.containsKey("flyhig")|| !valueMap.containsKey("flylat")|| !valueMap.containsKey("flylon")){ | 
 |  |  |                 continue; | 
 |  |  |             } | 
 |  |  |             //获取高度 | 
 |  |  |             Double height = Double.valueOf((String) valueMap.get("flyhig")); | 
 |  |  |             if (height < lowestHeight) | 
 |  |  |                 lowestHeight = height; | 
 |  |  |         } | 
 |  |  |         //获取最低点后,无人机所有飞行高度以最低点作为0点,所以无人机所有的高度全部加上最低点的绝对值。 | 
 |  |  |         lowestHeight = Math.abs(lowestHeight); | 
 |  |  |         for (HistorySecondUav data : datas) { | 
 |  |  |             String value = data.getValue(); | 
 |  |  |             Map<String, Object> valueMap = JSON.parseObject(value, Map.class); | 
 |  |  |             //判断value里面有没有高度 | 
 |  |  |             if (!valueMap.containsKey("flyhig")|| !valueMap.containsKey("flylat")|| !valueMap.containsKey("flylon")){ | 
 |  |  |                 continue; | 
 |  |  |             } | 
 |  |  |             //获取高度 | 
 |  |  |             Double height = Double.valueOf((String) valueMap.get("flyhig")); | 
 |  |  |             //加上最低点绝对值 | 
 |  |  |             height = MathUtils.add(height, lowestHeight); | 
 |  |  |             //重新封装数据 | 
 |  |  |             valueMap.put("flyhig", height); | 
 |  |  |             String newValue = JSON.toJSONString(valueMap); | 
 |  |  |             data.setValue(newValue); | 
 |  |  |         } | 
 |  |  |         //筛选无人机数据,保持每个点之间的距离在2米以上 | 
 |  |  |         datas = filterDatas(datas); | 
 |  |  |         if (datas.size()<2){ | 
 |  |  |             return null; | 
 |  |  |         } | 
 |  |  |         //转换单位 | 
 |  |  |         unitConvert(datas); | 
 |  |  |         return datas; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * @Description: 过滤数据 | 
 |  |  |      * @Param: [datas] | 
 |  |  |      * @return: java.util.List<com.moral.api.entity.HistorySecondUav> | 
 |  |  |      * @Author: 陈凯裕 | 
 |  |  |      * @Date: 2021/9/16 | 
 |  |  |      */ | 
 |  |  |     private List<HistorySecondUav> filterDatas(List<HistorySecondUav> datas) { | 
 |  |  |         //创建返回结果 | 
 |  |  |         List<HistorySecondUav> result = new ArrayList<>(); | 
 |  |  |         //把第一个数据暂时作为对比数据 | 
 |  |  |         HistorySecondUav tempData = datas.get(0); | 
 |  |  |         result.add(tempData); | 
 |  |  |         datas.remove(0); | 
 |  |  |         for (HistorySecondUav data : datas) { | 
 |  |  |             Double distance = getDistance(tempData, data); | 
 |  |  |             if (distance==null){ | 
 |  |  |                 continue; | 
 |  |  |             } | 
 |  |  |             if (distance > filterDistance) { | 
 |  |  |                 result.add(data); | 
 |  |  |                 tempData = data; | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |         return result; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * @Description: 获取两个数据之间的距离 | 
 |  |  |      * @Param: [uav1, uav2] | 
 |  |  |      * @return: java.lang.Double | 
 |  |  |      * @Author: 陈凯裕 | 
 |  |  |      * @Date: 2021/9/13 | 
 |  |  |      */ | 
 |  |  |     private Double getDistance(HistorySecondUav uav1, HistorySecondUav uav2) { | 
 |  |  |         String value1 = uav1.getValue(); | 
 |  |  |         String value2 = uav2.getValue(); | 
 |  |  |         Map<String, Object> value1Map = JSON.parseObject(value1, Map.class); | 
 |  |  |         Map<String, Object> value2Map = JSON.parseObject(value2, Map.class); | 
 |  |  |         //判断这两个数据里面有没有经纬度 | 
 |  |  |         if (!value1Map.containsKey("flylon")||!value1Map.containsKey("flylat")||!value1Map.containsKey("flyhig")|| | 
 |  |  |                 !value2Map.containsKey("flylon")||!value2Map.containsKey("flylat")||!value2Map.containsKey("flyhig")){ | 
 |  |  |             return null; | 
 |  |  |         } | 
 |  |  |         //获取数据1的经纬度和高度 | 
 |  |  |         Double longtitude1 = Double.valueOf((String) value1Map.get("flylon")); | 
 |  |  |         Double latitude1 = Double.valueOf((String) value1Map.get("flylat")); | 
 |  |  |         BigDecimal c1 = (BigDecimal) value1Map.get("flyhig"); | 
 |  |  |         double height1 = c1.doubleValue(); | 
 |  |  |         //获取数据2的经纬度和高度 | 
 |  |  |         Double longtitude2 = Double.valueOf((String) value2Map.get("flylon")); | 
 |  |  |         Double latitude2 = Double.valueOf((String) value2Map.get("flylat")); | 
 |  |  |         BigDecimal c2 = (BigDecimal) value2Map.get("flyhig"); | 
 |  |  |         double height2 = c2.doubleValue(); | 
 |  |  |         //计算经纬度之间的平面距离 | 
 |  |  |         Double planDistance = GeodesyUtils.getDistance(latitude1, longtitude1, latitude2, longtitude2); | 
 |  |  |         //根据直角三角形特性用勾股定理计算空间距离 | 
 |  |  |         Double heightDsitance = Math.abs(MathUtils.sub(height2, height1)); | 
 |  |  |         Double Distance = Math.sqrt(MathUtils.mul(planDistance, planDistance) + MathUtils.mul(heightDsitance, heightDsitance)); | 
 |  |  |         return Distance; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * @Description: 数据单位转换以及拼接 | 
 |  |  |      * @Param: [datas] | 
 |  |  |      * @return: java.util.List<com.moral.api.entity.HistorySecondUav> | 
 |  |  |      * @Author: 陈凯裕 | 
 |  |  |      * @Date: 2021/9/16 | 
 |  |  |      */ | 
 |  |  |     private void unitConvert(List<HistorySecondUav> datas) { | 
 |  |  |         //获取转换公式 | 
 |  |  |         List<UnitConversion> unitConversions = redisTemplate.opsForList().range(RedisConstants.UNIT_CONVERSION, 0, -1); | 
 |  |  |         //获取无人机基本数据 | 
 |  |  |         SpecialDevice specialDevice = (SpecialDevice) redisTemplate.opsForHash().get(RedisConstants.SPECIAL_DEVICE_INFO, datas.get(0).getMac()); | 
 |  |  |         //获取无人机型号所有的因子信息 | 
 |  |  |         List<Sensor> sensors = specialDevice.getVersion().getSensors(); | 
 |  |  |         Map<String, Sensor> sensorsMap = new HashMap<>(); | 
 |  |  |         sensors.forEach(value -> sensorsMap.put(value.getCode(), value)); | 
 |  |  |         //转换单位并且拼接单位 | 
 |  |  |         for (HistorySecondUav data : datas) { | 
 |  |  |             String valueStr = data.getValue(); | 
 |  |  |             ConcurrentHashMap<String, Object> valueMap = JSON.parseObject(valueStr, ConcurrentHashMap.class); | 
 |  |  |             Set<Map.Entry<String, Object>> entries = valueMap.entrySet(); | 
 |  |  |             Iterator<Map.Entry<String, Object>> iterator = entries.iterator(); | 
 |  |  |             //遍历单个数据的所有因子 | 
 |  |  |             while(iterator.hasNext()){ | 
 |  |  |                 Map.Entry<String, Object> entry = iterator.next(); | 
 |  |  |                 String code = entry.getKey(); | 
 |  |  |                 String value = String.valueOf(entry.getValue()); | 
 |  |  |                 Sensor sensor = sensorsMap.get(code); | 
 |  |  |                 if (sensor == null) {//如果型号中没有该因子则移除 | 
 |  |  |                     valueMap.remove(code); | 
 |  |  |                     continue; | 
 |  |  |                 } | 
 |  |  |                 String unit = sensor.getUnit(); | 
 |  |  |                 String unitKey = sensor.getUnitKey(); | 
 |  |  |                 String showUnit = sensor.getShowUnit(); | 
 |  |  |                 String showUnitKey = sensor.getShowUnitKey(); | 
 |  |  |                 //如果源单位和显示单位相同则直接拼接单位 | 
 |  |  |                 if (showUnitKey.equals(unitKey)) { | 
 |  |  |                     value += " " + unit; | 
 |  |  |                 } else { | 
 |  |  |                     String formula = sensor.getFormula(); | 
 |  |  |                     //如果sensor中的公式为空则从缓存中获取公式 | 
 |  |  |                     if (ObjectUtils.isEmpty(formula)) { | 
 |  |  |                         for (UnitConversion unitConversion : unitConversions) { | 
 |  |  |                             if (unitConversion.getOriginalUnitKey().equals(unitKey) && unitConversion.getTargetUnitKey().equals(showUnitKey)) | 
 |  |  |                                 formula = unitConversion.getFormula(); | 
 |  |  |                         } | 
 |  |  |                     } | 
 |  |  |                     //计算转换单位后的数据 | 
 |  |  |                     String resultValue = UnitConvertUtils.calculate((String) value, formula); | 
 |  |  |                     if (resultValue != null) { | 
 |  |  |                         resultValue += showUnit; | 
 |  |  |                     } else {//如果转换出的数据为null,则代表缓存中也没有公式,依然使用源单位。 | 
 |  |  |                         resultValue = value + unit; | 
 |  |  |                     } | 
 |  |  |                     value = resultValue; | 
 |  |  |                 } | 
 |  |  |                 //重新放入转换后的数据 | 
 |  |  |                 valueMap.put(code, value); | 
 |  |  |             } | 
 |  |  |             String value = JSON.toJSONString(valueMap); | 
 |  |  |             data.setValue(value); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  | } |