Java二次开发海康SDK-对接门禁机
阅读原文时间:2021年12月01日阅读:20

写在最前

SDK版本:CH-HCNetSDKV6.1.6.45_build20210302_win64

参考文档:海康SDK使用手册_V6.1

对接测试设备型号:DS-K1T671M

设备序列号:E50247795

业务目标

使用门禁设备实现对人脸的抓拍,将抓拍的人脸与其对应的数据进行上传。

业务流程图:

业务流程节点解释:

1.初始化SDK(NET_DVR_Init):进行海康提供开发库的载入,使用海康官方提供的文件库,进入之后,修改载入路径就可以了。

2.设置报警回调函数(NET_DVR_SetDVRMessageCallBack_V31):初始完SDK之后,进行报警回调函数的设置,当设备进行人脸抓拍之后,上传报警信息到SDK,触发回调函数进行内部业务逻辑处理。对于(门禁设备)人脸侦测,回调函数中的报警类型(lCommand)为COMM_ALARM_ACS,,报警信息(pAlarmInfo)对应结构体:NET_DVR_ACS_ALARM_INFO。

3.用户注册(NET_DVR_Login_V40):填写设备对应的设备参数,进行设备的注册,注册成功会返回一个lUserID,使用这个lUserID进行下面一系列的操作。

4.获取设备能力集(NET_DVR_GetDeviceAbility):能力集类型DEVICE_ABILITY_INFO,获取智能通道分析能力集可以判断设备是否支持相关功能。(可选功能)

5.设置人脸抓拍参数(NET_DVR_SetDVRConfig) (可选功能)

6.获取人脸抓拍参数(NET_DVR_GetDVRConfig) (可选功能)

7.报警布防(NET_DVR_SetupAlarmChan_v41):布防即建立设备跟客户端之间报警上传的连接通道,这样设备发生报警之后通过该连接上传报警信息,SDK在报警回调函数中接收和处理报警信息数据即可。如果设备同时支持人脸侦测和人脸抓拍方式,调用该接口时,NET_DVR_SETUPALARM_PARAM布防参数中byFaceAlarmDetection赋值为0即选择设备上传的报警信息类型为人脸抓拍类型。

注意:在报警布防中需要设置连接的参数,设置不对或没有设置会提示连接设备失败。

8.报警回调函数里面接收和处理数据:报警类型:COMM_ALARM_ACS,报警信息结构体:NET_DVR_ACS_ALARM_INFO。对设备上传来的数据信息进行接收

9.报警撤防(NET_DVR_CloseAlarmChan_v30)

10.注销用户(NET_DVR_logout)

11.释放SDK资源(NET_DVR_Cleanup):关闭连接通道,释放资源。

代码示例

1.首先根据你需要开发的系统去海康官网下载对应的程序包。

比如我的win64

2.创建好springboot项目,将这个程序包里面的库文件引进去。

3.将程序包里面它提供的 HCNetSDK.java 复制到你的项目里面,并修改你刚才放的库文件路径,注意以.dll结尾

4.接下来就是写 demo 测试连接

package com.example.testsdk;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
* @author LH
* @date 2021/11/29 10:37
*/
public class startHCNetAlarm {

private static final Logger LOGGER = LoggerFactory.getLogger(startHCNetAlarm.class);  
// 载入sdk库文件  
static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;

public static void main(String\[\] args) throws IOException {

    HCNetAlarm hcNetAlarm = new HCNetAlarm();  
    // 资源初始化  
    int row = hcNetAlarm.initDevice();  
    if (row == 1) {  
        LOGGER.info("初始化失败");  
    }

    // 设置连接超时时间与重连功能  
    hCNetSDK.NET\_DVR\_SetConnectTime(2000, 1);  
    hCNetSDK.NET\_DVR\_SetReconnect(10000, true);  
    // 设备注册,注册成功返回一个唯一标识符 lUserID,根据这个进行设备的其它操作  
    int luserID = hcNetAlarm.deviceRegister(-1, "填你设备的ip地址", "设备用户名", "设备密码", "设备端口,一般默认8000");  
    System.out.println(luserID);  
    // 设置报警回调函数,建立报警上传通道(启用布防)  
    int lAlarmHandle = hcNetAlarm.setupAlarmChan(luserID, -1);  
    // 检查设备状态(是否在线),打印设备信息  
    hcNetAlarm.onlineState(luserID);  
    // 设备抓拍功能,  

// hcNetAlarm.getDVRPic(luserID);
try {
// 等待设备上传报警信息
LOGGER.info("等待设备上传报警信息====================");
Thread.sleep(100 * 60 * 60);
} catch (InterruptedException e) {
e.printStackTrace();
}

    // 撤销布防上传通道  
    hcNetAlarm.closeAlarmChan(lAlarmHandle);  
    // 注销 释放sdk资源  
    hcNetAlarm.logout(luserID);  
    System.out.println("====== 设备注销 ======");  
}  

}

