From aae8b5dc994fffdc211b1f0fc860840cb0d4687a Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Tue, 25 Nov 2025 14:34:02 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev' into dev

---
 ard-work/src/main/java/com/ard/work/component/netty/handler/MessageProtocolDecoder.java          |    2 
 ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCancelCmd.java |   33 +++
 ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java                             |    3 
 ard-route/src/main/java/com/ard/route/util/ToolUtils.java                                        |    4 
 ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java                              |   37 ++++
 ard-route/src/main/resources/mapper/ArdRouteInfoMapper.xml                                       |    9 
 ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundReplyCmd.java         |   35 +++
 ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java                  |   22 +
 ard-admin/src/main/java/com/ard/web/controller/work/UavManualFlyController.java                  |   19 ++
 ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlCancelDTO.java              |   22 ++
 ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java            |  122 ++++++-------
 ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java            |   13 
 ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java                      |   63 ++----
 ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlDTO.java                    |   51 +++++
 ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCmd.java       |   50 +++++
 ard-work/src/main/java/com/ard/work/service/ITyService.java                                      |   11 +
 16 files changed, 370 insertions(+), 126 deletions(-)

diff --git a/ard-admin/src/main/java/com/ard/web/controller/work/UavManualFlyController.java b/ard-admin/src/main/java/com/ard/web/controller/work/UavManualFlyController.java
index ab6aad4..e3b17f6 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/work/UavManualFlyController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/work/UavManualFlyController.java
@@ -108,6 +108,25 @@
         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")
diff --git a/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java b/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java
index d12c4d4..509e3f5 100644
--- a/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java
+++ b/ard-route/src/main/java/com/ard/route/domain/route/ArdRouteInfo.java
@@ -64,4 +64,7 @@
     /** 航线数据 */
     private byte[] routeData;
 
+    /** 行点总数 */
+    private Integer pointCount;
+
 }
diff --git a/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java b/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
index 1190edc..57f6522 100644
--- a/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
+++ b/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
@@ -21,6 +21,7 @@
 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;
@@ -121,6 +122,7 @@
         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();
     }
