Replace all Redis entries with Caffeine.
This commit is contained in:
parent
b0e3347d6b
commit
e82b63c8a8
@ -17,26 +17,26 @@ import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
|||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
||||||
import com.zhangy.skyeye.publics.consts.CacheKey;
|
import com.zhangy.skyeye.publics.consts.CacheKey;
|
||||||
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
|
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 com.zhangy.skyeye.sar.service.ISarControlService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class PayloadServiceImpl implements IPayloadService {
|
public class PayloadServiceImpl implements IPayloadService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisTemplate redisTemplate;
|
private CacheManager cacheManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PayloadMapper payloadMapper;
|
private PayloadMapper payloadMapper;
|
||||||
@ -47,36 +47,70 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ISarControlService sarControlService;
|
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
|
@Override
|
||||||
public IPage<SkyeyePayload> selectPage(PageDTO param) {
|
public IPage<SkyeyePayload> selectPage(PageDTO param) {
|
||||||
return payloadMapper.selectPage(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) {
|
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
|
@Override
|
||||||
public List<SkyeyePayload> getSar(Long... sarId) {
|
public List<SkyeyePayload> getSar(Long... sarId) {
|
||||||
if (ObjectUtil.isNotEmpty(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
|
@Override
|
||||||
public SkyeyePayload getOne(Long payloadId) {
|
public SkyeyePayload getOne(Long payloadId) {
|
||||||
SkyeyePayload p = (SkyeyePayload) redisTemplate.opsForHash().get(CacheKey.DEVICE_SAR, payloadId.toString());
|
return CacheUtil.get(sarPermanentCache, payloadId.toString(), SkyeyePayload.class);
|
||||||
// 若有其它种类载荷,则判空,继续查询
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -84,10 +118,6 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
return payloadMapper.selectList(param);
|
return payloadMapper.selectList(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SkyeyePayload> getEnableList() {
|
public List<SkyeyePayload> getEnableList() {
|
||||||
// 从缓存查询所有sar
|
// 从缓存查询所有sar
|
||||||
@ -98,13 +128,16 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
// 从内存查询所有任务中的雷达
|
// 从内存查询所有任务中的雷达
|
||||||
Collection<JmJobStatusDTO> statusVos = sarJobStatusService.getAll();
|
Collection<JmJobStatusDTO> statusVos = sarJobStatusService.getAll();
|
||||||
Set<Long> jobSarSet = statusVos.stream()
|
Set<Long> jobSarSet = statusVos.stream()
|
||||||
.flatMap(job -> job.getUavMap().values().stream().filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
|
.flatMap(job -> job.getUavMap().values().stream()
|
||||||
.map(JmUavStatusDTO::getSarId)
|
.filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
|
||||||
|
.map(JmUavStatusDTO::getSarId) // 正确取 SAR ID
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
// 筛选出未在任务中且已连接的雷达
|
// 筛选出未在任务中且已连接的雷达
|
||||||
return sarList.stream()
|
List<SkyeyePayload> payloadList = sarList.stream()
|
||||||
.filter(sar -> !jobSarSet.contains(sar.getId()) && redisUtil.hasKey(CacheKey.getSarConnect(sar.getIp())))
|
.filter(sar -> !jobSarSet.contains(sar.getId())
|
||||||
|
&& CacheUtil.get(sarShortCache, CacheKey.getSarConnect(sar.getIp()), JmSarStatusDTO.class) != null)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
return payloadList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -171,38 +204,43 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public void delete(Long... id) {
|
public void delete(Long... id) {
|
||||||
// 查询任务中的sar
|
|
||||||
List<Long> inJobSarIds = sarJobStatusService.getAll()
|
List<Long> inJobSarIds = sarJobStatusService.getAll()
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(vo -> vo.getUavMap().values().stream())
|
.flatMap(vo -> vo.getUavMap().values().stream())
|
||||||
.filter(vo -> vo.getSarStatus() != ExecStatusEnum.OVER)
|
.filter(vo -> vo.getSarStatus() != ExecStatusEnum.OVER)
|
||||||
.map(JmUavStatusDTO::getSarId)
|
.map(JmUavStatusDTO::getSarId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
// 任务中的不可删除
|
|
||||||
for (Long sarId : id) {
|
for (Long sarId : id) {
|
||||||
if (inJobSarIds.contains(sarId)) {
|
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);
|
payloadMapper.deleteLogic(id);
|
||||||
redisTemplate.opsForHash().delete(CacheKey.DEVICE_SAR, id);
|
Arrays.stream(id).forEach(i -> sarPermanentCache.evict(i.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JmSarStatusDTO getLastStatus(String payloadIp) {
|
public JmSarStatusDTO getLastStatus(String payloadIp) {
|
||||||
String statusKey = CacheKey.getSarConnect(payloadIp);
|
String statusKey = CacheKey.getSarConnect(payloadIp);
|
||||||
if (!redisUtil.hasKey(statusKey)) {
|
JmSarStatusDTO status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class);
|
||||||
|
if (status == null) {
|
||||||
try {
|
try {
|
||||||
sarControlService.connect(payloadIp);
|
sarControlService.connect(payloadIp);
|
||||||
TimeUnit.SECONDS.sleep(1);
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
throw new ServiceException("获取雷达状态失败:" + ex.getMessage());
|
throw new ServiceException("获取雷达状态失败:" + ex.getMessage());
|
||||||
}
|
}
|
||||||
if (!redisUtil.hasKey(statusKey)) {
|
if (status == null) {
|
||||||
throw ServiceException.noLog("无法连接到SAR载荷[" + payloadIp + "]!");
|
throw ServiceException.noLog("无法连接到SAR载荷[" + payloadIp + "]!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (JmSarStatusDTO) redisUtil.get(statusKey);
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -10,18 +10,14 @@ import com.zhangy.skyeye.jm.dto.JmJobImageDTO;
|
|||||||
import com.zhangy.skyeye.jm.entity.JmImage;
|
import com.zhangy.skyeye.jm.entity.JmImage;
|
||||||
import com.zhangy.skyeye.jm.service.JmImageService;
|
import com.zhangy.skyeye.jm.service.JmImageService;
|
||||||
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
|
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
|
||||||
|
import com.zhangy.skyeye.publics.utils.LocalLockUtil;
|
||||||
import org.opencv.core.Core;
|
import org.opencv.core.Core;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.zhangy.skyeye.publics.consts.CacheKey.SAR_IMAGE_UPLOAD_LOCK;
|
import static com.zhangy.skyeye.publics.consts.CacheKey.SAR_IMAGE_UPLOAD_LOCK;
|
||||||
@ -30,12 +26,12 @@ import static com.zhangy.skyeye.publics.consts.CacheKey.SAR_IMAGE_UPLOAD_LOCK;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/sar/image")
|
@RequestMapping("/sar/image")
|
||||||
public class JmImageController {
|
public class JmImageController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JmImageService sarImageService;
|
private JmImageService sarImageService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisTemplate redisTemplate;
|
private LocalLockUtil localLockUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询
|
* 分页查询
|
||||||
@ -44,16 +40,16 @@ public class JmImageController {
|
|||||||
public Object selectPage(@Valid @RequestBody JmImagePageDTO param) {
|
public Object selectPage(@Valid @RequestBody JmImagePageDTO param) {
|
||||||
return sarImageService.selectPage(param);
|
return sarImageService.selectPage(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按任务查询低精度图像
|
* 按任务查询低精度图像
|
||||||
*/
|
*/
|
||||||
@GetMapping("/queryByJob")
|
@GetMapping("/queryByJob")
|
||||||
public Object selectList(@RequestParam Long jobId) {
|
public Object selectList(@RequestParam Long jobId) {
|
||||||
return sarImageService.selectByJob(FileTypeEnum.SAR_IMAGE_LOW, jobId);
|
return sarImageService.selectByJob(FileTypeEnum.SAR_IMAGE_LOW, jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询详情
|
* 查询详情
|
||||||
*/
|
*/
|
||||||
@GetMapping("/detail")
|
@GetMapping("/detail")
|
||||||
@ -77,29 +73,14 @@ public class JmImageController {
|
|||||||
@PostMapping("/addHigh")
|
@PostMapping("/addHigh")
|
||||||
public Object addHighImage(@Valid @RequestBody JmJobImageDTO dto) {
|
public Object addHighImage(@Valid @RequestBody JmJobImageDTO dto) {
|
||||||
String lockKey = SAR_IMAGE_UPLOAD_LOCK;
|
String lockKey = SAR_IMAGE_UPLOAD_LOCK;
|
||||||
String requestId = UUID.randomUUID().toString(); // 唯一标识当前请求
|
boolean locked = localLockUtil.tryLock(lockKey, 60); // 尝试 60 秒获取锁(比原来 1 分钟更灵活)
|
||||||
|
if (!locked) {
|
||||||
|
throw ServiceException.noLog("正在上传其它图像,请稍后重试!");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// 原子操作获取锁(设置过期时间30秒)
|
|
||||||
Boolean locked = redisTemplate.opsForValue().setIfAbsent(
|
|
||||||
lockKey,
|
|
||||||
requestId,
|
|
||||||
1,
|
|
||||||
TimeUnit.MINUTES
|
|
||||||
);
|
|
||||||
if (Boolean.FALSE.equals(locked)) {
|
|
||||||
throw ServiceException.noLog("正在上传其它图像,请稍后重试!");
|
|
||||||
}
|
|
||||||
sarImageService.addHighImage(dto);
|
sarImageService.addHighImage(dto);
|
||||||
} finally {
|
} finally {
|
||||||
// 仅删除自己的锁(Lua脚本保证原子性)
|
localLockUtil.unlock(lockKey); // 释放锁
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return "操作完成";
|
return "操作完成";
|
||||||
}
|
}
|
||||||
@ -135,6 +116,7 @@ public class JmImageController {
|
|||||||
.map(e -> BeanUtil.copyProperties(e, JmImageKtyDTO.class))
|
.map(e -> BeanUtil.copyProperties(e, JmImageKtyDTO.class))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,9 +42,6 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SarImageUdpProcessor imageProcessService;
|
private SarImageUdpProcessor imageProcessService;
|
||||||
|
|
||||||
// @Autowired
|
|
||||||
// private ISmpSubscriptService subscriptService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存所有执行中的任务的状态 key 任务配置ID,value 任务状态
|
* 缓存所有执行中的任务的状态 key 任务配置ID,value 任务状态
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,26 +1,24 @@
|
|||||||
package com.zhangy.skyeye.jm.task;
|
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.entity.SkyeyePayload;
|
||||||
import com.zhangy.skyeye.device.service.IPayloadService;
|
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.consts.CacheKey;
|
||||||
import com.zhangy.skyeye.publics.service.ISysLoginService;
|
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.sar.enums.SarControlTypeEnum;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
|
||||||
import com.zhangy.skyeye.sar.exception.SarConnectException;
|
import com.zhangy.skyeye.sar.exception.SarConnectException;
|
||||||
import com.zhangy.skyeye.sar.service.ISarControlService;
|
import com.zhangy.skyeye.sar.service.ISarControlService;
|
||||||
// import com.zhangy.skyeye.smp.dto.SmpUavStatusWsDTO;
|
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.cache.Cache;
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collection;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,18 +32,6 @@ public class JmTaskScheduler {
|
|||||||
@Setter
|
@Setter
|
||||||
private boolean isDebug;
|
private boolean isDebug;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JmJobStatusService jobStatusService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private SimpMessagingTemplate simpMessagingTemplate;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
RedisTemplate<String, Object> redisTemplate;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISysLoginService loginService;
|
private ISysLoginService loginService;
|
||||||
|
|
||||||
@ -56,7 +42,16 @@ public class JmTaskScheduler {
|
|||||||
private IPayloadService payloadService;
|
private IPayloadService payloadService;
|
||||||
|
|
||||||
@Autowired
|
@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返回
|
* 每1秒向前端推送雷达状态信息,断开连接则所有数据置0返回
|
||||||
@ -108,28 +103,31 @@ public class JmTaskScheduler {
|
|||||||
/**
|
/**
|
||||||
* 每10秒进行载荷连接判断:
|
* 每10秒进行载荷连接判断:
|
||||||
* 1. 如果有用户登录:
|
* 1. 如果有用户登录:
|
||||||
* 1)未执行连接指令,进行连接
|
* 1)未执行连接指令,进行连接
|
||||||
* 2)已经执行连接指令,但没有获取到sar状态信息,进行重连
|
* 2)已经执行连接指令,但没有获取到sar状态信息,进行重连
|
||||||
* 2. 没有用户在线:
|
* 2. 没有用户在线:
|
||||||
* 无。因为断开连接会先结束任务,不能断开连接。等硬件断电
|
* 无。因为断开连接会先结束任务,不能断开连接。等硬件断电
|
||||||
*/
|
*/
|
||||||
@Scheduled(fixedRate = 10000)
|
@Scheduled(fixedRate = 10000)
|
||||||
public void connectSar() {
|
public void connectSar() {
|
||||||
if (!loginService.hasLogged() || isDebug) { // 断开
|
if (!loginService.hasLogged() || isDebug) {
|
||||||
/*Map<Object, Object> map = redisUtil.hmget(CacheKey.SAR_CONNECTED);
|
// 断开逻辑:如果需要,可遍历 permanentCache 或其他方式获取 ip 列表
|
||||||
if (map != null && map.size() > 0) {
|
// 但当前注释掉了,保持原样或删除
|
||||||
map.keySet().forEach(ip -> controlInfoService.sendUdp((String) ip, SarControlTypeEnum.DISCONNECT));
|
} else {
|
||||||
}*/
|
|
||||||
} else { // 连接
|
|
||||||
List<SkyeyePayload> sarList = payloadService.getSar(null);
|
List<SkyeyePayload> sarList = payloadService.getSar(null);
|
||||||
sarList.forEach(sar -> {
|
sarList.forEach(sar -> {
|
||||||
if (!redisUtil.hHasKey(CacheKey.SAR_CONNECTED, sar.getIp()) ||
|
String ip = sar.getIp();
|
||||||
!redisUtil.hasKey(CacheKey.getSarConnect(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 {
|
try {
|
||||||
controlInfoService.sendUdp(sar.getIp(), SarControlTypeEnum.CONNECT);
|
controlInfoService.sendUdp(ip, SarControlTypeEnum.CONNECT);
|
||||||
|
log.info("尝试连接 SAR IP: {}", ip);
|
||||||
} catch (SarConnectException ex) {
|
} catch (SarConnectException ex) {
|
||||||
// sar可能没通电,连接失败不做处理
|
log.warn("连接雷达[{}]失败:{}", ip, ex.getMessage());
|
||||||
log.warn("连接雷达[" + sar.getIp() + "]失败:" + ex.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,19 +2,19 @@ package com.zhangy.skyeye.sar.context;
|
|||||||
|
|
||||||
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
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 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 org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
import javax.annotation.PostConstruct;
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* @PROJECT_NAME: skyeyesystem
|
* @PROJECT_NAME: skyeyesystem
|
||||||
* @DESCRIPTION: SAR 任务上下文提供者实现(基于 Redis 缓存,避免循环依赖)
|
* @DESCRIPTION: SAR 任务上下文提供者实现(基于 Redis 缓存,避免循环依赖)
|
||||||
* - JmJobStatusServiceImpl 在更新状态时,主动把 JmUavStatusDTO 推送到 Redis
|
* - JmJobStatusServiceImpl 在更新状态时,主动把 JmUavStatusDTO 推送到 Redis
|
||||||
* - 此类只从 Redis 读取,不注入 JmJobStatusService
|
* - 此类只从 Redis 读取,不注入 JmJobStatusService
|
||||||
* @AUTHOR: GuanCheng Long
|
* @AUTHOR: GuanCheng Long
|
||||||
* @DATE: 2026/1/21 1:10
|
* @DATE: 2026/1/21 1:10
|
||||||
*/
|
*/
|
||||||
@ -22,14 +22,21 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
||||||
|
|
||||||
@Autowired
|
private final CacheManager cacheManager;
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
// Redis 键前缀
|
private Cache uavStatusCache;
|
||||||
private static final String UAV_STATUS_KEY_PREFIX = "sar:context:uav:";
|
|
||||||
|
|
||||||
// 缓存过期时间(秒)
|
public SarTaskContextProviderImpl(CacheManager cacheManager) {
|
||||||
private static final long CACHE_EXPIRE_SECONDS = 600; // 10 分钟
|
this.cacheManager = cacheManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void initCache() {
|
||||||
|
this.uavStatusCache = cacheManager.getCache("uav-status");
|
||||||
|
if (this.uavStatusCache == null) {
|
||||||
|
log.error("缓存 uav-status 未找到,请检查 CacheConfig 配置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JmUavStatusDTO getCurrentUav(String payloadIp) {
|
public JmUavStatusDTO getCurrentUav(String payloadIp) {
|
||||||
@ -38,15 +45,8 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
|
String key = "sar:context:uav:" + payloadIp; // 保持原 key 格式
|
||||||
Object obj = redisUtil.get(key);
|
return CacheUtil.get(uavStatusCache, key, JmUavStatusDTO.class);
|
||||||
|
|
||||||
if (obj instanceof JmUavStatusDTO) {
|
|
||||||
return (JmUavStatusDTO) obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("Redis 中未找到 IP={} 的 uav 状态", payloadIp);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -80,11 +80,10 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
|
String key = "sar:context:uav:" + payloadIp;
|
||||||
try {
|
try {
|
||||||
// 设置值 + 过期时间
|
uavStatusCache.put(key, uavStatus); // 自动过期 10 分钟
|
||||||
redisUtil.set(key, uavStatus, CACHE_EXPIRE_SECONDS);
|
log.debug("已更新缓存:key={}, jobExecId={}", key, uavStatus.getJobExecId());
|
||||||
log.debug("已更新 Redis 缓存:key={}, jobExecId={}", key, uavStatus.getJobExecId());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("更新 SAR uav 状态缓存失败,ip={}", payloadIp, e);
|
log.error("更新 SAR uav 状态缓存失败,ip={}", payloadIp, e);
|
||||||
}
|
}
|
||||||
@ -97,8 +96,8 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
|||||||
if (payloadIp == null) {
|
if (payloadIp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
|
String key = "sar:context:uav:" + payloadIp;
|
||||||
redisUtil.del(key);
|
uavStatusCache.evict(key);
|
||||||
log.debug("SAR uav 缓存已标记清理(或自然过期):{}", key);
|
log.debug("SAR uav 缓存已清理:{}", key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,23 @@
|
|||||||
package com.zhangy.skyeye.sar.control;
|
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.SarControlDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 控制:连接
|
* 控制:建立连接
|
||||||
|
* 连接成功后,在回传状态时会将状态信息放入缓存,这里只记录连接标志
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Order(1)
|
@Order(1)
|
||||||
@ -23,7 +25,18 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SarControlConnectStrategy implements ISarControlStrategy {
|
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
|
@Override
|
||||||
public boolean supports(SarControlParamDTO param) {
|
public boolean supports(SarControlParamDTO param) {
|
||||||
@ -40,15 +53,16 @@ public class SarControlConnectStrategy implements ISarControlStrategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendPost(SarControlDTO sar) {
|
public void sendPost(SarControlDTO sar) {
|
||||||
// 建立连接后,回传状态时会将状态信息放入缓存
|
// 建立连接后,记录连接标志(value 可以是 Date、字符串 "connected" 或 boolean true,随你业务)
|
||||||
redisUtil.hset(CacheKey.SAR_CONNECTED, sar.getIp(), new Date());
|
if (permanentCache != null) {
|
||||||
|
permanentCache.put(sar.getIp(), new Date());
|
||||||
|
} else {
|
||||||
|
log.warn("无法记录连接标志,永久缓存未初始化");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int retryType() {
|
public int retryType() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2,8 +2,8 @@ package com.zhangy.skyeye.sar.control;
|
|||||||
|
|
||||||
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
||||||
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
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.consts.CacheKey;
|
||||||
|
import com.zhangy.skyeye.publics.utils.CacheUtil;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlPackDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlPackDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
@ -14,7 +14,8 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
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 org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@ -59,7 +60,7 @@ public class SarControlContext {
|
|||||||
private final int POLLING_INTERVAL = 100;
|
private final int POLLING_INTERVAL = 100;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private CacheManager cacheManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应答超时
|
* 应答超时
|
||||||
@ -74,6 +75,7 @@ public class SarControlContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行
|
* 执行
|
||||||
|
*
|
||||||
* @param param
|
* @param param
|
||||||
*/
|
*/
|
||||||
public void execute(SarControlParamDTO param) {
|
public void execute(SarControlParamDTO param) {
|
||||||
@ -89,12 +91,12 @@ public class SarControlContext {
|
|||||||
/**
|
/**
|
||||||
* 执行发送:失败则递归重试
|
* 执行发送:失败则递归重试
|
||||||
*
|
*
|
||||||
* @param param 控制参数
|
* @param param 控制参数
|
||||||
* @param matchedStrategy 控制策略
|
* @param matchedStrategy 控制策略
|
||||||
*/
|
*/
|
||||||
private void sendControl(SarControlParamDTO param, ISarControlStrategy matchedStrategy) {
|
private void sendControl(SarControlParamDTO param, ISarControlStrategy matchedStrategy) {
|
||||||
List<SarControlDTO> controls = matchedStrategy.handle(param);
|
List<SarControlDTO> controls = matchedStrategy.handle(param);
|
||||||
for(SarControlDTO control: controls) {
|
for (SarControlDTO control : controls) {
|
||||||
control.setIp(param.getIp());
|
control.setIp(param.getIp());
|
||||||
sendUdp(control);
|
sendUdp(control);
|
||||||
matchedStrategy.sendPost(control);
|
matchedStrategy.sendPost(control);
|
||||||
@ -103,30 +105,30 @@ public class SarControlContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送,未收到回执则重发
|
* 发送,未收到回执则重发
|
||||||
|
*
|
||||||
* @param control
|
* @param control
|
||||||
*/
|
*/
|
||||||
private void sendUdp(SarControlDTO control) {
|
private void sendUdp(SarControlDTO control) {
|
||||||
SarControlTypeEnum controlType = control.getControlType();
|
SarControlTypeEnum controlType = control.getControlType();
|
||||||
String ip = control.getIp();
|
String ip = control.getIp();
|
||||||
log.debug("开始发送雷达控制指令[" + controlType + "]----------------------");
|
log.debug("开始发送雷达控制指令[{}]", controlType);
|
||||||
//System.out.println(control);
|
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
byte[] content = pack(control);
|
byte[] content = pack(control);
|
||||||
socket.connect(new InetSocketAddress(ip, PORT));
|
socket.connect(new InetSocketAddress(ip, PORT));
|
||||||
DatagramPacket packet = new DatagramPacket(content, content.length);
|
DatagramPacket packet = new DatagramPacket(content, content.length);
|
||||||
|
|
||||||
int failCount = 0;
|
int failCount = 0;
|
||||||
SarErrorDTO info = null;
|
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);
|
socket.send(packet);
|
||||||
// 每0.1秒取回执,1秒后超时
|
// 每0.1秒取回执,1秒后超时
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
String cacheKey = CacheKey.getSarControlBack(ip);
|
|
||||||
|
|
||||||
while (System.currentTimeMillis() - startTime < ANSWER_TIMEOUT) {
|
while (System.currentTimeMillis() - startTime < ANSWER_TIMEOUT) {
|
||||||
if (redisUtil.hasKey(cacheKey)) {
|
// 从 Caffeine 获取
|
||||||
info = (SarErrorDTO) redisUtil.get(cacheKey);
|
Cache shortCache = cacheManager.getCache("sar-short-lived");
|
||||||
redisUtil.del(cacheKey);
|
info = CacheUtil.get(shortCache, cacheKey, SarErrorDTO.class);
|
||||||
|
if (info != null) {
|
||||||
|
shortCache.evict(cacheKey); // 相当于 del,读取后删除
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@ -146,10 +148,8 @@ public class SarControlContext {
|
|||||||
if (info == null) {
|
if (info == null) {
|
||||||
throw new SarConnectException("控制指令[" + controlType + "]发送失败,雷达[" + ip + "]无应答,请重试");
|
throw new SarConnectException("控制指令[" + controlType + "]发送失败,雷达[" + ip + "]无应答,请重试");
|
||||||
} else {
|
} else {
|
||||||
log.info("雷达控制指令[" + controlType + "]发送完毕----------------------");
|
log.info("雷达控制指令[{}]发送完毕----------------------", controlType);
|
||||||
}
|
}
|
||||||
} catch (RedisConnectionFailureException ex) {
|
|
||||||
throw ServiceException.errorLog("无法连接到Redis服务!");
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw ServiceException.errorLog("控制指令[" + controlType + "]发送失败 " + ex.getMessage());
|
throw ServiceException.errorLog("控制指令[" + controlType + "]发送失败 " + ex.getMessage());
|
||||||
}
|
}
|
||||||
@ -157,6 +157,7 @@ public class SarControlContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 封包
|
* 封包
|
||||||
|
*
|
||||||
* @param sarControl
|
* @param sarControl
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
package com.zhangy.skyeye.sar.control;
|
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.publics.consts.CacheKey;
|
||||||
|
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -22,7 +24,16 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SarControlDisconnectStrategy implements ISarControlStrategy {
|
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
|
@Override
|
||||||
public boolean supports(SarControlParamDTO param) {
|
public boolean supports(SarControlParamDTO param) {
|
||||||
@ -37,15 +48,15 @@ public class SarControlDisconnectStrategy implements ISarControlStrategy {
|
|||||||
return Collections.singletonList(connect);
|
return Collections.singletonList(connect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除缓存键
|
|
||||||
* @param sar
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void sendPost(SarControlDTO sar) {
|
public void sendPost(SarControlDTO sar) {
|
||||||
String connectKey = CacheKey.getSarConnect(sar.getIp());
|
String connectKey = CacheKey.getSarConnect(sar.getIp());
|
||||||
redisUtil.del(connectKey);
|
if (shortCache != null) {
|
||||||
redisUtil.hdel(CacheKey.SAR_CONNECTED, sar.getIp());
|
shortCache.evict(connectKey);
|
||||||
|
}
|
||||||
|
if (permanentCache != null) {
|
||||||
|
permanentCache.evict(sar.getIp());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package com.zhangy.skyeye.sar.listen;
|
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.dto.JmSarStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
||||||
import com.zhangy.skyeye.publics.consts.CacheKey;
|
import com.zhangy.skyeye.publics.consts.CacheKey;
|
||||||
@ -13,6 +12,8 @@ import com.zhangy.skyeye.sar.task.PriorityThreadFactory;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -50,7 +51,7 @@ public class SarStatusListener extends SarAbstractListener {
|
|||||||
private int connectTimeout;
|
private int connectTimeout;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private CacheManager cacheManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JmJobStatusService sarJobStatusService;
|
private JmJobStatusService sarJobStatusService;
|
||||||
@ -70,37 +71,40 @@ public class SarStatusListener extends SarAbstractListener {
|
|||||||
@Override
|
@Override
|
||||||
protected void processData(DatagramPacket packet) throws IOException {
|
protected void processData(DatagramPacket packet) throws IOException {
|
||||||
socket.receive(packet);
|
socket.receive(packet);
|
||||||
// 过滤无效数据包
|
|
||||||
if (packet.getLength() != 100) {
|
if (packet.getLength() != 100) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.debug("接收到状态包----------------------");
|
log.debug("接收到状态包----------------------");
|
||||||
String ip = packet.getAddress().getHostAddress();
|
String ip = packet.getAddress().getHostAddress();
|
||||||
// 处理接收到的数据
|
|
||||||
SarStatusPackDTO packDTO = SarStatusPackDTO.parse(ip, packet.getData());
|
SarStatusPackDTO packDTO = SarStatusPackDTO.parse(ip, packet.getData());
|
||||||
if (packDTO == null) {
|
if (packDTO == null) {
|
||||||
if (running)
|
if (running)
|
||||||
log.warn("[" + packDTO.getPayloadIp() + "]状态包校验失败,已丢弃。错误包=" +
|
log.warn("状态包校验失败,已丢弃。");
|
||||||
packDTO.getErrorPacketStatus() + ",状态包=" + packDTO.getDevicePacketStatus());
|
|
||||||
return;
|
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();
|
int errorStatus = packDTO.getErrorPacketStatus();
|
||||||
if (errorStatus == 1) {
|
if (errorStatus == 1) {
|
||||||
SarErrorDTO info = packDTO.getDeviceErrorInfo();
|
SarErrorDTO info = packDTO.getDeviceErrorInfo();
|
||||||
if (info.getErrorPacketType() == SarErrorTypeEnum.RESULT) {
|
if (info.getErrorPacketType() == SarErrorTypeEnum.RESULT) {
|
||||||
log.debug("收到回执包:" + ip);
|
log.debug("收到回执包:{}", ip);
|
||||||
redisUtil.set(CacheKey.getSarControlBack(ip), info, answerTimeout, TimeUnit.SECONDS);
|
String controlBackKey = CacheKey.getSarControlBack(ip);
|
||||||
|
sarShortCache.put(controlBackKey, info); // 写入,自动 2s 过期
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 状态包
|
// 状态包
|
||||||
int deviceStatus = packDTO.getDevicePacketStatus();
|
int deviceStatus = packDTO.getDevicePacketStatus();
|
||||||
if (deviceStatus == 1) {
|
if (deviceStatus == 1) {
|
||||||
JmSarStatusDTO info = packDTO.getDeviceStatusInfo();
|
JmSarStatusDTO info = packDTO.getDeviceStatusInfo();
|
||||||
log.debug("sar开机状态:" + info.getIsBoot());
|
log.debug("sar开机状态:{}", info.getIsBoot());
|
||||||
sarJobStatusService.update(ip, info);
|
sarJobStatusService.update(ip, info);
|
||||||
redisUtil.set(CacheKey.getSarConnect(ip), info, answerTimeout, TimeUnit.SECONDS);
|
String connectKey = CacheKey.getSarConnect(ip);
|
||||||
//System.out.println(info);
|
sarShortCache.put(connectKey, info); // 写入,自动过期
|
||||||
}
|
}
|
||||||
log.debug("----------------------状态包解析完毕");
|
log.debug("----------------------状态包解析完毕");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.JmJobDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmJobPayload;
|
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.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.control.SarControlContext;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
import com.zhangy.skyeye.sar.service.ISarControlService;
|
import com.zhangy.skyeye.sar.service.ISarControlService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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 org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,8 +29,17 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
|
|
||||||
private final SarControlContext udpSendContext;
|
private final SarControlContext udpSendContext;
|
||||||
|
|
||||||
@Autowired
|
private final CacheManager cacheManager; // final 字段
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
private Cache shortCache;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
this.shortCache = cacheManager.getCache("sar-short-lived");
|
||||||
|
if (shortCache == null) {
|
||||||
|
log.error("sar-short-lived cache 未找到!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendUdp(JmJobDTO job) {
|
public void sendUdp(JmJobDTO job) {
|
||||||
@ -60,7 +70,6 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendUdp(SarControlParamDTO param) {
|
public void sendUdp(SarControlParamDTO param) {
|
||||||
udpSendContext.execute(param);
|
udpSendContext.execute(param);
|
||||||
@ -87,7 +96,6 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
@Override
|
@Override
|
||||||
public JmSarStatusDTO getLatestStatus(String ip) {
|
public JmSarStatusDTO getLatestStatus(String ip) {
|
||||||
String connectKey = CacheKey.getSarConnect(ip);
|
String connectKey = CacheKey.getSarConnect(ip);
|
||||||
// 从缓存取载荷状态信息
|
return CacheUtil.get(shortCache, connectKey, JmSarStatusDTO.class);
|
||||||
return (JmSarStatusDTO) redisUtil.get(connectKey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
package com.zhangy.skyeye.sar.service.impl;
|
package com.zhangy.skyeye.sar.service.impl;
|
||||||
|
|
||||||
import com.zhangy.skyeye.common.extend.util.MathUtil;
|
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.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.JmImageRotateDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmImage;
|
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.jm.service.JmJobStatusService;
|
||||||
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
|
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
|
||||||
import com.zhangy.skyeye.publics.service.SysFileTypeService;
|
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.ImageUtil;
|
||||||
import com.zhangy.skyeye.publics.utils.OpenCVUtil;
|
import com.zhangy.skyeye.publics.utils.OpenCVUtil;
|
||||||
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
||||||
@ -22,8 +22,11 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.opencv.core.Mat;
|
import org.opencv.core.Mat;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -45,7 +48,7 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
private SarWsAsyncService sarWsAsyncService;
|
private SarWsAsyncService sarWsAsyncService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private CacheManager cacheManager;
|
||||||
|
|
||||||
// 图片最大宽度,前端说和电脑有关,4096保险点,一般是4096 8192 16384
|
// 图片最大宽度,前端说和电脑有关,4096保险点,一般是4096 8192 16384
|
||||||
@Value("${skyeye.sar.image.max:4096}")
|
@Value("${skyeye.sar.image.max:4096}")
|
||||||
@ -55,43 +58,53 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
private final String CACHE_FIELD_START_FRAME_NO = "startFrameNo";
|
private final String CACHE_FIELD_START_FRAME_NO = "startFrameNo";
|
||||||
// 当前帧号
|
// 当前帧号
|
||||||
private final String CACHE_FIELD_CURR_FRAME_NO = "currFrameNo";
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取基准图像信息
|
* 获取基准图像信息
|
||||||
*
|
*
|
||||||
* @param airlineId 航线执行ID
|
* @param airlineId 航线执行ID
|
||||||
* @param singleWidth 单条图片宽度
|
* @param singleWidth 单条图片宽度
|
||||||
* @param frameNo 当前帧号
|
* @param frameNo 当前帧号
|
||||||
* @return 返回非空的图像信息,其字段 imageNo 一定有值
|
* @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);
|
List<JmImage> imageList = imageService.selectLowByAirline(airlineId);
|
||||||
String cacheKey = "jmImgJoin-" + airlineId;
|
String cachePrefix = "jmImgJoin-" + airlineId;
|
||||||
JmImage base = null;
|
JmImage base;
|
||||||
// 情况1:航线第一张图
|
// 情况1:航线第一张图
|
||||||
if (ObjectUtil.isEmpty(imageList)) {
|
if (ObjectUtil.isEmpty(imageList)) {
|
||||||
base = new JmImage();
|
base = new JmImage();
|
||||||
base.setImageNo(1);
|
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;
|
return base;
|
||||||
}
|
}
|
||||||
// 情况2:如果最后一张还能拼图,则直接返回继续拼
|
// 情况2:如果最后一张还能拼图,则直接返回继续拼
|
||||||
JmImage last = imageList.get(imageList.size() - 1);
|
JmImage last = imageList.get(imageList.size() - 1);
|
||||||
Integer startFrameNo = (Integer) redisUtil.hget(cacheKey, CACHE_FIELD_START_FRAME_NO);
|
Integer startFrameNo = CacheUtil.get(joinStateCache, cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, Integer.class);
|
||||||
int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1); // 图宽(当前图+基准图)
|
int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1);
|
||||||
int surplusNum = (IMG_MAX_WITH - currWidth) / singleWidth; // 还可以拼图片数
|
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 ||
|
if (startFrameNo == null || currWidth < IMG_MAX_WITH ||
|
||||||
baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) { // 当前图+填充 不能超过允许拼接数
|
baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) {
|
||||||
log.info("当前宽度:" + currWidth + " < " + IMG_MAX_WITH + " 可以继续拼接");
|
log.info("当前宽度:{} < {} 可以继续拼接", currWidth, IMG_MAX_WITH);
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
// 情况3:已经拼接到最大数量,或者当前图+填充数量超过允许拼接数量,创建新图像文件
|
// 情况3:已经拼接到最大数量,或者当前图+填充数量超过允许拼接数量,创建新图像文件
|
||||||
log.info("当前宽度:" + currWidth + " > " + IMG_MAX_WITH + " 重新拼接,当前帧号" + frameNo + "作为首帧");
|
log.info("当前宽度:{} > {} 重新拼接,当前帧号{}作为首帧", currWidth, IMG_MAX_WITH, frameNo);
|
||||||
base = new JmImage();
|
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);
|
base.setImageNo(last.getImageNo() + 1);
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
@ -106,47 +119,12 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
}
|
}
|
||||||
// 使用前一张图的右侧坐标作为后一张图的左侧,前提是没丢图
|
// 使用前一张图的右侧坐标作为后一张图的左侧,前提是没丢图
|
||||||
if (!isFirst && !lostImage) {
|
if (!isFirst && !lostImage) {
|
||||||
/*imageFrame.setLon1(before[0]);
|
// 注释部分保持原样
|
||||||
imageFrame.setLat1(before[1]);
|
|
||||||
imageFrame.setLon4(before[2]);
|
|
||||||
imageFrame.setLat4(before[3]);*/
|
|
||||||
}
|
}
|
||||||
before[0] = imageFrame.getLon5();
|
before[0] = imageFrame.getLon5();
|
||||||
before[1] = imageFrame.getLat5();
|
before[1] = imageFrame.getLat5();
|
||||||
before[2] = imageFrame.getLon8();
|
before[2] = imageFrame.getLon8();
|
||||||
before[3] = imageFrame.getLat8();
|
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;
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,8 +132,8 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
*
|
*
|
||||||
* @param sourceIp
|
* @param sourceIp
|
||||||
* @param airlineExecId
|
* @param airlineExecId
|
||||||
* @param frameData 图像帧数据
|
* @param frameData 图像帧数据
|
||||||
* @param imageFrame 图像帧
|
* @param imageFrame 图像帧
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public JmImage parseImage(String sourceIp, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
public JmImage parseImage(String sourceIp, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
||||||
@ -186,28 +164,28 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3.保存图像png,用航线ID+序号命名
|
// 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 imageName = airlineExecId + "-" + base.getImageNo() + ".png";
|
||||||
String[] imagePath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, imageName);
|
String[] imagePath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, imageName);
|
||||||
String currPath = imagePath[0];
|
String currPath = imagePath[0];
|
||||||
|
|
||||||
System.out.println("帧:" + frameNo);
|
System.out.println("帧:" + frameNo);
|
||||||
|
|
||||||
// 4.保存基准图(同步),用于下次拼接
|
// 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); // 判断是否丢图
|
boolean lostImage = baseNo != null && (frameNo - baseNo > 1); // 判断是否丢图
|
||||||
String basePath = sysFileTypeService.getAbsolutePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId,
|
String basePath = sysFileTypeService.getAbsolutePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId,
|
||||||
airlineExecId + "-" + base.getImageNo() +"-base.png");
|
airlineExecId + "-" + base.getImageNo() + "-base.png");
|
||||||
Mat baseMat = generateBaseMat(base, currImage, frameNo, imageFrame.getMax(), basePath, baseNo);
|
Mat baseMat = generateBaseMat(base, currImage, frameNo, imageFrame.getMax(), basePath, baseNo);
|
||||||
if (baseMat == null) { // 拼接失败 或 基准图生成失败则跳过,按丢图处理
|
if (baseMat == null) { // 拼接失败 或 基准图生成失败则跳过,按丢图处理
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (lostImage) {
|
if (lostImage) {
|
||||||
log.warn("丢图"+(frameNo - baseNo)+"张!当前帧" + frameNo + ",前帧" + baseNo);
|
log.warn("丢图{}张!当前帧{},前帧{}", (frameNo - baseNo), frameNo, baseNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//modCoord(imageFrame, lostImage, currAirline);
|
//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
|
// ### 亮度调整,用于可靠udp版本图像,固定使用系数0.5
|
||||||
// 拆分多张图片,去掉自适应调整
|
// 拆分多张图片,去掉自适应调整
|
||||||
if (IMG_MAX_WITH > 20000) {
|
if (IMG_MAX_WITH > 20000) {
|
||||||
@ -220,15 +198,15 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
// 6.更新基准图坐标和帧号
|
// 6.更新基准图坐标和帧号
|
||||||
JmImage imageInfo = saveImage(uav, airlineExecId, base, imagePath, imageFrame, frameNo, lostImage);
|
JmImage imageInfo = saveImage(uav, airlineExecId, base, imagePath, imageFrame, frameNo, lostImage);
|
||||||
long end = System.currentTimeMillis();
|
long end = System.currentTimeMillis();
|
||||||
log.info("生成" + imageFrame.getImageBitDeep()+"位雷达回传图像:帧序号" + frameNo + "," +
|
log.info("生成{}位雷达回传图像:帧序号{},{},耗时{}秒",
|
||||||
imageInfo.getRelativePath() + ",耗时" + (end - start)/1000 + "秒");
|
imageFrame.getImageBitDeep(), frameNo, imageInfo.getRelativePath(), (end - start) / 1000);
|
||||||
return imageInfo;
|
return imageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载回传图像,先转置再转为Mat
|
* 加载回传图像,先转置再转为Mat
|
||||||
*
|
*
|
||||||
* @param frameData 图像帧数据,包含参数信息和未转置的8位灰度图像数据
|
* @param frameData 图像帧数据,包含参数信息和未转置的8位灰度图像数据
|
||||||
* @param imageFrame 图像帧对象
|
* @param imageFrame 图像帧对象
|
||||||
* @return 转置后的4通道图像Mat
|
* @return 转置后的4通道图像Mat
|
||||||
*/
|
*/
|
||||||
@ -261,10 +239,10 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
/**
|
/**
|
||||||
* 生成基准图,将原有基准图与新图拼接
|
* 生成基准图,将原有基准图与新图拼接
|
||||||
*
|
*
|
||||||
* @param base 基准图信息
|
* @param base 基准图信息
|
||||||
* @param currImage 当前图
|
* @param currImage 当前图
|
||||||
* @param currNo 当前图的帧号
|
* @param currNo 当前图的帧号
|
||||||
* @param currMax 当前图最大值
|
* @param currMax 当前图最大值
|
||||||
* @param imagePath 基准图路径
|
* @param imagePath 基准图路径
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -277,7 +255,7 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
if (baseMax < currMax) {
|
if (baseMax < currMax) {
|
||||||
OpenCVUtil.multiply(baseImage, baseMax / currMax);
|
OpenCVUtil.multiply(baseImage, baseMax / currMax);
|
||||||
} else if (baseMax > currMax) {
|
} else if (baseMax > currMax) {
|
||||||
OpenCVUtil.multiply(currImage , currMax / baseMax);
|
OpenCVUtil.multiply(currImage, currMax / baseMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mat baseMat = ImageUtil.join(baseImage, baseNo, currImage, currNo); // 会释放 currImage baseImage 资源
|
Mat baseMat = ImageUtil.join(baseImage, baseNo, currImage, currNo); // 会释放 currImage baseImage 资源
|
||||||
@ -291,9 +269,9 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
* 生成后处理图,将拼接好的基准图转置并调整对比度
|
* 生成后处理图,将拼接好的基准图转置并调整对比度
|
||||||
*
|
*
|
||||||
* @param currAirline 当前航线信息
|
* @param currAirline 当前航线信息
|
||||||
* @param baseMat 原基准图,处理后释放资源
|
* @param baseMat 原基准图,处理后释放资源
|
||||||
* @param imagePath 后处理图路径,每条航线对应一张图,每次生成会覆盖
|
* @param imagePath 后处理图路径,每条航线对应一张图,每次生成会覆盖
|
||||||
* @param imageLight 图像亮度倍数,0是不调整
|
* @param imageLight 图像亮度倍数,0是不调整
|
||||||
*/
|
*/
|
||||||
private void generateAfterMat(JmAirlineStatusDTO currAirline, Mat baseMat, String imagePath, int imageLight) {
|
private void generateAfterMat(JmAirlineStatusDTO currAirline, Mat baseMat, String imagePath, int imageLight) {
|
||||||
// 后处理参数
|
// 后处理参数
|
||||||
|
|||||||
@ -46,11 +46,11 @@ spring:
|
|||||||
date-format: yyyy-MM-dd'T'HH:mm:ss'Z'
|
date-format: yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||||
|
|
||||||
#redis 配置
|
#redis 配置
|
||||||
redis:
|
#redis:
|
||||||
database: 0
|
#database: 0
|
||||||
host: 127.0.0.1
|
#host: 127.0.0.1
|
||||||
port: 6379
|
#port: 6379
|
||||||
password: 'P@ssw0rd'
|
#password: 'P@ssw0rd'
|
||||||
|
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
mapper-locations: classpath*:mapping/**/*Mapping.xml
|
mapper-locations: classpath*:mapping/**/*Mapping.xml
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user