package com.example.testsdk;

import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
* @author LH
* @date 2021/11/29 9:06
*/
public class HCNetAlarm {

private static final Logger LOGGER = LoggerFactory.getLogger(HCNetAlarm.class);

// 载入sdk库文件  
static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;

// 设备登录信息  
HCNetSDK.NET\_DVR\_USER\_LOGIN\_INFO m\_strLoginInfo = new HCNetSDK.NET\_DVR\_USER\_LOGIN\_INFO();  
// 设备信息  
HCNetSDK.NET\_DVR\_DEVICEINFO\_V40 m\_strDeviceInfo = new HCNetSDK.NET\_DVR\_DEVICEINFO\_V40();  
// 已登录设备的IP  
String m\_sDeviceIP;  
// 设备用户名  
String m\_sUsername;  
// 设备密码  
String m\_sPassword;  
// 报警回调函数实现  
public static HCNetSDK.FMSGCallBack\_V31 fMSFCallBack\_V31;

/\*\*  
 \* sdk初始化  
 \*  
 \* @return  
 \*/  
public int initDevice() {  
    if (!hCNetSDK.NET\_DVR\_Init()) {  
        // sdk初始化失败  
        return 1;  
    }  
    return 0;  
}

/\*\*  
 \* 注销  
 \*  
 \* @param lUserID 设备注册成功唯一标识符  
 \*/  
public void logout(int lUserID) {  
    // 注销  
    hCNetSDK.NET\_DVR\_Logout(lUserID);  
    // 释放sdk资源  
    hCNetSDK.NET\_DVR\_Cleanup();  
}

/\*\*  
 \* 设备注册  
 \*  
 \* @param ip       设备ip  
 \* @param name     设备名  
 \* @param password 设备密码  
 \*/  
public int deviceRegister(int lUserID, String ip, String name, String password, String port) {  
    // 设备注册之前先进行判断,注销已注册的设备  
    if (lUserID > -1) {  
        // 先注销  
        hCNetSDK.NET\_DVR\_Logout(lUserID);  
        lUserID = -1;  
    }  
    // ip地址  
    m\_sDeviceIP = ip;  
    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());  
    // 设备用户名  
    m\_sUsername = name;  
    m\_strLoginInfo.sUserName = new byte\[HCNetSDK.NET\_DVR\_LOGIN\_USERNAME\_MAX\_LEN\];  
    System.arraycopy(m\_sUsername.getBytes(), 0, m\_strLoginInfo.sUserName, 0, m\_sUsername.length());  
    // 设备密码  
    m\_sPassword = password;  
    m\_strLoginInfo.sPassword = new byte\[HCNetSDK.NET\_DVR\_LOGIN\_PASSWD\_MAX\_LEN\];  
    System.arraycopy(m\_sPassword.getBytes(), 0, m\_strLoginInfo.sPassword, 0, m\_sPassword.length());  
    m\_strLoginInfo.wPort = (short) Integer.parseInt(port);  
    // 是否异步登录:0 - 否,1 - 是  
    m\_strLoginInfo.bUseAsynLogin = false;  
    m\_strLoginInfo.write();  
    // 设备注册调用 NET\_DVR\_Login\_V40,注册成功得到唯一标识符 lUserID  
    // 设备注册失败,调用 NET\_DVR\_GetLastError,根据错误号判断错误类型  
    lUserID = hCNetSDK.NET\_DVR\_Login\_V40(m\_strLoginInfo, m\_strDeviceInfo);  
    if (lUserID == -1) {  
        LOGGER.info("设备注册失败,错误号:", hCNetSDK.NET\_DVR\_GetLastError());  
        return -1;  
    } else {  
        LOGGER.info("设备注册成功");  
        return lUserID;  
    }  
}

/\*\*  
 \* 设置报警信息回调函数,根据上传的数据进行回调触发  
 \*/  
public class FMSGCallBack\_V31 implements HCNetSDK.FMSGCallBack\_V31 {  
    // lCommand 上传消息类型,这个是设备上传的数据类型,比如现在测试的门禁设备,回传回来的是 COMM\_ALARM\_ACS = 0x5002; 门禁主机报警信息  
    // pAlarmer 报警设备信息  
    // pAlarmInfo  报警信息 根据 lCommand 来选择接收的报警信息数据结构  
    // dwBufLen 报警信息缓存大小  
    // pUser  用户数据  
    @Override  
    public boolean invoke(int lCommand, HCNetSDK.NET\_DVR\_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {  
        alarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);  
        return true;  
    }  
}

