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 selectArdCameraList(ArdCamera ardCamera) { return ardCameraMapper.selectArdCameraList(ardCamera); } /** * 查询所有相机 * * @param ardCamera 相机设备 * @return 相机设备 */ @Override public List 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 nodes = camera.getHepuNodes(); if (nodes == null || nodes.isEmpty()) { throw new ServiceException("合浦设备必须配置可见光和热红外物理节点"); } List 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 newNodes = newCamera.getHepuNodes(); String cameraId = newCamera.getId(); // 1. 删除旧节点 int deleteCount = ardCameraHepuService.deleteByCameraId(cameraId); log.debug("已删除相机 [{}] 的旧子节点,数量:{}", cameraId, deleteCount); // 2. 准备新数据 List 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 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("
" + successNum + "、相机ID " + camera.getId() + " 导入成功"); } else if (isUpdateSupport) { BeanValidators.validateWithException(validator, camera); checkCameraDataScope(camera.getUserId()); camera.setUpdateBy(operatorName); this.updateArdCamera(camera); successNum++; successMsg.append("
" + successNum + "、相机ID " + camera.getId() + " 更新成功"); } else { failureNum++; failureMsg.append("
" + failureNum + "、相机ID " + camera.getId() + " 已存在"); } } catch (Exception e) { failureNum++; String msg = "
" + 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 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 oldArrayList, List newArrayList) { // 空值安全 if (oldArrayList == null) oldArrayList = new ArrayList<>(); if (newArrayList == null) newArrayList = new ArrayList<>(); // 【修复点】key = deviceId + ":" + chanNo,保证全局唯一 Map oldMap = oldArrayList.stream() .collect(Collectors.toMap( channel -> channel.getDeviceId() + ":" + channel.getChanNo(), channel -> channel )); Map 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.getLiveAddress(), oldChannel.getLiveAddress()) // 新增 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.setLiveAddress(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 channelList = camera.getChannelList(); if (channelList == null || channelList.isEmpty()) { ardChannelService.deleteArdChannelByDeviceId(camera.getId()); return; } // 查询数据库已有通道 ArdChannel query = new ArdChannel(); query.setDeviceId(camera.getId()); Map 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.setLiveAddress(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 getNearbyCameras(CameraCmd cmd) { try { if (cmd.getTargetPosition() == null) { log.debug("目标位置为空"); return new TreeMap<>(); } //获取所有大光电 List bigCameras = SpringUtils.getAopProxy(this).selectArdCameraList(new ArdCamera("1")); //获取所有车载光电 List vehCameras = SpringUtils.getAopProxy(this).selectArdCameraList(new ArdCamera("4")); List ardCamerasList = new ArrayList<>(); ardCamerasList.addAll(bigCameras); ardCamerasList.addAll(vehCameras); //统计所有光电可视范围内与报警点的距离 TreeMap 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 getCircularCameras(GeoDTO geoDTO) { List 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 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 getPolygonCameras(GeoDTO geoDTO) { List ardCameras = new ArrayList<>(); Long deptId = SecurityUtils.getLoginUser().getSysUser().getDeptId(); ArdCamera camera = new ArdCamera(); camera.setDeptId(deptId); List 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 getCamerasInVisibleRange(CameraFilterDTO cameraFilterDTO) { ArdCamera ardCamera = cameraFilterDTO.getArdCamera(); Point point = cameraFilterDTO.getPoint(); // 查询相机列表 List list = SpringUtils.getAopProxy(this).selectArdCameraList(ardCamera); // 过滤相机列表 List 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 selectArdCameraAllInfoById(String id) { Map result = ardCameraMapper.selectArdCameraAllInfoById(id); return result; } @Override public List> selectArdCameraAllInfoByUserId(Long userId) { List> result = ardCameraMapper.selectArdCameraAllInfoByUserId(userId); return result; } @Override public List> getCameraAllInfoByPosition(Long userId, Map 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 configKeyResult = remoteConfigService.getConfigKey("cameraVisibleDistance"); /*Integer configKey = 500; if(configKeyResult.getCode() == 200){ configKey = Integer.parseInt(configKeyResult.getData()); }*/ List> result = ardCameraMapper.selectArdCameraAllInfoByUserId(userId); List> resultList = new ArrayList(); for (Map 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 dbAllChannels = ardChannelService.selectArdChannelList(new ArdChannel()); // ====================== 第二步:查所有相机,从SDK获取【所有真实通道】 ====================== List cameraList = this.selectArdCameraList(new ArdCamera()); List sdkAllChannels = new ArrayList<>(); if (cameraList != null && !cameraList.isEmpty()) { for (ArdCamera camera : cameraList) { try { List 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 ardCameraList = this.selectArdCameraList(new ArdCamera()); // ========== 2. 过滤离线相机 ========== List 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 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; } }