当前位置: 首页 > news >正文

海康相机开发---设备布防(Setup Alarm)

在海康SDK(HCNetSDK)开发中,“布防”是衔接设备事件产生与客户端响应的核心环节,其本质是通过SDK指令让设备进入“主动事件上报模式”——设备一旦检测到预设事件(如车牌识别、车位状态变化、设备故障等),会立即向客户端推送数据,而非依赖客户端主动查询。这种“推模式”是安防系统实现实时监控、快速响应的关键,也是工业级设备对接中不可或缺的技术模块。

一、布防的核心概念

1. 布防的本质:建立“事件上报通道”(中断)

海康设备(如智能相机、NVR、视频分析服务器)默认处于“被动状态”——仅响应客户端的主动请求(如抓拍图片、查询设备状态),不会主动发送数据。而“布防”是客户端通过SDK向设备发送的“授权指令”,核心作用有两个:

  • 事件订阅:告知设备“需要接收哪些类型的事件”(如仅接收车牌识别事件,或同时接收故障报警);
  • 通道建立:在设备与客户端之间建立“长连接事件通道”,确保设备产生事件时能实时推送数据。

没有布防,客户端若想获取事件,只能通过“轮询查询”(如每秒调用一次“获取事件”接口),这种方式不仅效率低(延迟高),还会占用大量网络带宽和设备资源,无法满足实时性要求(如停车场的车辆进出检测需毫秒级响应)。

2. 布防与相关概念的区别

  • 布防 vs 登录:登录是“建立设备操作权限”,布防是“建立事件上报通道”;登录是布防的前提(布防需传入登录成功的user_id),但登录成功不代表设备会主动上报事件。
  • 布防 vs 回调注册:回调注册(如NET_DVR_SetDVRMessageCallBack_V51)是“指定事件的处理函数”,布防是“让设备开始发送事件”;两者需配合使用——布防让事件产生,回调让事件被处理。
  • 布防 vs 撤防:撤防(NET_DVR_CloseAlarmChan_V30)是布防的反向操作,用于终止事件通道、释放资源,避免设备端内存泄漏。

二、布防的核心函数与参数

海康SDK中布防函数有多个版本(如V30、V40、V50),其中NET_DVR_SetupAlarmChan_V50是目前最常用的版本(兼容多数智能设备,支持更多事件类型),其函数原型、参数含义、返回值逻辑是理解布防的关键。

1. 核心布防函数:NET_DVR_SetupAlarmChan_V50

函数原型(C语言风格,C++可直接调用):

LONG NET_DVR_SetupAlarmChan_V50(LONG lUserID,                                  // 输入:登录成功的用户ID(NET_DVR_Login_V30返回)LPNET_DVR_SETUPALARM_PARAM_V50 pSetupParam,    // 输入:布防参数结构体(核心配置)void* pUser,                                   // 输入:用户自定义数据(回调时透传,可选)DWORD dwUserLen                                // 输入:用户数据长度(通常为0,无需设置)
);
返回值说明
  • 正数(如1、2、3):布防成功,返回值为“布防句柄(Handle)”——后续撤防、查询布防状态需传入该句柄,是布防通道的唯一标识。
  • 负数(如-1、-2):布防失败,需通过NET_DVR_GetLastError()获取错误码(如参数错误返回1,权限不足返回17)。

2. 关键结构体:NET_DVR_SETUPALARM_PARAM_V50

布防的核心是通过该结构体配置“事件类型、上报格式、扩展功能”,结构体的每个字段都直接影响布防效果,甚至决定布防是否成功。其定义(简化版,保留核心字段)如下:

