大华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)