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> 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; } }