typedef struct _NET_DVR_SETUPALARM_PARAM_V50 {DWORD dwSize;                  // 结构体大小(必须设置,否则SDK无法解析)BYTE byLevel;                  // 布防等级(决定可接收的事件范围)BYTE byAlarmInfoType;          // 报警信息格式(决定事件数据的解析方式)BYTE byFaceAlarmDetection;     // 是否启用人脸报警检测(设备支持时有效)BYTE byRetAlarmTypeV40;        // 是否返回V40版本的报警类型(兼容旧SDK)BYTE byRetDevInfoVersion;      // 是否返回设备信息版本(对接CVR设备时用)BYTE byRetVQDAlarmType;        // 是否返回VQD(视频质量诊断)报警类型BYTE bySupport;                // 扩展支持位(通过位运算启用特定事件)BYTE byRes[25];                // 预留字段(必须清零,避免脏数据)
} NET_DVR_SETUPALARM_PARAM_V50;
字段详细解析
  1. dwSize:结构体大小(必设字段)

    • 作用:SDK通过该字段判断结构体版本(如V50与V40的结构体大小不同),若未设置或设置错误,SDK会直接返回“参数错误(错误码1)”。
    • 正确设置方式:dwSize = sizeof(NET_DVR_SETUPALARM_PARAM_V50)(用sizeof自动计算,避免手动写数值导致版本不兼容)。
    • 常见错误:忘记设置dwSize,或手动赋值为sizeof(NET_DVR_SETUPALARM_PARAM_V40)(版本混淆)。
  2. byLevel:布防等级(事件范围控制)

    • 取值范围:0(一级布防)、1(二级布防)、2(三级布防),具体含义由设备类型决定(需参考设备SDK文档)。
    • 工业常用配置:byLevel = 1(二级布防),适用于智能交通设备(如车牌识别相机),可接收“车牌识别、车位状态、车辆进出”等核心事件;byLevel = 0(一级布防)仅接收“设备故障、断连”等基础事件,无法接收智能分析事件。
    • 注意:若设备不支持某级布防(如低端相机仅支持一级布防),设置更高等级会返回“参数错误(错误码1)”。
  3. byAlarmInfoType:报警信息格式(数据解析关键)

    • 取值范围:0(二进制格式)、1(JSON格式)、2(扩展JSON格式)。
    • 工业常用配置:byAlarmInfoType = 1(JSON格式),因为JSON数据可读性强、解析方便(如用户代码中通过QJsonDocument解析车牌信息);byAlarmInfoType = 0(二进制)需按设备私有格式解析,开发效率低,仅用于对传输效率要求极高的场景。
    • 注意:若设置为JSON格式,但设备不支持(如旧固件设备),会返回“设备不支持该功能(错误码23)”,需升级设备固件。
  4. bySupport:扩展支持位(精细化事件控制)

    • 作用:通过“位运算”启用特定类型的事件(如仅接收“车牌识别成功”事件,过滤“车牌识别失败”事件)。
    • 原理:bySupport是8位字节,每一位(bit0~bit7)对应一种扩展功能,置1表示启用,置0表示禁用。例如:
      • bySupport |= (1 << 2):启用bit2对应的功能(假设bit2代表“车牌识别事件”);
      • bySupport |= (1 << 3):启用bit3对应的功能(假设bit3代表“车位状态事件”)。
    • 注意:每一位的含义需参考设备SDK文档(不同设备定义不同),盲目置位会导致事件上报异常。
  5. 其他字段(兼容性与扩展)

    • byFaceAlarmDetection = 1:启用人脸报警(若设备支持人脸检测,会上报“人脸出现、人脸消失”事件);
    • byRetAlarmTypeV40 = TRUE:返回V40版本的报警类型(兼容旧版SDK开发的客户端);
    • byRetVQDAlarmType = TRUE:返回视频质量诊断事件(如“画面模糊、亮度异常”)。

三、布防的完整流程

布防不是单一函数调用,而是“前置准备→参数配置→执行布防→事件处理→撤防”的完整链路,每个环节都需严格遵循SDK规范,否则会导致布防失败或事件不上报

1. 前置条件(布防前必须完成)

布防依赖三个核心前置操作,缺少任何一个都会导致布防失败:

  • 1. SDK初始化:调用NET_DVR_Init()初始化SDK资源(内存、网络模块),若未初始化,布防函数返回“SDK未初始化(错误码7)”。
  • 2. 设备登录:通过NET_DVR_Login_V30获取有效user_id(正数),布防函数需通过user_id定位设备,若user_id无效(负数),返回“用户ID无效(错误码6)”。
  • 3. 注册回调函数:通过NET_DVR_SetDVRMessageCallBack_V51注册报警回调函数(如g_cb_alarm),布防后设备上报的事件会通过该函数传递给客户端,若未注册,事件会被SDK丢弃,客户端无法感知。

示例代码(前置操作):

// 1. SDK初始化
if (!NET_DVR_Init()) {qDebug() << "SDK初始化失败:" << NET_DVR_GetLastError();return;
}// 2. 设备登录
NET_DVR_DEVICEINFO_V30 devInfo;
LONG user_id = NET_DVR_Login_V30("192.168.0.61", 8000, "admin", "PointCloud666", &devInfo);
if (user_id < 0) {qDebug() << "登录失败:" << NET_DVR_GetLastError();return;
}// 3. 注册报警回调(事件处理函数为g_cb_alarm,透传当前类对象this)
if (!NET_DVR_SetDVRMessageCallBack_V51(0, g_cb_alarm, this)) {qDebug() << "回调注册失败:" << NET_DVR_GetLastError();NET_DVR_Logout(user_id); // 登录成功但回调失败,需登出释放资源return;
}

