liusuyi
2024-08-10 5b017324148ea92d96f9f16ade215463d6c712e5
优化:海康大华sdk断线重连机制
已修改11个文件
186 ■■■■■ 文件已修改
ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/device/camera/service/ICameraSdkService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/device/camera/service/impl/ArdCamerasServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/device/camera/service/impl/CameraSdkServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/device/channel/mapper/ArdChannelMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/utils/sdk/common/GlobalVariable.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/resources/mapper/device/ArdChannelMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ard-work/src/main/java/com/ruoyi/device/camera/factory/CameraSDK.java
@@ -11,7 +11,7 @@
import java.util.Map;
/**
 *  相机SDK策略接口
 * 相机SDK策略接口
 * 刘苏义
 * 2023/11/7 10:48:52
 */
@@ -73,6 +73,7 @@
    //抓图
    String picCutCate(CameraCmd cmd);
    //获取ptz
    AjaxResult getPtz(CameraCmd cmd);
ard-work/src/main/java/com/ruoyi/device/camera/service/ICameraSdkService.java
@@ -3,6 +3,7 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.device.camera.domain.ArdCameras;
import com.ruoyi.device.camera.domain.CameraCmd;
import java.util.Map;
public interface ICameraSdkService {
ard-work/src/main/java/com/ruoyi/device/camera/service/impl/ArdCamerasServiceImpl.java
@@ -73,7 +73,6 @@
    @PostConstruct
    public void loadCameras() {
        //清理本地录像缓存文件
        String tempPath = ARDConfig.getProfile() + Constants.LOCAL_RECORD_TEMP_PREFIX;
        FileUtils.deleteFolder(tempPath);
@@ -85,6 +84,8 @@
        for (ArdCameras ardCamera : ardCameras) {
            redisCache.setCacheObject(getCacheKey(ardCamera.getId()), ardCamera);
        }
        //清空通道
        ardChannelMapper.clearArdChannel();
        //清空流媒体
        vtduService.clearVtdu();
    }
ard-work/src/main/java/com/ruoyi/device/camera/service/impl/CameraSdkServiceImpl.java
@@ -33,6 +33,9 @@
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -70,6 +73,23 @@
            ardCamerasService.resetCameraLoginId();
            List<ArdCameras> ardCameras = ardCamerasService.selectArdCamerasListNoDataScope(new ArdCameras());
            ardCameras.stream().forEach(ardCamera -> asyncLogin(ardCamera));
            //开启登录失败相机重连定时任务
            ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
            executor.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        log.info("开始执行登录失败相机重连定时任务");
                        List<Object> ardCameras = redisCache.getListKey(CacheConstants.CAMERA_LIST_KEY);
                        ardCameras.stream()
                                .map(object -> (ArdCameras) object) // 将Object转换为ArdCameras
                                .filter(camera -> !GlobalVariable.loginMap.containsKey(camera.getId()))
                                .forEach(camera -> asyncLogin(camera));
                    } catch (Exception ex) {
                        log.error("重连相机异常:" + ex.getMessage());
                    }
                }
            }, 30, 30, TimeUnit.SECONDS);  // 任务延迟
        } catch (Exception ex) {
            log.error("初始化登录相机异常:" + ex.getMessage());
        }
ard-work/src/main/java/com/ruoyi/device/channel/mapper/ArdChannelMapper.java
@@ -66,4 +66,11 @@
     * @return 结果
     */
    public int deleteArdChannelByDeviceId(String deviceId);
    /**
     * 清空通道
     *
     * @author 刘苏义
     * @date   2024/8/10 11:18
     */
    public int clearArdChannel();
}
ard-work/src/main/java/com/ruoyi/device/channel/service/IArdChannelService.java
@@ -7,15 +7,14 @@
/**
 * 通道管理Service接口
 *
 *
 * @author ard
 * @date 2023-08-19
 */
