From 8a1be2c973eacb1fb661caf920321eb2c58ef87c Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Mon, 27 Oct 2025 17:15:18 +0800
Subject: [PATCH] 增加云台速度简易控制接口
---
ard-admin/src/main/java/com/ard/web/controller/work/UavGimbalController.java | 14 ++++
ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedService.java | 74 ++++++++++++++++++++++++
ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java | 14 +++-
ard-work/src/main/java/com/ard/work/domain/dto/control/SpeedControlDTO.java | 5 +
ard-work/src/main/java/com/ard/work/component/websocket/handler/BaseSubscriptionManager.java | 21 +++++--
5 files changed, 117 insertions(+), 11 deletions(-)
diff --git a/ard-admin/src/main/java/com/ard/web/controller/work/UavGimbalController.java b/ard-admin/src/main/java/com/ard/web/controller/work/UavGimbalController.java
index aad82b9..d943bcc 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/work/UavGimbalController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/work/UavGimbalController.java
@@ -5,6 +5,7 @@
import com.ard.work.domain.dto.control.AngleControlDTO;
import com.ard.work.domain.dto.control.SpeedControlDTO;
import com.ard.work.service.ITyService;
+import com.ard.work.service.impl.GimbalSpeedService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -26,6 +27,8 @@
@Resource
private ITyService tyService;
+ @Resource
+ private GimbalSpeedService gimbalSpeedService;
@ApiOperation(value = "云台转动(基于角度)", notes = "actionCode值:0-回中,1-上,2-右上,3-右,4-右下,5-下,6-左下,7-左,8-左上,254-停止 \r\n rotationAngle值:取值范围:0~255【云台实际转动角度= 转动角度/100+2.5 单位:度】")
@@ -52,4 +55,15 @@
return AjaxResult.success();
}
+ @ApiOperation(value = "云台速度控制(开始/停止统一接口)")
+ @ApiOperationSupport(order = 4)
+ @PostMapping("/speedControlSimple")
+ public AjaxResult speedControlSimple(@RequestBody @Validated SpeedControlDTO speedControlDto) {
+ if (speedControlDto.isStart()) {
+ gimbalSpeedService.startControl(speedControlDto);
+ } else {
+ gimbalSpeedService.stopControl(speedControlDto.getBoxSn());
+ }
+ return AjaxResult.success();
+ }
}
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/work/component/websocket/handler/BaseSubscriptionManager.java b/ard-work/src/main/java/com/ard/work/component/websocket/handler/BaseSubscriptionManager.java
index 5569e22..8d0db3e 100644
--- a/ard-work/src/main/java/com/ard/work/component/websocket/handler/BaseSubscriptionManager.java
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/BaseSubscriptionManager.java
@@ -24,8 +24,9 @@
* 订阅指定设备的数据
*/
public void subscribe(String boxSn, Long userId, Integer period) {
- subscriptions.computeIfAbsent(boxSn, k -> new HashSet<>()).add(userId);
+ subscriptions.computeIfAbsent(boxSn, k -> ConcurrentHashMap.newKeySet()).add(userId);
if (subscriptions.get(boxSn).size() == 1) {
+ log.debug("首次订阅,启动推送任务");
startPushTask(boxSn, period);
}
}
@@ -34,15 +35,19 @@
* 取消订阅
*/
public void unsubscribe(String boxSn, Long userId) {
- Set<Long> userIds = subscriptions.get(boxSn);
- if (userIds != null) {
+ subscriptions.compute(boxSn, (key, userIds) -> {
+ if (userIds == null) {
+ return null;
+ }
userIds.remove(userId);
if (userIds.isEmpty()) {
stopPushTask(boxSn);
+ log.debug("无人订阅设备 {},移除订阅记录", boxSn);
+ return null; // 直接返回 null 等价于 remove(key)
}
- }
+ return userIds;
+ });
}
-
/**
* 启动推送任务
*/
@@ -52,7 +57,11 @@
return;
}
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> {
- sendDataToSubscribers(boxSn);
+ try {
+ sendDataToSubscribers(boxSn);
+ } catch (Exception e) {
+ log.error("Error in push task for boxSn={}", boxSn, e);
+ }
}, 0, period, TimeUnit.SECONDS);
pushTasks.put(boxSn, future);
diff --git a/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java b/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java
index 1e1e469..e0ac46a 100644
--- a/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java
+++ b/ard-work/src/main/java/com/ard/work/component/websocket/handler/UavAllManager.java
@@ -7,26 +7,30 @@
import com.ard.work.domain.dto.state.UavState;
import com.ard.work.domain.dto.telemetry.TelemetryData;
import com.ard.work.service.control.UavControlService;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
-
+@Slf4j
@Component
-public class UavAllManager extends BaseSubscriptionManager<Map<String,Object>> {
+public class UavAllManager extends BaseSubscriptionManager<Map<String, Object>> {
@Resource
private UavControlService uavControlService;
+
@Override
- protected Map<String,Object> getData(String boxSn) {
- Map<String,Object> map=new HashMap<>();
+ protected Map<String, Object> getData(String boxSn) {
+ log.debug("获取无人机所有数据");
+ Map<String, Object> map = new HashMap<>();
+ map.put("boxSn", boxSn);
UavState uavState = UavStateCache.get(boxSn);
if (uavState != null) {
uavState.calculateFieldAngle();// 计算当前无人机的FieldAngle
}
- map.put("boxSn", uavState.getBoxSn());
+
map.put("state", uavState);
TelemetryData uavTelemetryData = UavTelemetryDataCache.get(boxSn);
map.put("telemetry", uavTelemetryData);
diff --git a/ard-work/src/main/java/com/ard/work/domain/dto/control/SpeedControlDTO.java b/ard-work/src/main/java/com/ard/work/domain/dto/control/SpeedControlDTO.java
index 5144dd2..c3ede6f 100644
--- a/ard-work/src/main/java/com/ard/work/domain/dto/control/SpeedControlDTO.java
+++ b/ard-work/src/main/java/com/ard/work/domain/dto/control/SpeedControlDTO.java
@@ -31,4 +31,9 @@
*/
private float rollSpeed;
+ /**
+ * 指令类型:true=开始转动,false=停止转动
+ */
+ private boolean start;
+
}
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedService.java b/ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedService.java
new file mode 100644
index 0000000..062969d
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedService.java
@@ -0,0 +1,74 @@
+package com.ard.work.service.impl;
+
+import com.ard.common.core.domain.model.LoginUser;
+import com.ard.common.utils.SecurityUtils;
+import com.ard.work.domain.cache.UavStateCache;
+import com.ard.work.domain.dto.control.SpeedControlDTO;
+import com.ard.work.domain.dto.state.UavState;
+import com.ard.work.service.ITyService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Map;
+import java.util.concurrent.*;
+
+@Service
+@Slf4j
+public class GimbalSpeedService {
+
+ private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+ private final Map<String, ScheduledFuture<?>> tasks = new ConcurrentHashMap<>();
+
+ @Resource
+ private ITyService tyService;
+
+ public synchronized void startControl(SpeedControlDTO speed) {
+ String boxSn = speed.getBoxSn();
+ if (tasks.containsKey(boxSn)) {
+ return;
+ }
+ LoginUser loginUser = SecurityUtils.getLoginUser();// 主线程获取
+ ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
+ double currentYaw = getCurrentYaw(boxSn);
+ if ((speed.getYawSpeed() < 0 && currentYaw <= -180) ||
+ (speed.getYawSpeed() > 0 && currentYaw >= 180)) {
+ stopControl(boxSn);
+ return;
+ }
+ try {
+ // 在定时任务线程设置用户上下文
+ // 手动设置 SecurityContext
+ SecurityContextHolder.getContext().setAuthentication(
+ new UsernamePasswordAuthenticationToken(
+ loginUser, null, loginUser.getAuthorities())
+ );
+ Boolean b = tyService.speedControl(speed);
+ log.warn("speedControl result:{}", b);
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ }
+
+ }, 0, 200, TimeUnit.MILLISECONDS);
+
+ tasks.put(boxSn, future);
+ }
+
+ public synchronized void stopControl(String boxSn) {
+ ScheduledFuture<?> future = tasks.remove(boxSn);
+ if (future != null) {
+ future.cancel(false);
+ }
+ }
+
+ private double getCurrentYaw(String boxSn) {
+ UavState uavState = UavStateCache.get(boxSn);
+ if(uavState==null)
+ {
+ return 0;
+ }
+ return uavState.getPtzState().getYaw();
+ }
+}
--
Gitblit v1.9.3