大华SDK java实现车位和违停事件

Posted 绝命响应

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大华SDK java实现车位和违停事件相关的知识,希望对你有一定的参考价值。

写在前面

gitee地址如下: Gitee仓库传送门
大华SDK分语言和操作系统,不同的SDK实现方式不太一样,需要先根据自己的需求进行SDK的选取。比如,很多线上服务是部署在Linux系统的,所以开发的时候就尽量少折腾windows系统的东西。

首先,先将大华SDK开发所需的jar包引入,可以参考官网的SDK demo。官网传送门大华SDK官网

以下代码逻辑都是基于大华SDK Linux_64_java 版本的demo进行开发的。

有的人开发是直接把NetSDKLib 这个类里的代码全部复制一遍,这样也行,不过导入的东西太多太麻烦了。我是直接将SDK打包再引入整个包文件(顺带吐槽一下,64位java版本的Linux demo,官方竟然连个jar包都没有!我是自己使用maven打包然后丢到公司的maven仓库,一同丢进本公司maven仓库的还有dahua-netsdk-jni包。然后再通过maven引入),引包如下:

<dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna</artifactId>
      <version>5.4.0</version>
    </dependency>

<!-- 这是Linux版本的demo-->
    <dependency>
      <groupId>com.dahua.netsdk</groupId>
      <artifactId>dahua-netsdk-linux</artifactId>
      <version>1.0.0</version>
      <classifier>demo</classifier>
    </dependency>

<!--1.1.0是linux的jni包 -->
    <dependency>
      <groupId>com.dahua.netsdk</groupId>
      <artifactId>dahua-netsdk-jni</artifactId>
      <version>1.1.0</version>
    </dependency>

注:以上3个jar包,除了第一个是在maven的公共仓库都存在的,后面两个都是我从demo里得来,然后扔到自己公司的私服再引入的。

对于订阅智能事件的流程,我引用官网demo的文档里的流程图,如下:

通过流程图可以清晰得看到整个事件的过程。不多说,直接上代码:


@Component
public class DaHuaConnectorManagerImpl implements DaHuaConnectorManager 

    // 服务是否开启
    private boolean isStarted = false;

    private Logger logger = LoggerFactory.getLogger(getClass());

    // 服务监听回调
    private StartServiceCallBack serviceCallBack = StartServiceCallBack.getInstance();

    // 网络断线处理
    private static DisConnect disConnect = new DisConnect();

    // 设备连接恢复,实现设备连接恢复接口
    private static HaveReConnect haveReConnect = new HaveReConnect();

    @PostConstruct
    public void startServer() 
        // 先初始化sdk,再开启服务的监听
        SdkUtils.init(disConnect, haveReConnect);
        logger.info("初始化SDK结束");
        String ip = this.getIp();
        int port = Integer.parseInt(System.getenv("PORT"));
        try 
            boolean start = SdkUtils.startServer(ip, port, serviceCallBack);
            if (!start) 
                logger.error("开启服务失败");
             else 
                isStarted = true;
            
         catch (Exception e) 
            logger.error("开启服务异常", e);
        
    

    /**
     * 获取本机ip
     *
     * @return
     */
    private String getIp()
        try
            InetAddress addr = InetAddress.getLocalHost();
            if (addr != null)
                return addr.getHostAddress();
            
        catch (Exception e)
            logger.error("获取本机IP异常",e);
        
        return "";
    


初始化SDK和开启服务是在springboot项目启动时执行的,在初始化SDK时传入了两个回调disConnect haveReConnect 即断线回调和重连回调。这两个回调不是必须参数,根据业务场景选择是否传入。我的业务场景是在设备断线或者重连时向自己的平台上报设备状态,所以必传。不过很意外的是,在相机断连时断线回调会执行,但重连后重连回调竟然没有执行啊(暂时未找到原因),所以我只能在相机登录成功后向自己的平台上报一次设备状态。两个回调具体实现如下:

  /**
     * 设备断线回调: 通过 CLIENT_Init 设置该回调函数,当设备出现断线时,SDK会调用该函数
     */
    public static class DisConnect implements NetSDKLib.fDisConnect 

        @Override
        public void invoke(NetSDKLib.LLong m_hLoginHandle, String deviceIP, int port, Pointer dwUser) 
            logger.info("设备()端口号()断连", deviceIP, port);
            ConcurrentMap<String, DeviceInfo> deviceMap = serviceCallBack.deviceMap;
            String status = "offline";
            // 获取设备ID
            String deviceID = getDeviceIDByIP(deviceMap, deviceIP, status);
            // 上报设备状态到物联网平台
            if (!StringUtils.isEmpty(deviceID)) 
                reportDeviceStatus(deviceID, status);
             else 
                logger.error("通过设备IP获取设备ID失败,设备ID为空,deviceIP=", deviceIP);
            
        
    