2. 布防参数配置(核心步骤)

参数配置的关键是“清零结构体→设置必选字段→按需配置可选字段”,避免因脏数据导致布防失败:

// 1. 定义布防参数结构体并清零(避免栈内存脏数据)
NET_DVR_SETUPALARM_PARAM_V50 alarmParam;
memset(&alarmParam, 0, sizeof(alarmParam)); // 必须清零,否则预留字段可能有垃圾数据// 2. 设置必选字段
alarmParam.dwSize = sizeof(NET_DVR_SETUPALARM_PARAM_V50); // 结构体大小(必设)
alarmParam.byLevel = 1; // 二级布防(接收智能事件)
alarmParam.byAlarmInfoType = 1; // JSON格式事件数据// 3. 按需配置可选字段
alarmParam.byFaceAlarmDetection = 1; // 启用人脸报警(若设备支持)
alarmParam.byRetAlarmTypeV40 = TRUE; // 兼容旧版SDK
alarmParam.bySupport |= (1 << 2); // 启用bit2对应的车牌识别事件
alarmParam.bySupport |= (1 << 3); // 启用bit3对应的车位状态事件
关键注意点
  • memset清零:结构体的预留字段(如byRes[25])若有脏数据,SDK可能误判参数无效,导致布防失败,因此必须用memset清零。
  • 字段兼容性:若设备不支持某字段(如低端相机不支持byFaceAlarmDetection),设置该字段为1不会报错,但也不会生效,需提前确认设备能力。

3. 执行布防(调用函数)

参数配置完成后,调用NET_DVR_SetupAlarmChan_V50执行布防,并处理返回结果:

// 执行布防,pUser为NULL(无需透传额外数据),dwUserLen为0
LONG alarmHandle = NET_DVR_SetupAlarmChan_V50(user_id, &alarmParam, NULL, 0);
if (alarmHandle < 0) {// 布防失败,处理错误qDebug() << "布防失败:" << NET_DVR_GetLastError();NET_DVR_Logout(user_id); // 登出设备NET_DVR_Cleanup(); // 清理SDK资源return;
} else {// 布防成功,保存布防句柄(后续撤防用)qDebug() << "布防成功,句柄:" << alarmHandle;this->_handle = alarmHandle; // 类成员变量存储句柄
}

4. 事件处理(布防后的核心逻辑)

布防成功后,设备产生的事件会通过“长连接”主动推送给客户端,SDK自动调用之前注册的回调函数(如g_cb_alarm),客户端在回调中解析事件数据并处理业务逻辑。

以代码中的g_cb_alarm为例,事件处理流程:

  1. 事件类型判断:通过lCommand参数判断事件类型(如COMM_ISAPI_ALARM为ISAPI协议事件);
  2. 数据格式解析:若事件数据为JSON格式,用QJsonDocument解析(如提取车牌号plateNo、车位状态parkingSpaceStatus);
  3. 业务逻辑触发:根据解析结果发射信号(如sig_car_arrival车辆到达、sig_plate_found车牌发现),驱动UI更新或其他模块响应;
  4. 异常处理:若JSON解析失败,输出错误日志,便于排查问题。

5. 撤防(资源释放)

当客户端不再需要接收事件(如用户关闭程序、切换设备)时,必须调用NET_DVR_CloseAlarmChan_V30撤防,释放布防句柄和设备端资源:

// 撤防逻辑(通常在析构函数或关闭函数中)
void hik_car_plate_analyzer::stop() {// 1. 撤防(若布防句柄有效)if (this->_handle >= 0) {NET_DVR_CloseAlarmChan_V30(this->_handle);this->_handle = -1; // 重置句柄,避免重复撤防qDebug() << "撤防成功";}// 2. 登出设备(若登录ID有效)if (this->_user_id >= 0) {NET_DVR_Logout(this->_user_id);this->_user_id = -1;qDebug() << "设备登出成功";}// 3. 清理SDK资源NET_DVR_Cleanup();
}
不撤防的危害
  • 设备端:每个布防句柄对应设备端的一个事件通道,不撤防会导致通道资源泄漏,当通道数达到设备上限时,其他客户端无法布防;
  • 客户端:布防句柄未释放,SDK无法正常清理资源,可能导致客户端进程退出后仍占用网络端口。