/\*\*  
 \* 建立布防上传通道,用于传输数据  
 \*  
 \* @param lUserID      唯一标识符  
 \* @param lAlarmHandle 报警处理器  
 \*/  
public int setupAlarmChan(int lUserID, int lAlarmHandle) {  
    // 根据设备注册生成的lUserID建立布防的上传通道,即数据的上传通道  
    if (lUserID == -1) {  
        LOGGER.info("请先注册");  
        return lUserID;  
    }  
    if (lAlarmHandle < 0) {  
        // 设备尚未布防,需要先进行布防  
        if (fMSFCallBack\_V31 == null) {  
            fMSFCallBack\_V31 = new FMSGCallBack\_V31();  
            Pointer pUser = null;  
            if (!hCNetSDK.NET\_DVR\_SetDVRMessageCallBack\_V31(fMSFCallBack\_V31, pUser)) {  
                LOGGER.info("设置回调函数失败!", hCNetSDK.NET\_DVR\_GetLastError());  
            }  
        }  
        // 这里需要对设备进行相应的参数设置,不设置或设置错误都会导致设备注册失败  
        HCNetSDK.NET\_DVR\_SETUPALARM\_PARAM m\_strAlarmInfo = new HCNetSDK.NET\_DVR\_SETUPALARM\_PARAM();  
        m\_strAlarmInfo.dwSize = m\_strAlarmInfo.size();  
        // 智能交通布防优先级:0 - 一等级(高),1 - 二等级(中),2 - 三等级(低)  
        m\_strAlarmInfo.byLevel = 1;  
        // 智能交通报警信息上传类型:0 - 老报警信息(NET\_DVR\_PLATE\_RESULT), 1 - 新报警信息(NET\_ITS\_PLATE\_RESULT)  
        m\_strAlarmInfo.byAlarmInfoType = 1;  
        // 布防类型(仅针对门禁主机、人证设备):0 - 客户端布防(会断网续传),1 - 实时布防(只上传实时数据)  
        m\_strAlarmInfo.byDeployType = 1;  
        // 抓拍,这个类型要设置为 0 ,最重要的一点设置  
        m\_strAlarmInfo.byFaceAlarmDetection = 0;  
        m\_strAlarmInfo.write();  
        // 布防成功,返回布防成功的数据传输通道号  
        lAlarmHandle = hCNetSDK.NET\_DVR\_SetupAlarmChan\_V41(lUserID, m\_strAlarmInfo);  
        if (lAlarmHandle == -1) {  
            LOGGER.info("设备布防失败,错误码=========={}", hCNetSDK.NET\_DVR\_GetLastError());  
            // 注销 释放sdk资源  
            logout(lUserID);  
            return lAlarmHandle;  
        } else {  
            LOGGER.info("设备布防成功");  
            return lAlarmHandle;  
        }  
    }  
    return lAlarmHandle;  
}

/\*\*  
 \* 报警撤防  
 \*  
 \* @param lAlarmHandle 报警处理器  
 \*/  
public int closeAlarmChan(int lAlarmHandle) {  
    if (lAlarmHandle > -1) {  
        if (hCNetSDK.NET\_DVR\_CloseAlarmChan\_V30(lAlarmHandle)) {  
            LOGGER.info("撤防成功");  
            lAlarmHandle = -1;  
            return lAlarmHandle;  
        }  
        return lAlarmHandle;  
    }  
    return lAlarmHandle;  
}

/\*\*  
 \* 接收设备上传的报警信息,进行上传数据的业务逻辑处理  
 \*  
 \* @param lCommand   上传消息类型  
 \* @param pAlarmer   报警设备信息  
 \* @param pAlarmInfo 报警信息  
 \* @param dwBufLen   报警信息缓存大小  
 \* @param pUser      用户数据  
 \*/  
