写在最前

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。这样才能正确接收设备传过来的数据,也能得到上传的图片及其对应的人员信息。

Java二次开发海康SDK-对接门禁机的更多相关文章

  1. 海康SDK编程指南(C#二次开发版本)

    海康SDK编程指南 目前使用的海康SDK包括IPC_SDK(硬件设备),Plat_SDK(平台),其中两套SDK都需单独调用海康播放库PlayCtrl.dll来解码视频流,返回视频信息和角度信息.本文 ...

  2. 海康SDK JAVA版本调用步骤及问题介绍

    一.前言 本文为海康SDK JAVA版本Demo的介绍,采用Eclipse运行,以及一些问题记录. 海康SDK版本:SDK_Win32 Eclipse版本:Mars2.0 JDK版本:1.8.0_15 ...

  3. 海康解码器对接总结(java 版)

    本文只是对接海康解码器的动态解码功能,即配置解码器大屏上指定的某个窗口去解某一路IP视频源. 1. 首先,定义所需的结构体与接口.海康SDK中包含的结构体与接口非常之多,在官方的例子中,实现了大部分的 ...

  4. 使用golang对海康sdk进行业务开发

    目录 准备工作 开发环境信息 改写HCNetSDK.h头文件 开发过程 基本数据类型转换 业务开发 参考 项目最近需要改造升级:操作海康摄像头(包括登录,拍照,录像)等基本功能.经过一段时间研究后,发 ...

  5. 海康SDK编程指南

    转至心澄欲遣 目前使用的海康SDK包括IPC_SDK(硬件设备),Plat_SDK(平台),其中两套SDK都需单独调用海康播放库PlayCtrl.dll来解码视频流,返回视频信息和角度信息.本文仅对视 ...

  6. C#制作ActiveX控件中调用海康SDK的问题

    事情是这样的,有一台海康威视的摄像头,客户需要一个ActiveX控件嵌入到网页中,通过点击按钮开始录制和结束录制来进行视频的录制和保存,关于海康摄像头的二次开发在此就不多说了,可以参考SDK中的说明. ...

  7. 封装海康SDK出现无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块

    今天在封装海康设备的时候出现了这么一个问题,在初始化的时候提升无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块. 在网上查找了几个方法,并不是很靠谱,于是从源头找找,是什 ...

  8. 使用c#封装海康SDK出现无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块

    最近在研究网络摄像头的二次开发,测试了一款海康威视的网络摄像头,程序调试的时候,出现如题的报错. 调试随机自带的demo时,程序运行正常,但当把该程序引入到我自己的程序中时,就开始报错.根据开发软件包 ...

  9. golang调用海康sdk

    git地址:https://gitee.com/mimo431/hcnet-sdk_golang 网络不太流畅,先传gitee上 参考链接: https://www.cnblogs.com/dust9 ...

随机推荐

  1. Java(24)常用API三

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228417.html 博客主页:https://www.cnblogs.com/testero ...

  2. vue3双向数据绑定原理_demo

    <!DOCTYPE html> <head> <meta charset="UTF-8" /> <meta name="view ...

  3. python jinja2初见

    吸取了长城杯的教训,学习python-web迫在眉睫. 正常难度的python_template_injection,由于现在没学面向对象,理解原理比较困难,所以先使用简单版复现:并附上正常版的常用p ...

  4. Codeforces1575D

    思路分析 此题采用dfs,注意X选中了之后所有的X值相同,所以需要一个flag来存储X的值. 注意前导0要单独讨论,然后就是当'X'或者'_'在第一位时不能选0,其它位可以选0 - 9 任意一个数. ...

  5. JVM:内存结构

    JVM:内存结构 说明:这是看了 bilibili 上 黑马程序员 的课程 JVM完整教程 后做的笔记 内容 程序计数器 虚拟机栈 本地方法栈 堆 方法区 直接内存 1. 程序计数器 1.1 定义 P ...

  6. [敏捷软工团队博客]Beta阶段事后分析

    设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件要解决的问题是:现在的软工课程的作业分布在博客园.GitHub上,没有一个集成多种功能的一体化 ...

  7. 关于STM32 (Cortex-M3) 中NVIC的分析

    一.STM32 (Cortex-M3) 中的优先级概念 STM32(Cortex-M3)中有两个优先级的概念:抢占式优先级和响应优先级,也把响应优先级称作"亚优先级"或" ...

  8. 单片机STM32的启动文件详解--学习笔记

    启动文件简介 启动文件由汇编编写,是系统上电复位后第一个执行的程序.主要做了以下工作: 1.初始化堆栈指针SP=_initial_sp 2.初始化PC 指针=Reset_Handler 3.初始化中断 ...

  9. Python基础——数据类型——字符串

    整数.浮点数.布尔值的用法大同小异,而Python字符串的一些用法不易记住,这里以廖雪峰教程为基础,进行一些思考和复习总结. 字符串是什么? 以单引号'或者双引号"括起来的任意文本,比如:& ...

  10. Go并发编程--Mutex/RWMutex

    目录 一.前言 二. Mutex 2.1 案例 三. 实现原理 3.1 锁的实现模式 3.2 Go Mutex 实现原理 3.2.1 加锁 3.2.2 解锁 四. 源码分析 4.1 Mutex基本结构 ...