From b0da63410a7aaf64b11183c95b3da38013a6457b Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Tue, 28 Oct 2025 15:05:04 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev' into dev
---
ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedService.java | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 113 insertions(+), 0 deletions(-)
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..dc5956f
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedService.java
@@ -0,0 +1,113 @@
+package com.ard.work.service.impl;
+
+import com.ard.common.constant.CacheConstants;
+import com.ard.common.core.domain.model.LoginUser;
+import com.ard.common.core.redis.RedisCache;
+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.IGimbalSpeedService;
+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.*;
+
+/**
+ * 云台速度控制实现
+ *
+ * @author lsy
+ * @date 2025/10/28
+ */
+@Service
+@Slf4j
+public class GimbalSpeedService implements IGimbalSpeedService {
+
+ private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+ private final Map<String, ScheduledFuture<?>> tasks = new ConcurrentHashMap<>();
+
+ @Resource
+ private ITyService tyService;
+ @Resource
+ private RedisCache redisCache;
+
+ @Override
+ public synchronized void startControl(SpeedControlDTO speed) {
+ String boxSn = speed.getBoxSn();
+
+ // ✅ 先判断是否在线
+ if (!isUavOnline(boxSn)) {
+ throw new RuntimeException("无人机离线,无法启动云台控制: " + boxSn);
+ }
+
+ if (tasks.containsKey(boxSn)) {
+ return;
+ }
+ LoginUser loginUser = SecurityUtils.getLoginUser();// 主线程获取
+ // ✅ 启动前先试探一次,确保正常
+ try {
+ SecurityContextHolder.getContext().setAuthentication(
+ new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities())
+ );
+ Boolean ok = tyService.speedControl(speed);
+ if (ok == null || !ok) {
+ throw new RuntimeException("初始速度控制指令失败");
+ }
+ } catch (Exception e) {
+ log.error("初次控制指令异常: {}", e.getMessage());
+ throw new RuntimeException("无法启动云台控制:" + e.getMessage());
+ }
+
+ ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
+ double currentYaw = getCurrentYaw(boxSn);
+ log.warn("currentYaw: {}", currentYaw);
+ 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);
+ }
+ @Override
+ 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();
+ }
+
+ private boolean isUavOnline(String boxSn) {
+ // 如果 Redis 中存在对应 key,则认为在线
+ Object obj = redisCache.getCacheObject(CacheConstants.UAV_ONLINE_KEY + boxSn);
+ return obj != null;
+ }
+
+}
--
Gitblit v1.9.3