背景:由于工作问题,最近对接了海康的设备网络SDK,参考了SDK自带demo,还有一部分的网上博客,发现没有很全的,自己决定把自己开发的经验分享下
基本信息:
JDK11
springboot 2.5
官网下载的SDK:CH-HCNetSDKV6.1.6.45_build20210302_win64
对接的设备:人脸识别一体机(明眸设备——卡为中心、人为中心)
代码简介:

hklib是存放win下SDK文件的
custom下是核心代码,五个Service结尾的,分别是明眸卡为中心,明眸人脸为中心,设备登陆,设备订阅,设备SDK初始化,包含了人脸识别一体机基本操作,登陆,订阅,下发卡号,人脸,删除卡号人脸,刷卡刷脸事件上报。
lib目录下为SDK的对接文件,参考jSDK目录中 CH-HCNetSDKV6.1.6.45_build20210302_win64\Demo示例\4- Java 开发示例\4-明眸门禁优化接口(以人为中心)\Test1\src\Test1 部分修改
SDK继承:StdCallLibrary改成 Libray,StdCallLibaray是win下使用,改过之后可以win、linux通用
public interface HCNetSDK extends LibraryDemo中win32的都是只能win下使用,由于本人没有引入example.jar 所以直接删除了相关内容(主要涉及视屏流),我对接的设备不需要使用。

SDK加载方式修改:
HCNetSDK INSTANCE = Native.load(OSUtils.getLoadLibrary(), HCNetSDK.class);内部类代码全部继承:SdkStructure,重写一个getFiledOrder来解决空指针问题(不知道是不是只有我遇到的)
public static class SdkStructure extends Structure {
@Override
protected List<String> getFieldOrder(){
List<String> fieldOrderList = new ArrayList<String>();
for (Class<?> cls = getClass();
!cls.equals(SdkStructure.class);
cls = cls.getSuperclass()) {
Field[] fields = cls.getDeclaredFields();
int modifiers;
for (Field field : fields) {
modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
continue;
}
fieldOrderList.add(field.getName());
}
}
return fieldOrderList;
}
}/**
* 获取库文件
* 区分win、linux
* @return
*/
public static String getLoadLibrary() {
if (isChecking()) {
return null;
}
String loadLibrary = "";
String library = "";
String osPrefix = getOsPrefix();
if(osPrefix.toLowerCase().startsWith("win32-x86")) {
loadLibrary = System.getProperty("user.dir") + File.separator+ "hklib" + File.separator;
library = "HCNetSDK.dll";
} else if(osPrefix.toLowerCase().startsWith("win32-amd64") ) {
loadLibrary = System.getProperty("user.dir") + File.separator+ "hklib" + File.separator;
library = "HCNetSDK.dll";
} else if(osPrefix.toLowerCase().startsWith("linux-i386")) {
loadLibrary = "/lib/";
library = "";
}else if(osPrefix.toLowerCase().startsWith("linux-amd64")) {
loadLibrary = "/lib/";
library = "";
}
log.info("[HK] ================= Load library Path :{} ==================", loadLibrary + library);
return loadLibrary + library;
}
设备登陆


public static String login(String m_sDeviceIP, String m_sUsername,String m_sPassword){
HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息
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_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_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.valueOf("8000");
m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是
m_strLoginInfo.write();
HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息
int loginHandler = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
if (loginHandler == -1) {
int errorCode = hCNetSDK.NET_DVR_GetLastError();
IntByReference errorInt = new IntByReference(errorCode);
log.error("[HK] login fail errorCode:{}, errMsg:{}", errorCode, hCNetSDK.NET_DVR_GetErrorMsg(errorInt));
return hCNetSDK.NET_DVR_GetErrorMsg(errorInt);
} else {
loginHandleMap.put(m_sDeviceIP,loginHandler);
int iCharEncodeType = m_strDeviceInfo.byCharEncodeType;
encodeMap.put(loginHandler,iCharEncodeType);
log.info("[HK] login success iCharEncodeType:{}", iCharEncodeType);
log.info("[HK] login map:{}", loginHandleMap);
return "success";
}
}View Code
设备订阅+回调事件