public void alarmDataHandle(int lCommand, HCNetSDK.NET\_DVR\_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {  
    System.out.println("报警监听中================================");  
    System.out.println(pAlarmInfo);

    String sAlarmType = new String();  
    String\[\] newRow = new String\[3\];  
    //报警时间  
    Date today = new Date();  
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");  
    String\[\] sIP = new String\[2\];

    sAlarmType = new String("lCommand=0x") + Integer.toHexString(lCommand);  
    // lCommand是传的报警类型  
    switch (lCommand) {  
        // 摄像头实时人脸抓拍上传  
        case HCNetSDK.COMM\_UPLOAD\_FACESNAP\_RESULT:  
            // 分配存储空间  
            HCNetSDK.NET\_VCA\_FACESNAP\_RESULT strFaceSnapInfo = new HCNetSDK.NET\_VCA\_FACESNAP\_RESULT();  
            strFaceSnapInfo.write();  
            Pointer pFaceSnapInfo = strFaceSnapInfo.getPointer();

            // 写入传入数据  
            pFaceSnapInfo.write(0, pAlarmInfo.getByteArray(0, strFaceSnapInfo.size()), 0, strFaceSnapInfo.size());  
            strFaceSnapInfo.read();  
            sAlarmType = sAlarmType + ":人脸抓拍上传\[人脸评分:" + strFaceSnapInfo.dwFaceScore + ",年龄:" + strFaceSnapInfo.struFeature.byAge + ",性别:" + strFaceSnapInfo.struFeature.bySex + "\]";  
            newRow\[0\] = dateFormat.format(today);  
            // 报警类型  
            newRow\[1\] = sAlarmType;  
            // 报警设备IP地址  
            sIP = new String(strFaceSnapInfo.struDevInfo.struDevIP.sIpV4).split("\\0", 2);  
            newRow\[2\] = sIP\[0\];  
            LOGGER.info("人脸抓拍========{}", Arrays.toString(newRow));  
            // 设置日期格式  
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");  
            // new Date()为获取当前系统时间  
            String time = df.format(new Date());

            // 人脸图片写文件  
            File file = new File(System.getProperty("user.dir") + "\\\\pic1\\\\");  
            if (!file.exists()) {  
                file.mkdir();  
            }  
            try {  
                FileOutputStream big = new FileOutputStream(System.getProperty("user.dir") + "\\\\pic1\\\\" + time + "background.jpg");  
                if (strFaceSnapInfo.dwFacePicLen > 0) {  
                    if (strFaceSnapInfo.dwFacePicLen > 0) {  
                        try {  
                            big.write(strFaceSnapInfo.pBuffer2.getByteArray(0, strFaceSnapInfo.dwBackgroundPicLen), 0, strFaceSnapInfo.dwBackgroundPicLen);  
                            big.close();  
                        } catch (IOException e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            }  
            break;  
        // 门禁主机类型实时人脸抓拍上传,走这里  
        case HCNetSDK.COMM\_ALARM\_ACS:  
            // 分配存储空间  
            System.out.println("============ 这是门禁主机的报警信息 ============");  
            HCNetSDK.NET\_DVR\_ACS\_ALARM\_INFO strFaceSnapInfo1 = new HCNetSDK.NET\_DVR\_ACS\_ALARM\_INFO();  
            strFaceSnapInfo1.write();  
            Pointer pFaceSnapInfo1 = strFaceSnapInfo1.getPointer();

            // 写入传入数据  
            pFaceSnapInfo1.write(0, pAlarmInfo.getByteArray(0, strFaceSnapInfo1.size()), 0, strFaceSnapInfo1.size());  
            strFaceSnapInfo1.read();  
            // 设置日期格式  
            SimpleDateFormat df1 = new SimpleDateFormat("yyyyMMddHHmmss");  
            // new Date()为获取当前系统时间  
            String time1 = df1.format(new Date());

            // 人脸图片写文件  
            File file1 = new File(System.getProperty("user.dir") + "\\\\pic3\\\\");  
            if (!file1.exists()) {  
                file1.mkdir();  
            }  
            try {  
                FileOutputStream big = new FileOutputStream(System.getProperty("user.dir") + "\\\\pic3\\\\" + time1 + ".jpg");  
                if (strFaceSnapInfo1.dwPicDataLen > 0) {  
                    System.out.println("========== 图片有数据  ========");  
                    if (strFaceSnapInfo1.dwPicDataLen > 0) {  
                        try {  
                            System.out.println("============ 图片上传成功 =============");  
                            big.write(strFaceSnapInfo1.pPicData.getByteArray(0, strFaceSnapInfo1.dwPicDataLen), 0, strFaceSnapInfo1.dwPicDataLen);  
                            big.close();  
                            System.out.println("设备唯一编码=================" + strFaceSnapInfo1.struAcsEventInfo.byDeviceNo);  
                            System.out.println("数据采集时间=================" + strFaceSnapInfo1.struTime.dwYear + strFaceSnapInfo1.struTime.dwMonth + strFaceSnapInfo1.struTime.dwDay + strFaceSnapInfo1.struTime.dwHour + strFaceSnapInfo1.struTime.dwMinute + strFaceSnapInfo1.struTime.dwSecond);  
                            System.out.println("人员工号=================" + strFaceSnapInfo1.struAcsEventInfo.dwEmployeeNo);  
                            System.out.println("人员姓名=================" + strFaceSnapInfo1.sNetUser);  
                            System.out.println("通进类型(0:入场,1:离场)=================" + strFaceSnapInfo1.struAcsEventInfo.dwDoorNo);  
                            System.out.println("图片唯一标识(工号加时间)=================" + strFaceSnapInfo1.struAcsEventInfo.dwEmployeeNo + time1 + ".jpg");  
                            System.out.println("人员类型(0:白名单,1:访客,2:黑名单)=================" + strFaceSnapInfo1.struAcsEventInfo.byCardType);  
                        } catch (IOException e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            }  
            break;  
        default:  
            newRow\[0\] = dateFormat.format(today);  
            // 报警类型  
            newRow\[1\] = sAlarmType;  
            // 报警设备IP地址  
            sIP = new String(pAlarmer.sDeviceIP).split("\\0", 2);  
            newRow\[2\] = sIP\[0\];  
            LOGGER.info("其他报警信息=========={}", Arrays.toString(newRow));  
            break;  
    }  
}

// 抓拍图片  
public static void getDVRPic(int userId) throws IOException {  
    // 设置通道号,其中 1 正常,-1不正常  
    NativeLong chanLong = new NativeLong(1);  
    // 返回Boolean值,判断是否获取设备能力  
    HCNetSDK.NET\_DVR\_WORKSTATE\_V30 devwork = new HCNetSDK.NET\_DVR\_WORKSTATE\_V30();  
    if (!hCNetSDK.NET\_DVR\_GetDVRWorkState\_V30(userId, devwork)) {  
        System.out.println("返回设备状态失败");  
    }  
    // JPEG图像信息结构体  
    HCNetSDK.NET\_DVR\_JPEGPARA jpeg = new HCNetSDK.NET\_DVR\_JPEGPARA();  
    jpeg.wPicSize = 2; // 设置图片的分辨率  
    jpeg.wPicQuality = 2; // 设置图片质量  
    IntByReference a = new IntByReference();  
    SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss");  
    Date date = new Date();  
    int random = (int) (Math.random() \* 1000);  
    String fileNameString = sdf.format(date) + random + ".jpg";  
    // 设置字节缓存  
    ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 \* 1024);  
    // 抓图到文件  
    boolean is = hCNetSDK.NET\_DVR\_CaptureJPEGPicture(userId, chanLong.intValue(), jpeg, fileNameString);  
    if (is) {  
        System.out.println("图片抓取成功,返回长度:" + a.getValue());  
    } else {  
        System.out.println("图片抓取失败:" + hCNetSDK.NET\_DVR\_GetLastError());

    }  
}

/\*\*  
 \* 设备状态,是否在线,打印设备信息  
 \*/  
public Boolean onlineState(int lUserID) {  
    HCNetAlarm hcNetAlarm = new HCNetAlarm();  
    int row = hcNetAlarm.initDevice();  
    if (row == 1) {  
        LOGGER.info("初始化失败");  
    }  
    // 检查设备在线状态  
    LOGGER.info("设备信息========={}", hcNetAlarm.m\_strDeviceInfo.struDeviceV30);  
    boolean isOnLine = hCNetSDK.NET\_DVR\_RemoteControl(lUserID, 20005, null, 0);  
    LOGGER.info("checkDeviceOnLine---isOnLine============{}", isOnLine);  
    return isOnLine;  
}  

}

写在结尾

遇到的问题:无法上传图片。(官方文档是个坑)

可能原因:刚开始以为是设备不支持抓拍功能。

解决方式:一遍一遍地阅读官方文档,换了一个又一个接口,最后发现,官方文档上提示的抓拍功能流程图是基于海康摄像头的,但是我使用的设备是海康的门禁设备,两者虽然大体相似,但是还是有不同之处,对于不同的设备需要进行不同的判断。

如这次使用的设备是门禁设备,首先根据触发回调返回的lCommand 进行设备区分,本次测试返回的 lCommand = 0x5002, 即门禁主机报警信息,然后去官方文档上查看对应的sdk接收信息体,为NET_DVR_ACS_ALARM_INFO。这样才能正确接收设备传过来的数据,也能得到上传的图片及其对应的人员信息。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章