/**
     * 网络连接恢复,设备重连成功回调,通过 CLIENT_SetAutoReconnect 设置该回调函数,当已断线的设备重连成功时,SDK会调用该函数
     */
    public static class HaveReConnect implements NetSDKLib.fHaveReConnect 

        @Override
        public void invoke(NetSDKLib.LLong m_hLoginHandle, String deviceIP, int port, Pointer dwUser) 
            logger.info("设备()端口号()重连", deviceIP, port);
            ConcurrentMap<String, DeviceInfo> deviceMap = serviceCallBack.deviceMap;
            String status = "online";
            // 获取设备ID
            String deviceID = getDeviceIDByIP(deviceMap, deviceIP, status);
            // 上报设备状态到物联网平台
            if (!StringUtils.isEmpty(deviceID)) 
                reportDeviceStatus(deviceID, status);
             else 
                logger.error("通过设备IP获取设备ID失败,设备ID为空,deviceIP=", deviceIP);
            
        
    

SdkUtils中服务初始化和开启监听如下:

 /**
     * 初始化SDK
     *
     * @param disConnect
     * @param haveReConnect
     * @return
     */
    public static boolean init(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) 
        bInit = netsdk.CLIENT_Init(disConnect, null);
        if (!bInit) 
            logger.info("初始化SDK失败");
            return false;
        

        // 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK内部会自动进行重连操作
        netsdk.CLIENT_SetAutoReconnect(haveReConnect, null);

        //设置登录超时时间和尝试次数,可选
        int waitTime = 5000; //登录请求响应超时时间设置为5S
        int tryTimes = 1;    //登录时尝试建立链接1次
        netsdk.CLIENT_SetConnectTime(waitTime, tryTimes);

        // 设置更多网络参数,NET_PARAM的nWaittime,nConnectTryNum成员与CLIENT_SetConnectTime
        // 接口设置的登录设备超时时间和尝试次数意义相同,可选
        NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM();
        netParam.nConnectTime = 10000;      // 登录时尝试建立链接的超时时间
        netParam.nGetConnInfoTime = 3000;   // 设置子连接的超时时间
        netParam.nGetDevInfoTime = 3000;//获取设备信息超时时间,为0默认1000ms
        netsdk.CLIENT_SetNetworkParam(netParam);
        return true;
    
 /**
     * 开启服务
     *
     * @param address
     * @param port
     * @param callback
     * @return
     */
    public static boolean startServer(String address, int port, fServiceCallBack callback) 
        mServerHandler = netsdk.CLIENT_ListenServer(address, port, 1000, callback, (Pointer) null);
        if (0L == mServerHandler.longValue()) 
            logger.error("开启服务失败,error=,address=,port=", ToolKits.getErrorCodePrint(),address,port);
         else 
            logger.info("开启服务成功,address=,port=", address, port);
        

        return mServerHandler.longValue() != 0L;
    

服务开启和后还需要开启监听回调函数StartServiceCallBack,实现如下:

package cn.com.egova.connector.module.utils;

