大华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的文档里的流程图,如下:
大华SDK java实现车位和违停事件_第1张图片
通过流程图可以清晰得看到整个事件的过程。不多说,直接上代码:


@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,不然会登录不上哈。当然了,回调的前提是在大华相机后台开启了主动注册功能,如下图:
大华SDK java实现车位和违停事件_第2张图片

3 部署到测试环境或线上环境,不同的镜像可能也会影响sdk内部jna的运行。我打包的dockfile文件内容如下:
大华SDK java实现车位和违停事件_第3张图片

其中用的java jre是 openjdk:8-jre。

写在后面

你可能感兴趣的:(java,开发语言,maven,jenkins,后端)