public interface IArdChannelService
{
public interface IArdChannelService {
    /**
     * 查询通道管理
     *
     *
     * @param id 通道管理主键
     * @return 通道管理
     */
@@ -23,7 +22,7 @@
    /**
     * 查询通道管理列表
     *
     *
     * @param ardChannel 通道管理
     * @return 通道管理集合
     */
@@ -31,7 +30,7 @@
    /**
     * 新增通道管理
     *
     *
     * @param ardChannel 通道管理
     * @return 结果
     */
@@ -39,7 +38,7 @@
    /**
     * 修改通道管理
     *
     *
     * @param ardChannel 通道管理
     * @return 结果
     */
@@ -47,7 +46,7 @@
    /**
     * 批量删除通道管理
     *
     *
     * @param ids 需要删除的通道管理主键集合
     * @return 结果
     */
@@ -55,25 +54,34 @@
    /**
     * 删除通道管理信息
     *
     *
     * @param id 通道管理主键
     * @return 结果
     */
    public int deleteArdChannelById(String id);
    /**
     * 删除通道管理信息
     *
     * @param deviceId 所属设备ID
     * @return 结果
     */
     public int deleteArdChannelByDeviceId(String deviceId);
    public int deleteArdChannelByDeviceId(String deviceId);
    /**
     * 清空通道
     *
     * @author 刘苏义
     * @date 2024/8/10 11:18
     */
    public int clearArdChannel();
    /**
     * @return
     * @Author 刘苏义
     * @Description 同步通道信息
     * @Date 2024/7/10 13:20
     * @Param
     * @return
     */
    public void asyncChannel(ArdCameras ardCameras, List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList);
}
ard-work/src/main/java/com/ruoyi/device/channel/service/impl/ArdChannelServiceImpl.java
@@ -6,12 +6,7 @@
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.device.camera.domain.ArdCameras;
import com.ruoyi.device.camera.factory.CameraSDK;
import com.ruoyi.device.camera.factory.CameraSDKFactory;
import com.ruoyi.device.camera.mapper.ArdCamerasMapper;
import com.ruoyi.media.mapper.VtduMapper;
import com.ruoyi.media.service.IVtduService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.device.channel.mapper.ArdChannelMapper;
import com.ruoyi.device.channel.domain.ArdChannel;
@@ -29,8 +24,6 @@
public class ArdChannelServiceImpl implements IArdChannelService {
    @Resource
    private ArdChannelMapper ardChannelMapper;
    @Resource
    private CameraSDKFactory cameraSDKFactory;
    @Resource
    private IVtduService vtduService;
    /**
@@ -101,6 +94,16 @@
    }
    /**
     * 清空通道
     *
     * @author 刘苏义
     * @date   2024/8/10 11:18
     */
    @Override
    public int clearArdChannel(){
        return ardChannelMapper.clearArdChannel();
    }
    /**
     * 删除通道管理信息
     *
     * @param deviceId 所属设备ID
ard-work/src/main/java/com/ruoyi/utils/sdk/common/GlobalVariable.java
@@ -5,8 +5,11 @@
import lombok.Data;
import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
 * @ClassName: globalVariable
@@ -18,14 +21,14 @@
@Data
public class GlobalVariable {
    //保存相机登录信息
    public static Map<String, Object> loginMap = new HashMap<>();
    public static Map<String, Object> loginMap = new ConcurrentHashMap<>();
    //相机预览字典
    public static Map<String, Integer> previewMap = new HashMap<>();
    public static Map<String, Integer> previewMap = new ConcurrentHashMap<>();
    //线程字典
    public static Map<String, String> threadMap = new HashMap<>();
    public static Map<String, String> threadMap = new ConcurrentHashMap<>();
    //保存相机登录信息(loginId:Camera)
    public static Map<Integer, Object> loginCameraMap = new HashMap<>();
    public static Map<Integer, Object> loginCameraMap = new ConcurrentHashMap<>();
    //登录过相机集合
    public static ConcurrentHashSet<ArdCameras> loginedSet = new ConcurrentHashSet<>();
}
ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java
@@ -37,6 +37,7 @@
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.*;
import java.nio.file.Files;
@@ -80,6 +81,11 @@
    // 网络连接恢复
    private static HaveReConnect haveReConnect = new HaveReConnect();
    @PostConstruct
    public void initSdk() {
        log.info("初始化大华sdk");
        LoginModule.init(disConnect, haveReConnect);
    }
    /**
     * 登录
@@ -91,7 +97,6 @@
    @Override
    public AjaxResult login(ArdCameras camera) {
        try {
            LoginModule.init(disConnect, haveReConnect);
            NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
            NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo);
            if (loginId.longValue() <= 0) {
@@ -101,7 +106,7 @@
                ardCamerasService.updateArdCameras(camera);
                //删除管理通道
                ardChannelService.deleteArdChannelByDeviceId(camera.getId());
                log.error("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败:" + getErrorCodePrint());
                log.warn("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败:" + getErrorCodePrint());
                return AjaxResult.warn(ErrorCode.getErrorCode(LoginModule.netsdk.CLIENT_GetLastError()));
            }
            log.debug("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录成功:" + (int) loginId.longValue());
@@ -137,7 +142,6 @@
    @Async("globalExecutor")
    public AjaxResult asyncLogin(ArdCameras camera) {
        try {
            LoginModule.init(disConnect, haveReConnect);
            NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
            NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo);
            if (loginId.longValue() <= 0) {
@@ -147,7 +151,7 @@
                ardCamerasService.updateArdCameras(camera);
                //删除管理通道
                ardChannelService.deleteArdChannelByDeviceId(camera.getId());
                log.error("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败:" + getErrorCodePrint());
                log.warn("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败:" + getErrorCodePrint());
                return AjaxResult.warn(getErrorCodePrint());
            }
            log.debug("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录成功:" + loginId);
ard-work/src/main/java/com/ruoyi/utils/sdk/hiksdk/service/impl/HikvisionSDK.java
@@ -29,11 +29,10 @@
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.*;
import java.math.BigDecimal;
@@ -65,9 +64,37 @@
    private QueueHandler queueHandler;
    @Resource
    ICameraSdkService cameraSdkService;
    public Object _lock = new Object();
    public static HCNetSDK hCNetSDK = HCNetSDK.hCNetSDK;
    private static HCNetSDK.FExceptionCallBack fExceptionCallBack;//异常回调
    @PostConstruct
    public void initSdk() {
        log.info("初始化海康sdk");
        // 初始化
        if (!hCNetSDK.NET_DVR_Init()) {
            log.debug("SDK初始化失败");
        }
        //打印海康sdk日志
        if (Platform.isWindows()) {
            String WIN_PATH = System.getProperty("user.dir") + File.separator + "ardLog" + File.separator + "logs" + File.separator;
            hCNetSDK.NET_DVR_SetLogToFile(3, WIN_PATH, true);
        } else {
            hCNetSDK.NET_DVR_SetLogToFile(3, "/home/ardLog/hiklog", true);
        }
        if (fExceptionCallBack == null) {
            fExceptionCallBack = new ExceptionCallBack();//异常回调
            //设置异常回调函数(可在回调函数中获取设备上下线状态等)
            if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, null)) {
                log.debug("Set fExceptionCallBack function fail");
            } else {
                log.debug("Set fExceptionCallBack function successfully!");
            }
        }
        //设置连接时间与重连时间
        hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
        hCNetSDK.NET_DVR_SetReconnect(5000, true);
    }
    /**
     * @描述 注册登录 只支持同步登陆,且官方不建议直接在此接口下写耗时操作
@@ -80,24 +107,11 @@
    @Override
    public AjaxResult login(ArdCameras camera) {
        try {
            // 初始化
            if (!hCNetSDK.NET_DVR_Init()) {
                log.debug("SDK初始化失败");
            }
            //打印海康sdk日志
            if (Platform.isWindows()) {
                String WIN_PATH = System.getProperty("user.dir") + File.separator + "ardLog" + File.separator + "logs" + File.separator;
                hCNetSDK.NET_DVR_SetLogToFile(3, WIN_PATH, true);
            } else {
                hCNetSDK.NET_DVR_SetLogToFile(3, "/home/ardLog/hiklog", true);
            }
            String m_sDeviceIP = camera.getIp();
            String m_sUsername = camera.getUsername();
            String m_sPassword = camera.getPassword();
            short m_sPort = camera.getPort().shortValue();
            //设置连接时间与重连时间
            hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
            hCNetSDK.NET_DVR_SetReconnect(5000, true);
            //设备信息, 输出参数
            HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
            HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
@@ -115,6 +129,7 @@
            //是否异步登录:0- 否,1- 是  windowsSDK里是true和false
            m_strLoginInfo.bUseAsynLogin = false;
            m_strLoginInfo.write();
            //同步登录
            int lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
            if (lUserID < 0) {
@@ -125,20 +140,12 @@
                //删除管理通道
                ardChannelService.deleteArdChannelByDeviceId(camera.getId());
                ardCamerasService.updateArdCameras(camera);
                log.debug("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败: " + SdkErrorCodeEnum.getDescByCode(errorCode) + "(" + errorCode + ")");
                log.warn("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败: " + SdkErrorCodeEnum.getDescByCode(errorCode) + "(" + errorCode + ")");
                return AjaxResult.warn("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败: " + SdkErrorCodeEnum.getDescByCode(errorCode) + "(" + errorCode + ")");
            }
            log.debug("Login Success [ " + camera.getIp() + ":" + camera.getPort() + " ]");
            if (fExceptionCallBack == null) {
                fExceptionCallBack = new ExceptionCallBack();//异常回调
                //设置异常回调函数(可在回调函数中获取设备上下线状态等)
                if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, null)) {
                    log.debug("Set fExceptionCallBack function fail");
                } else {
                    log.debug("Set fExceptionCallBack function successfully!");
                }
            }
            GlobalVariable.loginMap.put(camera.getId(), lUserID);
            GlobalVariable.loginCameraMap.put(lUserID, camera);
            camera.setLoginId((long) lUserID);
@@ -187,29 +194,13 @@
    @Async("globalExecutor")
    public AjaxResult asyncLogin(ArdCameras camera) {
        try {
            // 初始化
            if (!hCNetSDK.NET_DVR_Init()) {
                log.error("SDK初始化失败");
                return AjaxResult.warn("SDK初始化失败");
            }
            //打印海康sdk日志
            if (Platform.isWindows()) {
                String WIN_PATH = System.getProperty("user.dir") + File.separator + "ardLog" + File.separator + "logs" + File.separator;
                hCNetSDK.NET_DVR_SetLogToFile(3, WIN_PATH, true);
            } else {
                hCNetSDK.NET_DVR_SetLogToFile(3, "/home/ardLog/hiklog", true);
            }
            String m_sDeviceIP = camera.getIp();
            String m_sUsername = camera.getUsername();
            String m_sPassword = camera.getPassword();
            short m_sPort = camera.getPort().shortValue();
            //设置连接时间与重连时间
            hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
            hCNetSDK.NET_DVR_SetReconnect(5000, true);
            //设备信息, 输出参数
            HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
            HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
            // 注册设备-登录参数,包括设备地址、登录用户、密码等
            m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
            System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());
@@ -233,7 +224,7 @@
                //删除管理通道
                ardChannelService.deleteArdChannelByDeviceId(camera.getId());
                ardCamerasService.updateArdCameras(camera);
                log.error("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败: " + SdkErrorCodeEnum.getDescByCode(errorCode) + "(" + errorCode + ")");
                log.warn("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败: " + SdkErrorCodeEnum.getDescByCode(errorCode) + "(" + errorCode + ")");
                return AjaxResult.warn("设备[" + camera.getIp() + ":" + camera.getPort() + "]登录失败: " + SdkErrorCodeEnum.getDescByCode(errorCode) + "(" + errorCode + ")");
            }
            log.debug("Login Success 【 " + camera.getIp() + ":" + camera.getPort() + " 】");
ard-work/src/main/resources/mapper/device/ArdChannelMapper.xml
@@ -72,4 +72,7 @@
        from ard_channel
        where device_id = #{deviceId}
    </delete>
    <delete id="clearArdChannel">
        delete from ard_channel
    </delete>
</mapper>