四、布防的底层原理(从设备到客户端的事件流转)

布防的本质是“设备与客户端通过海康私有协议建立长连接事件通道”,其底层流转过程可拆解为三个阶段:

1. 布防指令的发送与设备响应

  • 指令发送:客户端调用NET_DVR_SetupAlarmChan_V50后,SDK将布防参数(NET_DVR_SETUPALARM_PARAM_V50)封装成“布防指令包”(基于海康私有协议,TCP传输),通过登录时建立的TCP连接发送给设备。
  • 设备解析:设备接收指令包后,解析dwSizebyLevelbyAlarmInfoType等字段,验证参数合法性(如byLevel是否支持、user_id是否有权限)。
  • 通道创建:参数验证通过后,设备在内存中创建“事件通道”,记录关键信息:
    • 客户端信息:IP地址、端口(用于推送事件);
    • 事件过滤规则:byLevelbySupport定义的事件类型;
    • 数据格式:byAlarmInfoType定义的JSON/二进制格式;
      同时,设备返回“布防成功包”,包含布防句柄alarmHandle

2. 事件产生与主动推送

当设备检测到符合“事件过滤规则”的事件时(如车牌识别相机拍到车辆),会触发以下流程:

  • 事件数据采集:设备采集事件相关数据(如车牌号、时间戳、车位状态、图片帧);
  • 数据封装:按byAlarmInfoType配置的格式封装数据(如JSON格式包含"plateNo":"鲁A12345""parkingSpaceStatus":"normal");
  • 主动推送:设备通过之前创建的“事件通道”,将封装好的“事件数据包”主动推送给客户端(TCP传输,无需客户端请求)。

3. 客户端SDK的事件分发

  • 数据接收:客户端SDK监听指定端口,接收设备推送的“事件数据包”;
  • 数据解析:SDK根据byAlarmInfoType解析数据包(JSON格式用内置JSON解析器,二进制格式按私有结构解析);
  • 回调触发:SDK提取事件关键参数(如事件类型lCommand、事件数据pAlarmInfo),调用客户端注册的回调函数(如g_cb_alarm),并将pUser(透传数据)一并传入;
  • 业务处理:回调函数中,客户端解析pAlarmInfo(如用户代码中解析JSON获取车牌),执行业务逻辑(发射信号、更新UI)。

4. 心跳保持与通道维护

  • 设备端心跳:设备每隔30秒(默认,可配置)向客户端发送“通道心跳包”,确认客户端是否在线;若连续3次未收到客户端响应,设备自动销毁“事件通道”,释放资源。
  • 客户端心跳:客户端SDK每隔20秒(默认)向设备发送“确认包”,响应设备的心跳请求;若未收到设备心跳包,SDK会触发“设备离线”事件(通过g_cb_exception回调)。

五、布防失败的常见原因与排查方案

布防失败是开发中最常见的问题,需结合错误码和设备状态精准定位,以下是工业开发中高频问题的排查思路:

1. 错误码1:参数错误

  • 可能原因
    1. dwSize未设置或设置错误(如dwSize = sizeof(NET_DVR_SETUPALARM_PARAM_V40));
    2. 结构体未用memset清零,预留字段有脏数据;
    3. byLevel/byAlarmInfoType取值超出设备支持范围(如设备仅支持二进制格式,却设置byAlarmInfoType = 1)。
  • 排查步骤
    1. 确认dwSize = sizeof(NET_DVR_SETUPALARM_PARAM_V50)
    2. 检查是否调用memset(&alarmParam, 0, sizeof(alarmParam))
    3. 查阅设备SDK文档,确认byLevelbyAlarmInfoType的支持范围(可通过NET_DVR_GetDeviceAbility获取设备能力集)。

2. 错误码6:用户ID无效

  • 可能原因
    1. user_id为负数(未登录或登录失败);
    2. user_id已过期(设备重启、网络断连导致会话失效);
    3. user_id对应的设备已登出(调用过NET_DVR_Logout)。
  • 排查步骤
    1. 布防前检查user_id >= 0
    2. user_id曾有效,调用NET_DVR_RemoteControl(user_id, NET_DVR_CHECK_USER_STATUS, NULL, 0)检查会话是否有效;
    3. 会话失效时,重新登录获取新user_id