@@ -149,7 +151,15 @@
             }
             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;
     }
 
     /**
@@ -194,6 +204,7 @@
             information.setPath(url);
             information.setGroupId("0");     // 默认分组
             information.setReadOnly("1");   // 默认只读
+            // 增加航线实体类信息
             insertRouteInfomation(information);
             return MinioUploadResult.builder()
                     .url(url)
@@ -331,9 +342,10 @@
         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);
         }
@@ -359,11 +371,11 @@
             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());
             }
diff --git a/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java b/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java
index cd006dd..1a8b76e 100644
--- a/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java
+++ b/ard-route/src/main/java/com/ard/route/service/impl/UavRouteServiceImpl.java
@@ -178,54 +178,31 @@
 
         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);
         }
     }
 
diff --git a/ard-route/src/main/java/com/ard/route/util/ToolUtils.java b/ard-route/src/main/java/com/ard/route/util/ToolUtils.java
index 2d34f6c..d392f34 100644
--- a/ard-route/src/main/java/com/ard/route/util/ToolUtils.java
+++ b/ard-route/src/main/java/com/ard/route/util/ToolUtils.java
@@ -9,10 +9,6 @@
  */
 public class ToolUtils {
 
-    public static Boolean isLocal = false;
-
-    public static String localFilePath = "KmzFile/kmz/";
-
     public static String kmzPath = "kmz";
 
     /**
diff --git a/ard-route/src/main/resources/mapper/ArdRouteInfoMapper.xml b/ard-route/src/main/resources/mapper/ArdRouteInfoMapper.xml
index 5c23ab9..f1c5619 100644
--- a/ard-route/src/main/resources/mapper/ArdRouteInfoMapper.xml
+++ b/ard-route/src/main/resources/mapper/ArdRouteInfoMapper.xml
@@ -18,6 +18,7 @@
         <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">
@@ -33,7 +34,8 @@
                create_by,
                create_time,
                update_by,
-               update_time
+               update_time,
+               point_count
         from ard_route_info
     </sql>
 
@@ -74,6 +76,7 @@
             <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>
@@ -89,6 +92,7 @@
             <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>
 
@@ -107,6 +111,7 @@
             <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>
@@ -135,7 +140,7 @@
     </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>
diff --git a/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCancelCmd.java b/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCancelCmd.java
new file mode 100644
index 0000000..d6d137e
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCancelCmd.java
@@ -0,0 +1,33 @@
+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);          // 写入动作编号
+    }
+}
diff --git a/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCmd.java b/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCmd.java
new file mode 100644
index 0000000..3684c57
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundControlCmd.java
@@ -0,0 +1,50 @@
+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);             // 写入飞行模式
+    }
+}
diff --git a/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundReplyCmd.java b/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundReplyCmd.java
new file mode 100644
index 0000000..abfa708
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/component/netty/command/manual/FlyRoundReplyCmd.java
@@ -0,0 +1,35 @@
+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) {
+
+    }
+}
diff --git a/ard-work/src/main/java/com/ard/work/component/netty/handler/MessageProtocolDecoder.java b/ard-work/src/main/java/com/ard/work/component/netty/handler/MessageProtocolDecoder.java
index a36e068..337ed0c 100644
--- a/ard-work/src/main/java/com/ard/work/component/netty/handler/MessageProtocolDecoder.java
+++ b/ard-work/src/main/java/com/ard/work/component/netty/handler/MessageProtocolDecoder.java
@@ -174,6 +174,8 @@
                 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:
diff --git a/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java b/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
index a46cfb6..6d9f21e 100644
--- a/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/TyMessageHandler.java
@@ -290,14 +290,7 @@
         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(() -> {
@@ -309,6 +302,12 @@
             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);
                 }
diff --git a/ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlCancelDTO.java b/ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlCancelDTO.java
new file mode 100644
index 0000000..5132935
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlCancelDTO.java
@@ -0,0 +1,22 @@
+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;
+
+}
diff --git a/ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlDTO.java b/ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlDTO.java
new file mode 100644
index 0000000..7a10ec4
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/domain/dto/manual/FlyRoundControlDTO.java
@@ -0,0 +1,51 @@
+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;
+
+}
diff --git a/ard-work/src/main/java/com/ard/work/service/ITyService.java b/ard-work/src/main/java/com/ard/work/service/ITyService.java
index b5b1350..fb43af9 100644
--- a/ard-work/src/main/java/com/ard/work/service/ITyService.java
+++ b/ard-work/src/main/java/com/ard/work/service/ITyService.java
@@ -206,6 +206,17 @@
      */
     Boolean flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO);
 
+
+    /**
+     * 环绕飞行控制
+     */
+    Boolean FlyRoundControl(FlyRoundControlDTO flyRoundControlDTO);
+
+    /**
+     * 停止环绕飞行
+     */
+    Boolean FlyRoundControlCancel(FlyRoundControlCancelDTO flyRoundControlCancelDTO);
+
     /**
      * 紧急制动
      */
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
index cb0a57b..1627d98 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
@@ -1,5 +1,8 @@
 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;
@@ -16,8 +19,10 @@
 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;
@@ -57,7 +62,10 @@
     private OssServiceContext ossServiceContext;
     @Resource
     private ThreadPoolTaskExecutor threadPoolTaskExecutor;  // 使用线程池执行异步任务
-
+    @Resource
+    private UavControlService uavControlService;
+    @Autowired
+    private RedisCache redisCache;
     /**
      * 查询飞行架次列表
      *
@@ -128,6 +136,14 @@
                     .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);
@@ -144,12 +160,46 @@
         } 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());
     }
 
 
@@ -415,70 +465,6 @@
     }
 
     // 生成视频缩略图
-    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,
@@ -492,6 +478,12 @@
         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");
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
index 8db4ecb..432a475 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
@@ -603,6 +603,37 @@
         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;
+    }
+
     /**
      * 紧急制动
      */
@@ -1042,6 +1073,12 @@
                     logPrint("设置返航点", returnPointSettingReplyCmd.getResult(),
                             returnPointSettingReplyCmd.getErrorCode());
                 }
+                //到达环绕位置回复
+                if (tyCommand instanceof FlyRoundReplyCmd) {
+                    FlyRoundReplyCmd flyRoundReplyCmd = (FlyRoundReplyCmd) tyCommand;
+                    logPrint("到达环绕位置", flyRoundReplyCmd.getResult(),
+                            flyRoundReplyCmd.getErrorCode());
+                }
                 break;
             case (byte) 0xD2:
                 //设置视频码流回复

--
Gitblit v1.9.3