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.getOrDefault(deviceID, null);
if (deviceInfo != null) {
if (!isLogin || deviceInfo.isReconnectStatus()) { // 未登录相机或相机重连时需要登录
// 登录相机
LLong loginHandle = SdkUtils
.login(deviceInfo.getDeviceIp(), deviceInfo.getPort(), deviceInfo.getUserName(), deviceInfo.getPassword(),
deviceID);
if (loginHandle.intValue() == 0) {
logger.error("登录相机失败,deviceID={},ip={},port={}", deviceID, deviceInfo.getDeviceIp(),
deviceInfo.getPort());
continue;
} else {
// 登录成功后将相机登录状态保存在map中
deviceLoginMap.put(deviceID, true);
deviceInfo.setReconnectStatus(false);
// 登录成功后开启订阅事件
int channelID = 0; // 默认通道ID
int bNeedPicture = 1; // 是否有图 0-无图 1-有图
LLong attachHandle = SdkUtils.realLoadPicture(loginHandle, channelID, NetSDKLib.EVENT_IVS_ALL, // 订阅所有的事件
bNeedPicture, dataCallBack, deviceID);
if (attachHandle.intValue() == 0) {
logger.error("开启智能订阅事件失败,deviceID={}", deviceID);
continue;
}
}
}
}
} catch (Exception e) {
logger.error("相机登录异常,deviceID={}", deviceID, e);
}
}
}
// logger.info("结束执行相机登录的定时器任务,耗时{}ms",System.currentTimeMillis()-currentTimeStamp);
}
}
/**
* 登录
*
* @param ip
* @param port
* @param userName
* @param password
* @param deviceIDs
* @return
*/
public static LLong login(String ip, int port, String userName, String password, String deviceIDs) {
//入参
NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
Pointer deviceId = ToolKits.GetGBKStringToPointer(deviceIDs);
pstInParam.nPort = port;
pstInParam.szIP = ip.getBytes();
pstInParam.szPassword = password.getBytes();
pstInParam.szUserName = userName.getBytes();
pstInParam.emSpecCap = NetSDKLib.EM_LOGIN_SPAC_CAP_TYPE.EM_LOGIN_SPEC_CAP_SERVER_CONN; // 主动注册登录
pstInParam.pCapParam = deviceId;
// 出参
NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
try {
m_stDeviceInfo.sSerialNumber = deviceIDs.getBytes("GBK");
} catch (Exception e) {
logger.error("转换设备ID异常,deviceID={}", deviceIDs, e);
}
pstOutParam.stuDeviceInfo = m_stDeviceInfo;
m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
long value = m_hLoginHandle.longValue();
if (value == 0) {
logger.error("登录失败,ip={},port={},error={}", ip, port, ToolKits.getErrorCodePrint());
} else {
logger.info("设备登录成功。ip={},port={}", ip, port);
// 登录成功后像物联网平台上报设备状态变化
reportDeviceStatus(deviceIDs, "online");
}
return m_hLoginHandle;
}
在相机登录成功后订阅事件,我订阅的是所有的事件,所以当相机触发了相应事件后,会通过回调将事件的一些信息返回回来。比如车位事件,会将车位上车的信息以指针形式返回,然后由开发者自己去实现一些相应的功能。车位事件的回调如下:
public class AnalyzerDataCallBack implements NetSDKLib.fAnalyzerDataCallBack {
private static class CallBackHolder {
private static AnalyzerDataCallBack instance = new AnalyzerDataCallBack();
}
public static AnalyzerDataCallBack getInstance() {
return CallBackHolder.instance;
}
private static Logger logger = LoggerFactory.getLogger(AnalyzerDataCallBack.class);
@Override
public int invoke(LLong lAnalyzerHandle, int eventType,
Pointer alarmInfo, Pointer pBuffer, int dwBufSize,
Pointer dwUser, int nSequence, Pointer reserved) {
logger.info("订阅智能事件回调,订阅类型:{}", getEventType(eventType));
if (lAnalyzerHandle.intValue() == 0) {
return -1;
}
if (eventType == NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACEPARKING || eventType ==
NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACENOPARKING) { // 车位有车和无车
TrafficInfo trafficInfo = getTrafficInfo(alarmInfo, eventType);
if (trafficInfo == null) {
return -1;
}
// 转换车位图片,
String picture = revertPicture(pBuffer, dwBufSize);
if (!StringUtils.isEmpty(picture)) {
trafficInfo.setPictures(picture);
}
// 将车位事件上报到云平台
reportCameraRecord(trafficInfo, eventType);
}
return 0;
}
/**
* 转换车位事件数据信息
*
* @param alarmInfo
* @param eventType
* @return
*/
public TrafficInfo getTrafficInfo(Pointer alarmInfo, int eventType) {
logger.info("转换车位事件数据,事件类型:{}", getEventType(eventType));
TrafficInfo trafficInfo = new TrafficInfo();
if (alarmInfo == null) {
return trafficInfo;
}
switch (eventType) {
case (NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACEPARKING): // 车位有车
{
NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO msg = new NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO();
ToolKits.GetPointerData(alarmInfo, msg);
trafficInfo.eventName = Res.string().getEventName(NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACEPARKING);
try {
// windows 是GBK,Linux是UTF-8
trafficInfo.plateNumber = new String(msg.stuObject.szText, "UTF-8").trim();
} catch (Exception e) {
logger.error("车位有车时转换车牌号失败", e);
}
trafficInfo.plateType = new String(msg.stTrafficCar.szPlateType).trim();
trafficInfo.fileCount = String.valueOf(msg.stuFileInfo.bCount);
trafficInfo.fileIndex = String.valueOf(msg.stuFileInfo.bIndex);
trafficInfo.groupID = String.valueOf(msg.stuFileInfo.nGroupId);
trafficInfo.illegalPlace = ToolKits.GetPointerDataToByteArr(msg.stTrafficCar.szDeviceAddress);
trafficInfo.laneNumber = String.valueOf(msg.nLane);
trafficInfo.plateColor = new String(msg.stTrafficCar.szPlateColor).trim();
trafficInfo.vehicleColor = new String(msg.stTrafficCar.szVehicleColor).trim();
trafficInfo.vehicleType = this.convertCarType(new String(msg.stuVehicle.szObjectSubType).trim());
trafficInfo.vehicleSize = Res.string().getTrafficSize(msg.stTrafficCar.nVehicleSize);
trafficInfo.happenTime = this.getTime(msg.UTC);
trafficInfo.picEnable = msg.stuObject.bPicEnble;
trafficInfo.offSet = msg.stuObject.stPicInfo.dwOffSet;
trafficInfo.fileLength = msg.stuObject.stPicInfo.dwFileLenth;
trafficInfo.boundingBox = msg.stuObject.BoundingBox;
trafficInfo.actionCredible = msg.stuObject.nConfidence;
trafficInfo.parkingSpaceCode = new String(msg.szParkingNum).replaceAll("\\u0000", "");
String deviceID = getDeviceID(trafficInfo.parkingSpaceCode);
if (StringUtils.isEmpty(deviceID)) {
logger.error("获取设备ID失败,设备ID为空");
return null;
} else {
trafficInfo.deviceID = deviceID;
}
break;
}
case (NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACENOPARKING): // 车位无车
{
NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO msg = new NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO();
ToolKits.GetPointerData(alarmInfo, msg);
trafficInfo.eventName = Res.string().getEventName(NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACENOPARKING);
try {
// windows 是GBK,Linux是UTF-8
trafficInfo.plateNumber = new String(msg.stuObject.szText, "UTF-8").trim();
} catch (Exception e) {
logger.error("车位无车时转换车牌号失败", e);
}
trafficInfo.plateType = new String(msg.stTrafficCar.szPlateType).trim();
trafficInfo.fileCount = String.valueOf(msg.stuFileInfo.bCount);
trafficInfo.fileIndex = String.valueOf(msg.stuFileInfo.bIndex);
trafficInfo.groupID = String.valueOf(msg.stuFileInfo.nGroupId);
trafficInfo.illegalPlace = ToolKits.GetPointerDataToByteArr(msg.stTrafficCar.szDeviceAddress);
trafficInfo.laneNumber = String.valueOf(msg.nLane);
trafficInfo.plateColor = new String(msg.stTrafficCar.szPlateColor).trim();
trafficInfo.vehicleColor = new String(msg.stTrafficCar.szVehicleColor).trim();
trafficInfo.vehicleType = this.convertCarType(new String(msg.stuVehicle.szObjectSubType).trim());
trafficInfo.vehicleSize = Res.string().getTrafficSize(msg.stTrafficCar.nVehicleSize);
trafficInfo.happenTime = this.getTime(msg.UTC);
trafficInfo.picEnable = msg.stuObject.bPicEnble;
trafficInfo.offSet = msg.stuObject.stPicInfo.dwOffSet;
trafficInfo.fileLength = msg.stuObject.stPicInfo.dwFileLenth;
trafficInfo.boundingBox = msg.stuObject.BoundingBox;
trafficInfo.actionCredible = msg.stuObject.nConfidence;
trafficInfo.parkingSpaceCode = new String(msg.szParkingNum).replaceAll("\\u0000", "");
String deviceID = getDeviceID(trafficInfo.parkingSpaceCode);
if (StringUtils.isEmpty(deviceID)) {
logger.error("获取设备ID失败,设备ID为空");
return null;
} else {
trafficInfo.deviceID = deviceID;
}
break;
}
}
return trafficInfo;
}
/** 车位事件信息 */
@Data
public static class TrafficInfo {
@SuppressWarnings("FieldCanBeLocal")
private String eventName; // 事件名称
private String plateNumber; // 车牌号
private String plateType; // 车牌类型
private String plateColor; // 车牌颜色
private String vehicleColor; // 车身颜色
private String vehicleType; // 车身类型
private String vehicleSize; // 车辆大小
private String fileCount; // 文件总数
private String fileIndex; // 文件编号
private String groupID; // 组ID
private String illegalPlace; // 违法地点
private String laneNumber; // 通道号
private String happenTime; // 事件时间
private int picEnable; // 车牌对应信息,BOOL类型
private int offSet; // 车牌偏移量
private int fileLength; // 文件大小
private NetSDKLib.DH_RECT boundingBox; // 包围盒
private int actionCredible; // 置信度
private String parkingSpaceCode; // 车位编号
private String pictures; // 全景图
private String deviceID; // 设备ID
private double[] coordinate; // 坐标
}
/**
* 从UTC结构体中转换时间
*
* @param utc
* @return
*/
private String getTime(NET_TIME_EX utc) {
String time = "";
if (utc != null) {
time = utc.dwYear + "-" + convertNumber(utc.dwMonth) + "-" + convertNumber(utc.dwDay) + " " + convertNumber(utc.dwHour) + ":" +
convertNumber(utc.dwMinute) + ":" + convertNumber(utc.dwSecond);
}
return time;
}
/**
* 转换标识日期的数字,小于10的数字前面补0
*
* @param number
* @return
*/
private String convertNumber(int number) {
if (number < 10) {
return "0" + number;
}
return String.valueOf(number);
}
/**
* 事件类型转换
*
* @param eventType
* @return
*/
private String getEventType(int eventType) {
if (eventType == 299) {
return "车位有车事件";
} else if (eventType == 300) {
return "车位无车事件";
}else if(eventType == 264){
return "违停事件";
}else if(eventType == 358){
return "逆行事件";
}else if(eventType == 261){
return "掉头事件";
}else if(eventType == 257 || eventType == 267){
return "压线事件";
}else if(eventType == 266){
return "变道事件";
}
return "未知事件";
}
/**
* 转换车牌颜色
*
* @param color
* @return
*/
private int convertPlateColor(String color){
switch (color){
case "blue":
return 1;
case "white":
return 2;
case "black":
return 3;
case "yellow":
return 4;
case "green":
return 5;
default:
return 0;
}
}
}
说下个人踩过的坑:
1 Windows和Linux版本的jni不一样,如果是项目上线环境是ubuntu或者cent os,建议开发时不要折腾Windows版本了哈。
2 相机登录时的ip和端口号以回调中的为准,相机的端口号在未登录前会不断变化,所以不要指定相机登录时的port,不然会登录不上哈。当然了,回调的前提是在大华相机后台开启了主动注册功能,如下图:
3 部署到测试环境或线上环境,不同的镜像可能也会影响sdk内部jna的运行。我打包的dockfile文件内容如下:
其中用的java jre是 openjdk:8-jre。