3. 错误码17:权限不足

  • 可能原因
    1. 登录用户无“远程布防”权限(设备端用户权限控制);
    2. 登录用户为“操作员”角色,仅能查看事件,无法布防(需管理员角色)。
  • 排查步骤
    1. 用登录账号登录设备web界面(IP+端口,如http://192.168.0.61:80);
    2. 进入“用户管理”→“用户权限”,确认该用户已勾选“远程布防”“报警管理”权限;
    3. 若权限不足,修改用户角色为“管理员”或添加对应权限。

4. 错误码23:设备不支持该功能

  • 可能原因
    1. 设备固件版本过低(如旧相机不支持JSON格式事件);
    2. 布防参数与设备类型不匹配(如用NVR的布防参数配置相机);
    3. 设备未开启“智能事件上报”功能(需在web界面启用)。
  • 排查步骤
    1. 登录设备web界面,查看“系统信息”中的固件版本,确认是否与SDK版本兼容(海康官网可查SDK与固件的兼容列表);
    2. 进入设备“智能配置”→“事件上报”,确认“启用智能事件上报”已勾选;
    3. 若固件版本过低,升级设备固件(需联系海康技术支持获取升级包)。

5. 错误码10054:连接被重置

  • 可能原因
    1. 客户端与设备的网络中断(如网线松动、交换机故障);
    2. 设备重启中,TCP连接被强制断开;
    3. 设备开启“IP白名单”,客户端IP不在白名单内。
  • 排查步骤
    1. ping 192.168.0.61测试网络连通性,用telnet 192.168.0.61 8000测试端口是否开放;
    2. 检查设备是否正常运行(指示灯、web界面是否能访问);
    3. 登录设备web界面,查看“网络配置”→“IP白名单”,确认客户端IP已添加。

六、工业级布防的优化设计

start()函数中,布防逻辑体现了工业级开发的健壮性设计,以下是值得借鉴的优化点:

1. 后台线程执行,避免阻塞UI

布防、登录等操作可能耗时(如网络延迟),用户代码通过QtConcurrent::run在后台线程执行布防逻辑:

QtConcurrent::run([=](){// 布防、登录逻辑
});
  • 优势:避免主线程(UI线程)阻塞,确保界面流畅(如进度条动画、按钮响应)。

2. 错误处理的统一性

用户代码通过FUNC_FAIL_RET宏统一处理布防、登录等函数的失败场景:

#define FUNC_FAIL_RET(func) \if (!(func)) { \qDebug() << #func << "失败:" << NET_DVR_GetLastError(); \emit sig_dev_status(false); \return; \}// 布防时调用
FUNC_FAIL_RET(NET_DVR_SetupAlarmChan_V50(...) >= 0);
  • 优势:减少重复代码,确保错误处理逻辑一致,便于维护。

3. 设备状态的实时监控

布防成功后,用户代码通过循环检查设备状态,确保事件通道有效:

while(!_stopped) {bool online = (NET_DVR_RemoteControl(_user_id, NET_DVR_CHECK_USER_STATUS, NULL, 0) != 0);emit sig_dev_status(online); // 向UI推送设备状态QThread::msleep(5000); // 5秒检查一次,避免频繁调用
}
  • 优势:及时发现设备离线、会话失效,便于客户端触发重连、重布防逻辑。

4. 外部终止机制

用户代码通过_stopped变量允许外部终止布防流程:

while (_user_id < 0) {if (_stopped) return; // 外部设置_stopped=true,终止登录循环// 登录逻辑
}
  • 优势:在程序关闭、切换设备时,能优雅终止布防流程,避免资源泄漏。

七、布防的进阶功能(满足复杂场景需求)

除了基础布防,海康SDK还提供进阶功能,满足工业场景的复杂需求:

1. 多通道布防(针对多设备/多通道)

若客户端需同时接收多个设备(或同一设备的多个通道)的事件,需为每个设备/通道单独布防:

// 设备1布防
LONG handle1 = NET_DVR_SetupAlarmChan_V50(user_id1, &param1, NULL, 0);
// 设备2布防
LONG handle2 = NET_DVR_SetupAlarmChan_V50(user_id2, &param2, NULL, 0);
  • 注意:每个布防句柄独立,撤防时需分别调用NET_DVR_CloseAlarmChan_V30

2. 事件过滤(精细化接收事件)

通过bySupport字段和NET_DVR_SetAlarmFilter函数,可实现更精细的事件过滤(如仅接收“车牌识别成功”事件):

// 配置事件过滤:仅接收车牌识别成功事件
NET_DVR_ALARM_FILTER filter;
memset(&filter, 0, sizeof(filter));
filter.dwSize = sizeof(filter);
filter.byFilterType = 1; // 按事件类型过滤
filter.byEventType = 0x01; // 0x01代表车牌识别成功(需参考SDK文档)// 设置过滤规则
NET_DVR_SetAlarmFilter(alarmHandle, &filter);

3. 布防状态查询

通过NET_DVR_GetAlarmChanStatus函数查询布防通道的状态(如是否在线、事件统计):

NET_DVR_ALARM_CHAN_STATUS status;
memset(&status, 0, sizeof(status));
status.dwSize = sizeof(status);if (NET_DVR_GetAlarmChanStatus(alarmHandle, &status)) {qDebug() << "布防状态:" << (status.byStatus ? "在线" : "离线");qDebug() << "已上报事件数:" << status.dwAlarmCount;
}

4. 断线重布防(提高稳定性)

网络中断后,设备会销毁事件通道,需在客户端实现“断线重布防”逻辑:

// 监控线程中检查布防状态
while(!_stopped) {if (alarmHandle >= 0) {// 检查布防通道是否有效NET_DVR_ALARM_CHAN_STATUS status;memset(&status, 0, sizeof(status));status.dwSize = sizeof(status);if (!NET_DVR_GetAlarmChanStatus(alarmHandle, &status)) {// 布防失效,重新布防qDebug() << "布防失效,重新布防";NET_DVR_CloseAlarmChan_V30(alarmHandle);alarmHandle = NET_DVR_SetupAlarmChan_V50(user_id, &alarmParam, NULL, 0);}}QThread::msleep(10000); // 10秒检查一次
}

海康设备的布防是“实时事件响应”的核心技术,其底层涉及网络通信、参数解析、事件过滤、主动推送等多个环节,核心通过NET_DVR_SetupAlarmChan_V50函数和NET_DVR_SETUPALARM_PARAM_V50结构体实现。工业级布防开发需关注三个关键点:

  1. 参数配置的正确性:尤其是dwSizebyLevelbyAlarmInfoType等必选字段,直接决定布防是否成功;
  2. 错误处理的健壮性:通过错误码精准定位问题,结合重试、重布防机制提高稳定性;
  3. 资源释放的完整性:布防后必须撤防,避免设备端和客户端资源泄漏。
http://www.dtcms.com/a/356763.html

相关文章:

  • 数据库Mysql
  • 微积分 | 积分代换
  • 如何将iPhone日历传输到电脑
  • 内置高压MOS的智能调光方案:AP5126 LED降压恒流驱动芯片
  • 深度拆解判别式推荐大模型RankGPT!生成式精排落地提速94.8%,冷启动效果飙升,还解决了传统推荐3大痛点
  • 评价指标FID/R Precision
  • 《R for Data Science (2e)》免费中文翻译 (第6章) --- scripts and projects
  • 学习游戏制作记录(音频的制作和使用)8.28
  • 算法题打卡力扣第169题:多数元素(easy)
  • 【二叉树(DFS)- LeetCode】124. 二叉树中的最大路径和
  • 考民航安检员证需要具备哪些技能和知识?
  • 卷积神经网络为什么要填充(Padding)
  • Python爬虫实战:研究Pyplot模块,构建IMDb数据采集和分析系统
  • 【Tools】C#文件自动生成UML图
  • Vue3 全面介绍
  • ArcGIS Pro 地图打包与解包
  • STM32CubeMX + HAL 库:基于 I²C 通信的 AHT20 高精度温湿度测量实验
  • 佳易王钓场计时计费系统:全方位赋能钓场智能化管理,软件操作教程
  • Halcon那些事:如何使用差异模型create_variation_model检测印刷品缺陷
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(22):文法+单词第7回4 + 考え方1
  • Java全栈开发工程师面试实录:从基础到微服务的深度技术解析
  • LangChain如何使用通义千问的向量模型
  • 嵌入式学习日志————MPU6050简介
  • 2025年社交广告投放技术解析:应对CPM上涨的核心策略与实战方法
  • 元宇宙与娱乐产业:沉浸式体验与内容生态的重构
  • 前端工程化深度实践:从构建优化到CI/CD的完整解决方案
  • 基于无人机的风电叶片全自动智能巡检:高精度停角估计与细节优先曝光调控技术
  • 【Flask】测试平台开发,产品管理实现添加功能-第五篇
  • 【Flask】测试平台开发,集成禅道
  • 【Spring Cloud微服务】6.通信的利刃:深入浅出 Spring Cloud Feign 实战与原理