Merge remote-tracking branch 'origin/dev' into dev
5 files added
11 files modified
| | |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | @ApiOperation(value = "开始环绕飞行", notes = "速度speed:0~20 单位m/s " + |
| | | "环绕半径radius:单位m,"+ |
| | | "飞行模式flyMode:0-节能模式【飞机直线飞到目标点,需飞机在空中】," + |
| | | "1-安全模式【飞机飞行至目标点高度再平飞到目标点,飞机可以在地面或空中】 默认为节能模式") |
| | | @ApiOperationSupport(order = 9) |
| | | @PostMapping("/flyRoundControl") |
| | | public AjaxResult flyRoundControl(@RequestBody FlyRoundControlDTO flyRoundControlDTO) { |
| | | tyService.FlyRoundControl(flyRoundControlDTO); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | @ApiOperation("停止环绕飞行") |
| | | @ApiOperationSupport(order = 10) |
| | | @PostMapping("/flyRoundControlCancel") |
| | | public AjaxResult flyRoundControlCancel(@RequestBody FlyRoundControlCancelDTO flyRoundControlCancelDTO) { |
| | | tyService.FlyRoundControlCancel(flyRoundControlCancelDTO); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | @ApiOperation(value = "紧急制动", notes = "动作参数actionParam: 0-解除紧急制动 1-紧急制动") |
| | | @ApiOperationSupport(order = 11) |
| | | @PostMapping("/emeragencyBrake") |
| | |
| | | /** 航线数据 */ |
| | | private byte[] routeData; |
| | | |
| | | /** 行点总数 */ |
| | | private Integer pointCount; |
| | | |
| | | } |
| | |
| | | import com.ard.route.domain.dto.ArdNoflyDto; |
| | | import com.ard.route.domain.route.ArdRouteInfo; |
| | | import com.ard.route.domain.sixhe.ArdRouteChange; |
| | | import com.ard.route.domain.vo.KmzInfoVo; |
| | | import com.ard.route.domain.vo.KmzResultVo; |
| | | import com.ard.route.mapper.ArdRouteInfoMapper; |
| | | import com.ard.route.service.IArdRouteInfoService; |
| | |
| | | KmzResultVo kmzResult = routeService.buildKmz(ardRouteInfo.getUavRouteReq(), ardRouteInfo.getName()); |
| | | ardRouteInfo.setPath(kmzResult.getPath()); |
| | | ardRouteInfo.setReadOnly("0"); |
| | | ardRouteInfo.setPointCount(getPointCount(ardRouteInfo)); |
| | | ardRouteInfoMapper.insertArdRouteInfo(ardRouteInfo); |
| | | return kmzResult.getPath(); |
| | | } |
| | |
| | | } |
| | | ardRouteInfo.setPath(kmzResult.getPath()); |
| | | } |
| | | ardRouteInfo.setPointCount(getPointCount(ardRouteInfo)); |
| | | return ardRouteInfoMapper.updateArdRouteInfo(ardRouteInfo); |
| | | } |
| | | |
| | | private Integer getPointCount(ArdRouteInfo ardRouteInfo) { |
| | | if (ardRouteInfo.getUavRouteReq().getRoutePointList() != null) { |
| | | return ardRouteInfo.getUavRouteReq().getRoutePointList().size(); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | /** |
| | |
| | | information.setPath(url); |
| | | information.setGroupId("0"); // 默认分组 |
| | | information.setReadOnly("1"); // 默认只读 |
| | | // 增加航线实体类信息 |
| | | insertRouteInfomation(information); |
| | | return MinioUploadResult.builder() |
| | | .url(url) |
| | |
| | | if (autoFlightSpeed != null){ |
| | | planLineData.setAutoSpeed(8); |
| | | } |
| | | if (globalTransitionalSpeed != null){ |
| | | planLineData.setMaxSpeed(15); |
| | | } |
| | | planLineData.setMaxSpeed(15); |
| | | // if (globalTransitionalSpeed != null){ |
| | | // planLineData.setMaxSpeed(15); |
| | | // } |
| | | if (exitOnRcLostAction != null){ |
| | | planLineData.setFinishedAction(1); |
| | | } |
| | |
| | | if (height != null){ |
| | | pointData.setHeight(height.floatValue()); |
| | | } |
| | | Double longitude = routePointReq.getLatitude(); |
| | | Double longitude = routePointReq.getLongitude(); |
| | | if (longitude != null){ |
| | | pointData.setLng(longitude.floatValue()); |
| | | } |
| | | Double latitude = routePointReq.getLongitude(); |
| | | Double latitude = routePointReq.getLatitude(); |
| | | if (latitude != null){ |
| | | pointData.setLat(latitude.floatValue()); |
| | | } |
| | |
| | | |
| | | String kml = XML_HEADER + xStream.toXML(kmlInfo); |
| | | String wpml = XML_HEADER + xStream.toXML(wpmlInfo); |
| | | |
| | | KmzResultVo result = new KmzResultVo(); |
| | | try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| | | ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) { |
| | | zipOutputStream.setLevel(0); |
| | | |
| | | // 判断本地生成kmz文件 |
| | | if (isLocal) { |
| | | try (FileOutputStream fileOutputStream = new FileOutputStream(localFilePath + fileName + ".kmz"); |
| | | ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream)) { |
| | | zipOutputStream.setLevel(0); // 0 表示不压缩,存储方式 |
| | | // 创建 wpmz 目录中的 template.kml 文件条目 |
| | | buildZipFile("wpmz/template.kml", zipOutputStream, kml); |
| | | // 创建 wpmz 目录中的 waylines.wpml 文件条目 |
| | | buildZipFile("wpmz/waylines.wpml", zipOutputStream, wpml); |
| | | |
| | | // 创建KMZ文件内容 |
| | | buildZipFile("wpmz/template.kml", zipOutputStream, kml); |
| | | buildZipFile("wpmz/waylines.wpml", zipOutputStream, wpml); |
| | | zipOutputStream.finish(); |
| | | zipOutputStream.flush(); |
| | | |
| | | } catch (Exception e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | result.setPath(localFilePath + fileName + ".kmz"); |
| | | // 创建MultipartFile对象 |
| | | MultipartFile multipartFile = new MockMultipartFile( |
| | | fileName + ".kmz", |
| | | fileName + ".kmz", |
| | | "application/vnd.google-earth.kmz", |
| | | new ByteArrayInputStream(baos.toByteArray()) |
| | | ); |
| | | String kmzFilePath = ossService.upload(kmzPath,multipartFile); |
| | | result.setPath(kmzFilePath); |
| | | return result; |
| | | }else { |
| | | try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| | | ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) { |
| | | zipOutputStream.setLevel(0); |
| | | |
| | | // 创建 wpmz 目录中的 template.kml 文件条目 |
| | | buildZipFile("wpmz/template.kml", zipOutputStream, kml); |
| | | |
| | | // 创建 wpmz 目录中的 waylines.wpml 文件条目 |
| | | buildZipFile("wpmz/waylines.wpml", zipOutputStream, wpml); |
| | | |
| | | zipOutputStream.finish(); |
| | | zipOutputStream.flush(); |
| | | |
| | | // 获取文件大小 |
| | | Double fileSize = (double) baos.size(); // 字节数 |
| | | |
| | | // 创建MultipartFile对象 |
| | | MultipartFile multipartFile = new MockMultipartFile( |
| | | fileName + ".kmz", |
| | | fileName + ".kmz", |
| | | "application/vnd.google-earth.kmz", |
| | | new ByteArrayInputStream(baos.toByteArray()) |
| | | ); |
| | | String kmzFilePath = ossService.upload(kmzPath,multipartFile); |
| | | result.setPath(kmzFilePath); |
| | | return result; |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("KMZ文件生成或上传失败", e); |
| | | } |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("KMZ文件生成或上传失败", e); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | public class ToolUtils { |
| | | |
| | | public static Boolean isLocal = false; |
| | | |
| | | public static String localFilePath = "KmzFile/kmz/"; |
| | | |
| | | public static String kmzPath = "kmz"; |
| | | |
| | | /** |
| | |
| | | <result property="createTime" column="create_time" /> |
| | | <result property="updateBy" column="update_by" /> |
| | | <result property="updateTime" column="update_time" /> |
| | | <result property="pointCount" column="point_count" /> |
| | | </resultMap> |
| | | |
| | | <sql id="selectArdRouteInfoVo"> |
| | |
| | | create_by, |
| | | create_time, |
| | | update_by, |
| | | update_time |
| | | update_time, |
| | | point_count |
| | | from ard_route_info |
| | | </sql> |
| | | |
| | |
| | | <if test="createTime != null">create_time,</if> |
| | | <if test="updateBy != null">update_by,</if> |
| | | <if test="updateTime != null">update_time,</if> |
| | | <if test="pointCount != null">point_count,</if> |
| | | </trim> |
| | | <trim prefix="values (" suffix=")" suffixOverrides=","> |
| | | <if test="id != null">#{id},</if> |
| | |
| | | <if test="createTime != null">#{createTime},</if> |
| | | <if test="updateBy != null">#{updateBy},</if> |
| | | <if test="updateTime != null">#{updateTime},</if> |
| | | <if test="pointCount != null">#{pointCount},</if> |
| | | </trim> |
| | | </insert> |
| | | |
| | |
| | | <if test="createTime != null">create_time = #{createTime},</if> |
| | | <if test="updateBy != null">update_by = #{updateBy},</if> |
| | | <if test="updateTime != null">update_time = #{updateTime},</if> |
| | | <if test="pointCount != null">point_count = #{pointCount},</if> |
| | | </trim> |
| | | where id = #{id} |
| | | </update> |
| | |
| | | </delete> |
| | | |
| | | <select id="selectArdRouteInfoByGroupId" parameterType="String" resultMap="ArdRouteInfoResult"> |
| | | select id,name,path,group_id,type,aircraft,payload,read_only,create_by,create_time,update_by,update_time from ard_route_info |
| | | select id,name,path,group_id,type,aircraft,payload,read_only,create_by,create_time,update_by,update_time,point_count from ard_route_info |
| | | <where> |
| | | <if test="groupId != null "> and group_id = #{groupId}</if> |
| | | </where> |
| New file |
| | |
| | | package com.ard.work.component.netty.command.manual; |
| | | |
| | | import com.ard.work.component.netty.command.BaseCmd; |
| | | import com.ard.work.domain.dto.manual.FlyRoundControlCancelDTO; |
| | | import com.ard.work.domain.dto.manual.FlyRoundControlDTO; |
| | | import io.netty.buffer.ByteBuf; |
| | | |
| | | /** |
| | | * 环绕飞行控制指令 |
| | | * |
| | | * @author liusuyi |
| | | * @date 2024年10月26日15:43 |
| | | **/ |
| | | public class FlyRoundControlCancelCmd extends BaseCmd { |
| | | |
| | | |
| | | public FlyRoundControlCancelCmd(FlyRoundControlCancelDTO flyRoundControlCancelDTO) { |
| | | setDataHeader((short) 0x7479); |
| | | setDataLength((short) 18); |
| | | setBoxSn(flyRoundControlCancelDTO.getBoxSn().getBytes()); |
| | | setCmdCode((byte) 0xD1); |
| | | } |
| | | |
| | | |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); // 写入数据头 |
| | | out.writeShort(getDataLength()); // 写入数据长度 |
| | | out.writeBytes(getBoxSn()); // 写入盒子SN |
| | | out.writeByte(getCmdCode()); // 写入命令码 |
| | | out.writeByte(0x00); // 写入加密标志 |
| | | out.writeByte(0x3C); // 写入动作编号 |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ard.work.component.netty.command.manual; |
| | | |
| | | import com.ard.work.component.netty.command.BaseCmd; |
| | | import com.ard.work.domain.dto.manual.FlyPointControlDTO; |
| | | import com.ard.work.domain.dto.manual.FlyRoundControlDTO; |
| | | import io.netty.buffer.ByteBuf; |
| | | |
| | | /** |
| | | * 环绕飞行控制指令 |
| | | * |
| | | * @author liusuyi |
| | | * @date 2024年10月26日15:43 |
| | | **/ |
| | | public class FlyRoundControlCmd extends BaseCmd { |
| | | private final double longitude; // 兴趣点经度 |
| | | private final double latitude; // 兴趣点纬度 |
| | | private final float altitude; // 兴趣点高度 |
| | | private final float speed; // 环绕速度 |
| | | private final float radius; // 环绕半径 |
| | | private final byte flyMode; // 飞行模式 |
| | | |
| | | public FlyRoundControlCmd(FlyRoundControlDTO flyRoundControlDTO) { |
| | | setDataHeader((short) 0x7479); |
| | | setDataLength((short) 47); |
| | | setBoxSn(flyRoundControlDTO.getBoxSn().getBytes()); |
| | | setCmdCode((byte) 0xD1); |
| | | this.longitude = flyRoundControlDTO.getLongitude(); |
| | | this.latitude = flyRoundControlDTO.getLatitude(); |
| | | this.altitude = flyRoundControlDTO.getAltitude(); |
| | | this.speed = flyRoundControlDTO.getSpeed(); |
| | | this.radius= flyRoundControlDTO.getRadius(); |
| | | this.flyMode = flyRoundControlDTO.getFlyMode(); |
| | | } |
| | | |
| | | |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); // 写入数据头 |
| | | out.writeShort(getDataLength()); // 写入数据长度 |
| | | out.writeBytes(getBoxSn()); // 写入盒子SN |
| | | out.writeByte(getCmdCode()); // 写入命令码 |
| | | out.writeByte(0x00); // 写入加密标志 |
| | | out.writeByte(0x3B); // 写入动作编号 |
| | | out.writeDouble(longitude); // 写入兴趣点经度 |
| | | out.writeDouble(latitude); // 写入兴趣点纬度 |
| | | out.writeFloat(altitude); // 写入兴趣点高度 |
| | | out.writeFloat(speed); // 写入环绕速度 |
| | | out.writeFloat(radius); // 写入环绕半径 |
| | | out.writeByte(flyMode); // 写入飞行模式 |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ard.work.component.netty.command.manual; |
| | | |
| | | import com.ard.work.component.netty.command.BaseCmd; |
| | | import io.netty.buffer.ByteBuf; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | /** |
| | | * 到达环绕位置回复指令 |
| | | * |
| | | * @author liusuyi |
| | | * @date 2024年10月26日15:43 |
| | | **/ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | public class FlyRoundReplyCmd extends BaseCmd { |
| | | |
| | | private final byte encryptFlag = 0x00; // 加密标志 |
| | | private final byte actionCode = 0x3B; // 动作编号 |
| | | private final byte result; // 执行结果 |
| | | private final Integer errorCode; // 错误码 |
| | | |
| | | public FlyRoundReplyCmd(String boxSn, Byte result, Integer errorCode) { |
| | | setDataHeader((short) 0x6A77); |
| | | setDataLength((short) 19); |
| | | setBoxSn(boxSn.getBytes()); |
| | | setCmdCode((byte) 0xD1); |
| | | this.result = result; |
| | | this.errorCode = errorCode; |
| | | } |
| | | |
| | | public void encode(ByteBuf out) { |
| | | |
| | | } |
| | | } |
| | |
| | | return new EmergencyBrakeReplyCmd(boxSn, result, errorCode); |
| | | case 0x39: |
| | | return new PointControlReachedReplyCmd(boxSn, result, errorCode); |
| | | case 0x3B: |
| | | return new FlyRoundReplyCmd(boxSn, result, errorCode); |
| | | case 0x22: |
| | | return new CameraModeReplyCmd(boxSn, result, errorCode); |
| | | case 0x23: |
| | |
| | | Long userId = Long.parseLong(userIdStr); |
| | | WebSocketSessionManager.removeSession(userId); |
| | | log.info("【cmd】size: {}", WebSocketSessionManager.getOnlineUserCount()); |
| | | |
| | | // 释放该用户所有 UAV 控制权 |
| | | uavControlService.releaseAllControls(userId); |
| | | uavStreamManager.stopAllPullStreamByUser(userId); |
| | | uavStateManager.removeSubscribe(userId); |
| | | uavTelemetryManager.removeSubscribe(userId); |
| | | uavSignalManager.removeSubscribe(userId); |
| | | uavControlManager.removeSubscribe(userId); |
| | | |
| | | // 延迟10秒删除token(若期间没重连) |
| | | CompletableFuture.runAsync(() -> { |
| | |
| | | if (!WebSocketSessionManager.containsUser(userId)) { |
| | | LoginUser loginUser = tokenService.getLoginUserByUserId(userId); |
| | | if (loginUser != null) { |
| | | uavControlService.releaseAllControls(userId); |
| | | uavStreamManager.stopAllPullStreamByUser(userId); |
| | | uavStateManager.removeSubscribe(userId); |
| | | uavTelemetryManager.removeSubscribe(userId); |
| | | uavSignalManager.removeSubscribe(userId); |
| | | uavControlManager.removeSubscribe(userId); |
| | | tokenService.delLoginUser(loginUser.getToken(), userId); |
| | | log.info("【cmd】用户 [{}] 超时未重连,已删除 token", userId); |
| | | } |
| New file |
| | |
| | | package com.ard.work.domain.dto.manual; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * 环绕飞行控制实体对象 |
| | | * |
| | | * @author liusuyi |
| | | * @date 2024年10月26日9:02 |
| | | **/ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class FlyRoundControlCancelDTO { |
| | | |
| | | private String boxSn; |
| | | |
| | | } |
| New file |
| | |
| | | package com.ard.work.domain.dto.manual; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * 环绕飞行控制实体对象 |
| | | * |
| | | * @author liusuyi |
| | | * @date 2024年10月26日9:02 |
| | | **/ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class FlyRoundControlDTO { |
| | | |
| | | private String boxSn; |
| | | |
| | | /** |
| | | * 兴趣点经度 |
| | | */ |
| | | private Double longitude; |
| | | /** |
| | | * 兴趣点纬度 |
| | | */ |
| | | private Double latitude; |
| | | /** |
| | | * 兴趣点高度 |
| | | */ |
| | | private Float altitude; |
| | | |
| | | /** |
| | | * 环绕速度 |
| | | */ |
| | | private Float speed; |
| | | |
| | | /** |
| | | * 环绕半径 |
| | | */ |
| | | private Float radius; |
| | | |
| | | /** |
| | | * 飞行模式 |
| | | */ |
| | | @Builder.Default |
| | | private byte flyMode = 0; |
| | | |
| | | } |
| | |
| | | */ |
| | | Boolean flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO); |
| | | |
| | | |
| | | /** |
| | | * 环绕飞行控制 |
| | | */ |
| | | Boolean FlyRoundControl(FlyRoundControlDTO flyRoundControlDTO); |
| | | |
| | | /** |
| | | * 停止环绕飞行 |
| | | */ |
| | | Boolean FlyRoundControlCancel(FlyRoundControlCancelDTO flyRoundControlCancelDTO); |
| | | |
| | | /** |
| | | * 紧急制动 |
| | | */ |
| | |
| | | package com.ard.work.service.impl; |
| | | |
| | | import com.ard.common.constant.CacheConstants; |
| | | import com.ard.common.core.domain.entity.SysUser; |
| | | import com.ard.common.core.redis.RedisCache; |
| | | import com.ard.common.utils.DateUtils; |
| | | import com.ard.common.utils.file.MimeTypeUtils; |
| | | import com.ard.common.utils.uuid.IdUtils; |
| | |
| | | import com.ard.work.mapper.ArdMediaMapper; |
| | | import com.ard.work.service.IArdFlightTaskService; |
| | | import com.ard.work.service.IArdServerInfoService; |
| | | import com.ard.work.service.control.UavControlService; |
| | | import com.ard.work.util.osSelect; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | private OssServiceContext ossServiceContext; |
| | | @Resource |
| | | private ThreadPoolTaskExecutor threadPoolTaskExecutor; // 使用线程池执行异步任务 |
| | | |
| | | @Resource |
| | | private UavControlService uavControlService; |
| | | @Autowired |
| | | private RedisCache redisCache; |
| | | /** |
| | | * 查询飞行架次列表 |
| | | * |
| | |
| | | .startTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, boxHistoryReceiver.getStartTime())) |
| | | .endTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, boxHistoryReceiver.getEndTime())) |
| | | .build(); |
| | | |
| | | Long flyUserId = uavControlService.getCurrentController(flightTask.getBoxSn(), "fly"); |
| | | if (flyUserId != null) { |
| | | SysUser flyUser = redisCache.getCacheObject(CacheConstants.SYS_USER_KEY + flyUserId); |
| | | if (flyUser != null && flyUser.getNickName() != null) { |
| | | flightTask.setCreateBy(flyUser.getNickName()); |
| | | } |
| | | } |
| | | flightTask.setCreateTime(DateUtils.getNowDate()); |
| | | // 插入新记录 |
| | | int rows = ardFlightTaskMapper.insertArdFlightTask(flightTask); |
| | |
| | | } else { |
| | | // 如果存在,则更新记录 |
| | | log.debug("Updating task {}", boxHistoryReceiver.getTaskId()); |
| | | updateFlightTask(ardFlightTask, boxHistoryReceiver); |
| | | // 增量更新 |
| | | updateVideoToDatabase(ardFlightTask, boxHistoryReceiver.getVideoPathList()); |
| | | // 增量更新图片 |
| | | updatePhotoToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn()); |
| | | |
| | | } |
| | | } |
| | | private void updateFlightTask(ArdFlightTask local, BoxHistoryReceiver remote) { |
| | | |
| | | boolean needUpdate = false; |
| | | |
| | | // endTime 更新(重要!!!) |
| | | Date newEnd = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, remote.getEndTime()); |
| | | if (newEnd != null && (local.getEndTime() == null || !local.getEndTime().equals(newEnd))) { |
| | | local.setEndTime(newEnd); |
| | | needUpdate = true; |
| | | } |
| | | |
| | | // photoCount 更新 |
| | | int remotePhotoCount = Integer.parseInt(remote.getPhotoCount()); |
| | | if (local.getPhotoCount() != remotePhotoCount) { |
| | | local.setPhotoCount(remotePhotoCount); |
| | | needUpdate = true; |
| | | } |
| | | |
| | | // videoCount 更新 |
| | | int remoteVideoCount = remote.getVideoPathList() != null ? remote.getVideoPathList().size() : 0; |
| | | if (local.getVideoCount() != remoteVideoCount) { |
| | | local.setVideoCount(remoteVideoCount); |
| | | needUpdate = true; |
| | | } |
| | | |
| | | if (needUpdate) { |
| | | ardFlightTaskMapper.updateArdFlightTask(local); |
| | | } |
| | | |
| | | // 其它媒体增量更新 |
| | | updateVideoToDatabase(local, remote.getVideoPathList()); |
| | | updatePhotoToDatabase(local, local.getServerId(), remote.getBoxSn()); |
| | | } |
| | | |
| | | |
| | |
| | | } |
| | | |
| | | // 生成视频缩略图 |
| | | public ByteArrayInputStream generateThumbnail1(String videoFile, int skipFrames, int targetWidth) { |
| | | // long start = System.currentTimeMillis(); |
| | | // FFmpegFrameGrabber grabber = null; |
| | | // |
| | | // try { |
| | | // grabber = new FFmpegFrameGrabber(videoFile); |
| | | // grabber.start(); |
| | | // |
| | | // int totalFrames = grabber.getLengthInFrames(); |
| | | // System.out.println("视频总帧数: " + totalFrames); |
| | | // System.out.println("视频时长: " + grabber.getLengthInTime() / 1000000 + "秒"); |
| | | // |
| | | // Frame frame = null; |
| | | // int currentFrame = 0; |
| | | // |
| | | // // 读取指定帧数后的第一帧有效图像 |
| | | // while (currentFrame < totalFrames) { |
| | | // frame = grabber.grabImage(); |
| | | // if (frame == null) { |
| | | // break; |
| | | // } |
| | | // |
| | | // if (currentFrame > skipFrames && frame.image != null) { |
| | | // break; |
| | | // } |
| | | // currentFrame++; |
| | | // } |
| | | // |
| | | // if (frame == null || frame.image == null) { |
| | | // throw new Exception("无法获取有效视频帧"); |
| | | // } |
| | | // |
| | | // // 转换 Frame 为 BufferedImage |
| | | // Java2DFrameConverter converter = new Java2DFrameConverter(); |
| | | // BufferedImage image = converter.getBufferedImage(frame); |
| | | // |
| | | // // 按目标宽度等比缩放 |
| | | // int targetHeight = (int) ((double) targetWidth / image.getWidth() * image.getHeight()); |
| | | // BufferedImage scaledImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_3BYTE_BGR); |
| | | // scaledImage.getGraphics().drawImage(image, 0, 0, targetWidth, targetHeight, null); |
| | | // |
| | | // // 转成 ByteArrayInputStream |
| | | // ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| | | // ImageIO.write(scaledImage, "jpg", baos); |
| | | // long cost = System.currentTimeMillis() - start; |
| | | // System.out.println("处理耗时: " + cost + "ms"); |
| | | // |
| | | // return new ByteArrayInputStream(baos.toByteArray()); |
| | | // |
| | | // } catch (Exception e) { |
| | | // throw new RuntimeException(e); |
| | | // } finally { |
| | | // if (grabber != null) { |
| | | // try { |
| | | // grabber.stop(); |
| | | // grabber.close(); |
| | | // } catch (Exception e) { |
| | | // e.printStackTrace(); |
| | | // } |
| | | // } |
| | | // } |
| | | return null; |
| | | } |
| | | |
| | | public ByteArrayInputStream generateThumbnail( |
| | | String videoFile, |
| | | int skipSeconds, |
| | |
| | | try { |
| | | List<String> command = new ArrayList<>(); |
| | | command.add(ffmpegPath); |
| | | |
| | | command.add("-timeout"); |
| | | command.add("3000000"); |
| | | command.add("-rw_timeout"); |
| | | command.add("3000000"); |
| | | |
| | | command.add("-ss"); |
| | | command.add(String.format("00:00:%02d", skipSeconds)); |
| | | command.add("-i"); |
| | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 环绕飞行控制 |
| | | */ |
| | | @Override |
| | | public Boolean FlyRoundControl(FlyRoundControlDTO flyRoundControlDTO) { |
| | | String boxSn = flyRoundControlDTO.getBoxSn(); |
| | | ArdDroneInfo ardDroneInfo = ardDroneService.selectArdDroneByboxSn(boxSn); |
| | | if (ardDroneInfo == null) { |
| | | throw new ControlException("未找到对应的飞行器信息,boxSn:{}" + boxSn); |
| | | } |
| | | FlyRoundControlCmd flyPointControlCmd = new FlyRoundControlCmd(flyRoundControlDTO); |
| | | this.sendMessage(ardDroneInfo.getServerId(), flyPointControlCmd); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 停止环绕飞行控制 |
| | | */ |
| | | @Override |
| | | public Boolean FlyRoundControlCancel(FlyRoundControlCancelDTO flyRoundControlCancelDTO) { |
| | | String boxSn = flyRoundControlCancelDTO.getBoxSn(); |
| | | ArdDroneInfo ardDroneInfo = ardDroneService.selectArdDroneByboxSn(boxSn); |
| | | if (ardDroneInfo == null) { |
| | | throw new ControlException("未找到对应的飞行器信息,boxSn:{}" + boxSn); |
| | | } |
| | | FlyRoundControlCancelCmd flyRoundControlCancelCmd = new FlyRoundControlCancelCmd(flyRoundControlCancelDTO); |
| | | this.sendMessage(ardDroneInfo.getServerId(), flyRoundControlCancelCmd); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 紧急制动 |
| | | */ |
| | |
| | | logPrint("设置返航点", returnPointSettingReplyCmd.getResult(), |
| | | returnPointSettingReplyCmd.getErrorCode()); |
| | | } |
| | | //到达环绕位置回复 |
| | | if (tyCommand instanceof FlyRoundReplyCmd) { |
| | | FlyRoundReplyCmd flyRoundReplyCmd = (FlyRoundReplyCmd) tyCommand; |
| | | logPrint("到达环绕位置", flyRoundReplyCmd.getResult(), |
| | | flyRoundReplyCmd.getErrorCode()); |
| | | } |
| | | break; |
| | | case (byte) 0xD2: |
| | | //设置视频码流回复 |