OpenHarmony外设驱动使用 (四),Face_auth
OpenHarmony外设驱动使用 (四)
Face_auth
概述
功能简介
人脸识别功能是端侧设备不可或缺的一部分,为设备提供一种用户认证能力,可应用于设备解锁、支付、应用登录等身份认证场景。它是基于人的脸部特征信息进行身份识别的一种生物特征识别技术,用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别,通常也叫做人像识别、面部识别、人脸认证。人脸识别功能整体框架如图1。
基于HDF(Hardware Driver Foundation)驱动框架开发的Face_auth驱动,能够屏蔽硬件器件差异,为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力接口,包括人脸识别执行器列表查询、执行器信息查询、指定人脸模板ID查询模板信息、用户认证框架和执行器间的人脸模板信息对账、人脸录入、删除、认证和识别等。
图1 人脸识别功能整体框架
基本概念
用户认证框架与各基础认证服务组成的身份认证系统支持用户认证凭据设置、删除、认证等基础功能。系统支持用户身份认证,需要提供数据采集、处理、存储及比对能力。
-
执行器
执行器是能够提供以上能力的处理模块,各基础认证服务提供执行器能力,被身份认证框架调度完成各项基础能力。
-
执行器安全等级
执行器提供能力时所在运行环境达到的安全级别。
-
执行器角色
-
全功能执行器:执行器可独立处理一次凭据注册和身份认证请求,即可提供用户认证数据采集、处理、储存及比对能力。
-
采集器:执行器提供用户认证时的数据采集能力,需要和认证器配合完成用户认证。
-
认证器:认证器提供用户认证时数据处理能力,读取存储的凭据模板与当前认证信息完成比对。
-
-
执行器类型
同一种身份认证类型的不同认证方式会产生认证算法差异,设备器件差异也会导致算法差异,执行器根据支持的算法类型差异或对接的器件差异,会定义不同的执行器类型。
-
用户认证框架公钥 & 执行器公钥
用户身份认证处理需要保证用户数据安全以及认证结果的准确性,用户认证框架与基础认证服务间的关键交互信息需要做数据完整性保护,各基础认证服务将提供的执行器能力对接到用户认证框架时,需要交换各自的公钥,其中:
1)执行器通过用户认证框架公钥校验调度指令的准确性。
2)执行器公钥可被用户认证框架用于校验认证结果的准确性,同时用于执行器交互认证时的校验交互信息的完整性。
-
认证凭据模板
认证凭据是在用户设置认证凭据时由认证服务产生并存储,每个模板有一个ID,用于索引模板信息文件,在认证时读取模板信息并用于与当次认证过程中产生的认证数据做对比,完成身份认证。
-
执行器对账
用户认证框架统一管理用户身份和凭据ID的映射关系,执行器对接到用户认证框架时,会读取用户身份认证框架内保存的该执行器的模板ID列表,执行器需要与自己维护的模板ID列表进行比对,并删除冗余信息。
-
HAPs
HAPs(Harmony Ability Packages),广义上指可以安装在OpenHarmony上的应用包,本章节中仅代表Face_auth驱动的上层应用。
-
IDL接口
接口定义语言(Interface Definition Language)通过IDL编译器编译后,能够生成与编程语言相关的文件:客户端桩文件,服务器框架文件。本文主要是通过IDL接口生成的客户端和服务端来实现Face_auth服务和驱动的通信,详细使用方法可参考IDL简介。
-
IPC通信
IPC(Inter Process Communication),进程间通信是指两个进程的数据之间产生交互,详细原理可参考IPC通信简介。
-
HDI
HDI(Hardware Device Interface),硬件设备接口,位于基础系统服务层和设备驱动层之间,是提供给硬件系统服务开发者使用的、统一的硬件设备功能抽象接口,其目的是为系统服务屏蔽底层硬件设备差异,具体可参考HDI规范。
运作机制
Face_auth驱动的主要工作是为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力,保证设备上人脸识别功能可以正常运行。 开发者可基于HDF框架对不同芯片进行各自驱动的开发及HDI层接口的调用。
图2 Face_auth服务和Face_auth驱动交互
约束与限制
- 要求设备上具备摄像器件,且人脸图像像素大于100*100。
- 要求设备上具有可信执行环境,人脸特征信息高强度加密保存在可信执行环境中。
- 对于面部特征相似的人、面部特征不断发育的儿童,人脸特征匹配率有所不同。如果对此担忧,可考虑其他认证方式。
开发指导
场景介绍
Face_auth驱动的主要工作是为上层用户认证框架和Face_auth服务提供稳定的人脸识别基础能力,保证设备上人脸识别功能可以正常运行。
接口说明
注:以下接口列举的为IDL接口描述生成的对应C++语言函数接口,接口声明见idl文件。
在本文中,人脸凭据的录入、认证、识别和删除相关的HDI接口如表1所示,表2中的回调函数分别用于人脸执行器返回操作结果给框架和返回操作过程中的提示信息给上层应用。
表1 接口功能介绍
接口名称 | 功能介绍 |
---|---|
GetExecutorList(std::vector<sptr<IAllInOneExecutor>>& allInOneExecutors) | 获取V2_0版本执行器列表。 |
GetExecutorInfo(ExecutorInfo& info) | 获取执行器信息,包括执行器类型、执行器角色、认证类型、安全等级、执行器公钥等信息,用于向用户认证框架注册执行器。 |
OnRegisterFinish(const std::vector<uint64_t>& templateIdList, const std::vector<uint8_t>& frameworkPublicKey, const std::vector<uint8_t>& extraInfo) | 执行器注册成功后,获取用户认证框架的公钥信息;获取用户认证框架的人脸模板列表用于对账。 |
Enroll(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj) | 录入人脸模板。 |
Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList, const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj) | 认证人脸模板。 |
Identify(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj) | 识别人脸模板。 |
Delete(const std::vector<uint64_t>& templateIdList) | 删除人脸模板。 |
Cancel(uint64_t scheduleId) | 通过scheduleId取消指定录入、认证、识别操作。 |
SendCommand(int32_t commandId, const std::vector<uint8_t>& extraInfo, const sptr<IExecutorCallback>& callbackObj) | 人脸认证服务向Face_auth驱动传递参数的通用接口。 |
SetBufferProducer(const sptr<BufferProducerSequenceable> &bufferProducer) | 设置预览流缓冲区。 |
GetProperty(const std::vector<uint64_t>& templateIdList, const std::vector<int32_t>& propertyTypes, Property& property) | 获取执行器属性信息。 |
SetCachedTemplates(const std::vector<uint64_t> &templateIdList) | 设置需缓存模板列表。 |
RegisterSaCommandCallback(const sptr<ISaCommandCallback> &callbackObj) | 注册SA命令回调。 |
表2 回调函数介绍
接口名称 | 功能介绍 |
---|---|
ExecutorCallbackService::OnResult(int32_t result, const std::vector<uint8_t>& extraInfo) | 返回操作的最终结果。 |
ExecutorCallbackService::OnTip(int32_t tip, const std::vector<uint8_t>& extraInfo) | 返回操作的过程交互信息。 |
SaCommandCallbackService::OnSaCommands(const std::vector<SaCommand>& commands) | 发送命令列表。 |
开发步骤
以Hi3516DV300平台为例,我们提供了Face_auth驱动DEMO实例,以下是目录结构及各部分功能简介。
// drivers/peripheral/face_auth
├── BUILD.gn # 编译脚本
├── bundle.json # 组件描述文件
└── hdi_service # Face_auth驱动实现├── BUILD.gn # 编译脚本├── include # 头文件└── src # 源文件├── executor_impl.cpp # 认证、录入等功能接口实现├── face_auth_interface_driver.cpp # Face_auth驱动入口└── face_auth_interface_service.cpp # 获取执行器列表接口实现
下面结合DEMO实例介绍驱动开发的具体步骤。
-
基于HDF驱动框架,按照驱动Driver Entry程序,完成Face_auth驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现,详细代码参见face_auth_interface_driver.cpp文件。
// 通过自定义的HdfFaceAuthInterfaceHost对象包含ioService对象和真正的HDI Service实现IRemoteObject对象 struct HdfFaceAuthInterfaceHost {struct IDeviceIoService ioService;OHOS::sptr<OHOS::IRemoteObject> stub; };// 服务接口调用响应接口 int32_t FaceAuthInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply) {IAM_LOGI("start");if (client == nullptr || data == nullptr || reply == nullptr || client->device == nullptr ||client->device->service == nullptr) {IAM_LOGE("invalid param");return HDF_ERR_INVALID_PARAM;}auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(client->device->service, struct HdfFaceAuthInterfaceHost, ioService);if (hdfFaceAuthInterfaceHost == nullptr || hdfFaceAuthInterfaceHost->stub == nullptr) {IAM_LOGE("hdfFaceAuthInterfaceHost is invalid");return HDF_ERR_INVALID_PARAM;}OHOS::MessageParcel *dataParcel = nullptr;OHOS::MessageParcel *replyParcel = nullptr;OHOS::MessageOption option;if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) {IAM_LOGE("invalid data sbuf object to dispatch");return HDF_ERR_INVALID_PARAM;}if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) {IAM_LOGE("invalid reply sbuf object to dispatch");return HDF_ERR_INVALID_PARAM;}return hdfFaceAuthInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); }// 初始化接口 int HdfFaceAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject) {IAM_LOGI("start");if (deviceObject == nullptr) {IAM_LOGE("deviceObject is nullptr");return HDF_ERR_INVALID_PARAM;}if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_USERAUTH)) {IAM_LOGE("set face auth hdf class failed");return HDF_FAILURE;}return HDF_SUCCESS; }// Face_auth驱动对外提供的服务绑定到HDF框架 int HdfFaceAuthInterfaceDriverBind(struct HdfDeviceObject *deviceObject) {IAM_LOGI("start");if (deviceObject == nullptr) {IAM_LOGE("deviceObject is nullptr");return HDF_ERR_INVALID_PARAM;}auto *hdfFaceAuthInterfaceHost = new (std::nothrow) HdfFaceAuthInterfaceHost;if (hdfFaceAuthInterfaceHost == nullptr) {IAM_LOGE("failed to create HdfFaceAuthInterfaceHost object");return HDF_FAILURE;}hdfFaceAuthInterfaceHost->ioService.Dispatch = FaceAuthInterfaceDriverDispatch;hdfFaceAuthInterfaceHost->ioService.Open = NULL;hdfFaceAuthInterfaceHost->ioService.Release = NULL;auto serviceImpl = IFaceAuthInterface::Get(true);if (serviceImpl == nullptr) {IAM_LOGE("failed to get of implement service");delete hdfFaceAuthInterfaceHost;return HDF_FAILURE;}hdfFaceAuthInterfaceHost->stub =OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, IFaceAuthInterface::GetDescriptor());if (hdfFaceAuthInterfaceHost->stub == nullptr) {IAM_LOGE("failed to get stub object");delete hdfFaceAuthInterfaceHost;return HDF_FAILURE;}deviceObject->service = &hdfFaceAuthInterfaceHost->ioService;IAM_LOGI("success");return HDF_SUCCESS; }// 释放Face_auth驱动中的资源 void HdfFaceAuthInterfaceDriverRelease(struct HdfDeviceObject *deviceObject) {IAM_LOGI("start");if (deviceObject == nullptr || deviceObject->service == nullptr) {IAM_LOGE("deviceObject is invalid");return;}auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(deviceObject->service, struct HdfFaceAuthInterfaceHost, ioService);if (hdfFaceAuthInterfaceHost == nullptr) {IAM_LOGE("hdfFaceAuthInterfaceHost is nullptr");return;}delete hdfFaceAuthInterfaceHost;IAM_LOGI("success"); }// 注册Face_auth驱动入口数据结构体对象 struct HdfDriverEntry g_faceAuthInterfaceDriverEntry = {.moduleVersion = 1,.moduleName = "drivers_peripheral_face_auth",.Bind = HdfFaceAuthInterfaceDriverBind,.Init = HdfFaceAuthInterfaceDriverInit,.Release = HdfFaceAuthInterfaceDriverRelease, };// 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 HDF_INIT(g_faceAuthInterfaceDriverEntry);
-
实现获取执行器列表接口,详细代码参见face_auth_interface_service.cpp文件。
// 执行器实现类 class AllInOneExecutorImpl : public IAllInOneExecutor { public:AllInOneExecutorImpl(struct ExecutorInfo executorInfo);virtual ~AllInOneExecutorImpl() {}private:struct ExecutorInfo executorInfo_; // 执行器信息 };static constexpr uint16_t SENSOR_ID = 123; // 执行器sensorID static constexpr uint32_t EXECUTOR_TYPE = 123; // 执行器类型 static constexpr size_t PUBLIC_KEY_LEN = 32; // 执行器32字节公钥// 创建HDI服务对象 extern "C" IFaceAuthInterface *FaceAuthInterfaceImplGetInstance(void) {auto faceAuthInterfaceService = new (std::nothrow) FaceAuthInterfaceService();if (faceAuthInterfaceService == nullptr) {IAM_LOGE("faceAuthInterfaceService is nullptr");return nullptr;}return faceAuthInterfaceService; }// 获取V2_0执行器列表实现 int32_t GetExecutorList(std::vector<sptr<IAllInOneExecutor>> &executorList) {IAM_LOGI("interface mock start");for (auto executor : executorList_) {executorList.push_back(executor);}IAM_LOGI("interface mock success");return HDF_SUCCESS; }
-
实现执行器每个功能接口,详细代码参见all_in_one_executor_impl.cpp文件。
// 实现获取执行器信息接口 int32_t AllInOneExecutorImpl::GetExecutorInfo(ExecutorInfo &executorInfo) {IAM_LOGI("interface mock start");executorInfo = executorInfo_;IAM_LOGI("get executor information success");return HDF_SUCCESS; }// 实现执行器注册成功后,获取用户认证框架的公钥信息、获取用户认证框架的模板列表接口。将公钥信息保持,模板列表用于和本地的模板做对账 int32_t AllInOneExecutorImpl::OnRegisterFinish(const std::vector<uint64_t> &templateIdList,const std::vector<uint8_t> &frameworkPublicKey, const std::vector<uint8_t> &extraInfo) {IAM_LOGI("interface mock start");static_cast<void>(templateIdList);static_cast<void>(extraInfo);static_cast<void>(frameworkPublicKey);IAM_LOGI("register finish");return HDF_SUCCESS; }// 实现人脸录入接口 int32_t AllInOneExecutorImpl::Enroll(uint64_t scheduleId, const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj) {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}IAM_LOGI("enroll, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT);int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}return HDF_SUCCESS; }// 实现人脸认证接口 int32_t AllInOneExecutorImpl::Authenticate(uint64_t scheduleId, const std::vector<uint64_t> &templateIdList,const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj) {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);static_cast<void>(templateIdList);static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}IAM_LOGI("authenticate, result is %{public}d", ResultCode::NOT_ENROLLED);int32_t ret = callbackObj->OnResult(ResultCode::NOT_ENROLLED, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}return HDF_SUCCESS; }// 实现人脸识别接口 int32_t AllInOneExecutorImpl::Identify(uint64_t scheduleId, const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj) {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}IAM_LOGI("identify, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT);int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}return HDF_SUCCESS; }// 实现删除人脸模板接口 int32_t AllInOneExecutorImpl::Delete(const std::vector<uint64_t> &templateIdList) {IAM_LOGI("interface mock start");static_cast<void>(templateIdList);IAM_LOGI("delete success");return HDF_SUCCESS; }// 实现通过scheduleId取消指定操作接口 int32_t AllInOneExecutorImpl::Cancel(uint64_t scheduleId) {IAM_LOGI("interface mock start");static_cast<void>(scheduleId);IAM_LOGI("cancel success");return HDF_SUCCESS; }// 实现人脸认证服务向Face_auth驱动传递参数的通用接口,当前需要实现冻结与解锁模板命令 int32_t AllInOneExecutorImpl::SendCommand(int32_t commandId, const std::vector<uint8_t> &extraInfo, const sptr<IExecutorCallback> &callbackObj) {IAM_LOGI("interface mock start");static_cast<void>(extraInfo);if (callbackObj == nullptr) {IAM_LOGE("callbackObj is nullptr");return HDF_ERR_INVALID_PARAM;}int32_t ret;switch (commandId) {case DriverCommandId::LOCK_TEMPLATE:IAM_LOGI("lock template, result is %{public}d", ResultCode::SUCCESS);ret = callbackObj->OnResult(ResultCode::SUCCESS, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}break;case DriverCommandId::UNLOCK_TEMPLATE:IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS);ret = callbackObj->OnResult(ResultCode::SUCCESS, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}break;case DriverCommandId::INIT_ALGORITHM:IAM_LOGI("init algorithm, result is %{public}d", ResultCode::SUCCESS);ret = callbackObj->OnResult(ResultCode::SUCCESS, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}break;default:IAM_LOGD("not support DriverCommandId : %{public}d", commandId);ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {});if (ret != HDF_SUCCESS) {IAM_LOGE("callback result is %{public}d", ret);return HDF_FAILURE;}}return HDF_SUCCESS; }// 实现设置预览流缓冲区接口 int32_t FaceAuthInterfaceService::SetBufferProducer(const sptr<BufferProducerSequenceable> &bufferProducer) {IAM_LOGI("interface mock start set buffer producer %{public}s",UserIam::Common::GetPointerNullStateString(bufferProducer.GetRefPtr()).c_str());return HDF_SUCCESS; }// 实现获取执行器属性接口 int32_t AllInOneExecutorImpl::GetProperty(const std::vector<uint64_t> &templateIdList, const std::vector<int32_t> &propertyTypes, Property &property) {IAM_LOGI("interface mock start");property = {};IAM_LOGI("get property success");return HDF_SUCCESS; }// 实现设置需缓存模板列表接口 int32_t AllInOneExecutorImpl::SetCachedTemplates(const std::vector<uint64_t> &templateIdList) {IAM_LOGI("interface mock start");IAM_LOGI("set cached templates success");return HDF_SUCCESS; }// 实现注册SA命令回调接口 int32_t AllInOneExecutorImpl::RegisterSaCommandCallback(const sptr<ISaCommandCallback> &callbackObj) {IAM_LOGI("interface mock start");IAM_LOGI("register sa command callback success");return HDF_SUCCESS; }
-
用户身份认证框架支持多driver,当增加driver或者修改driver信息,需要修改如下文件中serviceName2Config。
// base/user_iam/face_auth/services/src/face_auth_service.cpp void FaceAuthService::StartDriverManager() {IAM_LOGI("start");int32_t ret = UserAuth::IDriverManager::Start(HDI_NAME_2_CONFIG);if (ret != FACE_AUTH_SUCCESS) {IAM_LOGE("start driver manager failed");} }
调测验证
驱动开发完成后,通过用户认证API接口开发HAP应用,基于RK3568平台验证。认证和取消功能验证的测试代码如下:
1.发起认证并获取认证结果的测试代码如下:
// API version 10import type {BusinessError} from '@ohos.base';import userIAM_userAuth from '@ohos.userIAM.userAuth';// 设置认证参数const authParam: userIAM_userAuth.AuthParam = {challenge: new Uint8Array([49, 49, 49, 49, 49, 49]),authType: [userIAM_userAuth.UserAuthType.PIN, userIAM_userAuth.UserAuthType.FACE],authTrustLevel: userIAM_userAuth.AuthTrustLevel.ATL3,};// 配置认证界面const widgetParam: userIAM_userAuth.WidgetParam = {title: '请进行身份认证',};try {// 获取认证对象let userAuthInstance = userIAM_userAuth.getUserAuthInstance(authParam, widgetParam);console.info('get userAuth instance success');// 订阅认证结果userAuthInstance.on('result', {onResult(result) {console.info(`userAuthInstance callback result: ${JSON.stringify(result)}`);// 可在认证结束或其他业务需要场景,取消订阅认证结果userAuthInstance.off('result');}});console.info('auth on success');userAuthInstance.start();console.info('auth start success');} catch (error) {const err: BusinessError = error as BusinessError;console.error(`auth catch error. Code is ${err?.code}, message is ${err?.message}`);}
2.取消认证的测试代码如下:
// API version 10import type {BusinessError} from '@ohos.base';import userIAM_userAuth from '@ohos.userIAM.userAuth';const authParam: userIAM_userAuth.AuthParam = {challenge: new Uint8Array([49, 49, 49, 49, 49, 49]),authType: [userIAM_userAuth.UserAuthType.PIN, userIAM_userAuth.UserAuthType.FACE],authTrustLevel: userIAM_userAuth.AuthTrustLevel.ATL3,};const widgetParam: userIAM_userAuth.WidgetParam = {title: '请进行身份认证',};try {// 获取认证对象let userAuthInstance = userIAM_userAuth.getUserAuthInstance(authParam, widgetParam);console.info('get userAuth instance success');// 开始认证userAuthInstance.start();console.info('auth start success');// 取消认证userAuthInstance.cancel();console.info('auth cancel success');} catch (error) {const err: BusinessError = error as BusinessError;console.error(`auth catch error. Code is ${err?.code}, message is ${err?.message}`);}
相关文章:
OpenHarmony外设驱动使用 (四),Face_auth
OpenHarmony外设驱动使用 (四) Face_auth 概述 功能简介 人脸识别功能是端侧设备不可或缺的一部分,为设备提供一种用户认证能力,可应用于设备解锁、支付、应用登录等身份认证场景。它是基于人的脸部特征信息进行身份识别的一种…...
【Java ee初阶】jvm(1)
一、JVM Java虚拟机 面试中相关的问题有三块: 1.JVM内存区域划分 2.JVM的类加载机制 3.JVM的垃圾回收机制 JDK、JRE 和 JVM 的关系 JDK(Java Development Kit)是 Java 开发工具包,包含了编写、编译和调试 Java 程序所需的所…...
【Java ee初阶】jvm(2)
类加载机制: JVM从最开始的读取.class文件,到最终构造完成 类 对象的整个过程,也就是把 类 从硬盘 加载到内存中的机制。 Java的类加载机制主要分为五个步骤:加载、验证、准备、解析和初始化。 步骤一 加载(Loading…...
Django 项目创建全攻略
目录 一、环境准备 1. 安装 Python 2. 安装虚拟环境(可选但推荐) 3. 安装 Django 二、创建 Django 项目 1. 使用命令创建项目 2. 运行开发服务器 三、创建 Django 应用 1. 创建应用 2. 注册应用 四、配置项目 1. 数据…...
windows11 安装好后右键没有 git bash 命令
win键 R 键,输出 regedit,打开注册表 找到 \HKEY_CLASSES_ROOT\Directory\Background\shell 新建项 git-bash 然后在 git-bash 下在新建项 Command,默认值设为 "C:\Program Files\Git\git-bash.exe" --cd"%v." 在 …...
Java八股文——Java基础篇
目录 1、你是怎样理解OOP面向对象2、重载和重写的区别3、接口与抽象类的区别4、深拷贝与浅拷贝的理解5、sleep和wait区别主要区别 6、什么是自动拆装箱,int和Integer有什么区别自动拆装箱int和Integer的区别Integer缓存机制 7、和equals区别String特殊情况 8、Strin…...
蓝桥杯19682 完全背包
问题描述 有 N 件物品和一个体积为 M 的背包。第 i 个物品的体积为 vi,价值为 wi。每件物品可以使用无限次。 请问可以通过什么样的方式选择物品,使得物品总体积不超过 M 的情况下总价值最大,输出这个最大价值即可。 输入格式 第一行…...
2025年- H27-Lc135- 239.滑动窗口最大值(自定义双端队列)---java版
1.题目描述 2.思路 (1)双端队列可以移除最左边的元素,也可以移除最右边的元素(两端移除) (2)在最右边插入元素(右边加入) (3)队列单调性…...
EKS 工作节点的集群网络架构
AWS EKS(弹性 Kubernetes 服务)是亚马逊提供的托管 Kubernetes 服务,一旦配置完成,即可像变魔术一样运行。但这通常是 EKS 的默认设置。如果您打算根据组织的设计、合规性标准和隐私要求进行自定义,该怎么办࿱…...
【技海登峰】Kafka漫谈系列(十一)SpringBoot整合Kafka之消费者Consumer
【技海登峰】Kafka漫谈系列(十一)SpringBoot整合Kafka之消费者Consumer spring-kafka官方文档: https://docs.spring.io/spring-kafka/docs/2.8.10/reference/pdf/spring-kafka-reference.pdf KafkaTemplate API: https://docs.spring.io/spring-kafka/api/org/springframe…...
Python字符串格式化(一):三种经典格式化方法
文章目录 一、% operator:C语言风格的初代格式化方案(Python 2.0引入)1. 语法核心:占位符与类型码2. 进阶用法:格式修饰符3. 致命缺陷:类型严格匹配的陷阱4. 适用场景:旧代码维护的兼容性选择 二…...
浅谈无服务器WebSocket的优势
实际上,一个实用的解决方案是将构建业务关键型实时平台的复杂性卸载到专门的云服务中。 完全托管的无服务器 WebSocket 解决方案为事件驱动的消息传递提供了基础结构;它使底层基础设施成为一种商品。客户端使用提供程序服务发送/接收低延迟消息,并专注于…...
10.7 LangChain v0.3架构大升级:模块化设计+多阶段混合检索,开发效率飙升3倍!
LangChain v0.3 技术生态与未来发展 关键词:LangChain Chains, Agents 架构, Retrieval Strategy, LangGraph, 模块化设计 3. LangChain 项目:Chains, Agents, Retrieval Strategy LangChain v0.3 通过 Chains-Agents-Retrieval 三位一体的技术栈,构建起完整的大模型应用开…...
GLPK(GNU线性规划工具包)中建模语言MathProg的使用
GNU MathProg是一种用于描述线性数学规划模型的建模语言。用GNU MathProg语言编写的模型描述由一组语句和数据块组成。 在MathProg中,模型以集合、参数、变量、约束和目标(sets, parameters, variables, constraints, objectives称为模型对象)的形式进行描述。 在Ma…...
系统思考:IT企业项目困境分析
最近遇到一家快速发展的IT技术公司,遭遇了项目进度滞后、团队沟通不畅和资源分配不合理等一系列挑战。虽然他们拥有一支技术强大的团队,但在项目管理和团队协作上却显得力不从心。结果,多个项目超预算、交期延迟,客户满意度直线下…...
计算机网络 - 2.基础协议
1.TCP协议 1.TCP(Transmission Control Protocol):传输控制协议2.TCP协议是一种面向连接的、可靠的、 基于字节流的传输层通信协议 1.面向连接:两个使用TCP协议的应用(通常一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接2.可靠的 1.数据传输之前都要建立…...
go语法大赏
前些日子单机房稳定性下降,找了好一会才找到真正的原因。这里面涉及到不少go语法细节,正好大家一起看一下。 一、仿真代码 这是仿真之后的代码 package mainimport ("fmt""go.uber.org/atomic""time" )type StopSignal…...
运行vscode编辑器源码
距离上次二次开发vscode已经是三年前的事了,当时是1.60.0版本,目前vscode已升级到了1.99.2版本,里面改动很大,最近下载下来了新版源码跑起来看看 准备node、python 源码里面node版本做了限制 2025-01-27 09:53:00.450 [info] Fo…...
ShenNiusModularity项目源码学习(26:ShenNius.Admin.Mvc项目分析-11)
本文学习并分析ShenNiusModularity项目中商城系统模块的小程序用户页面、用户收货地址页面。 1、小程序用户页面 小程序用户页面用于检索、浏览使用商城系统的用户数据(保存在shop_appuser表内,系统用户保存在sys_user表内),该页…...
C#中的成员常量:编译时的静态魔法
在C#编程中,常量(const)是一个强大而特殊的语言特性,特别是当它们作为类的成员时。本文将深入探讨成员常量的特性、使用场景以及与静态量的区别。 成员常量的基本特性 成员常量是声明在类内部的常量,具有以下核心特点: 声明位置…...
C# 深入理解类(成员常量)
成员常量 成员常量类似前一章所述的局部常量,只是它们被声明在类声明中而不是方法内,如下面的 示例: 与局部常量类似,用于初始化成员肯量的值在编译时必须是可计算的,而且通常是一个预定 义简单类型或由它们组成的表达…...
服务端HttpServletRequest、HttpServletResponse、HttpSession
一、概述 在JavaWeb 开发中,获取客户端传递的参数至关重要。http请求是客户端向服务端发起数据传输协议,主要包含包含请求行、请求头、空行和请求体四个部分,在这四部分中分别携带客户端传递到服务端的数据。常见的http请求方式有get、post、…...
有哪些GIF图片转换的开源工具
以下是关于GIF图片转换的开源工具的详细总结,涵盖功能特点、适用场景及用户评价: 1. FFmpeg 功能特点: 作为开源命令行工具,FFmpeg支持视频转GIF、调整帧率、分辨率、截取片段等操作,可通过脚本批量处理。适用场景: 适合开发者或技术用户进行高效批处理,常用于服务器端自…...
Vue.js教学第五章:计算属性与侦听器详解
Vue.js 之计算属性与侦听器详解 一、计算属性 (一)概念 计算属性(Computed Properties)是 Vue.js 中的一个核心概念。它允许我们基于一个或多个数据属性来定义一个新的属性,该属性的值会根据其依赖的数据属性的变化而自动更新。这就好像是一个 “智能” 属性,它的值不是…...
第三章:UI 系统架构拆解与动态界面管理实录
还记得我们第二章刚跑通主场景,那时候是不是觉得“终于见到界面了”?但请等等,你看到的只是冰山一角,下面藏着的是 UIManager 的地狱之门。 本章我们将深入探讨: UI 界面如何加载(Prefab 动态加载机制&…...
第四章:WebSocket 通信机制全解与客户端发包实录
如果你以为一个游戏启动后只靠本地逻辑运转,那你真是低估了网络通信的存在感。在互动组件项目里,WebSocket 才是真正的灵魂通道——UI 再好看,没包发出去也等于白搭。 本章我们深入讲解 WebSocket 在安卓前端项目中的应用,从封装结…...
pnpm项目内网迁移
pnpm项目内网迁移 文章目录 pnpm项目内网迁移0.前言1.基础环境安装2.构建pnpm离线安装包3.使用pnpm重新安装项目依赖4.项目迁移参考链接: 0.前言 要将一个依赖pnpm的项目迁移到内网离线环境下进行开发。 1.基础环境安装 要保证NodeJS版本一致,否则执行…...
C++23 放宽范围适配器以允许仅移动类型(P2494R2)
文章目录 引言背景与动机提案内容与实现细节提案 P2494R2实现细节编译器支持 对开发者的影响提高灵活性简化代码向后兼容性 示例代码总结 引言 C23 标准中引入了许多重要的改进,其中一项值得关注的特性是放宽范围适配器(range adaptors)以允…...
[ctfshow web入门] web119
信息收集 import requestsurl "http://51a7e437-2e66-4742-bbfe-e4cce44e360b.challenge.ctf.show/" for i in range(255):data {"code": f"{chr(i)}"}response requests.post(url, datadata)# print(len(response.text))# print(response.t…...
vector--OJ3
链接: [link](链接: link) class Solution { public: vector<string> str{ "","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz" };void CB(string& …...
第6章 实战案例:基于 STEVAL-IDB011V1 板级 CI/CD 全流程
在前五章中,我们完成了嵌入式 CI/CD 从环境搭建、编译自动化、测试自动化、发布分发到监控回归的全技术链条。本章将以 STEVAL-IDB011V1(搭载 BlueNRG-355)评估板为实战载体,手把手演示如何在 GitLab CI(或 Jenkins)上,构建一条从 Git Push → 编译 → 测试 → 刷写 → …...
逆变器的输出外特性分析
VSG 并网状态下,考虑到弱电网下线路阻抗不能忽略,VSG 的功率传输模型可以表示为 VSG 的输出电压经过线路阻抗后串联至电网(考虑滤波电感,且忽略滤波电容作用)。设 U 为 VSG 输出电压的幅值,Ug为电网电压的幅…...
如何给PSCAD添加库文件
1、点击Options 2、选择蓝色的选项 3、查看Intel(R) Visual Fortran Compiler XE 的版本 4、打开原文件的Library 5、打开 6、点击这个文件的右键 7、然后选择第一项project setting 9、先把第8步中link里面原有的路径删除,再点browes[A1] ,然后选择 [A…...
基于simulink搭建的模块化多电平MMC仿真模型
1. 模块化多电平换流器的运行原理 1.1模块化多电平换流器的拓扑结构 MMC共由6个桥臂构成。其中每个桥臂由若干个串联且结构相同的子模块(Sub-Module, SM)与一个电抗器L串联…...
基于simulink的LCC-HVDC输电模型
1.本模型基于simulink搭建常规直流输电模型,运行稳定。 有需要交流或者模型的可在CSDN上留言...
PWM整流器双闭环PI参数的整定
1. 整流侧电路拓扑图 整流电路由交流侧电源、线路电阻、线路电抗、整流器、滤波电容以及负载组成。整体电路图如图1所示。 图 1 整流电路拓扑图 其中,IGBT的开关状函数如下式所示 根据图 1 列出 KVL 公式如下,电流的正方向是从左到右 对于每一相的IGBT相…...
柔性直流输电系统介绍及simulink模型的搭建
1. 柔性直流输电的运行原理 柔性直流输电系统由电压源型换流器与直流输电线路构成, 图 1‑1所示为单端电压源型换流器原理图。柔性直流输电系统的功率可以双向流动,即换流器既可以作为整流站将从交流系统接收的功率通过直流线路输送出去,也可以作为逆变站将通过直流…...
matlab求矩阵的逆、行列式、秩、转置
inv - 计算矩阵的逆 用途:计算一个可逆矩阵的逆矩阵。 D [1, 2; 3, 4]; % 定义一个2x2矩阵 D_inv inv(D); % 计算矩阵D的逆 disp(D_inv);det - 计算矩阵的行列式 用途:计算方阵的行列式。 E [1, 2; 3, 4]; determinant det(E); % 计算行列式 disp…...
如何在纷杂的环境当中保持保持独立思考能力?
引言 当你在人群当中时,你是否经常容易受到身边人的干扰,而丢失自己原有的想法,其实在环境纷杂当中,我们很容易产生“从众效应”而丢失了自己对这件事独立思考的能力。拥有不受外界影响,独立思考的能力可以让我们更加…...
Linux:计算机的层状结构
1.冯诺依曼体系结构 我们常见的计算机,如笔记本、台式机。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系结构。 CPU:运算器和控制器组成。运算器主要工作是做算术运算和逻辑运算。控制器主要工作是协调设备之间信息流动的…...
注册表设置windows背景护眼色
方法一: CtrlR,输入regedit打开注册表 HKEY_CURRENT_USER\Control Panel\Colors 右侧窗口Windows键值由255 255 255改为202 234 206。 方法二: 还是注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\DefaultColo…...
《算法导论(第4版)》阅读笔记:p82-p82
《算法导论(第4版)》学习第 17 天,p82-p82 总结,总计 1 页。 一、技术总结 1. Matrix Matrices(矩阵) (1)教材 因为第 4 章涉及到矩阵,矩阵属于线性代数(linear algebra)范畴,如果不熟悉,可以看一下作者推荐的两本…...
政府数据开放试点企业如何抢占特许经营协议黄金席位
首席数据官高鹏律师团队 《中共中央办公厅 国务院办公厅关于 加快公共数据资源开发利用的意见》的落地,标志着数据从“封闭管理的行政资源”正式转变为“可流通的市场要素”。但机遇与风险从来是一枚硬币的两面——特许经营协议的黄金席位背后,隐藏着…...
Spring之Bean的初始化 Bean的生命周期 全站式解析
目录 导图 步骤 第一步 实例化 第二步 属性赋值 第三步 初始化 aware 接口 BeanPostProcessor 接口 InitializingBean 和 init-method 第四步使用 第五步使用后销毁 描述一下 Bean 的 生命周期 导图 步骤 总体上可以分为五步 首先是 Bean 的实例化Bean 在进行实例…...
exit耗时高
背景:程序退出发现被强制退出,而不是正常的退出。正常退出是发送15信号,而异常退出是发送信号9,强制退出。退出机制是先发送信号15,然后6s内没有退出完成,会发送信号9。通过查看退出流程,是将初…...
密文搜索-map容器+substr
https://www.luogu.com.cn/problem/P8630 ///因为密码是打乱顺序的,所以只要字母个数对上就行 ///用map存字母种类和个数 ///vector存每行密码 ///不用set,每行独立 再考察一个字符串分割函数substr,map自动比较 #include<bits/stdc.h…...
从专家编码到神经网络学习:DTM 的符号操作新范式
1st author: Paul Soulos paper: Differentiable Tree Operations Promote Compositional Generalization ICML 2023 code: psoulos/dtm: Differentiable Tree Machine 1. 问题与思路 现代深度学习在连续向量空间中取得了巨大成功,然而在处理具有显式结构&#x…...
江协科技GPIO输入输出hal库实现
首先先介绍一下GPIO在hal库里面的函数 GPIOhal库函数介绍 GPIO在hal库里面有两个文件,一个hal_gpio.h一个hal_gpio_ex.h 第一个文件主要存放的就是hal库里面对gpio的相关函数以及GPIO配置的结构体,还有hal库与标准库的一大区别回调函数。以及一些对gp…...
软件设计师教程—— 第二章 程序设计语言基础知识(上)
前言 在竞争激烈的就业市场中,证书是大学生求职的重要加分项。中级软件设计师证书专业性强、认可度高,是计算机相关专业学生考证的热门选择,既能检验专业知识,又有助于职业发展。本教程将聚焦核心重点,以点带面构建知…...
Java 快速转 C# 教程
以下是一个针对 Java 开发者快速转向 C# 的简明教程,重点对比 Java 与 C# 的异同,帮助你快速上手。 项目结构: .sln :解决方案文件,管理多个项目之间的依赖关系。.csproj :项目文件,定义目标框…...