Replace all Redis entries with Caffeine.

This commit is contained in:
longguancheng 2026-02-02 16:18:42 +08:00
parent b0e3347d6b
commit e82b63c8a8
12 changed files with 298 additions and 268 deletions

View File

@ -17,26 +17,26 @@ import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
import com.zhangy.skyeye.jm.service.JmJobStatusService;
import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.publics.utils.CacheUtil;
import com.zhangy.skyeye.sar.service.ISarControlService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Service
public class PayloadServiceImpl implements IPayloadService {
@Autowired
private RedisTemplate redisTemplate;
private CacheManager cacheManager;
@Autowired
private PayloadMapper payloadMapper;
@ -47,36 +47,70 @@ public class PayloadServiceImpl implements IPayloadService {
@Autowired
private ISarControlService sarControlService;
private Cache sarPermanentCache; // SAR 专用永久缓存
private Cache sarShortCache; // 短时状态
@PostConstruct
public void initAndCacheAllSar() {
// 1. 初始化缓存实例获取盒子
sarPermanentCache = cacheManager.getCache("device-permanent");
sarShortCache = cacheManager.getCache("sar-short-lived");
// 2. 防护检查不抛异常而是日志 + 降级
if (sarPermanentCache == null) {
log.error("device-permanent 缓存未找到!请检查 CacheConfig 是否正确注册");
throw new IllegalStateException("device-permanent 缓存未找到");
}
if (sarShortCache == null) {
log.error("sar-short-lived 缓存未找到!请检查 CacheConfig");
throw new IllegalStateException("sar-short-lived 缓存未找到");
}
// 3. 开始缓存所有 SAR如果 permanentCache null这里会安全跳过
PayloadQueryDTO payloadQueryDTO = new PayloadQueryDTO();
payloadQueryDTO.setType(PayloadTypeEnum.SAR.getCode());
List<SkyeyePayload> sarList = selectList(payloadQueryDTO);
if (sarPermanentCache != null) {
sarList.forEach(this::cacheSar);
log.info("SAR 载荷缓存完成,共 {} 条", sarList.size());
} else {
log.warn("永久缓存不可用,跳过 SAR 预加载");
}
}
@Override
public IPage<SkyeyePayload> selectPage(PageDTO param) {
return payloadMapper.selectPage(param);
}
@PostConstruct
public void cacheAllSar() {
PayloadQueryDTO payloadQueryDTO = new PayloadQueryDTO();
payloadQueryDTO.setType(PayloadTypeEnum.SAR.getCode());
List<SkyeyePayload> sarList = selectList(payloadQueryDTO);
sarList.forEach(this::cacheSar);
}
private void cacheSar(SkyeyePayload e) {
redisTemplate.opsForHash().put(CacheKey.DEVICE_SAR, e.getId().toString(), e);
if (sarPermanentCache != null) {
sarPermanentCache.put(e.getId().toString(), e);
}
}
@Override
public List<SkyeyePayload> getSar(Long... sarId) {
if (ObjectUtil.isNotEmpty(sarId)) {
return redisTemplate.opsForHash().multiGet(CacheKey.DEVICE_SAR, Arrays.asList(sarId));
return Arrays.stream(sarId)
.map(id -> CacheUtil.get(sarPermanentCache, id.toString(), SkyeyePayload.class))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
return redisTemplate.opsForHash().values(CacheKey.DEVICE_SAR);
// 获取所有 SAR全量从缓存读取
com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache =
(com.github.benmanes.caffeine.cache.Cache<Object, Object>) sarPermanentCache.getNativeCache();
return nativeCache.asMap().values().stream()
.filter(v -> v instanceof SkyeyePayload)
.map(v -> (SkyeyePayload) v)
.collect(Collectors.toList());
}
@Override
public SkyeyePayload getOne(Long payloadId) {
SkyeyePayload p = (SkyeyePayload) redisTemplate.opsForHash().get(CacheKey.DEVICE_SAR, payloadId.toString());
// 若有其它种类载荷则判空继续查询
return p;
return CacheUtil.get(sarPermanentCache, payloadId.toString(), SkyeyePayload.class);
}
@Override
@ -84,10 +118,6 @@ public class PayloadServiceImpl implements IPayloadService {
return payloadMapper.selectList(param);
}
@Autowired
private RedisUtil redisUtil;
@Override
public List<SkyeyePayload> getEnableList() {
// 从缓存查询所有sar
@ -98,13 +128,16 @@ public class PayloadServiceImpl implements IPayloadService {
// 从内存查询所有任务中的雷达
Collection<JmJobStatusDTO> statusVos = sarJobStatusService.getAll();
Set<Long> jobSarSet = statusVos.stream()
.flatMap(job -> job.getUavMap().values().stream().filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
.map(JmUavStatusDTO::getSarId)
.flatMap(job -> job.getUavMap().values().stream()
.filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
.map(JmUavStatusDTO::getSarId) // 正确取 SAR ID
.collect(Collectors.toSet());
// 筛选出未在任务中且已连接的雷达
return sarList.stream()
.filter(sar -> !jobSarSet.contains(sar.getId()) && redisUtil.hasKey(CacheKey.getSarConnect(sar.getIp())))
List<SkyeyePayload> payloadList = sarList.stream()
.filter(sar -> !jobSarSet.contains(sar.getId())
&& CacheUtil.get(sarShortCache, CacheKey.getSarConnect(sar.getIp()), JmSarStatusDTO.class) != null)
.collect(Collectors.toList());
return payloadList;
}
@Override
@ -171,38 +204,43 @@ public class PayloadServiceImpl implements IPayloadService {
@Transactional
@Override
public void delete(Long... id) {
// 查询任务中的sar
List<Long> inJobSarIds = sarJobStatusService.getAll()
.stream()
.flatMap(vo -> vo.getUavMap().values().stream())
.filter(vo -> vo.getSarStatus() != ExecStatusEnum.OVER)
.map(JmUavStatusDTO::getSarId)
.collect(Collectors.toList());
// 任务中的不可删除
for (Long sarId : id) {
if (inJobSarIds.contains(sarId)) {
throw ServiceException.noLog("[" + getSar(sarId).get(0).getName() + "]在执行任务,不可删除");
List<SkyeyePayload> sar = getSar(sarId);
if (!sar.isEmpty()) {
throw ServiceException.noLog("[" + sar.get(0).getName() + "]在执行任务,不可删除");
}
}
}
payloadMapper.deleteLogic(id);
redisTemplate.opsForHash().delete(CacheKey.DEVICE_SAR, id);
Arrays.stream(id).forEach(i -> sarPermanentCache.evict(i.toString()));
}
@Override
public JmSarStatusDTO getLastStatus(String payloadIp) {
String statusKey = CacheKey.getSarConnect(payloadIp);
if (!redisUtil.hasKey(statusKey)) {
JmSarStatusDTO status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class);
if (status == null) {
try {
sarControlService.connect(payloadIp);
TimeUnit.SECONDS.sleep(1);
status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class);
} catch (InterruptedException ex) {
throw new ServiceException("获取雷达状态失败:" + ex.getMessage());
}
if (!redisUtil.hasKey(statusKey)) {
if (status == null) {
throw ServiceException.noLog("无法连接到SAR载荷[" + payloadIp + "]");
}
}
return (JmSarStatusDTO) redisUtil.get(statusKey);
return status;
}
@Override

View File

@ -10,18 +10,14 @@ import com.zhangy.skyeye.jm.dto.JmJobImageDTO;
import com.zhangy.skyeye.jm.entity.JmImage;
import com.zhangy.skyeye.jm.service.JmImageService;
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
import com.zhangy.skyeye.publics.utils.LocalLockUtil;
import org.opencv.core.Core;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.net.ConnectException;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.zhangy.skyeye.publics.consts.CacheKey.SAR_IMAGE_UPLOAD_LOCK;
@ -35,7 +31,7 @@ public class JmImageController {
private JmImageService sarImageService;
@Autowired
private RedisTemplate redisTemplate;
private LocalLockUtil localLockUtil;
/**
* 分页查询
@ -77,29 +73,14 @@ public class JmImageController {
@PostMapping("/addHigh")
public Object addHighImage(@Valid @RequestBody JmJobImageDTO dto) {
String lockKey = SAR_IMAGE_UPLOAD_LOCK;
String requestId = UUID.randomUUID().toString(); // 唯一标识当前请求
try {
// 原子操作获取锁设置过期时间30秒
Boolean locked = redisTemplate.opsForValue().setIfAbsent(
lockKey,
requestId,
1,
TimeUnit.MINUTES
);
if (Boolean.FALSE.equals(locked)) {
boolean locked = localLockUtil.tryLock(lockKey, 60); // 尝试 60 秒获取锁比原来 1 分钟更灵活
if (!locked) {
throw ServiceException.noLog("正在上传其它图像,请稍后重试!");
}
try {
sarImageService.addHighImage(dto);
} finally {
// 仅删除自己的锁Lua脚本保证原子性
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then "
+ "return redis.call('del', KEYS[1]) "
+ "else return 0 end";
redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey),
requestId
);
localLockUtil.unlock(lockKey); // 释放锁
}
return "操作完成";
}
@ -135,6 +116,7 @@ public class JmImageController {
.map(e -> BeanUtil.copyProperties(e, JmImageKtyDTO.class))
.collect(Collectors.toList());
}
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

View File

@ -42,9 +42,6 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
@Autowired
private SarImageUdpProcessor imageProcessService;
// @Autowired
// private ISmpSubscriptService subscriptService;
/**
* 缓存所有执行中的任务的状态 key 任务配置IDvalue 任务状态
*/

View File

@ -1,26 +1,24 @@
package com.zhangy.skyeye.jm.task;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.device.entity.SkyeyePayload;
import com.zhangy.skyeye.device.service.IPayloadService;
import com.zhangy.skyeye.jm.dto.JmJobStatusDTO;
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.publics.service.ISysLoginService;
import com.zhangy.skyeye.publics.utils.CacheUtil;
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
import com.zhangy.skyeye.jm.service.JmJobStatusService;
import com.zhangy.skyeye.sar.exception.SarConnectException;
import com.zhangy.skyeye.sar.service.ISarControlService;
// import com.zhangy.skyeye.smp.dto.SmpUavStatusWsDTO;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Collection;
import javax.annotation.PostConstruct;
import java.util.List;
/**
@ -34,18 +32,6 @@ public class JmTaskScheduler {
@Setter
private boolean isDebug;
@Autowired
private RedisUtil redisUtil;
@Autowired
private JmJobStatusService jobStatusService;
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
@Autowired
RedisTemplate<String, Object> redisTemplate;
@Autowired
private ISysLoginService loginService;
@ -56,7 +42,16 @@ public class JmTaskScheduler {
private IPayloadService payloadService;
@Autowired
private ISarControlService sarControlService;
private CacheManager cacheManager;
private Cache sarPermanentCache;
private Cache sarShortCache;
@PostConstruct
public void initCaches() {
sarPermanentCache = cacheManager.getCache("sar-payload-permanent");
sarShortCache = cacheManager.getCache("sar-short-lived");
}
/**
* 每1秒向前端推送雷达状态信息断开连接则所有数据置0返回
@ -115,21 +110,24 @@ public class JmTaskScheduler {
*/
@Scheduled(fixedRate = 10000)
public void connectSar() {
if (!loginService.hasLogged() || isDebug) { // 断开
/*Map<Object, Object> map = redisUtil.hmget(CacheKey.SAR_CONNECTED);
if (map != null && map.size() > 0) {
map.keySet().forEach(ip -> controlInfoService.sendUdp((String) ip, SarControlTypeEnum.DISCONNECT));
}*/
} else { // 连接
if (!loginService.hasLogged() || isDebug) {
// 断开逻辑如果需要可遍历 permanentCache 或其他方式获取 ip 列表
// 但当前注释掉了保持原样或删除
} else {
List<SkyeyePayload> sarList = payloadService.getSar(null);
sarList.forEach(sar -> {
if (!redisUtil.hHasKey(CacheKey.SAR_CONNECTED, sar.getIp()) ||
!redisUtil.hasKey(CacheKey.getSarConnect(sar.getIp()))) {
String ip = sar.getIp();
// 判断是否已连接 shortCache 里是否有该 ip 的状态最近有状态包
boolean hasStatus = CacheUtil.get(sarShortCache, CacheKey.getSarConnect(ip), JmSarStatusDTO.class) != null;
// 判断是否已执行连接指令 permanentCache 里是否有该 ip 的连接标志
boolean hasConnectedFlag = sarPermanentCache.get(ip) != null;
// 如果缺少状态 缺少连接标志则尝试连接
if (!hasConnectedFlag || !hasStatus) {
try {
controlInfoService.sendUdp(sar.getIp(), SarControlTypeEnum.CONNECT);
controlInfoService.sendUdp(ip, SarControlTypeEnum.CONNECT);
log.info("尝试连接 SAR IP: {}", ip);
} catch (SarConnectException ex) {
// sar可能没通电连接失败不做处理
log.warn("连接雷达[" + sar.getIp() + "]失败:" + ex.getMessage());
log.warn("连接雷达[{}]失败:{}", ip, ex.getMessage());
}
}
});

View File

@ -2,14 +2,14 @@ package com.zhangy.skyeye.sar.context;
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.publics.utils.CacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
/**
*
*/
import javax.annotation.PostConstruct;
/**
* @PROJECT_NAME: skyeyesystem
* @DESCRIPTION: SAR 任务上下文提供者实现基于 Redis 缓存避免循环依赖
@ -22,14 +22,21 @@ import org.springframework.stereotype.Component;
@Component
public class SarTaskContextProviderImpl implements SarTaskContextProvider {
@Autowired
private RedisUtil redisUtil;
private final CacheManager cacheManager;
// Redis 键前缀
private static final String UAV_STATUS_KEY_PREFIX = "sar:context:uav:";
private Cache uavStatusCache;
// 缓存过期时间
private static final long CACHE_EXPIRE_SECONDS = 600; // 10 分钟
public SarTaskContextProviderImpl(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@PostConstruct
public void initCache() {
this.uavStatusCache = cacheManager.getCache("uav-status");
if (this.uavStatusCache == null) {
log.error("缓存 uav-status 未找到,请检查 CacheConfig 配置");
}
}
@Override
public JmUavStatusDTO getCurrentUav(String payloadIp) {
@ -38,15 +45,8 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
return null;
}
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
Object obj = redisUtil.get(key);
if (obj instanceof JmUavStatusDTO) {
return (JmUavStatusDTO) obj;
}
log.debug("Redis 中未找到 IP={} 的 uav 状态", payloadIp);
return null;
String key = "sar:context:uav:" + payloadIp; // 保持原 key 格式
return CacheUtil.get(uavStatusCache, key, JmUavStatusDTO.class);
}
@Override
@ -80,11 +80,10 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
return;
}
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
String key = "sar:context:uav:" + payloadIp;
try {
// 设置值 + 过期时间
redisUtil.set(key, uavStatus, CACHE_EXPIRE_SECONDS);
log.debug("已更新 Redis 缓存key={}, jobExecId={}", key, uavStatus.getJobExecId());
uavStatusCache.put(key, uavStatus); // 自动过期 10 分钟
log.debug("已更新缓存key={}, jobExecId={}", key, uavStatus.getJobExecId());
} catch (Exception e) {
log.error("更新 SAR uav 状态缓存失败ip={}", payloadIp, e);
}
@ -97,8 +96,8 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
if (payloadIp == null) {
return;
}
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
redisUtil.del(key);
log.debug("SAR uav 缓存已标记清理(或自然过期){}", key);
String key = "sar:context:uav:" + payloadIp;
uavStatusCache.evict(key);
log.debug("SAR uav 缓存已清理:{}", key);
}
}

View File

@ -1,21 +1,23 @@
package com.zhangy.skyeye.sar.control;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
import com.zhangy.skyeye.sar.dto.SarControlDTO;
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* 控制连接
* 控制建立连接
* 连接成功后在回传状态时会将状态信息放入缓存这里只记录连接标志
*/
@Slf4j
@Order(1)
@ -23,7 +25,18 @@ import java.util.List;
@RequiredArgsConstructor
public class SarControlConnectStrategy implements ISarControlStrategy {
private final RedisUtil redisUtil;
private final CacheManager cacheManager;
// 不加 final因为在 PostConstruct 中初始化
private Cache permanentCache;
@PostConstruct
public void initCache() {
this.permanentCache = cacheManager.getCache("sar-payload-permanent");
if (this.permanentCache == null) {
log.error("永久缓存 sar-payload-permanent 未找到,请检查 CacheConfig 配置");
}
}
@Override
public boolean supports(SarControlParamDTO param) {
@ -40,8 +53,12 @@ public class SarControlConnectStrategy implements ISarControlStrategy {
@Override
public void sendPost(SarControlDTO sar) {
// 建立连接后回传状态时会将状态信息放入缓存
redisUtil.hset(CacheKey.SAR_CONNECTED, sar.getIp(), new Date());
// 建立连接后记录连接标志value 可以是 Date字符串 "connected" boolean true随你业务
if (permanentCache != null) {
permanentCache.put(sar.getIp(), new Date());
} else {
log.warn("无法记录连接标志,永久缓存未初始化");
}
}
@Override
@ -49,6 +66,3 @@ public class SarControlConnectStrategy implements ISarControlStrategy {
return 0;
}
}

View File

@ -2,8 +2,8 @@ package com.zhangy.skyeye.sar.control;
import com.zhangy.skyeye.common.extend.exception.ServiceException;
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.publics.utils.CacheUtil;
import com.zhangy.skyeye.sar.dto.SarControlDTO;
import com.zhangy.skyeye.sar.dto.SarControlPackDTO;
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
@ -14,7 +14,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@ -59,7 +60,7 @@ public class SarControlContext {
private final int POLLING_INTERVAL = 100;
@Autowired
private RedisUtil redisUtil;
private CacheManager cacheManager;
/**
* 应答超时
@ -74,6 +75,7 @@ public class SarControlContext {
/**
* 执行
*
* @param param
*/
public void execute(SarControlParamDTO param) {
@ -103,30 +105,30 @@ public class SarControlContext {
/**
* 发送未收到回执则重发
*
* @param control
*/
private void sendUdp(SarControlDTO control) {
SarControlTypeEnum controlType = control.getControlType();
String ip = control.getIp();
log.debug("开始发送雷达控制指令[" + controlType + "]----------------------");
//System.out.println(control);
log.debug("开始发送雷达控制指令[{}]", controlType);
try (DatagramSocket socket = new DatagramSocket()) {
byte[] content = pack(control);
socket.connect(new InetSocketAddress(ip, PORT));
DatagramPacket packet = new DatagramPacket(content, content.length);
int failCount = 0;
SarErrorDTO info = null;
while (info == null && failCount < RETRY_MAX) { // 失败重试
String cacheKey = CacheKey.getSarControlBack(ip); // skyeye:sar:control + ip
while (info == null && failCount < RETRY_MAX) {
socket.send(packet);
// 每0.1秒取回执1秒后超时
long startTime = System.currentTimeMillis();
String cacheKey = CacheKey.getSarControlBack(ip);
while (System.currentTimeMillis() - startTime < ANSWER_TIMEOUT) {
if (redisUtil.hasKey(cacheKey)) {
info = (SarErrorDTO) redisUtil.get(cacheKey);
redisUtil.del(cacheKey);
// Caffeine 获取
Cache shortCache = cacheManager.getCache("sar-short-lived");
info = CacheUtil.get(shortCache, cacheKey, SarErrorDTO.class);
if (info != null) {
shortCache.evict(cacheKey); // 相当于 del读取后删除
break;
} else {
try {
@ -146,10 +148,8 @@ public class SarControlContext {
if (info == null) {
throw new SarConnectException("控制指令[" + controlType + "]发送失败,雷达[" + ip + "]无应答,请重试");
} else {
log.info("雷达控制指令[" + controlType + "]发送完毕----------------------");
log.info("雷达控制指令[{}]发送完毕----------------------", controlType);
}
} catch (RedisConnectionFailureException ex) {
throw ServiceException.errorLog("无法连接到Redis服务");
} catch (IOException ex) {
throw ServiceException.errorLog("控制指令[" + controlType + "]发送失败 " + ex.getMessage());
}
@ -157,6 +157,7 @@ public class SarControlContext {
/**
* 封包
*
* @param sarControl
* @return
*/

View File

@ -1,15 +1,17 @@
package com.zhangy.skyeye.sar.control;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
import com.zhangy.skyeye.sar.dto.SarControlDTO;
import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.sar.dto.SarControlDTO;
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
@ -22,7 +24,16 @@ import java.util.List;
@RequiredArgsConstructor
public class SarControlDisconnectStrategy implements ISarControlStrategy {
private final RedisUtil redisUtil;
private final CacheManager cacheManager;
private Cache shortCache;
private Cache permanentCache;
@PostConstruct
public void init() {
this.shortCache = cacheManager.getCache("sar-short-lived");
this.permanentCache = cacheManager.getCache("sar-payload-permanent");
}
@Override
public boolean supports(SarControlParamDTO param) {
@ -37,15 +48,15 @@ public class SarControlDisconnectStrategy implements ISarControlStrategy {
return Collections.singletonList(connect);
}
/**
* 删除缓存键
* @param sar
*/
@Override
public void sendPost(SarControlDTO sar) {
String connectKey = CacheKey.getSarConnect(sar.getIp());
redisUtil.del(connectKey);
redisUtil.hdel(CacheKey.SAR_CONNECTED, sar.getIp());
if (shortCache != null) {
shortCache.evict(connectKey);
}
if (permanentCache != null) {
permanentCache.evict(sar.getIp());
}
}
@Override

View File

@ -1,6 +1,5 @@
package com.zhangy.skyeye.sar.listen;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
import com.zhangy.skyeye.jm.service.JmJobStatusService;
import com.zhangy.skyeye.publics.consts.CacheKey;
@ -13,6 +12,8 @@ import com.zhangy.skyeye.sar.task.PriorityThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import java.io.IOException;
@ -50,7 +51,7 @@ public class SarStatusListener extends SarAbstractListener {
private int connectTimeout;
@Autowired
private RedisUtil redisUtil;
private CacheManager cacheManager;
@Autowired
private JmJobStatusService sarJobStatusService;
@ -70,37 +71,40 @@ public class SarStatusListener extends SarAbstractListener {
@Override
protected void processData(DatagramPacket packet) throws IOException {
socket.receive(packet);
// 过滤无效数据包
if (packet.getLength() != 100) {
return;
}
log.debug("接收到状态包----------------------");
String ip = packet.getAddress().getHostAddress();
// 处理接收到的数据
SarStatusPackDTO packDTO = SarStatusPackDTO.parse(ip, packet.getData());
if (packDTO == null) {
if (running)
log.warn("[" + packDTO.getPayloadIp() + "]状态包校验失败,已丢弃。错误包=" +
packDTO.getErrorPacketStatus() + ",状态包=" + packDTO.getDevicePacketStatus());
log.warn("状态包校验失败,已丢弃。");
return;
}
// 错误包结果放入缓存超时1秒
Cache sarShortCache = cacheManager.getCache("sar-short-lived");
if (sarShortCache == null) {
log.error("sar-short-lived 缓存未找到!请检查 CacheConfig");
throw new IllegalStateException("sar-short-lived 缓存未找到");
}
// 错误包 回执
int errorStatus = packDTO.getErrorPacketStatus();
if (errorStatus == 1) {
SarErrorDTO info = packDTO.getDeviceErrorInfo();
if (info.getErrorPacketType() == SarErrorTypeEnum.RESULT) {
log.debug("收到回执包:" + ip);
redisUtil.set(CacheKey.getSarControlBack(ip), info, answerTimeout, TimeUnit.SECONDS);
log.debug("收到回执包:{}", ip);
String controlBackKey = CacheKey.getSarControlBack(ip);
sarShortCache.put(controlBackKey, info); // 写入自动 2s 过期
}
}
// 状态包
int deviceStatus = packDTO.getDevicePacketStatus();
if (deviceStatus == 1) {
JmSarStatusDTO info = packDTO.getDeviceStatusInfo();
log.debug("sar开机状态" + info.getIsBoot());
log.debug("sar开机状态{}", info.getIsBoot());
sarJobStatusService.update(ip, info);
redisUtil.set(CacheKey.getSarConnect(ip), info, answerTimeout, TimeUnit.SECONDS);
//System.out.println(info);
String connectKey = CacheKey.getSarConnect(ip);
sarShortCache.put(connectKey, info); // 写入自动过期
}
log.debug("----------------------状态包解析完毕");
}

View File

@ -4,18 +4,19 @@ import com.zhangy.skyeye.common.extend.util.JsonUtil;
import com.zhangy.skyeye.jm.dto.JmJobDTO;
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
import com.zhangy.skyeye.jm.entity.JmJobPayload;
import com.zhangy.skyeye.jm.entity.JmJobUav;
import com.zhangy.skyeye.publics.consts.CacheKey;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.publics.utils.CacheUtil;
import com.zhangy.skyeye.sar.control.SarControlContext;
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
import com.zhangy.skyeye.sar.service.ISarControlService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Optional;
/**
@ -28,8 +29,17 @@ public class SarControlServiceImpl implements ISarControlService {
private final SarControlContext udpSendContext;
@Autowired
private RedisUtil redisUtil;
private final CacheManager cacheManager; // final 字段
private Cache shortCache;
@PostConstruct
public void init() {
this.shortCache = cacheManager.getCache("sar-short-lived");
if (shortCache == null) {
log.error("sar-short-lived cache 未找到!");
}
}
@Override
public void sendUdp(JmJobDTO job) {
@ -60,7 +70,6 @@ public class SarControlServiceImpl implements ISarControlService {
});
}
@Override
public void sendUdp(SarControlParamDTO param) {
udpSendContext.execute(param);
@ -87,7 +96,6 @@ public class SarControlServiceImpl implements ISarControlService {
@Override
public JmSarStatusDTO getLatestStatus(String ip) {
String connectKey = CacheKey.getSarConnect(ip);
// 从缓存取载荷状态信息
return (JmSarStatusDTO) redisUtil.get(connectKey);
return CacheUtil.get(shortCache, connectKey, JmSarStatusDTO.class);
}
}

View File

@ -1,9 +1,8 @@
package com.zhangy.skyeye.sar.service.impl;
import com.zhangy.skyeye.common.extend.util.MathUtil;
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
import com.zhangy.skyeye.redis.utils.RedisUtil;
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
import com.zhangy.skyeye.jm.dto.JmImageRotateDTO;
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
import com.zhangy.skyeye.jm.entity.JmImage;
@ -11,6 +10,7 @@ import com.zhangy.skyeye.jm.service.JmImageService;
import com.zhangy.skyeye.jm.service.JmJobStatusService;
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
import com.zhangy.skyeye.publics.service.SysFileTypeService;
import com.zhangy.skyeye.publics.utils.CacheUtil;
import com.zhangy.skyeye.publics.utils.ImageUtil;
import com.zhangy.skyeye.publics.utils.OpenCVUtil;
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
@ -22,8 +22,11 @@ import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Mat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.File;
import java.util.Date;
import java.util.List;
@ -45,7 +48,7 @@ public class SarImageServiceImpl implements ISarImageService {
private SarWsAsyncService sarWsAsyncService;
@Autowired
private RedisUtil redisUtil;
private CacheManager cacheManager;
// 图片最大宽度前端说和电脑有关4096保险点一般是4096 8192 16384
@Value("${skyeye.sar.image.max:4096}")
@ -55,8 +58,16 @@ public class SarImageServiceImpl implements ISarImageService {
private final String CACHE_FIELD_START_FRAME_NO = "startFrameNo";
// 当前帧号
private final String CACHE_FIELD_CURR_FRAME_NO = "currFrameNo";
// 缓存超时
private final long CACHE_EXPIRE_SECOND = 24 * 3600;
private Cache joinStateCache;
@PostConstruct
public void initCache() {
joinStateCache = cacheManager.getCache("image-join-state");
if (joinStateCache == null) {
log.error("缓存 image-join-state 未找到,请检查 CacheConfig");
}
}
/**
* 获取基准图像信息
@ -66,32 +77,34 @@ public class SarImageServiceImpl implements ISarImageService {
* @param frameNo 当前帧号
* @return 返回非空的图像信息其字段 imageNo 一定有值
*/
private JmImage getBaseImage(Long airlineId, int singleWidth, int frameNo) {IMG_MAX_WITH=1;
private JmImage getBaseImage(Long airlineId, int singleWidth, int frameNo) {
List<JmImage> imageList = imageService.selectLowByAirline(airlineId);
String cacheKey = "jmImgJoin-" + airlineId;
JmImage base = null;
String cachePrefix = "jmImgJoin-" + airlineId;
JmImage base;
// 情况1航线第一张图
if (ObjectUtil.isEmpty(imageList)) {
base = new JmImage();
base.setImageNo(1);
redisUtil.hset(cacheKey, CACHE_FIELD_START_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);
// 存起始帧号
joinStateCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo);
return base;
}
// 情况2如果最后一张还能拼图则直接返回继续拼
JmImage last = imageList.get(imageList.size() - 1);
Integer startFrameNo = (Integer) redisUtil.hget(cacheKey, CACHE_FIELD_START_FRAME_NO);
int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1); // 图宽当前图+基准图
Integer startFrameNo = CacheUtil.get(joinStateCache, cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, Integer.class);
int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1);
int surplusNum = (IMG_MAX_WITH - currWidth) / singleWidth; // 还可以拼图片数
Integer baseNo = (Integer) redisUtil.hget("jmImgJoin-" + airlineId, CACHE_FIELD_CURR_FRAME_NO);
Integer baseNo = CacheUtil.get(joinStateCache, cachePrefix + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class);
if (startFrameNo == null || currWidth < IMG_MAX_WITH ||
baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) { // 当前图+填充 不能超过允许拼接数
log.info("当前宽度:" + currWidth + " < " + IMG_MAX_WITH + " 可以继续拼接");
baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) {
log.info("当前宽度:{} < {} 可以继续拼接", currWidth, IMG_MAX_WITH);
return last;
}
// 情况3已经拼接到最大数量或者当前图+填充数量超过允许拼接数量创建新图像文件
log.info("当前宽度:" + currWidth + " > " + IMG_MAX_WITH + " 重新拼接,当前帧号" + frameNo + "作为首帧");
log.info("当前宽度:{} > {} 重新拼接,当前帧号{}作为首帧", currWidth, IMG_MAX_WITH, frameNo);
base = new JmImage();
redisUtil.hset(cacheKey, CACHE_FIELD_START_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);
joinStateCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo);
base.setImageNo(last.getImageNo() + 1);
return base;
}
@ -106,47 +119,12 @@ public class SarImageServiceImpl implements ISarImageService {
}
// 使用前一张图的右侧坐标作为后一张图的左侧前提是没丢图
if (!isFirst && !lostImage) {
/*imageFrame.setLon1(before[0]);
imageFrame.setLat1(before[1]);
imageFrame.setLon4(before[2]);
imageFrame.setLat4(before[3]);*/
// 注释部分保持原样
}
before[0] = imageFrame.getLon5();
before[1] = imageFrame.getLat5();
before[2] = imageFrame.getLon8();
before[3] = imageFrame.getLat8();
/*switch (rotateDTO.getType()) {
case 0:
case 1:
// 使用前一张图的右侧坐标作为后一张图的左侧前提是没丢图
if (!isFirst && !lostImage) {
imageFrame.setLon1(before[0]);
imageFrame.setLat1(before[1]);
imageFrame.setLon4(before[2]);
imageFrame.setLat4(before[3]);
} else if (before == null) {
before = new Double[4];
currAirline.setBeforeRight(before);
}
before[0] = imageFrame.getLon5();
before[1] = imageFrame.getLat5();
before[2] = imageFrame.getLon8();
before[3] = imageFrame.getLat8();
break;
case 2:
case 3:
if (!isFirst && !lostImage) {
imageFrame.setLon5(before[0]);
imageFrame.setLat5(before[1]);
imageFrame.setLon8(before[2]);
imageFrame.setLat8(before[3]);
}
before[0] = imageFrame.getLon1();
before[1] = imageFrame.getLat1();
before[2] = imageFrame.getLon4();
before[3] = imageFrame.getLat4();
break;
} */
}
/**
@ -186,7 +164,7 @@ public class SarImageServiceImpl implements ISarImageService {
}
// 3.保存图像png用航线ID+序号命名
JmImage base = getBaseImage(airlineExecId, currImage.width(), imageFrame.getFrameNo());
JmImage base = getBaseImage(airlineExecId, currImage.width(), frameNo);
String imageName = airlineExecId + "-" + base.getImageNo() + ".png";
String[] imagePath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, imageName);
String currPath = imagePath[0];
@ -194,7 +172,7 @@ public class SarImageServiceImpl implements ISarImageService {
System.out.println("帧:" + frameNo);
// 4.保存基准图同步用于下次拼接
Integer baseNo = (Integer) redisUtil.hget("jmImgJoin-" + airlineExecId, CACHE_FIELD_CURR_FRAME_NO);
Integer baseNo = CacheUtil.get(joinStateCache, "jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class);
boolean lostImage = baseNo != null && (frameNo - baseNo > 1); // 判断是否丢图
String basePath = sysFileTypeService.getAbsolutePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId,
airlineExecId + "-" + base.getImageNo() + "-base.png");
@ -203,11 +181,11 @@ public class SarImageServiceImpl implements ISarImageService {
return null;
}
if (lostImage) {
log.warn("丢图"+(frameNo - baseNo)+"张!当前帧" + frameNo + ",前帧" + baseNo);
log.warn("丢图{}张!当前帧{},前帧{}", (frameNo - baseNo), frameNo, baseNo);
}
//modCoord(imageFrame, lostImage, currAirline);
redisUtil.hset("jmImgJoin-" + airlineExecId, CACHE_FIELD_CURR_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);// 更新帧号
joinStateCache.put("jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, frameNo);
// ### 亮度调整用于可靠udp版本图像固定使用系数0.5
// 拆分多张图片去掉自适应调整
if (IMG_MAX_WITH > 20000) {
@ -220,8 +198,8 @@ public class SarImageServiceImpl implements ISarImageService {
// 6.更新基准图坐标和帧号
JmImage imageInfo = saveImage(uav, airlineExecId, base, imagePath, imageFrame, frameNo, lostImage);
long end = System.currentTimeMillis();
log.info("生成" + imageFrame.getImageBitDeep()+"位雷达回传图像:帧序号" + frameNo + "," +
imageInfo.getRelativePath() + ",耗时" + (end - start)/1000 + "");
log.info("生成{}位雷达回传图像:帧序号{}{},耗时{}秒",
imageFrame.getImageBitDeep(), frameNo, imageInfo.getRelativePath(), (end - start) / 1000);
return imageInfo;
}

View File

@ -46,11 +46,11 @@ spring:
date-format: yyyy-MM-dd'T'HH:mm:ss'Z'
#redis 配置
redis:
database: 0
host: 127.0.0.1
port: 6379
password: 'P@ssw0rd'
#redis:
#database: 0
#host: 127.0.0.1
#port: 6379
#password: 'P@ssw0rd'
mybatis-plus:
mapper-locations: classpath*:mapping/**/*Mapping.xml