package com.ard.work.device.camera.service.impl;
|
|
import com.ard.common.core.constant.CacheConstants;
|
import com.ard.common.core.constant.DeviceConstants;
|
import com.ard.common.core.constant.UserConstants;
|
import com.ard.common.core.exception.ServiceException;
|
import com.ard.common.core.utils.DateUtils;
|
import com.ard.common.core.utils.SpringUtils;
|
import com.ard.common.core.utils.StringUtils;
|
import com.ard.common.core.utils.bean.BeanValidators;
|
import com.ard.common.core.utils.uuid.IdUtils;
|
import com.ard.common.core.web.domain.AjaxResult;
|
import com.ard.common.datascope.annotation.DataScope;
|
import com.ard.common.redis.service.RedisService;
|
import com.ard.common.security.utils.SecurityUtils;
|
import com.ard.system.api.domain.SysUser;
|
import com.ard.work.api.domian.*;
|
import com.ard.work.device.camera.domain.CameraFilterDTO;
|
import com.ard.work.device.camera.domain.GeoDTO;
|
import com.ard.work.device.camera.mapper.ArdCameraMapper;
|
import com.ard.work.device.camera.service.IArdCameraHepuService;
|
import com.ard.work.device.camera.service.IArdCameraPtzScopeService;
|
import com.ard.work.device.camera.service.IArdCameraService;
|
import com.ard.work.device.channel.service.IArdChannelService;
|
import com.ard.work.event.LoginEvent;
|
import com.ard.work.sdk.annotation.SdkOperate;
|
import com.ard.work.sdk.model.PtzScope;
|
import com.ard.work.sdk.service.CameraSDKService;
|
import com.ard.work.utils.gis.GisUtil;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.ObjectUtils;
|
import org.springframework.boot.ApplicationArguments;
|
import org.springframework.boot.ApplicationRunner;
|
import org.springframework.context.event.EventListener;
|
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import jakarta.validation.Validator;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* 相机设备Service业务层处理
|
*
|
* @author 刘苏义
|
* @date 2023-02-11
|
*/
|
@Service
|
@Slf4j
|
public class ArdCameraServiceImpl implements IArdCameraService, ApplicationRunner {
|
@Resource
|
private ArdCameraMapper ardCameraMapper;
|
|
@Resource
|
private CameraSDKService cameraSDKService;
|
|
@Resource
|
private RedisService redisService;
|
|
@Resource
|
private IArdChannelService ardChannelService;
|
|
@Resource
|
private IArdCameraHepuService ardCameraHepuService;
|
|
@Resource
|
private IArdCameraPtzScopeService ardCameraPtzScopeService;
|
|
@Resource
|
protected Validator validator;
|
|
@Override
|
public void run(ApplicationArguments args) {
|
initCamera();
|
}
|
|
public void initCamera() {
|
//清空相机缓存
|
redisService.deleteObject(CacheConstants.CAMERA_LIST);
|
redisService.deleteObject(CacheConstants.CAMERA_NAME);
|
redisService.deleteObject(CacheConstants.CAMERA_ONLINE);
|
//重置所有相机登录ID
|
ardCameraMapper.resetCameraLoginId();
|
ardCameraPtzScopeService.clearAll();//清空所有相机 PTZ 范围
|
//重新加载相机到缓存
|
ardCameraMapper.selectArdCameraList(new ArdCamera()).forEach(ardCamera -> {
|
redisService.setCacheMapValue(CacheConstants.CAMERA_LIST, ardCamera.getId(), ardCamera);
|
// 建立 NAME 到 ID 的映射
|
redisService.setCacheMapValue(CacheConstants.CAMERA_NAME, ardCamera.getName(), ardCamera);
|
cameraSDKService.login(ardCamera);
|
});
|
}
|
|
/**
|
* 查询相机设备
|
*
|
* @param id 相机设备主键
|
* @return 相机设备
|
*/
|
@Override
|
public ArdCamera selectArdCameraById(String id) {
|
return ardCameraMapper.selectArdCameraById(id);
|
}
|
|
/**
|
* 查询相机设备
|
*
|
* @param name 相机设备名称
|
* @return 相机设备
|
*/
|
@Override
|
public ArdCamera selectArdCameraByName(String name) {
|
return ardCameraMapper.selectArdCameraByName(name);
|
}
|
|
/**
|
* 查询相机设备列表
|
*
|
* @param ardCamera 相机设备
|
* @return 相机设备
|
*/
|
@Override
|
@DataScope(deptAlias = "d", userAlias = "u")
|
public List<ArdCamera> selectArdCameraList(ArdCamera ardCamera) {
|
return ardCameraMapper.selectArdCameraList(ardCamera);
|
}
|
|
/**
|
* 查询所有相机
|
*
|
* @param ardCamera 相机设备
|
* @return 相机设备
|
*/
|
@Override
|
public List<ArdCamera> selectArdCameraListAll(ArdCamera ardCamera) {
|
return ardCameraMapper.selectArdCameraList(ardCamera);
|
}
|
|
/**
|
* 新增相机设备
|
*
|
* @param ardCamera 相机设备
|
* @return 结果
|
*/
|
@Override
|
@Transactional
|
public int insertArdCamera(ArdCamera ardCamera) {
|
ardCamera.setId(IdUtils.simpleUUID());
|
ardCamera.setCreateBy(SecurityUtils.getUsername());
|
ardCamera.setCreateTime(DateUtils.getNowDate());
|
ardCamera.setUserId(SecurityUtils.getUserId());
|
int res = ardCameraMapper.insertArdCamera(ardCamera);
|
if (res > 0) {
|
if (Objects.equals(ardCamera.getFactory(), "5")) {
|
//若为合浦设备,则处理合浦节点
|
this.handleHepuNodesInsert(ardCamera);
|
}
|
redisService.setCacheMapValue(CacheConstants.CAMERA_LIST, ardCamera.getId(), ardCamera);
|
cameraSDKService.login(ardCamera);//登录
|
}
|
return res;
|
}
|
|
/**
|
* 处理合浦子节点保存逻辑
|
* 私有方法,封装子表插入细节
|
*/
|
private void handleHepuNodesInsert(ArdCamera camera) {
|
List<ArdCameraHepu> nodes = camera.getHepuNodes();
|
if (nodes == null || nodes.isEmpty()) {
|
throw new ServiceException("合浦设备必须配置可见光和热红外物理节点");
|
}
|
|
List<ArdCameraHepu> insertList = new ArrayList<>();
|
for (ArdCameraHepu node : nodes) {
|
// 初始化子节点 ID
|
node.setId(IdUtils.simpleUUID());
|
// 关联主表 ID
|
node.setCameraId(camera.getId());
|
|
// 继承主表的创建信息
|
node.setCreateBy(camera.getCreateBy());
|
node.setCreateTime(DateUtils.getNowDate());
|
|
// 初始化运行时字段 (SDK句柄等)
|
node.setLoginId(-1L);
|
node.setSerialHandle(-1L);
|
node.setStatus("0"); // 初始离线
|
|
insertList.add(node);
|
}
|
|
// 调用子表 Service 批量插入
|
int subRes = ardCameraHepuService.batchInsertArdCameraHepu(insertList);
|
|
if (subRes != insertList.size()) {
|
throw new ServiceException("合浦子节点保存失败,数据不一致");
|
}
|
}
|
|
/**
|
* 修改相机设备
|
*
|
* @param ardCamera 相机设备
|
* @return 结果
|
*/
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public int updateArdCamera(ArdCamera ardCamera) {
|
ardCamera.setUpdateTime(DateUtils.getNowDate());
|
ardCamera.setUpdateBy(SecurityUtils.getUsername());
|
int res = ardCameraMapper.updateArdCamera(ardCamera);
|
if (res > 0) {
|
if (Objects.equals(ardCamera.getFactory(), "5")) {
|
//若为合浦设备,则处理合浦节点
|
this.handleHepuNodesUpdate(ardCamera);
|
}
|
redisService.setCacheMapValue(CacheConstants.CAMERA_LIST, ardCamera.getId(), ardCamera);
|
cameraSDKService.logout(ardCamera.getId());//注销
|
cameraSDKService.login(ardCamera);//登录
|
}
|
return res;
|
}
|
|
/**
|
* 处理合浦子节点更新逻辑
|
* 策略:全量替换 (先删后插)
|
*/
|
private void handleHepuNodesUpdate(ArdCamera newCamera) {
|
List<ArdCameraHepu> newNodes = newCamera.getHepuNodes();
|
|
String cameraId = newCamera.getId();
|
|
// 1. 删除旧节点
|
int deleteCount = ardCameraHepuService.deleteByCameraId(cameraId);
|
log.debug("已删除相机 [{}] 的旧子节点,数量:{}", cameraId, deleteCount);
|
|
// 2. 准备新数据
|
List<ArdCameraHepu> insertList = new ArrayList<>();
|
String operator = SecurityUtils.getUsername();
|
Date now = DateUtils.getNowDate();
|
|
for (ArdCameraHepu node : newNodes) {
|
node.setId(IdUtils.simpleUUID()); // 生成新 ID
|
node.setCameraId(cameraId);
|
node.setCreateBy(operator);
|
node.setCreateTime(now);
|
// 重置运行状态
|
node.setLoginId(-1L);
|
node.setSerialHandle(-1L);
|
node.setStatus("0");
|
insertList.add(node);
|
}
|
|
// 3. 批量插入
|
int subRes = ardCameraHepuService.batchInsertArdCameraHepu(insertList);
|
if (subRes != insertList.size()) {
|
throw new ServiceException("合浦子节点保存失败,数据不一致");
|
}
|
|
log.info("合浦设备 [{}] 子节点更新成功,新数量:{}", cameraId, insertList.size());
|
}
|
|
/**
|
* 更新相机状态
|
*
|
* @param cameraId 相机主键
|
* @param state 状态
|
* @return 结果
|
*/
|
@Override
|
public int updateCameraState(String cameraId, String state) {
|
ArdCamera ardCamera = new ArdCamera();
|
ardCamera.setId(cameraId);
|
ardCamera.setState(state);
|
return ardCameraMapper.updateArdCamera(ardCamera);
|
}
|
|
@Override
|
public void updateAlarmGuideEnable(ArdCamera ardCamera) {
|
ardCameraMapper.updateArdCamera(ardCamera);
|
}
|
|
/**
|
* 更新相机坐标和基座偏航角
|
*
|
* @param cameraId 相机设备
|
* @param longitude 经度
|
* @param latitude 纬度
|
* @param altitude 高度
|
* @param baseYaw 基座偏航角
|
* @return 结果
|
*/
|
@Override
|
public int updateCameraLocationAndYaw(String cameraId, Double longitude, Double latitude, Double altitude,
|
Float baseYaw) {
|
ArdCamera camera = ardCameraMapper.selectArdCameraById(cameraId);
|
if (camera == null) {
|
throw new ServiceException("相机不存在");
|
}
|
// 只更新非空字段
|
if (longitude != null) {
|
camera.setLongitude(longitude);
|
}
|
if (latitude != null) {
|
camera.setLatitude(latitude);
|
}
|
if (altitude != null) {
|
camera.setAltitude(altitude);
|
}
|
if (baseYaw != null) {
|
camera.setBaseYaw(baseYaw);
|
}
|
camera.setUpdateTime(DateUtils.getNowDate());
|
camera.setUpdateBy(SecurityUtils.getUsername());
|
int res = ardCameraMapper.updateArdCamera(camera);
|
if (res > 0) {
|
redisService.setCacheMapValue(CacheConstants.CAMERA_LIST, camera.getId(), camera);
|
}
|
return res;
|
}
|
|
/**
|
* 批量删除相机设备
|
*
|
* @param ids 需要删除的相机设备主键
|
* @return 结果
|
*/
|
@Override
|
public int deleteArdCameraByIds(String[] ids) {
|
for (String id : ids) {
|
ArdCamera camera = ardCameraMapper.selectArdCameraById(id);
|
if (camera != null) {
|
if (Objects.equals(camera.getFactory(), "5")) {
|
//若为合浦设备,则处理合浦节点
|
this.handleHepuNodesDelete(camera);
|
}
|
//redis中删除
|
redisService.deleteCacheMapValue(CacheConstants.CAMERA_LIST, id);
|
redisService.deleteCacheMapValue(CacheConstants.CAMERA_NAME, camera.getName());
|
//删除当前相机的所有通道
|
ardChannelService.deleteArdChannelByDeviceId(id);
|
//sdk注销
|
cameraSDKService.logout(id);
|
}
|
}
|
return ardCameraMapper.deleteArdCameraByIds(ids);
|
}
|
|
/**
|
* 处理合浦子节点删除逻辑
|
* 策略:全量替换 (先删后插)
|
*/
|
private void handleHepuNodesDelete(ArdCamera ardCamera) {
|
String cameraId = ardCamera.getId();
|
int deleteCount = ardCameraHepuService.deleteByCameraId(cameraId);
|
log.debug("已删除相机 [{}] 的旧子节点,数量:{}", cameraId, deleteCount);
|
}
|
|
/**
|
* 删除相机设备信息
|
*
|
* @param id 相机设备主键
|
* @return 结果
|
*/
|
@Override
|
public int deleteArdCameraById(String id) {
|
int i = ardCameraMapper.deleteArdCameraById(id);
|
if (i > 0) {
|
//redis中删除
|
redisService.deleteCacheMapValue(CacheConstants.CAMERA_LIST, id);
|
//删除当前相机的所有通道
|
ardChannelService.deleteArdChannelByDeviceId(id);
|
//sdk注销
|
cameraSDKService.logout(id);
|
}
|
return i;
|
}
|
|
@Override
|
public String importCamera(List<ArdCamera> ardCameraList, Boolean isUpdateSupport, String operatorName) {
|
if (StringUtils.isNull(ardCameraList) || ardCameraList.isEmpty()) {
|
throw new ServiceException("导入相机数据不能为空!");
|
}
|
int successNum = 0;
|
int failureNum = 0;
|
StringBuilder successMsg = new StringBuilder();
|
StringBuilder failureMsg = new StringBuilder();
|
for (ArdCamera camera : ardCameraList) {
|
try {
|
//获取当前登录用户id
|
Long userId = SecurityUtils.getUserId();
|
camera.setUserId(userId);
|
// 验证是否存在这个用户
|
ArdCamera u = ardCameraMapper.selectArdCameraById(camera.getId());
|
if (StringUtils.isNull(u)) {
|
BeanValidators.validateWithException(validator, camera);
|
camera.setCreateBy(operatorName);
|
this.insertArdCamera(camera);
|
successNum++;
|
successMsg.append("<br/>" + successNum + "、相机ID " + camera.getId() + " 导入成功");
|
} else if (isUpdateSupport) {
|
BeanValidators.validateWithException(validator, camera);
|
checkCameraDataScope(camera.getUserId());
|
camera.setUpdateBy(operatorName);
|
this.updateArdCamera(camera);
|
successNum++;
|
successMsg.append("<br/>" + successNum + "、相机ID " + camera.getId() + " 更新成功");
|
} else {
|
failureNum++;
|
failureMsg.append("<br/>" + failureNum + "、相机ID " + camera.getId() + " 已存在");
|
}
|
} catch (Exception e) {
|
failureNum++;
|
String msg = "<br/>" + failureNum + "、相机ID " + camera.getId() + " 导入失败:";
|
failureMsg.append(msg + e.getMessage());
|
log.error(msg, e);
|
}
|
}
|
if (failureNum > 0) {
|
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
throw new ServiceException(failureMsg.toString());
|
} else {
|
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
|
}
|
return successMsg.toString();
|
}
|
|
/**
|
* 校验用户是否有数据权限
|
*
|
* @param userId 用户id
|
*/
|
@Override
|
public void checkCameraDataScope(Long userId) {
|
if (!UserConstants.isAdmin(SecurityUtils.getUserId())) {
|
ArdCamera camera = new ArdCamera();
|
camera.setUserId(userId);
|
List<ArdCamera> cameras = SpringUtils.getAopProxy(this).selectArdCameraList(camera);
|
if (StringUtils.isEmpty(cameras)) {
|
throw new ServiceException("没有权限访问井数据!");
|
}
|
}
|
}
|
|
/**
|
* 校验相机是否唯一
|
*
|
* @param camera 相机
|
* @return 结果
|
*/
|
@Override
|
public String checkCameraIpAndPortUnique(ArdCamera camera) {
|
String id = camera.getId();
|
String ip = camera.getIp();
|
Integer port = camera.getPort();
|
ArdCamera info = ardCameraMapper.checkCameraIpAndPortUnique(ip, port);
|
if (StringUtils.isNotNull(info) && !info.getId().equals(id)) {
|
return DeviceConstants.NOT_UNIQUE;
|
}
|
return DeviceConstants.UNIQUE;
|
}
|
|
/**
|
* 操控锁定 0-解锁 1-锁定
|
*
|
* @param cmd 相机指令
|
* @author 刘苏义
|
* @date 2024/8/23 15:16
|
*/
|
@Override
|
@SdkOperate
|
public AjaxResult controlLock(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();//申请锁的相机
|
ArdCamera ardCamera = selectArdCameraById(cameraId);
|
if (ardCamera == null) {
|
return AjaxResult.warn("设备不存在");
|
}
|
Date now = new Date();
|
now.setTime(now.getTime() + cmd.getExpired() * 1000);
|
ardCamera.setOperatorExpired(now);//设置当前过期时间
|
ardCameraMapper.updateArdCamera(ardCamera);
|
return AjaxResult.success("操控锁定成功");
|
}
|
|
/**
|
* 操控解锁
|
*
|
* @param cmd 相机指令
|
* @author 刘苏义
|
* @date 2024/8/23 15:16
|
*/
|
@Override
|
public AjaxResult controlUnLock(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();//申请解锁的相机
|
String operator = cmd.getOperator();
|
ArdCamera ardCamera = selectArdCameraById(cameraId);
|
if (ardCamera == null) {
|
return AjaxResult.warn("设备不存在");
|
}
|
if (ardCamera.getOperatorId().equals(operator)) {
|
//如果解锁相机的当前用户是申请者,设置过期时间为当前时间
|
ardCamera.setOperatorExpired(new Date());
|
ardCameraMapper.updateArdCamera(ardCamera);
|
}
|
return AjaxResult.success("操控解锁成功");
|
}
|
|
/**
|
* 同步通道
|
*
|
* @param oldArrayList 旧通道列表(库中)
|
* @param newArrayList 新通道列表(sdk)
|
* @author 刘苏义
|
* @date 2024/8/23 16:28
|
*/
|
@Override
|
public void asyncChannel(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) {
|
// 空值安全
|
if (oldArrayList == null) oldArrayList = new ArrayList<>();
|
if (newArrayList == null) newArrayList = new ArrayList<>();
|
|
// 【修复点】key = deviceId + ":" + chanNo,保证全局唯一
|
Map<String, ArdChannel> oldMap = oldArrayList.stream()
|
.collect(Collectors.toMap(
|
channel -> channel.getDeviceId() + ":" + channel.getChanNo(),
|
channel -> channel
|
));
|
|
Map<String, ArdChannel> newMap = newArrayList.stream()
|
.collect(Collectors.toMap(
|
channel -> channel.getDeviceId() + ":" + channel.getChanNo(),
|
channel -> channel
|
));
|
|
// ====================== 更新 ======================
|
newArrayList.stream()
|
.filter(channel -> {
|
String key = channel.getDeviceId() + ":" + channel.getChanNo();
|
ArdChannel oldChannel = oldMap.get(key);
|
return oldChannel != null && (
|
!Objects.equals(channel.getName(), oldChannel.getName()) ||
|
!Objects.equals(channel.getVideoCode(), oldChannel.getVideoCode()) ||
|
!Objects.equals(channel.getRtspUrl(), oldChannel.getRtspUrl()) // 新增 rtspUrl 判断
|
);
|
})
|
.forEach(channel -> {
|
String key = channel.getDeviceId() + ":" + channel.getChanNo();
|
ArdChannel oldChannel = oldMap.get(key);
|
channel.setId(oldChannel.getId());
|
ardChannelService.updateArdChannel(channel);
|
});
|
|
// ====================== 删除 ======================
|
oldArrayList.stream()
|
.filter(channel -> {
|
String key = channel.getDeviceId() + ":" + channel.getChanNo();
|
return !newMap.containsKey(key);
|
})
|
.forEach(channel -> ardChannelService.deleteArdChannelById(channel.getId()));
|
|
// ====================== 新增 ======================
|
newArrayList.stream()
|
.filter(channel -> {
|
String key = channel.getDeviceId() + ":" + channel.getChanNo();
|
return !oldMap.containsKey(key);
|
})
|
.forEach(channel -> {
|
if (ardChannelService.checkArdChannelUnique(channel)) {
|
ArdCamera camera = this.selectArdCameraById(channel.getDeviceId());
|
if(camera!=null) {
|
channel.setRtspUrl(getRtspUrl(camera, channel));
|
}
|
ardChannelService.insertArdChannel(channel);
|
}
|
});
|
}
|
|
/**
|
* 监听登录事件
|
*
|
* @param loginEvent 登录事件
|
* @author 刘苏义
|
* @date 2024/8/23 15:42
|
*/
|
@Async
|
@EventListener(LoginEvent.class)
|
public void LoginEventListener(LoginEvent loginEvent) {
|
ArdCamera camera = loginEvent.getCamera();
|
String state = camera.getState();
|
Long loginId = camera.getLoginId();
|
String cameraId = camera.getId();
|
String type = camera.getType();
|
if ("1".equals(state)) {
|
redisService.setCacheMapValue(CacheConstants.CAMERA_ONLINE, cameraId, loginId);
|
} else {
|
redisService.deleteCacheMapValue(CacheConstants.CAMERA_ONLINE, cameraId);
|
}
|
// 更新相机信息
|
ardCameraMapper.updateArdCamera(camera);
|
redisService.setCacheMapValue(CacheConstants.CAMERA_LIST, cameraId, camera);
|
// 更新通道信息
|
List<ArdChannel> channelList = camera.getChannelList();
|
if (channelList == null || channelList.isEmpty()) {
|
ardChannelService.deleteArdChannelByDeviceId(camera.getId());
|
return;
|
}
|
// 查询数据库已有通道
|
ArdChannel query = new ArdChannel();
|
query.setDeviceId(camera.getId());
|
Map<Integer, ArdChannel> dbMap = ardChannelService.selectArdChannelList(query)
|
.stream()
|
.collect(Collectors.toMap(ArdChannel::getChanNo, c -> c));
|
// 逐条处理
|
channelList.forEach(channel -> {
|
ArdChannel dbChannel = dbMap.get(channel.getChanNo());
|
if (dbChannel == null) {
|
channel.setRtspUrl(getRtspUrl(camera,channel));
|
// 不存在 → 插入
|
ardChannelService.insertArdChannel(channel);
|
} else {
|
// 存在 → 更新
|
channel.setId(dbChannel.getId());
|
ardChannelService.updateArdChannel(channel);
|
}
|
});
|
|
// ========== 新增:获取并存储 PTZ 范围 ==========
|
// 仅在大光电登录成功时获取(state == "1")
|
if ("1".equals(state) && "1".equals(type)) {
|
savePtzScopeOnLogin(camera);
|
}
|
}
|
|
/**
|
* 登录成功后获取并存储PTZ范围
|
*
|
* @param camera 摄像头信息
|
*/
|
private void savePtzScopeOnLogin(ArdCamera camera) {
|
String cameraId = camera.getId();
|
Long loginId = camera.getLoginId();
|
try {
|
// 构建 CameraCmd
|
CameraCmd cmd = new CameraCmd();
|
cmd.setCameraId(cameraId);
|
cmd.setChanNo(1);// 默认通道号
|
cmd.setLoginId(loginId);
|
|
// 调用SDK获取PTZ范围
|
PtzScope ptzScope = cameraSDKService.getPtzScope(cmd);
|
if (ptzScope != null) {
|
// 存储到数据库
|
ardCameraPtzScopeService.saveOnLoginSuccess(cameraId, ptzScope);
|
}
|
|
} catch (Exception e) {
|
log.error("获取或存储PTZ范围异常,cameraId: {}", cameraId, e);
|
}
|
}
|
|
|
/**
|
* 通过坐标,获取附近的相机
|
*
|
* @param cmd 相机指令
|
* @author XiaJiJian
|
* @date 2024-09-14
|
*/
|
@Override
|
public TreeMap<Double, ArdCamera> getNearbyCameras(CameraCmd cmd) {
|
try {
|
if (cmd.getTargetPosition() == null) {
|
log.debug("目标位置为空");
|
return new TreeMap<>();
|
}
|
//获取所有大光电
|
List<ArdCamera> bigCameras = SpringUtils.getAopProxy(this).selectArdCameraList(new ArdCamera("1"));
|
//获取所有车载光电
|
List<ArdCamera> vehCameras = SpringUtils.getAopProxy(this).selectArdCameraList(new ArdCamera("4"));
|
List<ArdCamera> ardCamerasList = new ArrayList<>();
|
ardCamerasList.addAll(bigCameras);
|
ardCamerasList.addAll(vehCameras);
|
//统计所有光电可视范围内与报警点的距离
|
TreeMap<Double, ArdCamera> ardCameras = new TreeMap<>();
|
for (ArdCamera camera : ardCamerasList) {
|
if (camera.getLongitude() == null && camera.getLatitude() == null) {
|
continue;
|
}
|
double distance = GisUtil.getDistance(cmd.getTargetPosition(), new Point(camera.getLongitude(),
|
camera.getLatitude(), camera.getAltitude()));
|
if (camera.getCamMaxVisibleDistance() == null) {
|
continue;
|
}
|
if (distance != 0.0 && distance <= camera.getCamMaxVisibleDistance()) {
|
ardCameras.put(distance, camera);
|
}
|
}
|
return ardCameras;
|
} catch (Exception ex) {
|
log.error("获取附近相机异常:{}", ex.getMessage());
|
}
|
return new TreeMap<>();
|
}
|
|
/**
|
* 获取圆内所有相机
|
*
|
* @param geoDTO 几何实体
|
* @author 刘苏义
|
* @date 2024-10-08
|
*/
|
@Override
|
public List<ArdCamera> getCircularCameras(GeoDTO geoDTO) {
|
List<ArdCamera> ardCameras = new ArrayList<>();
|
Long deptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
|
Point centerPoint = geoDTO.getCenterPoint();//中心点
|
Integer radius = geoDTO.getRadius();//半径
|
ArdCamera ardCamera = new ArdCamera();
|
ardCamera.setDeptId(deptId);
|
List<ArdCamera> ardCameraList = ardCameraMapper.selectArdCameraList(ardCamera);
|
ardCameraList.forEach(camera -> {
|
if (camera.getLongitude() == null || camera.getLatitude() == null) {
|
return;
|
}
|
Point point = new Point(camera.getLongitude(), camera.getLatitude(), null);
|
if (GisUtil.getDistance(centerPoint, point) <= radius) {
|
ardCameras.add(camera);
|
}
|
});
|
return ardCameras;
|
}
|
|
/**
|
* 获取多边形内所有相机
|
*
|
* @param geoDTO 几何实体
|
* @author 刘苏义
|
* @date 2024-10-08
|
*/
|
@Override
|
public List<ArdCamera> getPolygonCameras(GeoDTO geoDTO) {
|
List<ArdCamera> ardCameras = new ArrayList<>();
|
Long deptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
|
ArdCamera camera = new ArdCamera();
|
camera.setDeptId(deptId);
|
List<ArdCamera> ardCameraList = ardCameraMapper.selectArdCameraList(camera);
|
ardCameraList.forEach(ardCamera -> {
|
if (ardCamera.getLongitude() == null || ardCamera.getLatitude() == null) {
|
return;
|
}
|
Point point = new Point(ardCamera.getLongitude(), ardCamera.getLatitude(), null);
|
if (GisUtil.isInPolygon(point, geoDTO.getPolygonPoints())) {
|
ardCameras.add(ardCamera);
|
}
|
});
|
return ardCameras;
|
}
|
|
/**
|
* 获取雷达所在塔上的大光电
|
*
|
* @param radarId 雷达id
|
* @author wmm
|
* @date 2024-10
|
*/
|
@Override
|
public ArdCamera getCameraByRadar(String radarId) {
|
ArdCamera ardCamera = ardCameraMapper.getCameraByRadar(radarId);
|
ArdChannel ardChannel = new ArdChannel();
|
ardChannel.setDeviceId(ardCamera.getId());
|
List list = ardChannelService.selectArdChannelList(ardChannel);
|
if (ObjectUtils.isNotEmpty(list)) {
|
ardCamera.setChannelList(list);
|
}
|
return ardCamera;
|
}
|
|
/**
|
* 获取可视范围内的摄像头
|
*
|
* @param cameraFilterDTO 过滤实体
|
* @author 刘苏义
|
* @date 2024-11-4
|
*/
|
@Override
|
public List<ArdCamera> getCamerasInVisibleRange(CameraFilterDTO cameraFilterDTO) {
|
ArdCamera ardCamera = cameraFilterDTO.getArdCamera();
|
Point point = cameraFilterDTO.getPoint();
|
// 查询相机列表
|
List<ArdCamera> list = SpringUtils.getAopProxy(this).selectArdCameraList(ardCamera);
|
// 过滤相机列表
|
List<ArdCamera> filteredList = list.stream()
|
.filter(camera -> {
|
// 获取相机最大可视距离
|
Double maxVisibleDistance = camera.getCamMaxVisibleDistance();
|
// 如果最大可视距离为 null,选择排除该相机或定义默认值
|
if (maxVisibleDistance == null) {
|
return false; // 排除该相机
|
}
|
// 计算距离并进行比较
|
return GisUtil.getDistance(point, new Point(camera.getLongitude(), camera.getLatitude(), null)) <= maxVisibleDistance;
|
})
|
.collect(Collectors.toList());
|
return filteredList;
|
}
|
|
@Override
|
public Map<String, Object> selectArdCameraAllInfoById(String id) {
|
Map<String, Object> result = ardCameraMapper.selectArdCameraAllInfoById(id);
|
return result;
|
}
|
|
@Override
|
public List<Map<String, Object>> selectArdCameraAllInfoByUserId(Long userId) {
|
List<Map<String, Object>> result = ardCameraMapper.selectArdCameraAllInfoByUserId(userId);
|
return result;
|
}
|
|
@Override
|
public List<Map<String, Object>> getCameraAllInfoByPosition(Long userId, Map<String, Number> para) {
|
Double longitude = para.get("longitude").doubleValue();
|
Double latitude = para.get("latitude").doubleValue();
|
Integer radial = para.get("radial").intValue();
|
|
Point point = new Point(longitude, latitude, null);
|
|
//R<String> configKeyResult = remoteConfigService.getConfigKey("cameraVisibleDistance");
|
/*Integer configKey = 500;
|
if(configKeyResult.getCode() == 200){
|
configKey = Integer.parseInt(configKeyResult.getData());
|
}*/
|
|
List<Map<String, Object>> result = ardCameraMapper.selectArdCameraAllInfoByUserId(userId);
|
List<Map<String, Object>> resultList = new ArrayList();
|
for (Map<String, Object> map : result) {
|
try {
|
Point cameraPoint = new Point();
|
cameraPoint.setLongitude((Double) map.get("longitude"));
|
cameraPoint.setLatitude((Double) map.get("latitude"));
|
double cameraRadial = GisUtil.getDistance(point, cameraPoint);
|
//double cameraRadial = GisUtil.getDistance((Double) map.get("longitude"), (Double) map.get
|
// ("latitude"), longitude, latitude);
|
if (cameraRadial > radial) {//大于给定距离,则舍弃
|
continue;
|
}
|
|
/*String dictLabel = (String) map.get("dictLabel");
|
if("大光电".equals(dictLabel)){
|
resultList.add(map);
|
}else{*/
|
//Integer camMaxVisibleDistance = (Integer) map.get("camMaxVisibleDistance");
|
Integer camMaxVisibleDistance = Integer.parseInt((String) map.get("camMaxVisibleDistance"));
|
Point camera = new Point();
|
camera.setLongitude((Double) map.get("longitude"));
|
camera.setLatitude((Double) map.get("latitude"));
|
double distance = GisUtil.getDistance(point, camera);
|
if (distance <= camMaxVisibleDistance) {
|
resultList.add(map);
|
}
|
//}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
return resultList;
|
}
|
|
/**
|
* 同步设备通道与本地通道库信息
|
*
|
* @author 刘苏义
|
* @date 2024/8/22 14:37
|
*/
|
@Scheduled(cron = "0/30 * * * * ?")
|
public void syncChannel() {
|
try {
|
log.info("定时执行同步通道任务");
|
// ====================== 第一步:查数据库【所有通道】(全量) ======================
|
List<ArdChannel> dbAllChannels = ardChannelService.selectArdChannelList(new ArdChannel());
|
// ====================== 第二步:查所有相机,从SDK获取【所有真实通道】 ======================
|
List<ArdCamera> cameraList = this.selectArdCameraList(new ArdCamera());
|
List<ArdChannel> sdkAllChannels = new ArrayList<>();
|
if (cameraList != null && !cameraList.isEmpty()) {
|
for (ArdCamera camera : cameraList) {
|
try {
|
List<ArdChannel> cameraChannels = cameraSDKService.getChannels(camera);
|
// 单个相机通道不为空才添加
|
if (cameraChannels != null && !cameraChannels.isEmpty()) {
|
sdkAllChannels.addAll(cameraChannels);
|
}
|
} catch (Exception e) {
|
log.error("相机[{}]获取SDK通道失败:{}", camera.getId(), e.getMessage());
|
}
|
}
|
}
|
asyncChannel(dbAllChannels, sdkAllChannels);
|
} catch (Exception ex) {
|
log.error("定时执行同步通道任务异常:{}", ex.getMessage());
|
}
|
}
|
|
/**
|
* 定时执行登录失败相机重新登录
|
*
|
* @author 刘苏义
|
* @date 2024/8/22 14:37
|
*/
|
@Scheduled(initialDelay = 90000, fixedDelay = 30000)
|
public void failedReconnect() {
|
try {
|
log.info("定时执行失败相机重连任务");
|
|
// ========== 1. 从Redis获取全量相机(CAMERA_LIST Hash的所有value) ==========
|
List<ArdCamera> ardCameraList = this.selectArdCameraList(new ArdCamera());
|
|
// ========== 2. 过滤离线相机 ==========
|
List<ArdCamera> offlineList = ardCameraList.stream()
|
.filter(camera -> redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, camera.getId()) == null)
|
.collect(Collectors.toList());
|
|
int offlineCount = offlineList.size();
|
|
// ========== 3. 对离线相机执行重登录 ==========
|
offlineList.forEach(camera -> {
|
log.info("开始重连离线相机:{}(IP:{})", camera.getId(), camera.getIp());
|
cameraSDKService.login(camera);
|
});
|
|
log.info("定时重连任务执行完成,共检查{}台离线相机", offlineCount);
|
} catch (Exception ex) {
|
log.error("定时执行失败相机重连任务异常:{}", ex.getMessage(), ex);
|
}
|
}
|
|
// 根据通道信息获取源地址
|
private String getRtspUrl(ArdCamera ardCamera,ArdChannel channel) {
|
String rtspSource = "";
|
if (ardCamera != null) {
|
switch (ardCamera.getFactory()) {
|
case "1": //海康
|
rtspSource =
|
"rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream";
|
break;
|
case "2":
|
rtspSource =
|
"rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0";
|
break;
|
case "4":
|
rtspSource =
|
"rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/00010" + (channel.getChanNo() - 1);
|
break;
|
case "5": //合浦
|
// 1. 直接通过 sortOrder 精准匹配节点
|
Optional<ArdCameraHepu> targetNodeOpt = ardCamera.getHepuNodes().stream()
|
.filter(node -> node.getSortOrder() != null && node.getSortOrder().equals(channel.getChanNo()))
|
.findFirst();
|
if (targetNodeOpt.isPresent()) {
|
ArdCameraHepu node = targetNodeOpt.get();
|
switch (node.getNodeType()) {
|
case "1":
|
rtspSource =
|
"rtsp://" + node.getUsername() + ":" + node.getPassword() + "@" + node.getIp() +
|
":" + node.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main" +
|
"/av_stream";
|
break;
|
|
case "2":
|
rtspSource = "rtsp://" + node.getIp() + ":" + node.getRtspPort() + "/live/video";
|
break;
|
}
|
}
|
break;
|
case "7": //汉邦
|
rtspSource = "publisher";
|
break;
|
case "8": //富吉瑞
|
rtspSource = "rtsp://" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/live/video";
|
break;
|
}
|
}
|
return rtspSource;
|
}
|
}
|