static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;
final HCNetSDK.FMSGCallBack_V31 callback = new HKEventCallback();
//key: loginHandler value: subscribeHandler
public static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public String startSubscribe(int loginHandler) {
String msg = "success";
boolean b = hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(callback, null);
if (!b) {
log.error("订阅失败");
}
HCNetSDK.NET_DVR_SETUPALARM_PARAM m_strAlarmInfo = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
m_strAlarmInfo.dwSize = m_strAlarmInfo.size();
m_strAlarmInfo.byLevel = 1;
m_strAlarmInfo.byAlarmInfoType = 1;
// m_strAlarmInfo.byDeployType = 0;// 客户端布防
m_strAlarmInfo.byDeployType = 1;// 服务端布防
m_strAlarmInfo.write();
int subscribeHandler = hCNetSDK.NET_DVR_SetupAlarmChan_V41(loginHandler, m_strAlarmInfo);
if (subscribeHandler == -1) {
int errorCode = hCNetSDK.NET_DVR_GetLastError();
msg = hCNetSDK.NET_DVR_GetErrorMsg(new IntByReference(errorCode));
log.error("布防失败");
} else {
map.put(loginHandler, subscribeHandler);
}
return msg;
}
public void stopSubscribe(Integer subscribeHandler) {
if (subscribeHandler == null) {
log.error("stopSubscribe null");
return;
}
boolean b = hCNetSDK.NET_DVR_CloseAlarmChan_V30(subscribeHandler);
//防止数据紊乱(重复登陆等)导致两个map内容不一致,采用循环删除
for (var loginHandler : map.keySet()) {
if (subscribeHandler.intValue() == map.get(loginHandler)) {
map.remove(loginHandler);
}
}
if (!b) {
log.error("撤防失败");
}
}
/**
* 2021-06-09
*/
class HKEventCallback implements HCNetSDK.FMSGCallBack_V31 {
@Override
public boolean invoke(int lCommand, HCNetSDK.NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {
int loginHandler = pAlarmer.lUserID;
Integer subscribeHandler = map.get(loginHandler);
if (subscribeHandler == null) {
log.error("上报句柄不存在");
}
// lCommand是传的报警类型
switch (lCommand) {
case HCNetSDK.COMM_ALARM_ACS: // 门禁主机报警信息
try {
processAlarmEvent(pAlarmer, pAlarmInfo);
} catch (Exception e) {
log.error("门禁事件异常 error:{}", ExceptionUtils.getStackTrace(e));
}
break;
default:
break;
}
return true;
}
}
private void processAlarmEvent(HCNetSDK.NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo) {
String[] sIP = new String[2];
sIP = new String(pAlarmer.sDeviceIP).split("\0", 2);
//报警设备ip
String ip = sIP[0];
String eventTime = null;
String employeeId = null;
String cardNo = null;
Byte cardType = null;
HCNetSDK.NET_DVR_ACS_ALARM_INFO strACSInfo = new HCNetSDK.NET_DVR_ACS_ALARM_INFO();
strACSInfo.write();
Pointer pACSInfo = strACSInfo.getPointer();
pACSInfo.write(0, pAlarmInfo.getByteArray(0, strACSInfo.size()), 0, strACSInfo.size());
strACSInfo.read();
//主类型 5 事件上报
if (strACSInfo.dwMajor != 5) {
return;
}
cardNo = new String(strACSInfo.struAcsEventInfo.byCardNo).trim();
cardType = strACSInfo.struAcsEventInfo.byCardType;
byte timeType = strACSInfo.byTimeType;
HCNetSDK.NET_DVR_TIME struTime = strACSInfo.struTime;
LocalDateTime uplaodTime = LocalDateTime.of(struTime.dwYear, struTime.dwMonth, struTime.dwDay, struTime.dwHour, struTime.dwMinute, struTime.dwSecond);
//以人为中心设备
if (strACSInfo.byAcsEventInfoExtend == 1) {
var test = new HCNetSDK.NET_DVR_ACS_EVENT_INFO_EXTEND();
test.write();
var pAcsEventInfoExtend = test.getPointer();
pAcsEventInfoExtend.write(0, strACSInfo.pAcsEventInfoExtend.getByteArray(0, test.size()), 0, test.size());
test.read();
employeeId = new String(test.byEmployeeNo).trim();
}
if (timeType == 0) {
eventTime = dtf.format(uplaodTime);
} else if (timeType == 1) {
var zonedDateTime = uplaodTime.atZone(ZoneId.from(ZoneOffset.UTC)).withZoneSameInstant(ZoneId.systemDefault());
eventTime = dtf.format(zonedDateTime);
} else {
log.error("上报时间不正确");
}
if (strACSInfo.dwPicDataLen > 0) {
long offset = 0;
ByteBuffer buffers = strACSInfo.pPicData.getByteBuffer(offset, strACSInfo.dwPicDataLen);
byte[] bytes = new byte[strACSInfo.dwPicDataLen];
buffers.rewind();
buffers.get(bytes);
log.info("有图片");
}
// 刷卡成功
if (strACSInfo.dwMinor == 1) {
log.info("刷卡成功");
//刷脸成功
} else if (strACSInfo.dwMinor == 75) {
log.info("刷脸成功");
//刷脸失败
} else if (strACSInfo.dwMinor == 76) {
log.info("刷脸失败");
} else {
}
}View Code
完整代码见地址:
https:///theWangs/haikang/tree/master
耐得住寂寞,经得起诱惑,受得了折磨
















