背景:由于工作问题,最近对接了海康的设备网络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 Library
Demo中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 = "libhcnetsdk.so"; }else if(osPrefix.toLowerCase().startsWith("linux-amd64")) { loadLibrary = "/lib/"; library = "libhcnetsdk.so"; } 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"; } }
设备订阅+回调事件

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 { } }
完整代码见地址:
https://github.com/theWangs/haikang/tree/master