import cn.com.egova.connector.bean.DeviceInfo;
import com.netsdk.lib.NetSDKLib;
import com.sun.jna.Pointer;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StartServiceCallBack implements NetSDKLib.fServiceCallBack 

    public static ConcurrentMap<String, DeviceInfo> deviceMap = new ConcurrentHashMap<>();

    private Logger logger = LoggerFactory.getLogger(getClass());

    private StartServiceCallBack() 
        System.out.println("监听回调函数初始化");
    

    private static class CallBackHolder 

        private static StartServiceCallBack instance = new StartServiceCallBack();
    

    public static StartServiceCallBack getInstance() 
        return StartServiceCallBack.CallBackHolder.instance;
    

    @Override
    public int invoke(NetSDKLib.LLong lHandle, String ip, int port, int lCommand, Pointer pParam, int dwParamLen, Pointer dwUserData) 
        // 将 pParam 转化为序列号
        byte[] buffer = new byte[dwParamLen];
        pParam.read(0, buffer, 0, dwParamLen);
        String deviceId = "";
        try 
            deviceId = new String(buffer, "GBK").trim();
         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
        
        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.setDeviceIp(ip);
        deviceInfo.setPort(port);
        // 用户名和密码暂时写死,所有的相机都一样
        deviceInfo.setUserName("admin");
        deviceInfo.setPassword("admin");
        if (deviceMap.containsKey(deviceId))  // 设备断连后重连
            deviceInfo.setReconnectStatus(true);
        
        deviceMap.put(deviceId, deviceInfo);
        logger.info("当前时间:,注册设备地址:,端口:,deviceID:", SdkUtils.getTime(System.currentTimeMillis()), ip, port, deviceId);
        return 0;
    


import java.io.Serializable;
import lombok.Data;

/**
 * 设备信息
 *
 * @author :
 * @date :Created in 2022/5/17 18:00
 */
@Data
public class DeviceInfo implements Serializable 

    private String deviceIp;
    private int port;
    private String userName;
    private String password;
    private int eventType;
    private boolean reconnectStatus;

当大华相机开启主动注册的功能后,相机通电通网,会执行这个回调,在回调中我打印了相机的相关信息并将相机存到map中,以供后续的逻辑使用。当然,如果没有什么需求,这个回调可以什么都不写,直接返回一个int值。

接下来是相机的登录,因为线上可能是很多个相机连接同一个服务,而相机什么时候连接也是未知的,所以我的思路是,先在上一步的回调中将相机的基本信息保存下来,然后开启一个定时器任务,每分钟执行一次相机登录的定时器任务,遍历map,去登录相机。代码如下:

mport java.util.Timer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class StartTask implements CommandLineRunner 

    @Autowired
    private ServiceTask serviceTask;

    @Override
    public void run(String... args) throws Exception 
        Timer timer = new Timer();
        // 服务启动后间隔1分钟执行一次,以后每分钟会执行一次
        timer.scheduleAtFixedRate(serviceTask, 60 * 1000, 60 * 1000);
    

package cn.com.egova.connector.module.task;

/**
 * @author :
 * @date :Created in 2022/7/6 17:41
 */

import cn.com.egova.connector.bean.DeviceInfo;
import cn.com.egova.connector.module.utils.AnalyzerDataCallBack;
import cn.com.egova.connector.module.utils.SdkUtils;
import cn.com.egova.connector.module.utils.StartServiceCallBack;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.NetSDKLib.LLong;
import java.util.HashMap;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class ServiceTask extends TimerTask 

    private static Logger logger = LoggerFactory.getLogger(ServiceTask.class);

    // 设备信息
    public static ConcurrentMap<String, DeviceInfo> deviceMap = new ConcurrentHashMap<>();


    // 设备是否登录
    public static Map<String, Boolean> deviceLoginMap = new HashMap<>();

    // 服务监听回调
    private StartServiceCallBack serviceCallBack = StartServiceCallBack.getInstance();

    // 订阅智能事件回调
    private AnalyzerDataCallBack dataCallBack = AnalyzerDataCallBack.getInstance();

    @Override
    public void run() 
        long currentTimeStamp = System.currentTimeMillis();
        deviceMap = serviceCallBack.deviceMap;
        if (!deviceMap.isEmpty())   // 设备太多可以考虑开多线程实现
            for (String deviceID : deviceMap.keySet()) 
                try 
                    Boolean isLogin = deviceLoginMap.getOrDefault(deviceID, false);
                    DeviceInfo deviceInfo = deviceMap大华门禁SDK二次开发

华为OD机试真题Java实现找车位真题+解题思路+代码(2022&2023)

基于微信小程序共享停车位设计与实现SSM_car.rar(项目源码+数据库文件+微信小程序开发+后端java语言)

TSINGSEE青犀视频接入大华摄像机实现改变预置点名称

TSINGSEE青犀视频开发大华sdk以图搜图功能简介

共享车位|基于SpringBoot+vue+node共享车位平台的设计与实现