From 482c166ade49c093c32f6d715ce6b10ad2ded3e0 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Thu, 25 Jul 2024 19:48:28 +0800 Subject: [PATCH 01/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E4=B8=AD=E8=B5=84=E6=BA=90=E4=B8=8B=E8=BD=BDftp=E9=93=BE?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- src/cli/h2dts/h2dts_README_ZH.md | 2 +- src/cli/h2sa/docs/usage/h2sa_INSTRUCTION_ZH.md | 6 +----- src/tool/api/docs/scan_DEVELOP_ZH.md | 12 ++++++++---- src/tool/api/scan_README_ZH.md | 6 ++++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/cli/h2dts/h2dts_README_ZH.md b/src/cli/h2dts/h2dts_README_ZH.md index 6ca97837..689e6c9f 100644 --- a/src/cli/h2dts/h2dts_README_ZH.md +++ b/src/cli/h2dts/h2dts_README_ZH.md @@ -24,7 +24,7 @@ pip install CppHeaderParser npm i stdio -4.将待转换的文件tsTest.h拷贝到napi_generator/src/cli/h2dts/src/tsGen下;TsGenTest.h文件如下所示: +4.将待转换的文件TsGenTest.h拷贝到napi_generator/src/cli/h2dts/src/tsGen下;TsGenTest.h文件如下所示: ``` #ifndef TSGENTEST_H diff --git a/src/cli/h2sa/docs/usage/h2sa_INSTRUCTION_ZH.md b/src/cli/h2sa/docs/usage/h2sa_INSTRUCTION_ZH.md index c11a89ce..47134ff4 100644 --- a/src/cli/h2sa/docs/usage/h2sa_INSTRUCTION_ZH.md +++ b/src/cli/h2sa/docs/usage/h2sa_INSTRUCTION_ZH.md @@ -13,11 +13,7 @@ h2sa工具,即SERVICE框架生成工具,该工具支持命令行和VS Code 下载python脚本可执行程序header_parser.exe(linux系统为header_parser),下载链接如下: -[下载链接1](http://ftpkaihongdigi.i234.me:5000/sharing/kBG1c7CvT) - -[下载链接2](http://ftp.kaihong.com:5000/sharing/kBG1c7CvT) - -[下载链接3](http://ftp.kaihongdigi.com:5000/sharing/kBG1c7CvT) +// 下载header_parser工具 从发行版下载的链接 获取命令行可执行程序service-gen-win.exe、service-gen-linux,用户可根据以下步骤生成命令行可执行程序: diff --git a/src/tool/api/docs/scan_DEVELOP_ZH.md b/src/tool/api/docs/scan_DEVELOP_ZH.md index 3bd5c64e..ccccc1e1 100644 --- a/src/tool/api/docs/scan_DEVELOP_ZH.md +++ b/src/tool/api/docs/scan_DEVELOP_ZH.md @@ -13,9 +13,11 @@ #### 开发步骤 ##### Linux -1.下载Andr_N_Games_api.xlsx文件,并放置在napi_generator/src/tool/api/src文件夹下,下载链接如下: +1.下载Andr_N_Games_api.xlsx文件,下载链接如下: -//待增加链接 +https://gitee.com/openharmony/napi_generator/releases/download/napigen_resource/napi_resouce.zip + +下载之后解压,进入scan/depend目录下获取Andr_N_Games_api.xlsx文件,并放置在napi_generator/src/tool/api/src文件夹下。 2.安装typescript:在napi_generator/src/tool/api/src目录下执行命令: @@ -54,9 +56,11 @@ ##### Windows -1.下载Andr_N_Games_api.xlsx文件,并放置在napi_generator/src/tool/api/src文件夹下,下载链接如下: +1.下载Andr_N_Games_api.xlsx文件,下载链接如下: + +https://gitee.com/openharmony/napi_generator/releases/download/napigen_resource/napi_resouce.zip -//待增加链接 +下载之后解压,进入scan/depend目录下获取Andr_N_Games_api.xlsx文件,并放置在napi_generator/src/tool/api/src文件夹下。 2.使用管理员身份进入终端: diff --git a/src/tool/api/scan_README_ZH.md b/src/tool/api/scan_README_ZH.md index bd352c7f..2a76797c 100644 --- a/src/tool/api/scan_README_ZH.md +++ b/src/tool/api/scan_README_ZH.md @@ -11,9 +11,11 @@ scan工具可以扫描三方库中包含OpenHarmony源码不包含的接口, ## 使用方法 -1.下载Andr_N_Games_api.xlsx文件,并放置在napi_generator/src/tool/api/src文件夹下,下载链接如下: +1.下载Andr_N_Games_api.xlsx文件,下载链接如下: -// todo +https://gitee.com/openharmony/napi_generator/releases/download/napigen_resource/napi_resouce.zip + +下载之后解压,进入scan/depend目录下获取Andr_N_Games_api.xlsx文件,并放置在napi_generator/src/tool/api/src文件夹下。 2.安装typescript:在napi_generator/src/tool/api/src目录下执行命令: -- Gitee From 16276379e013868234a9a75b9ab524390838a110 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Thu, 25 Jul 2024 20:02:13 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0h2hdf=E6=BA=90=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/package.json | 34 +++ src/cli/h2hdf/src/generate.js | 285 ++++++++++++++++++ src/cli/h2hdf/src/main.js | 34 +++ .../HcsconfigTemplete/hcsconfigTemplete.gen | 13 + .../IdlInterfaceTemplete/buildgnTemplete.gen | 11 + .../bundlejsonTemplete.gen | 62 ++++ .../idlInterfaceTemplete.gen | 5 + .../DumpExampleTemplete/buildgnTemplete.gen | 51 ++++ .../DumpExampleTemplete/dumpCTemplete.gen | 114 +++++++ .../DumpExampleTemplete/dumpHTemplete.gen | 37 +++ .../HdiServiceTemplete/buildgnTemplete.gen | 98 ++++++ .../HdiServiceTemplete/driverTemplete.gen | 115 +++++++ .../HdiServiceTemplete/logHTemplete.gen | 26 ++ .../HdiServiceTemplete/serviceCppTemplete.gen | 29 ++ .../HdiServiceTemplete/serviceHTemplete.gen | 23 ++ .../PeripheralTemplete/buildgnTemplete.gen | 18 ++ .../PeripheralTemplete/bundlejsonTemplete.gen | 46 +++ src/cli/h2hdf/src/templete/framework.json | 24 ++ 18 files changed, 1025 insertions(+) create mode 100644 src/cli/h2hdf/package.json create mode 100644 src/cli/h2hdf/src/generate.js create mode 100644 src/cli/h2hdf/src/main.js create mode 100644 src/cli/h2hdf/src/templete/HcsconfigTemplete/hcsconfigTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/PeripheralTemplete/bundlejsonTemplete.gen create mode 100644 src/cli/h2hdf/src/templete/framework.json diff --git a/src/cli/h2hdf/package.json b/src/cli/h2hdf/package.json new file mode 100644 index 00000000..04b9082f --- /dev/null +++ b/src/cli/h2hdf/package.json @@ -0,0 +1,34 @@ +{ + "name": "hdf-gen", + "version": "1.0.0", + "description": "", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "stdio": "^2.1.3", + "typescript": "^4.9.5" + }, + "bin": "./src/main.js", + "pkg": { + "assets": [ + "./src/templete/HcsconfigTemplete/hcsconfigTemplete.gen", + "./src/templete/IdlInterfaceTemplete/buildgnTemplete.gen", + "./src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen", + "./src/templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen", + "./src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen", + "./src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen", + "./src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen", + "./src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen", + "./src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen", + "./src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen", + "./src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen", + "./src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen", + "./src/templete/PeripheralTemplete/buildgnTemplete.gen", + "./src/templete/PeripheralTemplete/bundlejsonTemplete.gen" + ] + } +} diff --git a/src/cli/h2hdf/src/generate.js b/src/cli/h2hdf/src/generate.js new file mode 100644 index 00000000..5495a774 --- /dev/null +++ b/src/cli/h2hdf/src/generate.js @@ -0,0 +1,285 @@ +/* +* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the 'License'); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an 'AS IS' BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const path = require('path'); +const fs = require('fs'); + +/** + * 获取Json文件内容 + * @returns + */ +function getJsonCfg(jsonFilePath) { + let jsonCfg = null; // json 配置文件 + let jsonFile = fs.readFileSync(jsonFilePath, { encoding: 'utf8' }); + jsonCfg = JSON.parse(jsonFile); + return jsonCfg; +} + +function replaceAll(s, sfrom, sto) { + while (s.indexOf(sfrom) >= 0) { + s = s.replace(sfrom, sto); + } + return s; +} + +// 创建路径 +function createDirectorySync(directoryPath) { + try { + if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath, { recursive: true }); + } + } catch (err) { + console.error(`无法创建文件夹 ${directoryPath}: ${err}`); + } +} + +function pathJoin(...args) { + return path.join(...args); +} + +// 写文件 +function writeFile(fn, str) { + fs.writeFileSync(fn, str, { encoding: 'utf8' }); +} + +function utf8ArrayToStr(array) { + let char2, char3; + + let outStr = ''; + let len = array.length; + let i = 0; + while (i < len) { + let ch = array[i++]; + switch (ch >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + outStr += String.fromCharCode(ch); + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + outStr += String.fromCharCode(((ch & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + outStr += String.fromCharCode(((ch & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + break; + } + } + + return outStr; +} + +function readFile(fn) { + if (!fs.existsSync(fn)) { + return ''; + } + let data = fs.readFileSync(fn); + data = utf8ArrayToStr(data); + return data; +} + +/* 根据用户输入的driver名字生成framework框架 + * drivername:用户输入的驱动名,out:生成框架路径 + * 1. 读取json文件模板 + * 2. 替换模板中的名字并写文件输出 + */ +function genDriverFramework(driverName, out = '') { + // 读取Json文件,获取各模板路径 + let frameworkJsonPath = path.join(__dirname, './templete/framework.json'); + let frameworkJson = getJsonCfg(frameworkJsonPath); + + let frameworkPath = pathJoin(out, 'hdf'); + + let namespaceName = driverName.substring(0,1).toUpperCase() + driverName.substring(1, driverName.length); + let idlFileName = 'I' + namespaceName + 'Interface'; + let rootInfo = { + 'driverName': driverName, + 'namespaceName': namespaceName, + 'idlFileName': idlFileName, + } + + // 生成Hcs配置文件 + genHcsconfigFile(frameworkJson, driverName, frameworkPath); + + // 生成Idl接口 + genInterface(frameworkPath, frameworkJson, rootInfo); + + // 生成hdi_service + genPeripheral(frameworkPath,frameworkJson, rootInfo); +} + +function genPeripheral(frameworkPath, frameworkJson, rootInfo) { + + let peripheralPath = pathJoin(frameworkPath, 'Peripheral/' + rootInfo.driverName); + createDirectorySync(peripheralPath); + + // dump文件路径:dump.c 与 BUILD.gn + // build.gn out/hdf/Peripheral/xxx/hal/BUILD.gn + genExampleDumpfile(peripheralPath, frameworkJson, rootInfo); + + // hdi路径 + genHdiService(peripheralPath, frameworkJson, rootInfo); + + // 日志文件路径 out/hdf/Peripheral/xxx/utils/interface/xxx_log.h + genLogFile(peripheralPath, rootInfo, frameworkJson); + + // 生成编译文件bundle.json和BUILD.gn文件 + genBuildFile(peripheralPath, frameworkJson, rootInfo); +} + +function genBuildFile(peripheralPath, frameworkJson, rootInfo) { + // out/hdf/Peripheral/xxx/bundle.json + let genBundlejsonPath = pathJoin(peripheralPath, 'bundle.json'); + let bundlejsonPath = path.join(__dirname, frameworkJson.PeripheralTemplete.bundlejsonTemplete); + let bundlejsonContent = readFile(bundlejsonPath); + bundlejsonContent = replaceAll(bundlejsonContent, '[driver_name]', rootInfo.driverName); + writeFile(genBundlejsonPath, bundlejsonContent); + // out/hdf/Peripheral/xxx/BUILD.gn + let genBuildgnPath = pathJoin(peripheralPath, 'BUILD.gn'); + let buildgnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.buildgnTemplete); + let buildgnContent = readFile(buildgnPath); + buildgnContent = replaceAll(buildgnContent, '[driver_name]', rootInfo.driverName); + writeFile(genBuildgnPath, buildgnContent); +} + +function genLogFile(peripheralPath, rootInfo, frameworkJson) { + let genLogPath = pathJoin(peripheralPath, 'utils/interface'); + createDirectorySync(genLogPath); + let genLogFile = pathJoin(genLogPath, rootInfo.driverName + '_log.h'); + let logPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.logHTemplete); + let logContent = readFile(logPath); + logContent = replaceAll(logContent, '[upper_driver_name]', rootInfo.driverName.toUpperCase()); + writeFile(genLogFile, logContent); +} + +function genHdiService(peripheralPath, frameworkJson, rootInfo) { + let hdiPath = pathJoin(peripheralPath, 'hdi_service'); + let driverInterName = rootInfo.namespaceName + 'Interface'; + // 创建hdi_service文件夹 + createDirectorySync(hdiPath); + // out/hdf/Peripheral/xxx/hdi_service/xxx_interface_driver.cpp + let genHdiDriverPath = pathJoin(hdiPath, rootInfo.driverName + '_interface_driver.cpp'); + let driverPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.driverTemplete); + let driverContent = readFile(driverPath); + driverContent = replaceAll(driverContent, '[driver_name]', rootInfo.driverName); + driverContent = replaceAll(driverContent, '[driver_idl_name]', rootInfo.idlFileName); + driverContent = replaceAll(driverContent, '[driver_inter_name]', driverInterName); + driverContent = replaceAll(driverContent, '[driver_namespace_name]', rootInfo.namespaceName); + writeFile(genHdiDriverPath, driverContent); + + // out/hdf/Peripheral/xxx/hdi_service/xxx_interface_service.cpp + let genHdiServiceCppPath = pathJoin(hdiPath, rootInfo.driverName + '_interface_service.cpp'); + let serviceCppPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.serviceCppTemplete); + let serviceCppContent = readFile(serviceCppPath); + serviceCppContent = replaceAll(serviceCppContent, '[driver_name]', rootInfo.driverName); + serviceCppContent = replaceAll(serviceCppContent, '[driver_idl_name]', rootInfo.idlFileName); + serviceCppContent = replaceAll(serviceCppContent, '[driver_inter_name]', driverInterName); + serviceCppContent = replaceAll(serviceCppContent, '[driver_namespace_name]', rootInfo.namespaceName); + writeFile(genHdiServiceCppPath, serviceCppContent); + + // out/hdf/Peripheral/xxx/hdi_service/xxx_interface_service.h + let genHdiServiceHPath = pathJoin(hdiPath, rootInfo.driverName + '_interface_service.h'); + let serviceHPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.serviceHTemplete); + let serviceHContent = readFile(serviceHPath); + serviceHContent = replaceAll(serviceHContent, '[driver_name]', rootInfo.driverName); + serviceHContent = replaceAll(serviceHContent, '[driver_idl_name]', rootInfo.idlFileName); + serviceHContent = replaceAll(serviceHContent, '[driver_inter_name]', driverInterName); + serviceHContent = replaceAll(serviceHContent, '[driver_namespace_name]', rootInfo.namespaceName); + serviceHContent = replaceAll(serviceHContent, '[upper_driver_name]', rootInfo.driverName.toUpperCase()); + writeFile(genHdiServiceHPath, serviceHContent); + + // 生成hdi_service下面的BUILD.gn: out/hdf/Peripheral/xxx/hdi_service/ + let genHdiServiceGnPath = pathJoin(hdiPath, 'BUILD.gn'); + let serviceGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.buildgnTemplete); + let serviceGnContent = readFile(serviceGnPath); + serviceGnContent = replaceAll(serviceGnContent, '[driver_name]', rootInfo.driverName); + writeFile(genHdiServiceGnPath, serviceGnContent); +} + +function genExampleDumpfile(peripheralPath, frameworkJson, rootInfo) { + let dumpExamplePath = pathJoin(peripheralPath, 'hal'); + createDirectorySync(dumpExamplePath); + let genDumpExampleGnPath = pathJoin(dumpExamplePath, 'BUILD.gn'); + let dumpExampleGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.buildgnTemplete); + let dumpExampleGnContent = readFile(dumpExampleGnPath); + dumpExampleGnContent = replaceAll(dumpExampleGnContent, '[driver_name]', rootInfo.driverName); + writeFile(genDumpExampleGnPath, dumpExampleGnContent); + + // dump.c out/hdf/Peripheral/xxx/hal/xxx_dump.c + let genDumpCExamplePath = pathJoin(dumpExamplePath, rootInfo.driverName + '_dump.c'); + let dumpCExamplePath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.dumpCTemplete); + let dumpCExampleContent = readFile(dumpCExamplePath); + dumpCExampleContent = replaceAll(dumpCExampleContent, '[driver_name]', rootInfo.driverName); + dumpCExampleContent = replaceAll(dumpCExampleContent, '[driver_func_name]', rootInfo.namespaceName); + writeFile(genDumpCExamplePath, dumpCExampleContent); + // dump.h out/hdf/Peripheral/xxx/hal/include/xxx_dump.h + let genDumpHExamplePath = pathJoin(dumpExamplePath, 'include'); + createDirectorySync(genDumpHExamplePath); + let dumpHExamplePath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.dumpHTemplete); + let dumpHExampleContent = readFile(dumpHExamplePath); + dumpHExampleContent = replaceAll(dumpHExampleContent, '[driver_name_toupper]', rootInfo.driverName.toUpperCase()); + dumpHExampleContent = replaceAll(dumpHExampleContent, '[driver_func_name]', rootInfo.namespaceName); + let genDumpHExampleFile = pathJoin(genDumpHExamplePath, rootInfo.driverName + '_dump.h'); + writeFile(genDumpHExampleFile, dumpHExampleContent); +} + +function genInterface(frameworkPath, frameworkJson, rootInfo) { + // idl文件路径 out/hdf/IdlInterface/foo/v1_0/IXxxInterface.idl + let idlPath = pathJoin(frameworkPath, 'IdlInterface/' + rootInfo.driverName); + let genIdlFilepath = pathJoin(idlPath, 'v1_0/'); + createDirectorySync(genIdlFilepath); + let idlFilepath = path.join(__dirname, frameworkJson.IdlInterfaceTemplete.idlInterfaceTemplete); + let idlFileContent = readFile(idlFilepath); + idlFileContent = replaceAll(idlFileContent, '[driver_name]', rootInfo.driverName); + idlFileContent = replaceAll(idlFileContent, '[driver_idl_name]', rootInfo.idlFileName); + let genIdlFile = pathJoin(genIdlFilepath, rootInfo.idlFileName + '.idl'); + writeFile(genIdlFile, idlFileContent); + + // idl接口bundlejson路径 out/hdf/IdlInterface/foo/bundle.json + let genIdlBundlejsonPath = pathJoin(idlPath, 'bundle.json'); + let idlBundlejsonPath = path.join(__dirname, frameworkJson.IdlInterfaceTemplete.bundlejsonTemplete); + let idlBundlejsonContent = readFile(idlBundlejsonPath); + idlBundlejsonContent = replaceAll(idlBundlejsonContent, '[driver_name]', rootInfo.driverName); + writeFile(genIdlBundlejsonPath, idlBundlejsonContent); + + // idl接口BUILD.gn路径 out/hdf/IdlInterface/foo/v1_0/BUILD.gn + let genIdlBuildgnPath = pathJoin(genIdlFilepath, 'BUILD.gn'); + let idlBuildgnPath = path.join(__dirname, frameworkJson.IdlInterfaceTemplete.buildgnTemplete); + let idlBuildgnContent = readFile(idlBuildgnPath); + idlBuildgnContent = replaceAll(idlBuildgnContent, '[driver_name]', rootInfo.driverName); + idlBuildgnContent = replaceAll(idlBuildgnContent, '[driver_idl_name]', rootInfo.idlFileName); + writeFile(genIdlBuildgnPath, idlBuildgnContent); +} + +function genHcsconfigFile(frameworkJson, driverName, frameworkPath) { + // 读取hcs配置文件模板 hcs配置文件 + let hcsconfigPath = path.join(__dirname, frameworkJson.HcsconfigTemplete); + let hcsconfigContent = readFile(hcsconfigPath); + // 生成hcs config文件内容: 根据用户输入的drivername配置hcs文件 + hcsconfigContent = replaceAll(hcsconfigContent, '[driver_name]', driverName); + // 生成device_info.hcs 配置文件路径 out/hdf/HcsConfig/device_info.hcs + let genHcsconfigPath = pathJoin(frameworkPath, 'HcsConfig'); + createDirectorySync(genHcsconfigPath); + let genHcsconfigFile = pathJoin(genHcsconfigPath, 'device_info.hcs'); + writeFile(genHcsconfigFile, hcsconfigContent); +} + +module.exports = { + genDriverFramework +}; \ No newline at end of file diff --git a/src/cli/h2hdf/src/main.js b/src/cli/h2hdf/src/main.js new file mode 100644 index 00000000..4423ca5e --- /dev/null +++ b/src/cli/h2hdf/src/main.js @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the 'License'); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an 'AS IS' BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const stdio = require('stdio'); +const main = require('./generate'); + +let ops = stdio.getopt({ + // 输入driver name ,输入一个字符串,默认为hello + 'drivername': { key: 'n', args: 1, description: 'driver name', default: 'hello' }, + // 输出文件夹路径 + 'out': { key: 'o', args: 1, description: 'output directory', default: '.' }, +}); + +let drivername = ops.drivername; +// 若drivername不为空,则生成,否则打印错误信息 +if (drivername.trim().length !== 0 && checkInput(drivername)) { + main.genDriverFramework(drivername); +} + +function checkInput(input) { + const regex = /\b[a-zA-Z_][a-zA-Z0-9_]*\b/; + return regex.test(input); +} diff --git a/src/cli/h2hdf/src/templete/HcsconfigTemplete/hcsconfigTemplete.gen b/src/cli/h2hdf/src/templete/HcsconfigTemplete/hcsconfigTemplete.gen new file mode 100644 index 00000000..fc2ff6a5 --- /dev/null +++ b/src/cli/h2hdf/src/templete/HcsconfigTemplete/hcsconfigTemplete.gen @@ -0,0 +1,13 @@ +[driver_name] :: host { + hostName = "[driver_name]_host"; + priority = 50; + [driver_name]_device :: device { + device0 :: deviceNode { + preload = 0; + policy = 2; + priority = 100; + moduleName = "lib[driver_name]_driver.z.so"; + serviceName = "[driver_name]_interface_service"; + } + } +} \ No newline at end of file diff --git a/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen new file mode 100644 index 00000000..d6627116 --- /dev/null +++ b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen @@ -0,0 +1,11 @@ +import("//drivers/hdf_core/adapter/uhdf2/hdi.gni") # 编译idl必须要导入的模板 +hdi("[driver_name]") { # 目标名称,会生成两个so,分别对应 lib[driver_name]_client_v1.0.z.so 和 lib[driver_name]_stub_v1.0.z.so + # package = "ohos.hdi.[driver_name].v1_0" # 包名,必须与idl路径匹配 + module_name = "[driver_name]_service" # module_name控制dirver文件中驱动描 述符(struct HdfDriverEntry)的moduleName + sources = [ # 参与编译的idl文件 + "[driver_idl_name].idl", # 接口idl + ] + language = "cpp" # 控制idl生成c或c++代码 可选择`c`或`cpp` + subsystem_name = "hdf" # 子系统名 + part_name = "drivers_interface_[driver_name]" # 组件名 +} diff --git a/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen new file mode 100644 index 00000000..f38d3b44 --- /dev/null +++ b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen @@ -0,0 +1,62 @@ +{ + "name": "@ohos/drivers_interface_[driver_name]", + "description": "[driver_name] device driver interface", + "version": "4.1", + "license": "Apache License 2.0", + "publishAs": "code-segment", + "segment": { + "destPath": "drivers/interface/[driver_name]" + }, + "dirs": {}, + "scripts": {}, + "component": { + "name": "drivers_interface_[driver_name]", + "subsystem": "hdf", + "syscap": [], + "adapted_system_type": ["standard"], + "rom": "675KB", + "ram": "1024KB", + "deps": { + "components": [ + "ipc", + "hdf_core", + "hilog", + "c_utils" + ], + "third_party": [] + }, + "build": { + "sub_component": [ + "//drivers/interface/[driver_name]/v1_0:[driver_name]_idl_target" + ], + "test": [ + ], + "inner_kits": [ + { + "name": "//drivers/interface/[driver_name]/v1_0:lib[driver_name]_proxy_1.0", + "header": { + "header_files": [ + ], + "header_base": "//drivers/interface/[driver_name]" + } + }, + { + "name": "//drivers/interface/[driver_name]/v1_0:lib[driver_name]_stub_1.0", + "header": { + "header_files": [ + ], + "header_base": "//drivers/interface/[driver_name]" + } + }, + { + "name": "//drivers/interface/[driver_name]/v1_0:[driver_name]_idl_headers", + "header": { + "header_files": [ + ], + "header_base": "//drivers/interface/[driver_name]" + } + } + ] + } + } + } \ No newline at end of file diff --git a/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen new file mode 100644 index 00000000..fa767b19 --- /dev/null +++ b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen @@ -0,0 +1,5 @@ +package ohos.hdi.[driver_name].v1_0; + +interface [driver_idl_name] { + Helloworld([in] String sendMsg, [out] String recvMsg); +} \ No newline at end of file diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen new file mode 100644 index 00000000..0001c148 --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen @@ -0,0 +1,51 @@ +#Copyright (c) 2022-2023 Huawei Device Co., Ltd. +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +# + +import("//build/ohos.gni") + +config("libhdi_[driver_name]_pub_config") { + visibility = [ ":*" ] +} +ohos_shared_library("hdi_[driver_name]") { + public_configs = [ ":libhdi_[driver_name]_pub_config" ] + sources = [ + "[driver_name]_dump.c", + ] + include_dirs = [ + "include", + "../utils/interface", + "//third_party/bounds_checking_function/include", + ] + cflags = [ + "-Wall", + "-Wextra", + "-Werror", + "-fsigned-char", + "-fno-common", + "-fno-strict-aliasing", + ] + install_images = [ chipset_base_dir ] + subsystem_name = "hdf" + part_name = "drivers_peripheral_[driver_name]" + if (is_standard_system) { + external_deps = [ + "c_utils:utils", + "hdf_core:libhdf_host", + "hdf_core:libhdf_utils", + "hilog:libhilog", + ] + } else { + external_deps = [ "hilog:libhilog" ] + } +} diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen new file mode 100644 index 00000000..56fcb800 --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "[driver_name]_dump.h" +#include +#include +#include "devhost_dump_reg.h" +#include "hdf_base.h" +#include "[driver_name]_log.h" + +#define HDF_LOG_TAG uhdf_[driver_name]_service + +// -c dump the helloworld info +static const char *g_dumpHelp = + " usage:\n" + " -h, --help: dump help\n" + " -c, --channel: dump the [driver_name] channel info\n"; + +static uint32_t ShowHelloworldInfo(struct HdfSBuf *reply) +{ + int32_t ret; + const char *helloWorldMessage = "Hello, World!"; + + ret = HdfSbufWriteString(reply, helloWorldMessage); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: write hello world info failed", __func__); + return HDF_FAILURE; + } + + HDF_LOGI("%{public}s: [driver_name]dump: print hello world !", __func__); + + return HDF_SUCCESS; + +} + +static int32_t Dump[driver_func_name]Channel(struct HdfSBuf *reply) +{ + int32_t ret; + HDF_LOGI("%{public}s: get [driver_name] dump channel begin", __func__); + ret = ShowHelloworldInfo(reply); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: show hello world info failed", __func__); + return HDF_FAILURE; + } + + return HDF_SUCCESS; +} + +static int32_t [driver_func_name]DriverDump(struct HdfSBuf *data, struct HdfSBuf *reply) +{ + uint32_t i; + uint32_t argv = 0; + HDF_LOGI("%{public}s: get [driver_name] dump begin xx", __func__); + if (data == NULL || reply == NULL) { + return HDF_FAILURE; + } + + if (!HdfSbufReadUint32(data, &argv)) { + HDF_LOGE("%{public}s: read argv failed", __func__); + return HDF_FAILURE; + } + + if (argv == 0) { + if (!HdfSbufWriteString(reply, g_dumpHelp)) { + HDF_LOGE("%{public}s: write -h failed", __func__); + return HDF_FAILURE; + } + } + + for (i = 0; i < argv; i++) { + const char *value = HdfSbufReadString(data); + if (value == NULL) { + HDF_LOGE("%{public}s value is invalid", __func__); + return HDF_FAILURE; + } + + if (strcmp(value, "-h") == HDF_SUCCESS) { + if (!HdfSbufWriteString(reply, g_dumpHelp)) { + HDF_LOGE("%{public}s: write -h failed", __func__); + return HDF_FAILURE; + } + continue; + } else if (strcmp(value, "-c") == HDF_SUCCESS) { + Dump[driver_func_name]Channel(reply); + continue; + } + } + + return HDF_SUCCESS; +} + +int32_t Get[driver_func_name]Dump(struct HdfSBuf *data, struct HdfSBuf *reply) +{ + HDF_LOGI("%{public}s: get [driver_name] dump begin", __func__); + int32_t ret = [driver_func_name]DriverDump(data, reply); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: get [driver_name] dump failed", __func__); + return HDF_FAILURE; + } + + return HDF_SUCCESS; +} diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen new file mode 100644 index 00000000..e3cd4d11 --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef [driver_name_toupper]_DUMP_H +#define [driver_name_toupper]_DUMP_H + +#include +#include +#include "hdf_sbuf.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +int32_t Get[driver_func_name]Dump(struct HdfSBuf *data, struct HdfSBuf *reply); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* HDI_[driver_name_toupper]_DUMP_H */ \ No newline at end of file diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen new file mode 100644 index 00000000..3b9c6820 --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen @@ -0,0 +1,98 @@ +# Copyright (c) 2022-2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +HDF_CORE_PATH = "../../../hdf_core" +import("//build/ohos.gni") +import("$HDF_CORE_PATH/adapter/uhdf2/uhdf.gni") + +ohos_shared_library("lib[driver_name]_interface_service_1.0") { + include_dirs = [ + ".", + "../utils/interface", + "../hal/include" + ] + + sources = [ "[driver_name]_interface_service.cpp"] + deps = [ "../hal:hdi_[driver_name]" ] + + cflags = [ + "-Wall", + "-Wextra", + "-Werror", + "-fsigned-char", + "-fno-common", + "-fno-strict-aliasing", + ] + + if (is_standard_system) { + external_deps = [ + "c_utils:utils", + "drivers_interface_[driver_name]:[driver_name]_idl_headers", + "hdf_core:libhdf_host", + "hilog:libhilog", + "hitrace:hitrace_meter", + ] + } else { + external_deps = [ "hilog:libhilog" ] + } + external_deps += [ "ipc:ipc_single" ] + + install_images = [ chipset_base_dir ] + subsystem_name = "hdf" + part_name = "drivers_peripheral_[driver_name]" +} + +ohos_shared_library("lib[driver_name]_driver") { + include_dirs = [ + ] + sources = [ "[driver_name]_interface_driver.cpp" ] + + cflags = [ + "-Wall", + "-Wextra", + "-Werror", + "-fsigned-char", + "-fno-common", + "-fno-strict-aliasing", + ] + + if (is_standard_system) { + external_deps = [ + "c_utils:utils", + "drivers_interface_[driver_name]:lib[driver_name]_stub_1.0", + "hdf_core:libhdf_host", + "hdf_core:libhdf_ipc_adapter", + "hdf_core:libhdf_utils", + "hdf_core:libhdi", + "hilog:libhilog", + "ipc:ipc_single", + ] + } else { + external_deps = [ + "hilog:libhilog", + "ipc:ipc_single", + ] + } + + shlib_type = "hdi" + install_images = [ chipset_base_dir ] + subsystem_name = "hdf" + part_name = "drivers_peripheral_[driver_name]" +} + +group("hdf_[driver_name]_service") { + deps = [ + ":lib[driver_name]_driver", + ":lib[driver_name]_interface_service_1.0", + ] +} diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen new file mode 100644 index 00000000..440777b4 --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include "v1_0/[driver_name]_interface_stub.h" + +#define HDF_LOG_TAG [driver_name]_interface_driver + +using namespace OHOS::HDI::[driver_namespace_name]::V1_0; + +struct Hdf[driver_inter_name]Host { + struct IDeviceIoService ioService; + OHOS::sptr stub; +}; + +// 处理客户端请求的Dispatch方法 +static int32_t [driver_inter_name]DriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, + struct HdfSBuf *reply) +{ + auto *hdf[driver_inter_name]Host = CONTAINER_OF(client->device->service, struct Hdf[driver_inter_name]Host, ioService); + + OHOS::MessageParcel *dataParcel = nullptr; + OHOS::MessageParcel *replyParcel = nullptr; + OHOS::MessageOption option; + + if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) { + HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__); + return HDF_ERR_INVALID_PARAM; + } + if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) { + HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__); + return HDF_ERR_INVALID_PARAM; + } + + return hdf[driver_inter_name]Host->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); +} + +// 驱动自身业务初始化的接口 +static int Hdf[driver_inter_name]DriverInit(struct HdfDeviceObject *deviceObject) +{ + HDF_LOGI("%{public}s: driver init start", __func__); + return HDF_SUCCESS; +} + +// 将驱动对外提供的服务能力接口绑定到HDF框架 +static int Hdf[driver_inter_name]DriverBind(struct HdfDeviceObject *deviceObject) +{ + HDF_LOGI("%{public}s: driver bind start", __func__); + auto *hdf[driver_inter_name]Host = new (std::nothrow) Hdf[driver_inter_name]Host; + if (hdf[driver_inter_name]Host == nullptr) { + HDF_LOGE("%{public}s: failed to create create Hdf[driver_inter_name]Host object", __func__); + return HDF_FAILURE; + } + + hdf[driver_inter_name]Host->ioService.Dispatch = [driver_inter_name]DriverDispatch; + hdf[driver_inter_name]Host->ioService.Open = NULL; + hdf[driver_inter_name]Host->ioService.Release = NULL; + + auto serviceImpl = OHOS::HDI::[driver_namespace_name]::V1_0::[driver_idl_name]::Get(true); + if (serviceImpl == nullptr) { + HDF_LOGE("%{public}s: failed to get of implement service", __func__); + delete hdf[driver_inter_name]Host; + return HDF_FAILURE; + } + + hdf[driver_inter_name]Host->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, + OHOS::HDI::[driver_namespace_name]::V1_0::[driver_idl_name]::GetDescriptor()); + if (hdf[driver_inter_name]Host->stub == nullptr) { + HDF_LOGE("%{public}s: failed to get stub object", __func__); + delete hdf[driver_inter_name]Host; + return HDF_FAILURE; + } + + deviceObject->service = &hdf[driver_inter_name]Host->ioService; + HDF_LOGI("%{public}s: driver bind end", __func__); + return HDF_SUCCESS; +} + +// 驱动释放资源的接口 +static void Hdf[driver_inter_name]DriverRelease(struct HdfDeviceObject *deviceObject) +{ + HDF_LOGI("%{public}s: driver release start", __func__); + if (deviceObject->service == nullptr) { + return; + } + + auto *hdf[driver_inter_name]Host = CONTAINER_OF(deviceObject->service, struct Hdf[driver_inter_name]Host, ioService); + if (hdf[driver_inter_name]Host != nullptr) { + delete hdf[driver_inter_name]Host; + } +} + +/* + * 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。 + */ +struct HdfDriverEntry g_[driver_name]interfaceDriverEntry = { + .moduleVersion = 1, + .moduleName = "[driver_name]_service", + .Bind = Hdf[driver_inter_name]DriverBind, + .Init = Hdf[driver_inter_name]DriverInit, + .Release = Hdf[driver_inter_name]DriverRelease, +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * 调用HDF_INIT将驱动入口注册到HDF框架中。 + * 在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + */ +HDF_INIT(g_[driver_name]interfaceDriverEntry); +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen new file mode 100644 index 00000000..c4f24fef --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef [upper_driver_name]_UHDF_LOG_H +#define [upper_driver_name]_UHDF_LOG_H + +#include "hdf_log.h" + +#ifdef LOG_DOMAIN +#undef LOG_DOMAIN +#endif +#define LOG_DOMAIN 0xD002516 + +#endif //[upper_driver_name]_UHDF_LOG_H \ No newline at end of file diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen new file mode 100644 index 00000000..b20b968b --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen @@ -0,0 +1,29 @@ +#include "v1_0/[driver_name]_interface_service.h" +#include +#include "[driver_name]_log.h" +#include "devhost_dump_reg.h" +#include "[driver_name]_dump.h" + +#define HDF_LOG_TAG [driver_name]_interface_service + +namespace OHOS { +namespace HDI { +namespace [driver_namespace_name] { +namespace V1_0 { +extern "C" [driver_idl_name] *[driver_inter_name]ImplGetInstance(void) +{ + DevHostRegisterDumpHost(Get[driver_namespace_name]Dump); + HDF_LOGI("%{public}s: [driver_idl_name] init", __func__); + return new (std::nothrow) [driver_inter_name]Service(); +} + +int32_t [driver_inter_name]Service::Helloworld(const std::string& sendMsg, std::string& recvMsg) +{ + HDF_LOGI("%{public}s: [driver_namespace_name]Service::Helloworld print", __func__); + return HDF_SUCCESS; +} + +} // V1_0 +} // [driver_namespace_name] +} // HDI +} // OHOS diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen new file mode 100644 index 00000000..f6c33dbf --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen @@ -0,0 +1,23 @@ +#ifndef OHOS_HDI_[upper_driver_name]_V1_0_[upper_driver_name]INTERFACESERVICE_H +#define OHOS_HDI_[upper_driver_name]_V1_0_[upper_driver_name]INTERFACESERVICE_H + +#include "v1_0/i[driver_name]_interface.h" + +namespace OHOS { +namespace HDI { +namespace [driver_namespace_name] { +namespace V1_0 { +class [driver_inter_name]Service : public OHOS::HDI::[driver_namespace_name]::V1_0::[driver_idl_name] { +public: + [driver_inter_name]Service() = default; + virtual ~[driver_inter_name]Service() = default; + + int32_t Helloworld(const std::string& sendMsg, std::string& recvMsg) override; + +}; +} // V1_0 +} // [driver_namespace_name] +} // HDI +} // OHOS + +#endif // OHOS_HDI_[upper_driver_name]_V1_0_[upper_driver_name]INTERFACESERVICE_H \ No newline at end of file diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen new file mode 100644 index 00000000..7c92b45a --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +group("[driver_name]_entry") { + deps = [ "hdi_service:hdf_[driver_name]_service" ] +} + + diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/bundlejsonTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/bundlejsonTemplete.gen new file mode 100644 index 00000000..a7ddf03b --- /dev/null +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/bundlejsonTemplete.gen @@ -0,0 +1,46 @@ +{ + "name": "@ohos/drivers_peripheral_[driver_name]", + "description": "[driver_name] device driver", + "version": "4.1", + "license": "Apache License 2.0", + "publishAs": "code-segment", + "segment": { + "destPath": "drivers/peripheral/[driver_name]" + }, + "dirs": {}, + "scripts": {}, + "component": { + "name": "drivers_peripheral_[driver_name]", + "subsystem": "hdf", + "features": [ + ], + "syscap": [], + "adapted_system_type": ["standard"], + "rom": "675KB", + "ram": "7400KB", + "deps": { + "components": [ + "ipc", + "hdf_core", + "hilog", + "c_utils", + "drivers_interface_[driver_name]", + "hitrace", + "hilog_lite" + ], + "third_party": [ + "bounds_checking_function" + ] + }, + "build": { + "sub_component": [ + "//drivers/peripheral/[driver_name]:[driver_name]_entry" + ], + "test": [ + ], + "inner_kits": [ + + ] + } + } +} diff --git a/src/cli/h2hdf/src/templete/framework.json b/src/cli/h2hdf/src/templete/framework.json new file mode 100644 index 00000000..e667f66c --- /dev/null +++ b/src/cli/h2hdf/src/templete/framework.json @@ -0,0 +1,24 @@ +{ + "HcsconfigTemplete":"./templete/HcsconfigTemplete/hcsconfigTemplete.gen", + "IdlInterfaceTemplete":{ + "buildgnTemplete":"./templete/IdlInterfaceTemplete/buildgnTemplete.gen", + "bundlejsonTemplete":"./templete/IdlInterfaceTemplete/bundlejsonTemplete.gen", + "idlInterfaceTemplete":"./templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen" + }, + "PeripheralTemplete": { + "DumpExampleTemplete": { + "buildgnTemplete":"./templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen", + "dumpCTemplete":"./templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen", + "dumpHTemplete":"./templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen" + }, + "HdiServiceTemplete": { + "buildgnTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen", + "driverTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen", + "logHTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen", + "serviceCppTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen", + "serviceHTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen" + }, + "buildgnTemplete":"./templete/PeripheralTemplete/buildgnTemplete.gen", + "bundlejsonTemplete":"./templete/PeripheralTemplete/bundlejsonTemplete.gen" + } +} \ No newline at end of file -- Gitee From ba0f9eb78b15e2041dfe8009146b0877a5b5eb08 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Thu, 25 Jul 2024 20:02:51 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0h2hdf=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/docs/DEVELOP.md | 244 ++++++++++++++++++ src/cli/h2hdf/docs/INSTRUCTION.md | 160 ++++++++++++ src/cli/h2hdf/docs/figures/pic_code_frame.png | Bin 0 -> 33533 bytes src/cli/h2hdf/docs/figures/pic_frame.png | Bin 0 -> 41038 bytes .../docs/figures/pic_show_devhostPid.png | Bin 0 -> 10412 bytes src/cli/h2hdf/docs/figures/pic_show_dump.png | Bin 0 -> 3041 bytes src/cli/h2hdf/docs/figures/pic_show_host.png | Bin 0 -> 21082 bytes .../h2hdf/docs/figures/pic_show_hostId.png | Bin 0 -> 3617 bytes 8 files changed, 404 insertions(+) create mode 100644 src/cli/h2hdf/docs/DEVELOP.md create mode 100644 src/cli/h2hdf/docs/INSTRUCTION.md create mode 100644 src/cli/h2hdf/docs/figures/pic_code_frame.png create mode 100644 src/cli/h2hdf/docs/figures/pic_frame.png create mode 100644 src/cli/h2hdf/docs/figures/pic_show_devhostPid.png create mode 100644 src/cli/h2hdf/docs/figures/pic_show_dump.png create mode 100644 src/cli/h2hdf/docs/figures/pic_show_host.png create mode 100644 src/cli/h2hdf/docs/figures/pic_show_hostId.png diff --git a/src/cli/h2hdf/docs/DEVELOP.md b/src/cli/h2hdf/docs/DEVELOP.md new file mode 100644 index 00000000..927e1561 --- /dev/null +++ b/src/cli/h2hdf/docs/DEVELOP.md @@ -0,0 +1,244 @@ +# Develop guide + +## h2hdf工具使用场景 + +在OpenHarmony系统中,上层应用或服务层通过调用HDF框架提供的HDI接口,能够以一种标准化和抽象化的方式与底层硬件设备进行交互。使用h2hdf工具,用户只需提供一个drivername,工具会自动生成整个框架的代码,包含驱动配置文件、idl接口、驱动程序driver和驱动服务框架。 + +![image-20240724093743837](./figures/pic_frame.png)) + +## h2hdf工具代码框架说明 + +``` +napi_generator/src/cli/h2hdf + +h2hdf +├── docs # 文档 +│ ├── usage.md # 使用文档 +│ ├── develop.md # 设计文档 +├── src +│ ├── templete # 模板文件 +│ │ ├── HcsconfigTemplete +│ │ │ ├── hcsconfigTemplete.gen # hcs配置模板 +│ │ ├── IdlInterfaceTemplete +│ │ │ ├── buildgnTemplete.gen # idl接口BUILD.gn模板 +│ │ │ ├── bundlejsonTemplete.gen # idl接口bundle.json模板 +│ │ │ ├── idlInterfaceTemplete.gen # idl接口定义文件模板 +│ │ ├── PeripheralTemplete +│ │ │ ├── DumpExampleTemplete # dump示例 +│ │ │ │ ├── buildgnTemplete.gen # BUILD.gn模板 +│ │ │ │ ├── dumpCTemplete.gen # dump实现示例模板 +│ │ │ │ ├── dumpHTemplete.gen # dump h文件模板 +│ │ │ ├── HdiServiceTemplete # hdi_service 模板 +│ │ │ │ ├── buildgnTemplete.gen # BUILD.gn模板 +│ │ │ │ ├── driverTemplete.gen # driver模板 +│ │ │ │ ├── logHTemplte.gen # 日志文件模板 +│ │ │ │ ├── serviceCppTemplete.gen # 驱动服务模板 +│ │ │ │ ├── serviceHTemplete.gen # 驱动服务 h 文件模板 +│ │ │ ├── buildgnTemplete.gen # hdi service BUILD.gn模板 +│ │ │ ├── bundlejsonTemplete.gen # hdi service bundle.json模板 +│ │ ├── framework.json # 存储模板对应相对路径 +│ ├── generate.js # 使用templete中对应的模板生成代码。 +│ ├── main.js # 工具入口文件,定义输入参数,调用generate.js来启动代码生成过程。 +├── package.json # Node.js打包配置文件 +``` + +运行逻辑 + +![image-20240724093743837](./figures/pic_code_frame.png) + +// 脚本重要函数 + +``` +// main.js + +let ops = stdio.getopt({ + // 输入driver name ,输入一个字符串,默认为hello + 'drivername': { key: 'n', args: 1, description: 'driver name', default: 'hello' }, + // 输出文件夹路径 + 'out': { key: 'o', args: 1, description: 'output directory', default: '.' }, +}); +``` + +``` +// generate.js + +/* 根据用户输入的driver名字生成framework框架 + * drivername:用户输入的驱动名,out:生成框架路径 + * 1. 读取json文件模板 + * 2. 替换模板中的名字并写文件输出 + */ +function genDriverFramework(driverName, out = '') { + // 读取Json文件,获取各模板路径 + let frameworkJsonPath = path.join(__dirname, './templete/framework.json'); + let frameworkJson = getJsonCfg(frameworkJsonPath); + + let frameworkPath = pathJoin(out, 'hdf'); + + let namespaceName = driverName.substring(0,1).toUpperCase() + driverName.substring(1, driverName.length); + let idlFileName = 'I' + namespaceName + 'Interface'; + let rootInfo = { + 'driverName': driverName, + 'namespaceName': namespaceName, + 'idlFileName': idlFileName, + } + + // 生成Hcs配置文件 + genHcsconfigFile(frameworkJson, driverName, frameworkPath); + + // 生成Idl接口 + genInterface(frameworkPath, frameworkJson, rootInfo); + + // 生成hdi_service + genPeripheral(frameworkPath,frameworkJson, rootInfo); +} +``` + +## 工具使用方法说明 + +### 生成 + +1.安装typescript:在napi_generator/src/cli/h2hdf/src目录下执行命令: + + npm i typescript + +2.安装stdio:在napi_generator/src/cli/h2hdf目录下执行命令: + + npm i stdio + +3.在napi_generator/src/cli/h2hdf/src下执行以下命令生成ts声明文件: + +``` +node main.js -n hello +``` + +其中,参数详情如下: + + -n, drivername,例如:hello + + -o, 可选参数,默认为当前目录,指定生成框架代码输出路径。 + +6.执行成功后在napi_generator/src/cli/h2hdf/src/下生成hellohdf文件夹,文件夹中目录结构如下所示: + +``` +├── HcsConfig # hcs配置文件 +│ ├── device_info.hcs # 内容配置到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 +├── IdlInterface +│ ├── hello # 拷贝到源码drivers/interface +│ │ ├── v1_0 +│ │ │ ├── BUILD.gn +│ │ │ ├── IHelloInterface.idl # idl接口 +│ │ ├── bundle.json +├── Peripheral # 拷贝到源码drivers/peripheral +│ ├── hello +│ │ ├── hal +│ │ │ ├── include +│ │ │ │ ├── hello_dump.h +│ │ │ ├── BUILD.gn +│ │ │ ├── hello_dump.c # hidump实现 +│ │ ├── hdi_service # hdi_service +│ │ │ ├── BUILD.gn # 编译两个动态库:libhello_driver、libhello_interface_service_1.0 +│ │ │ ├── hello_interface_driver.cpp # driver:定义驱动入口的对象,将驱动入口注册到HDF框架中;在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 +│ │ │ ├── hello_interface_service.cpp # 驱动服务 +│ │ │ ├── hello_interface_service.h +│ │ ├── utils/interface +│ │ │ ├── hello_log.h # 日志文件 +│ │ ├── BUILD.gn +│ │ ├── bundle.json +``` + +### 编译 + +1.将hellohdf/Peripheral文件夹下的hello文件夹拷贝到源码drivers/peripheral目录下,将hellohdf/IdlInterface文件夹下的hello文件夹拷贝到源码drivers/interface目录下,将hellohdf/HcsConfig/device_info.hcs中的内容拷贝到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 + +2.配置产品:在源码productdefine/common/inherit/rich.json文件中增加以下代码: + +``` +{ + "component": "drivers_interface_hello", + "features": [] +}, +``` + +其中drivers_interface_hello为drivers/interface/hello/v1_0/BUILD.gn中的part_name。 + +在源码productdefine/common/inherit/chipset_common.json文件中增加以下代码: + +``` +{ + "component": "drivers_peripheral_hello", + "features": [] + }, +``` + +其中drivers_peripheral_hello为drivers/peripheral/hello/bundle.json中的component。 + +3.编译,在源码下执行以下命令进行编译: + +``` +./build.sh --product-name rk3568 +``` + +编译成功后,将源码下out/rk3568/packages/phone/image镜像烧录在dayu200开发板上 + +### 验证 + +#### 动态加载 + +1.查看hostId:hdc连接开发板,进入/vendor/etc/init路径下,并查看hdf_devhost.cfg文件,使用hdc命令如下: + +``` +cat hdf_devhost.cfg +``` + +根据hostName找到对应hostId,如本例的hostName为hello_host,对应找到“name”为“hello_host”那一项,查看“path”的第二个参数,则为hostName对应的hostId,即14,如下所示: + +![image-20240724093743837](./figures/pic_show_hostid.png) + +2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入第一个参数为hostId,第二个参数为hostName;运行命令如下所示: + +``` +./hdf_devhost 14 hello_host +``` + +3.查看host是否加载:新开一个命令行窗口,hdc进入开发板,执行以下命令查看进程是否拉起: + +``` +ps -A | grep host +``` + +屏幕显示hello_host进程号,则表明host已被拉起 + +![image-20240724093743837](./figures/pic_show_devhostPid.png) + +4.使用hidumper查看更多细节信息: + +查询所有正在运行的host + +``` + hidumper -s HdfDeviceServiceManager -a "-query" +``` + +![image-20240724093543096](./figures/pic_show_host.png) + +``` +----------------------------------HdfDeviceServiceManager-------------------------------- +hdf device information in user space, format: +... +hello_host :0xf + device0 :0xf000101 :hello_interface_service +``` + +使用hidumper查看更多信息 + +``` +hidumper -s HdfDeviceServiceManager -a "-host hello_host -c" +``` + +打印出Hello, World! + +![image-20240724093535915](./figures/pic_show_dump.png) + +#### 静态加载 + +// todo 待补充 + diff --git a/src/cli/h2hdf/docs/INSTRUCTION.md b/src/cli/h2hdf/docs/INSTRUCTION.md new file mode 100644 index 00000000..56da00f8 --- /dev/null +++ b/src/cli/h2hdf/docs/INSTRUCTION.md @@ -0,0 +1,160 @@ +# Usage guide + +## 简介 +在OpenHarmony系统中,上层应用或服务层通过调用HDF框架提供的HDI接口,能够以一种标准化和抽象化的方式与底层硬件设备进行交互。使用h2hdf工具,用户只需提供一个drivername,工具会自动生成整个框架的代码,包含驱动配置文件、idl接口、驱动程序driver和驱动服务框架。 + +![image-20240724093743837](./figures/pic_frame.png) + +## 约束 +系统:建议Ubuntu 20.04或者Windows 10 + +依赖版本:VS Code 1.62.0 + +## 使用方法 + +### 生成 + +1.安装typescript:在napi_generator/src/cli/h2hdf/src目录下执行命令: + + npm i typescript + +2.安装stdio:在napi_generator/src/cli/h2hdf目录下执行命令: + + npm i stdio + +3.在napi_generator/src/cli/h2hdf/src下执行以下命令生成ts声明文件: + +``` +node main.js -n hello +``` + +其中,参数详情如下: + + -n, drivername,例如:hello + + -o, 可选参数,默认为当前目录,指定生成框架代码输出路径。 + +6.执行成功后在napi_generator/src/cli/h2hdf/src/下生成hellohdf文件夹,文件夹中目录结构如下所示: + +``` +├── HcsConfig # hcs配置文件 +│ ├── device_info.hcs # 内容配置到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 +├── IdlInterface +│ ├── hello # 拷贝到源码drivers/interface +│ │ ├── v1_0 +│ │ │ ├── BUILD.gn +│ │ │ ├── IHelloInterface.idl # idl接口 +│ │ ├── bundle.json +├── Peripheral # 拷贝到源码drivers/peripheral +│ ├── hello +│ │ ├── hal +│ │ │ ├── include +│ │ │ │ ├── hello_dump.h +│ │ │ ├── BUILD.gn +│ │ │ ├── hello_dump.c # hidump实现 +│ │ ├── hdi_service # hdi_service +│ │ │ ├── BUILD.gn # 编译两个动态库:libhello_driver、libhello_interface_service_1.0 +│ │ │ ├── hello_interface_driver.cpp # driver:定义驱动入口的对象,将驱动入口注册到HDF框架中;在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 +│ │ │ ├── hello_interface_service.cpp # 驱动服务 +│ │ │ ├── hello_interface_service.h +│ │ ├── utils/interface +│ │ │ ├── hello_log.h # 日志文件 +│ │ ├── BUILD.gn +│ │ ├── bundle.json +``` + +### 编译 + +1.将hellohdf/Peripheral文件夹下的hello文件夹拷贝到源码drivers/peripheral目录下,将hellohdf/IdlInterface文件夹下的hello文件夹拷贝到源码drivers/interface目录下,将hellohdf/HcsConfig/device_info.hcs中的内容拷贝到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 + +2.配置产品:在源码productdefine/common/inherit/rich.json文件中增加以下代码: + +``` +{ + "component": "drivers_interface_hello", + "features": [] +}, +``` + +其中drivers_interface_hello为drivers/interface/hello/v1_0/BUILD.gn中的part_name。 + +在源码productdefine/common/inherit/chipset_common.json文件中增加以下代码: + +``` +{ + "component": "drivers_peripheral_hello", + "features": [] + }, +``` + +其中drivers_peripheral_hello为drivers/peripheral/hello/bundle.json中的component。 + +3.编译,在源码下执行以下命令进行编译: + +``` +./build.sh --product-name rk3568 +``` + +编译成功后,将源码下out/rk3568/packages/phone/image镜像烧录在dayu200开发板上 + +### 验证 + +#### 动态加载 + +1.查看hostId:hdc连接开发板,进入/vendor/etc/init路径下,并查看hdf_devhost.cfg文件,使用hdc命令如下: + +``` +cat hdf_devhost.cfg +``` + +根据hostName找到对应hostId,如本例的hostName为hello_host,对应找到“name”为“hello_host”那一项,查看“path”的第二个参数,则为hostName对应的hostId,即14,如下所示: + +![image-20240724093743837](./figures/pic_show_hostid.png) + +2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入第一个参数为hostId,第二个参数为hostName;运行命令如下所示: + +``` +./hdf_devhost 14 hello_host +``` + +3.查看host是否加载:新开一个命令行窗口,hdc进入开发板,执行以下命令查看进程是否拉起: + +``` +ps -A | grep host +``` + +屏幕显示hello_host进程号,则表明host已被拉起 + +![image-20240724093743837](./figures/pic_show_devhostPid.png) + +4.使用hidumper查看更多细节信息: + +查询所有正在运行的host + +``` + hidumper -s HdfDeviceServiceManager -a "-query" +``` + +![image-20240724093543096](./figures/pic_show_host.png) + +``` +----------------------------------HdfDeviceServiceManager-------------------------------- +hdf device information in user space, format: +... +hello_host :0xf + device0 :0xf000101 :hello_interface_service +``` + +使用hidumper查看更多信息 + +``` +hidumper -s HdfDeviceServiceManager -a "-host hello_host -c" +``` + +打印出Hello, World! + +![image-20240724093535915](./figures/pic_show_dump.png) + +#### 静态加载 + +// todo 待补充 \ No newline at end of file diff --git a/src/cli/h2hdf/docs/figures/pic_code_frame.png b/src/cli/h2hdf/docs/figures/pic_code_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..b5af154bd3581d32fcab280f41592c6cf24ea328 GIT binary patch literal 33533 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%0AkQMMsJCz}b%4v* zliiJi?KhS$78$fG&OF2ho=kM&BMC{CQ(R8ZO9Qr&#)gW@B5znY=jAl%0?pw#p?1yqD;aOK7*hL`H;}+YHpUCTM;nd47J4V|F7LLjICB zxMV)%J*ct_&C4>7@(pOJ-;)XXs<{r_iqqPcszz7q_dTt9lNv)Ss@72sl*Clf3|q`M zXQE{u4``ZJ2CRj;)8qPnbb4wlNQQY1g2Ycmy+hZgZ&*9fH)C4`!1+tY!6y@E1uf=? zAB{^#7|l*@60uQ-`u4vs-uhtzJ(?5`=G@dl7)N71t2yDyJ#9*=+(N>LwH0T=tds~) zA!(vuu73(54DGr^6OiTP3O(W*wR1~W4;UC~6i5;bz610%tOG`!ET0tRB54HALlgV2 z6#ael$^OBTixuYJ&@>QP+fW0Vo_fLEqxmdc_&pyigi>Q*Jyr0R>kUn)PdG}Z%Y3I5 zcs}J4s9xEDuwsM%uEN>|UzG&p%}CbT3<2j}WOY;Gcn=)!8Q(&X6WHG9^@$RB%(=i< z)OMrK2D_Y$lp5FbO_m&AXkk$Pn4;V zd<^lRlt>MX=<*(yt(8Yg`E1Q_)@g*nX-7d(y12{rZUS!i#=Wf)R9P~|%~Epkj0)-3 zpiT^yup@_03cqx=nZI?+dK7gaW6QkxsQ}#W?Qln}z?l1NMQRvD0B<|pD8sjE{ouLc z`{I@VmeHJ1ZQsKEder6AgWKZLGkgZCW*+oiu@cRhmkgUhedia-3zKgPu*6CBXEG z9oKv+TvHPAst2(laL{i_E0`;NP$%s1n^je2$ipAT=W@Vih1hj8ZMKv9%f1o;;J9{+ z?03W%vPh&8FAU8tu-E+=5^nKo5!l<=M%|Il;FUziyRwTvr+vtM2ihZ8{W(F%iS-aZ=rZ?+48S~c(~zO z4|H($BR4CCpa+hshzz`(QIw~W_g+;zmgW5&!xd(1y>#aV{(D>Gm+4y-t=w%i&K z-%y?ZwxqvU;`mJ>je?YJ&p5Vk}}@3+BeT+l9{n2!5sg>08FSLl4eYP$o15hRLkmj;(9Vz!z;=?FW#8DM*F%0ZH&GS z9eyd^K7RU7(fy+$JjUw_7fgWrlX13np-=p{hG6W_0qVq2T>R)2lq%HZGbG)xMgP>` zt*FeK;;-<}dVTJ}DG!Y>8Y$E6;V8b+r9=?+zmFvd6Btp?$3>gM!=ed3>y`W^ZZq?el4IoV*z_n9s(n3;xH29HjiwbJB&7k>i^`ACdOsfMBgm*nU;GPeYD69Jr?R^T2o;BFdtzIFJOWqsItKT%NoI{b+g0q`DP{XOsK^5liHdNK`LhY5fs3AEd_4lDQ6hDA$O;!5GxFqZ~9w&$_GW{ z-qGVv*Lx!GFK9H1%Nfs)hU8OcKE9P0i+kd|-{p08e0h-YZZ#d;fnT~TBk%d+y`ju+ zjRL@d8hA5r6O0DMIh0ZVvRNpW1cdMMs-xRpL2khr~u@yNSR=!)CM~Jf^uD~T0 z-nnh1NQby$rYU8G-xI6h5NrssWPUKQA@^G9jef_Uqp@|(s%8G*#gmM3$y!5U*NQL7 z++o>Qc$;N37HyrkzG$Ltk|T?s77aZ-61{MdPQ56~|nR)Swy_2-giqQ6+z zQLb9qNUiWbTEw*DXuAsIaDX-4w>Zv!s8&IjRqntZwMw<<5lxAtmwbF)Ys^Wbm3q8< z6&7$4Yc-VYLY81`Z!~bR?UZDi14zI0EdAr+cTbjM@lPQwkfR7oXuw#=Nz@3RPbGd$@OZzECO1^q4vd{o z#5Gz0TDBen5uIM0j4=B-00z?EUvhLlcs6KkbrZHA#{ax;jIk3w5xa9jHAK*Cwq-tNHV$pABk3Orj=B7g;LZ2^@yCT$kifAu93cbu3ORbfb`8T;@j)+)6E((R7r zvdX+5l-xPD%1A_Zr+>0BpL^!3EH;R)RbDbKq{lKbdks+1HSH-Pt_tJ_3l2yV1pj87 zUSG%7hx8AUUAA|Q3zxWPbP&1%=?`BaU4YF#M>x_O_2j2w*Ew+x)~jy_%O%UTz?Ith zi*HNx-1}&RCYWba=OQ^vetb3bp3~Y@>OKe5gYtW-kJ}}As*6r@|CT!^_GQO!l;HZ` z1kJVs5^pYpc}~}GtT?ELmCR>0$P2c|RQtWiFKW-r)Nw^hS5HWFvkK-znwU{dWEF|% zWm@*-tWv_8JzS>ba{W^7?3*m)qO9|2*2TQwI&8-C;+vpimkcgMA}wn7Z3;LXu^Kea z>C_Ym*mAyB-L6`9co6jZf>GvyxSrhQX*)DwElXsn*1hl)Z{^qpt1;@gITIl=W&P`h z;o#b+Lli>;IW8j~V89IJD=ijjNfs+`_9?*ci?mEMXWtW}o5dzwgy|LQAzotTr4INJ zwix;K)A$GLM?AqWmv*1rfa8M8^_>U%pQ^OmbVt*#7%r=k?JW6Y{B59ly&y{6T&^qXNdce~>;=xfP*F~Td(AL=rxbtn^6#4MnvV*ep$b`9aQLb*eJESa*&Ht^)OkDoKp_% z7sc0#QFH9+PqsjEuK4^ zTv@KbmF$5;Hq~;^uOY8#3o)Ooex1_zXv1S98g&qoS2f8vN6wJ&w}Uj|wUo~H;D3ze zZ#zBo*>fjS6}+t8UffA-x`-`>8eFfUl6bZb?&5Wv(f=XuEu*52-nLN{rKAx7=^jEr zItCDsuA!wR21I(uAyrDGVd#<;>1Jr82M`4X=^PO0ZaBa3|2*${)_LC#=gawU)>(@W z4D*Z4z4yNN9oK!`fB$A`QXVMf+E5i)_ws!JR1D@S%k&T20jWBe>tFuoddzcEAKyFA z2WUXXtE~V~;|Bnn3-EM2z5m~L129dJ|J-eiyE@8_`7(+eLnWgA@E>`I5X1l1Ka4Nw zFfeHYh)Q4vLjOtA^`CUa|05kaj1;?jF>*lvlcF&$aA_(LB>+X0M~p*54)&Iwi2Hn! z*o(nTA%I0JNYoT)68HE6S=67=wV-6;Y7U9h{D`)j&S^Ft6`&LafU4o3^`^(F{&(ty zkFihXyM9Ktv~K?-laqMbkh6hJL=OgedU|3veD50EqWS)A zl{^kH&7K5R2d*!=>gfydEOXGY;A__HZOIRFJ|x^!tZ3Tc8cVf55T~V1`iNyX0BjY%=c( zOld^WNtiB&#l{t!-KQ3Jw0RLlCGnm_jvxpqShgAHWJ*X!aIN%9F_T+lf`(FfQm`(G zJDs1LYxDr4G5|5fk(^_N$0~bfx!c6hy8Ue5`Jk*~S)%UaoX1KKost_w%_G0Vz!TA} z$B5G(Mk2!+13cOdOG$O>Eop*?fJ#Dm@8F`06e|k@RT%+dhbytTFOyLvSV?uclQiM; z_|e&;Z3}GF-X)HGo27uy=%Oza~Bm zU*AIyMnv z38<%p1~>A(q4_h4%aoNEmIFPKsg_@f%1^yfen1{T3fPfpV$3RJb)#_WzZp7h7SUun z8%f;_BxsK}xgJ{XEdmkX>z-ON837eVqR2$q;JR`8C)~Feqe19#BX{Wa8o}ToA3js# zCowr7R*{wbJ!k!W9@!vjFa^qOfJ*n=gdYE`uW_EkHmbGN*elA6rV$yMuHS@ijG>E$ z50Xj?U3aAixp6yW2D!(4;~_6qaW~4&0<%w(44?1~{?6vy6mg8VA1@l}xH(~o;GB=TGB?% zS-xK~U8n8I#@kxbf<~T-I%F~HGWg6mMwpse*yxb}yM$0?=nrwt$5U#)K|XHLZwZZ+ zdibYFgWt@i4t^$%s_K}2r0(kBJm8?ejs0m@dh7GxnQ30;Hx9&naIUSn-*0g)m$s7_ z>Ui2`U~O#Th{VQ)%a=6|KeGApNf+o05>&Mm0ESK3)>x4L4(ANtu6j=HJ$N4eY6({~ zSHhegNb4`}oaSXzys}_#4SCB@LF2qg2kzd(t_c9DS{GI}c0d9{(e4i~80K0TG3~Zp zRxj$ouNLojbCX+$-jIXM{G!IiL&u~R=w4Y^mOML^B_@o&ixm!R#D%yyxF1_(0rLxX z08SjY_B-1UGEf`Zv!=Z4z~`i(}0=Up&2NEeJViB z;)P1)=`8C)ht{%cI4Ya=dn@lAM52JmEoo8g0N=B%U*G8bZ*gu}I6P%EG^GdQ2gtQN z6xmyPGg%E!J7h1@nFqTIg^>@6TFcGklqao6=hy62K_kiq@3mE;AkA_Sr)T}n`|Z~? zPAi(!H{X?2sM0|ZEII{wK0)DeD8MWo$eo^!lSBsNXs(x=)%B%H5N!qB?ngaBKURAe z+bTu)XcdT7en|TIer$153h4(uDLYqW9K9Mgg2}0Si*JPJ7W#><|2(Cs>>wX&tO^1m z&haUgw_cm6PG^ zJxSS<1vl<~OQ8g+E={%4#CTL})idZk@~kmLJaH=OrLjLlW`CG{VL;^>J%swBe5eC& z`Bi8$UlqAwEfZ8@(d;-SzqJX$IbgV7#0O4GWf?LQ~ z&|i$djcX6Re#m4NPAdQ0Dv{uj-{v)zeLj8CE%-Q3-RJzNQJa(?D9MkDcyK3xA|ZeI zPwxktJF__N9aX{vNn#fx1(8@atv z)QlF_*NhQY(flATtGVfwKI=Crwf2y_HQ?bTrCjq9JfI{8(elrY5HkWqc9oHNpcLy*skf=!qaXWsa`XLq~~&&P_AFp}NC~n}Sx=`807YD@`eRD@`$lqW0VB z)omG8N&M3^G_Ujd9&SJxHB-ULdj~4{-zM~OUY#}@5t zZ#8}eRrO_X_6R_SEuemy@sAkF@HQgfcI-aCWWnB$vAGra8kOAKu4iGvSN~3?%O~p# z9Z(jcQzFSMuoS9di+IK)qEGMR+SJOwFU%58ZFFAK@~HhEyOh5_CVZ7u zxUB$;26Ga(mO2LQ9iJ=(v=3*wg#L|0$kArRekoN6{P4>}Hrh^1r0!`oPUbavKpV{#D5 zm6_pPm))U3X$Vrff`=@&lP2==A#7iYyj5Z)6lUISy{TZ0?jeEY5%|CQ+99mRtk6BfXJ+bgcYC`ncZf+llYnE}<7um}R9A z_L++4YKTAWY<5T|u6VfwYWnU4%_G#~6;f6C?xC?OEB6(`>}TSuT}x|t`%``=4_uJ_ z9}2h=L)j$e)<3Xo23!^bkxJvQQr&mI_b&zno_8(&x%VyP)f?Vy;wZ4seuDz;#`IaJ zfYR-=k7}r* zNeLUQy}hX?KcNqGfK==;*i8{QC+0ut`Y5&(|D&A zk6J&vGfD5nT^dLC@lrO#U)gdo@H$I`<4WZJ-hyRdPw>jD=-ZD>$g&Y4v8Wwd zkq_k6=D}C3pZ`{sL4soN?SU~HqNz{7TB(9mp-hzbnHfHc2s;!ZXCJ|xs0=qsYTdiCIV7a;nP8&O-~nMc#KY`Io}r zbtR(NBh5sIK>CPO5-cU?juvsLC0;(zec&s=*XW-<9ecj4uZbPCz~2pAKwh>Hs7`h~cX z259q`P?5vVpI?paSF_tE970;oU5yOh&^I$@5(@s$#a=hf-_czpA{GtxtLDOpVL;p3 z!U$L4k{C6AfOW8=G!E_TTzb#(i-3k-hh05TBcL~4#SlbC8(WPB-RivWenGo%?u0$1 zS~?6f zeeHP?J1?02F-X>NNCTVR=EOYQd;a!CD4!;_4FB*fM~&B^muE6|v3(?@d9#<(1)T7C z2#@U>jONSc>aP^@#I7KE<}>r14(St@!x2#`cHRmxka#HD+8sY)A5b)uEX2R*-?~%6 z%7N?*@Fhq=xxy9L5|G8p*}eyG>a$J=^)mq#`pZl5J~xQ7cZ6=cuv}WouBl;!WZ&Gn zv6o8jQBlq6CxY`Kh1o=Ym;Qw zp5nnc+3K~m6@8k9RPAAPg#PTi>Y@;`iWj*@1&_NE(|<)yre^hp76CBNAgG`7yJsG| zpaKmlldmVam|RK-twyUHp4)7E7M~XQxRn&O)J?X!Lha60`uT-dQj(gjLU-`D406J$;0{{FBSef%sjFg~mZ! z5Z{i&l|R$JeQM&JK{_}GEu?=kWaUs5@EJfCy~9)@uu{$?5UnS>a^7i@9RwVcp4q{+T6JPa`?sAqR*8LUdx5Dy zwj)vlzpaU~vN5D1+sTXm(Srtm$|kjf;uvBJrW(XPX*h6@4L$0;AeV_JPlvUTV$ zX9OuY+Z-@&%`@4#4LRxP=(rn3YJ)t!}? zJ#d!PsE^fEqe`1Ks57kLJ`pvl;G8{JnsB%O(jFuY_$u!LJCCgCbE6D%dGzdst}{;} zfqY|dbOnW=M4A{#!3u{p4GYmm%&d~jy6W?lf{T(RUYkVql>-9{;^Bk~B6T#I>DjZn z^x2pq*P(Ee(ZZ68n{yyd@!;a_cq}|b!0Tn+BMOPpg(64;n`iSc;XHvf)_p7;ebBG( zheK?oRK+ck`uH<}@Tq+hHdo$g+3?@pUAmp1MqQpaUVx3lSM=mMGQm*%`0&Bf{( z+B;~OR?o0yP6KoAS3uHT?h}BqLA5=W-tj1F?N9;+5TVH?*$QbLl>#r@honV5 zw;Oo>tx~?~w2ANoDJX&wJb)su@#9nE0Ni$3tuuu$@er@goxL={tPOjv_asB(qx@Fz ze&Qg<_Q3k8gHQ3O$>S=M!cxJ24D7n~Rj384&4P)aIJS`pn6rfeN{896hRi z`g`YzEkRBp2Pe0u)i1NR>yzy9OagYnuOtnCJVMH= zAXh{v8M6_$aM-IW<_$75P#-}sj~F-bXJBSp{j)^LArtpJGr3Q25}WliB-`VQI($8V zB`ps8;S=SsyR3B*9| zo?x^Ph2|dFII6UlB$?QThZYfLDu&x==5)Lq@=B4ucHuva1RMp=QH%NsWD)tK&%J=_ z`C)Ne>gg`u3FsCgRactDLi|+j@VI*6d}DOP{_+;NKB*X2-Nx5`Qsl16P`s}tt^aLv zeA3a-`8?2_w)J-m;GVd@+9Wrtnmuv|WfSmF&O|cV{azH~92gIJF6xGADa?RPvmyc` zj>v#`G9Hst-$K^~a3tJpZ>&dsmpWM$1W*C{x6+%Y?s)s-r-I29{GtavJ()bkSr_8E z^fR7r3xURKHs`PUXa&jcFulNusg$DOXEs;Z^?+|Ai4Gs9r=9yjXBTdr*3vd5o_Si7 z@>9W|%V>j^%ndHmgIpyuoykt@#R z?Vu6r$;>VW>h!M`cZu6xiH8cHfSfVxYI*n7bqF_tt?okqKydd8YllY%XHI;5KW;&M=T5O|a>K)} z%x5musC7=**9|*V-mlH9a3Y{676u}Y@9~X-WM3GaPq0a?_Kf;#dhKu`#ER9mZy60x z3C9Da^EX;Smh{|u5OZh&@zDNF0l5qdN6wLv`^Z?sduF40ct-q-0Y)3L16-!eiwQs5 zkbS=3aGI{+SzT6{vq8S7C|;A=AGjO<6=>l3_GvUDkSg9RxBUqz6P_;083?MxXqZVFEKsl3yjlX&10 z-SVw|TM1CdM(YR18PWc7sy_gW;}q86Tw78)c77bmEBKyZqxZ(D1r#4bEA#u}1tm{0 zL=nh}$i-M((Us-6h6;#1DH#*lDE9_1K&vEj5kG(vU&8SNEy_Dmyf=!#DoX*Xe{eCw z090%pM~l^Oo8D5fP#*u~?oA3;pJ_Qf`3t7#Ow^-)mPaiHb>gqo64E{WiKNa2FWI;g zvP}a>%;LeebHkuN10Lf3l9w z`M&gIF-J~12^Cj8`+C}mOQD)|ijriYA`O99y&+ev3y$xfeNQHSUTe2eL&cIyZXPh+ zntgULqp0(cn}~`tgwac8g-3AtwIW?TPp5;jgOO)Bf>z8?5j5hNFsCU{TAyVl?6}U)q1wo3bW9@E zn0<}4_hjs&?k0(X`Uq|zKH6}!Wxx^VZ~jw6x5KHgMpRw%OkiLoH|v_9-N2Rb=09m2 z{_oOiSY5-Zr11wyYxkCCs0Re4r{R1gGEnP!Coixgqm|yj`JAA`4X!mfz$aAvfyt(+ z8m%QCmeK!UNk~kQNkgeT6$giV%~vT&4wr}jP=9Z4SoNo1-WMlg6;)xUJi&fk=AyGT zdUs#rnbh{zB}NR2s@2&MF6}H961#G`j}P5WMd%-f^pinXS8-h0Wiwue;nd`pU2YBa z($s;ccLHI1PgMDCR)7cu_U+cwMMwShjCw`Uca+&!iJ|Ro_QZBl#W|Mr zQ8AkIPI3}vOjk_K6I~6tv=x3mn~AQ3&?x`S1M?#AL<>nQ8UCqKLe&Pn{9}k8&5tvkB+zMq7&L$4*PxeV)EftgDw~YrtWT!ZVft=SwlCm>=paCDOU~s{#b-T$a-@&IMQF?(`_e#ecd@bKNAY)rbl1FKutoSQVCJ=a*`)B5iv;KG-C|B)(2 z%oI076Iyi-xG=Z1y@KoJ{aLoyS>{`J8W-u{yg~z85a-_({P75NHTLC``unRq{9=Mo z3}?*#Tg++T^bWsKDhN&)ge-dTgFD{*3Z4FDvPF~T<^ULGFlLwsukoPH+MgkL^#5*i z3;ln1+sR|p5^*1OZd3qBgwga7YG5zmLr7eHct^K&(hS*A>b+W!`MXw>ezslnYEDhq_U4pScLNzR3 zlHf5Z5eMM_MHOZb{xdC)QBo8*Y#mphI1f>)jjYffPy|gJBM>|DR0J)>-PJCDG(7k} zyek=E71)5!RzQaKUf)V0l_z7f{M>N9=*l_5?H&>-XCL3=`hSr2Yuv(1TGd&!nJN4w z3?H?+a0@qAB+^I-zY$W-zMgcyJ9V%a>z$T@Ie~HQ1#gIwf%**mf18Dc0u>)j&L(4* zgGi#)ahc}p_2af#i+Uj70VyM3pyGCuCx_1TW%bp^==~CUtWXJ_{s03k@;H%||0z+O z+$b7=VQ;Eer~330;;{j0cH<$dTZr$^Ph#Dhy7L67$=H*S3xyxtu_AdG&ZdHq|C@x# z!wOYZv0KiWLPP(F(m|DY+oPj_U9mcM{7A8zWl1c%KMX~M1#>gBxAN$bUXl3 zlfp3|?_shM7b2K}ifL3@SXPlJCdCp<5nG9!DKVT&uWa_S?SmBxg^uSJ5&vV@A!4a^ zijNV4-@CXzbSI1iqude+>D6O09Ixbz^gLfS?4sNinQ?ls*~pjy7r2#xT1=f?%g5;N zYn>(9mL;`N31CNwH%TL5r0?CI8l-3C`I|A(w{)Lbf zdcQw-|AEHQb%fQqMujTDv4}*KCm0ieNp4M0oic+kD-_~D{~|i%jBx3PgGA4jogTH1qz*)I&5hz2k82e-E;?T>u}aI$z}$cQ|}U~&j+h0*3cTJ zHI*IdT%LjPV9d0jcgRx6f;FCGbYJ`@*i*-{mrA0?Y&M)V+pC|DVUhbawLmV7%f8wg zy1r(bCKFVzo_Q2@&Q5#SI*6l)stTt~1MBH;4zT9IKBVx{NhxWA$b*59Rph#0(ojW0 zzy$!K#uN^XAz!P4m=ePR^b>R;O!7D!Dzo49B4`jiD&2}had+`lGkb{i=szYQNJ|9H zLrc~)ccwe%X6vPa`23qFhyhLr>@B^X)F-%~Dr;li`~UD^X{tB?LamYoh09QJ1mA+y z6$vzxF&qA->m}UkN`{StW#}w{RMoTSNxjl%T{}`0(QRJD+Uw^aoZiw1ZY~UACk7I2 zYs`%Zb_-y2M*Y}0(~cb^zxCIg_eGlx&NrK^#^tLz$1K7V-a$auKl0r^FUMW^U3Jjq z(xx&x7`Vg(4(Z)oj~Bqv1UxD3+j|R;`$sd{Tpk`79?6=M!MNN(Tf{*nCSY|3*_YYY zODI$!{MP~eWPmQ~CXHG@3R@SL|QHVyV>-IE7jf#ULM8yWe&O)mV z{sQHhyMOF&b?4vdCDFxUKtB0aN`qB*S)hjvW(gJs6%7As@`0RKhQ>QOJ z?O0PWweiJUllR9)Isf&u4NcE4o%D875NQ0Y=e71I)n#CCUYConb+4I#HkFk{F)D#0 z+y4jBR!7%jxApVG8~eDvwLgFCy>ojA)x+;8S-EJ&_Yl(BhrssU<{r$~X#uNdIO@fk zp}vY@VBiOJIgx0w9c~vg|J3MP0iT_kH;;?Ko3+xLwSn^2d#{_@owCd`VbPm2sOeV$ z6L#0uW*wG#It!G^1M_3S2__KsoNuPg!Pso5w|Cuua)Pq0HrZScfX0nw=k6YTcOyp! z=f3>1&1#hxwFp?#-0zlUO1sGrS_5IL*;lBHQ#?FY&{({Un{%Kv4o!vvF9?_*E@8kP znP@B@SJORqf;v?JL%DfYzWcsX$72xnG?`j3d7$$ho+A5h(5qDSw1eaofbXpV2j|mO zmAe{wcDcvuCwJk$GJUsZVPGM>Fd}MBVgh>JQT0qN8gJVNO0;bZuGSG{_;(q2c8Xy3 zN4X~AB%Scli!$J6;dA>srK0=&%hK1C9V#z@8CVDiI@%P)o>Y5ZHcJLS2{jz%-08T2 zHi3`6<3_^MG7rHqih(VnqYn+vCM%tCugZ%TA64p)4AWdxXz!%k?H{(`qa|=24qSF%F9)(hin@@n$WAoN7Uk=DgOnw3fWG<%=ZKu`ic*f zlb9~L2%pSLKqf3b?yR?!ml0wna98fQ-op14B$Ggks<1L~wd)-V`rTa?br~VBS6zEd zlC1m@k@?pusAX!*HRCjD`%K@2g~E+Bl#g@VH_%7<7^I{B-Z?R zl?n!*XURO5gn6tH6GaJ5#0K3J>lu^}Ci{ACQg|`gyDG(==-;53k8%13tA6WI^RpVT z37A`Hg7vMeG?TmJ>vn(KyiUDgp@P2uJ-z3q*SK&GGeO}N3ni1CjK{?Oha**%Txr2% zBjCg1kKHF~c#Ax3L0-%LQ2z58P!x`jZb`ekPL|C*@Q&KJ^L9!OjayGX-IB^Fx4iwd zP*PHw=**GL`0N?SAHEM^o4FXbq@QoCD1FWK;j$<9vQx`bk2+fG+@JaMLcMo{pQp(3 zVxga?6ox-1mBWpYz-_P2D{5D4_Kb~Bb$-T$s8{4C4T8%J?5lhb6M2AdHH|u(B;}=h z+W#DM{Mtl2*aL6c3d^U)pF}Ew7r^Ikr(Ikamz0*qlWXebMn%L=18EDXZhkYKFDy)F z<3F$1mp@vXY`oq`m7{)N3w{BT0y+fN*`+^LJ&_?8FMQ7c6vy6QZ`3?*h5ddEOhP;4 zV&&S^v5q9eCJYO}{kqG_Xd4=GaAdx90O^Aq7*px0CGDuz{`6PE&C_Gc6~#R2{)0Bg zyTHR>gHO(cN7W9if8mD5BwO<#Nc;SxpeC=V!t8nfaJU|i;Bk0}dc!9tKEL|w7iQV) z^@(KUhmND?*WCPnEeXffziVM0w~vGWgZ62!0SB#w@jr<2$o=d47|d?a{~I?*v|$<} z*4h0B3>}W?jX19U|DO+lO#|u&{$+i60Q?+g&rYJCyjoJ6&0CxaOQ)4KwO(t{fv+l; zbFKh=CdVoMU4++@)U#D)IygAbq7d4fP%qg}LQZb|yWXL3>vuahZZa|fBg6kZa34)6 zb6e=K>1DMjp{Uz z@qL9F%jprG3{jVNx)A{0`r*I1S$S9yc{OF$fg7bRp|gQrP1F=VbZJAMJSPP3?G<#G zRT#xaBkCx^lGsGuPdZ2*^{V+*wxmQvyoR*u2jCLG^#WEliEmaZsf4#4Vq`1xPY2j%Y?Wrv-E?Z?5Mot)DI8{;F6+p^I zh+oK^vQwW(n*@B}hQW}B*)#w8rDW56p}`fu6D`h>N{T|duVsH2)FG--)jzPHuj%BshXDdl6N}z3={QBP#Q?6C@KqtKPkP~80;pubi8sA?6Mm1EE znt7t~MjCCZ1u2iGBc=b<-Mi|IMQ|gMzSWthcyr}?i2ih}%TL^iY0i(2k#U%XIdz$7 znNlKrho5ac#oR(ZAR+x?!c?!H%COd0 ztS>p<$U-}GiV$Hc5wQVIJqtuk1)Elz*574vD?#4_hn@z*cl`JoE^eeb(tRcYMhD(M z3@6w{X#9%oG6U0~keoD8Iu($aTV1c2!6(1>bjImV4+dr=Z3Z5itQHgVC1qIIq3G=0 zR~xjK+#RO3NSsyDez9L|12GNW2I;kvAMP3!ZUJ98x&%_!+fS`ZcX!?MBE07SW(J38 z_3n{;TU7lZ!w{YII_mbuz5Z%{Bl_Sp_H@wkxC(q+&HTV{`-LDW9-GY%DKnSlY3Elg zb3-zs43AW@YZTj4jw)u;!Acj!JSp5dBC3@Lf(`LEwMVK8k3^)DTnKWNrY}U6T*67l zJgbkNwe>rS1oZZ*lmvbI@_of+=hdsGi07cJ@H=$cZ>rxoDtGd15=&&fXPRjA`j~#e zjO4P+Hce{+*oMuX2Gp@^C4G!}Oeb6v{qA)Gwuu=4M}F8KUBqHNg99w^lp?3NAze}B zSOno1H#F-=)tIx5JViOD=K*Z}H}U`#_Qu_K7EHT6VCmV!vNrALFH5D!*PVm{;T9f3 zV70cRSuvNMGtyJAm(HptpcMA<#Q6;qG%CEYv6 zjrAJDaoD^{N3R>$9YlF5-#sAj@z3%+<$J z#+(PWZtevxUzu4F*ZV8@$0xi(<~3`@9X)|S$iH(V9*3^FXOo|NNkiO6OkC5En~FCS^2RlMnn1V6#5c}0>fqh4Tjrl@ZOP*yQr8ZznYo; z9dI~D>obf*fEKvHhzn-}gCx@&F{O*Bubew!+A1_RSg!!H?B)XS<_#PJ)Rp=gh=E_p z+Quf-c7N8xR?d!Lx?>*apmBI+{?AhNi4`Q6suui3E}|6V_R*w7(AlAnI|m?^s8BJh zplLJlEpn>$3gNxUs!Osb3IbYp+H?P}`LxHIr3yg*DIentNHistvFOv_6B(T>Pk96F z$g_fbgpO&eUUd=9SItZ-45Ku_Unop*tY@8@n;^`{W$}jJzi|6}07rWVX*T zecbpW@FwTiJIVG z;f)zUT)s#!ylwV;KRMOr_cS9gqUXEXH2Un5C^Tor>P3&yT3@CySdZ4*Cp;+8`v~Y()#lb0&AgR>hWTw0SlA!{C1eP34A!`eqD-fB^;i7K<->#{Ssq0Io}e z5nR1#dA=@lsZq>-BQ=4KGLprUyFwVhGU z&vIiDYVs#hg&W)> zKa*7X8a#nw9GCa%C%?8gGMddpVde2C>5`cOEfmpVQjL^ish;sadEdpzZ}8ofnFJRL zV?o{9?a;DD>)S9{wkNB1|ka#y9#K={YP7kyn1k6WI7&pQ&Y9ua_Y(ja#&7^Sj zj|R-`3SY4Ibnrye#UKCt>zO`GEp|rROZg+6(u82#eyC z9ITA|X+>HV(r=o}_l`Oi~=ac_PW*_T+Lg4yL&w>gMTn znia~1r0Uv6>Zu$lg**vE35KaHC0Q`)wJ^F_g*k4g@*{O~PN{_C4Uc+K$qaQxHLkV* zU50?5q>s))X1nz&YG_ml+ICKA%nPUBzvRoG>j+p`BvwF!q4t%A5_RV!`iRW}m+3bf zr+eTmQMHRG^EVY5J%(?W6tg0gwh?M$A}W>ILuk|y8>Z2ziWLr4yG28F_G4L(2!yFxJI~#r+l!I2=e4R-pTuB?7>eTLrjreRm>lbcAtA(`j z`o?R&0`iWN{X)j#Rica~7VCrEx1nK3k#VUc=NDXz4dx}yLQYxKguUPo%sY#Hbn}x1 zWe67zf38w}(2q*O^k(ymg0i?FMVE)BN*i#G`1lFz?|p?TG{c+o8{d?RxD!dt>lTJQ z*1kP^L!vPkV>9f2+4nrWafcj?n zhp{4*3|gsd#wp4Xz$}R0gUBecf;%0^vBVm(Zo<$tJx^it--TiOAL?Nm7eqjdTrZ&i zu`?26nPHlcPy%Z(y0y$;qGYQ?NXPyWa*8yS_6j!zd*x?l)Tme7obn*xC(~Zw~*?9-weW6$uKGfFXX@%AV<*wcD09RIvI*P z!Eo?xNt#a4wSsx{0O9`b0EP9$7l^Y#t_MAfN7Bc6Iy{EKd-W;^lU9=<`{HnyA4q0S z-3vB5lrUGIA$xg3`~FZthw6$3SMDThIf7fih*x8ZS1OUw)1lz>52m3(J#|)#-eiCq z%0H7*UJTlTt{pd3JPJK%ce--eR38=i^sJ*$5R9GYdv47R?~~Z zr%Chsk%gn(Cg`ixV;KCx3#p@UjOy`hHfz>QQc^}!aD8q|5LMX?5grqC4n6wr)W!@g zZ?|BQvZhQsKGHJF&udkgVxBB=Ol(Jmx@{Jo{wAQ%qgXo9=L+IR%RLPrx>EtM?~^u* z--$9de|yXwFg~GL84@}cwDrcU;xe^q0{muUxvuPOL5p0;A*L3Nhy(F5*T~*FcS{P> zx6ZH#o;#e-Hf>?!(C;aK367##&+uS}L)wne?qga9n9&kR|&;sdKq(0xr) zFuT7>2Xorv=!PC*bVHLjdICXRbh{z>CfGEo*?h*RWLQ@=i^Z^*CwWte4*{?X@`%P{ zvl~WhKcg*up9HK(bqn^%!jZVmgn7gHBpULCWJ!c)bAT9{PSUb{_Spp%((8x^2|v1r zgkDLA^93SJMHEi~i|UB>nW1y+rJghWGx-ZvCw{p|(*D2_u6J(kt6zGInl*+->ht5~ zGjvjhwo96?ImgkGDz#|Ih)B;%!%bh&!Cxzat6%G&V-8XHucxh$B2MwMLeBA~l3v+I z8aZr+F&4ZjvBtKF{FgE2`Iq7r7aZz|3`OdRCgE+oqQ(APqEQn;KZ`3SM!UpssIGhe zu>Q+7VAww-2xA~$1{M*euxE;!U^%0L>)veRc$4M%I2!$(;rdBVZbGrP0K2R3E>cuz z+HukNW_+u{1^aG-1G(YQWwWDa^ni%j&cQqUKfjE3F%2s?EJJ7GTJ68N?`AWaP3q5#ygXy8-X0Hjt z(Qwm*r+m3{4TjgurtLKxw2O}fn0)m>%3sFsvP*Lu9uljSBzNPYqX!R%1-UC}+f>bY zJ?_KiNR{sVIDHN|Od>w{_%Rb@una@81txDvvjY4AKNXXj-antJM+w_MTX(Ulaw5=@l zHZQ25b&{4{Vl8Z@XFtz;m$A<_rIu7%+ibAgJb4u`lccU7IdObgBAm_+&s6!eYYm>hmM^;SdOBd zkro6H2>?er?gr~~cq$ zg}ExZauV0qP{d>$L&7W;B64SEdB&VNB~oRc(gUC48H(E3MEg6~8;SfT2G*tL+O4u; ze4Hz0r4LxnPc!9pa(6{3CkIa%j94dQP1r>H+Bj(H8D(rC@8W-NOz)Kq8=tFu9XhBj5MAY9$w?#@rHI@~h#z$zUmuKKX&iD=VHm=m z<3B2I8B)kLiYwntXAB5yV{VM~waB(;9(801kv#6ThZ!EI9dwh9s-SwNY_m)}Gq5#A z;>@il2F|lxn7{Io9G1%?fHv{`07-wfRSx7t#f-VFucnr{fO~~PHCPGchN5BCD+<76 zedGQLzr;}$XgqIX({5C3h_c$q5K_D7ZR1z`%Oj*&3yqpI6X{Dpf=UXxZ+1D<01;=y8ko1u-{D*BDjMwaGfG zo3Y*|9wgg8WXb6^f63ffSe|T>_&6~H5`03fFC&rYY#VaKVVV-B-#4pR%-Sr(FZW3t&~0(`>!Wp?05m!G;iFSqM17aibh zeK{s%MbtnuZYYwuG3x4h#Yb=FH6+V1`BY(6g+crea=U@7;9Ir@Er0Vs5wDsn)*DW^ zgIUC*zcnRqCIiL|2Ojv?BnGmt#mkp`^ZpDFOKzY%SaW+&vlN@Rz~M^52SD{t5vTI+ z96*Qq?Sbi6AH~tvZD);W$^G4-a=dZ$L{IE$OixK3y{#fHON|Y^#bu!9`4LBTyMB^9FqnhN;FAB8K`EqtWZUzQfM{23o zYYW-Zy3$WQHJ#-9T+Ns<O%DX`cy*`j06Jj{TN_BDZwxLv4t%5Kwx)kV@ayH?#Oi z&!0*_yT|uFN@E$Yh=!o4v!`Q+mUG_<;4p`=mYV01B2m&e_V?(O>U1umjTv1WD+D$# zZDjto@y_UP)zkrvPnKEX`jWCo!M8pE@w0+k@TrQ+#x4GfUgAQn1FO00!h;BdK4l)1 z6Yg6FJ~u8px$~LH3DulxhN0uZ-3OuGJvK>QtA#KXiFTZT;MwehYNX9`?-54^MGvNq z*W&>lrf{vs{ez#quv~gwu-Bh41Oed3x0qJw0$!CFH3qE{ynsvl-(?_-HbR zkR>##LATO>PK~)|;(r6JYm1OT>fAjKz~_vNh+6kI=;;EZF-fl&@PU1H{J^hY%%a4; zay!a!wH@_TF7MbgB?t(oy-xO`uspGpJY|5FLSmRfg0tUW^c1R6TCnh2f7-CkWxDJ0 zytksJo>3NuR9|^Yw)NxyMZ`EdXe^6V?#Ll1txm2Y@O~_%F)NFOnRN)Pq~i9HCFhxd zn6+Wn>@(kVfr!uYj86Ay+JJLQg^^91SIiK*RSW*KF;$qwyd33BMV zqCiCvRK0a;ZUnRpyMFGhS0Y&QXiprV_I3u&k3BdWHa;8wbY19z1#_|gRe4<_|$V%ZBeF)c?tT!GTS zN}F^CtXe%#rA8L76hqkJ;*9kk-JHY7B+rRfHrqHp?VxdMi>k9~Ci`U6+4IL=EagZn z=6GO!f7Ow0vq0MgN$=j9eq35jnFSg9;3K*=%?b@u#&a$gocfNBSyj3fY8Y43AeHoo zNrFrg`ieKJ%+~d&peO?fDo9g$mEMF<6qFzxX+j7nRUm*60R<5e z1`7~sh*DIfg8>mqp{i6VDxriPga85}7zil!>@auMtaZjxX>aHUhnY#zE(?K6})Oq<+L+-(cFUL7?)nS{oTMzg-4h)vVw zJJ+?9XFvQPcZNJWDX1EV9Z8`@ZA#603>J7)=!q7*NWGnlY4ey3LqN;ZGgAsW*b!ON zbcaD&YI1pp+y!JO%U9d)CV7!1xJ+GWnb}iW`*^q<&un0wlt-m#S~0AXd~rw@^2dTa z4kQ`A3qa#vdYt4Y3Qvg=T}NUs>wv>`5iXgk2D%qT4WM|{%J%r`(X`D6FFx@ShjtJz zjuBP9n-SIMZj4Y?m$y4h7I8Xo&Xee~5{7J5EmQ05Gy2-J5MLvN!tCQu>sUfkPIKdg?O)D);7@vrJ<_qmg#RrQ&dRn^EC`~^p z)}UaY;8>!p0k`AU3N2qOit|TdM=F@3@=+TpaDf9uYu@IqHI4qnxK6Dh_L$A8{V#DtFe57|6>Xr+BD?QI4 z2sweU3QcErT%4n7lDv9-h>Dz)*OlTzX_-`-%`GxvYZ;t0W@f}#*io{vgjlX@v}{_P zVStAI15W)c+6Qv|J`VK;;csR{vD;BU{pVN>z1q*`h5{@-w!@+sfz~090)}UK^gd?oZJnhy*|Sp9yZPf^uJYA5 zhZQQIw{vyX3adiBBlo}kP3*Z&Qh5jlKI=sJF|K0Li4;@d18LVpm}G9jNktE#JB0Af znhQPDbtPHbOSbBmZz@~DKdzInsJt3Pc1jRcLn^d?7(ZF=G#U*>AW`Ps+Yg^R7L(kf zK&t&p_DHfiv>vlmO)Ne@w%q5d3P~F&!Q!vU8Nv}g_{@Mr5IS<) zriDfES~aP~l(My}3HO62UEDp4&gQ80H=h-g_znkU(}HB4hN!wAU-$Vu|Hm9>&n z4ub{r@9IbNq@n)Ca2bsj^HdP;%k;agCk|ULkXNTHvgO2F(w9-Hh6J0T9eDD1F?-KQ zD20W?NQFVU=?lfKqb7+9Q*Jg=(D@mzA`5VaN)|w$!CVJ+{~cV}i?kMazfLMoTc5N_ zo(t_d(WlpSxvrNdaqJXN!k9cy{8+KIoSrAvNhu`XDPJU*E`@FgES=gLG3vE)ad~UR z9+f3w-&z+?AH4xo1M?S)h|_#fqnUgVG<&$xHSH@db3=LZnYdwOYyHhPwyv_ zODCo~%%tO(Cfv_Q z7K9yfL}*5Qe4|jKohb9%B@QN}WAjYj&Z=5FINK?u+U|pegX|1ghT~dZV3utj$sfl% z$VZ47c&HkbnNp;5tZk0BmRVN}iSRW0hD-ArN;ROFh$E-$b)LxSw3N6P_$hw$VIu6~ z8xFrxYfS!vomP9Y=jPO-QBt^Ry3?a9;TZ`ZNN#9H{sUO&_UCWjwe>fs#T>8Pv5b5;!{foAi9A z;L>T2u_OBNAuE@zPy!JOCu>aP`JejAA0vn{Q%;sWfBe!=9t9JxbH%);J1)M;3l0i= z^^M0+GO(IIH&p8<5nily7?=;M3E zcsL6ffc#5Wc1dUduLu=_HAP z3|VGY>8`L*{tN>^^}E7wtU=L;Agg|&mkFV@3l|T>AQ%yU z)DZ?;hH)FlziOz0W?x$Y$kq`=t5`J6U+zi?|87Redq~DWqAcKQ0;duV2W4H})e>e% z4J!$JO$k-4IB5^kSq%4*dioEucCQy-yy~h~2C`-hXT2@(?ygKSLq!>A5u1#4H{Esu zi9ip=nl{PwA1$+cJ*GDI!4?4|YB~hg^k)|p0TxMC{Q~Q~@zC3NPFUvW%fp4aaaazJ z>+3%QMuC9jfSBBFgW=R0-{LHwJg}31VGR3LB+a=T;;CO;&Irf>`D4djlM#Ng(SA=+ zAiKj<@{ADwQ2rqv*l)Aj9$m>EPFpL|QbJV=BYSH-F~K0?8WUN4_h# z37J|H01yV|%sfH3vX&+;LIm(-o!tqaz28R?^{-put%BFD4e&`{jD!e`-*=@d#{=$d zwZrX)nE6-6BQYhGC3MsWo1BpnesfS|)t3HlK{z6%|MAO(BY<3yW1Zql3Vna<0Srj7 zCeS(xac$0Yyc>u?!Xpw?`Y!LgHncJ8ph;2)u5!|qun&tbVB~xTw5oIC%t%^C{;+0v zCs}|fae)LRJhA;hoBDY=@|MrC`iGSw`8ETxbZ za+oZg(KBj%M#7h_lO~^a&P*y=mc6rx%uA?#b?NbAY2_cp^R&9WH4txpOj5W)TrnPz z!btj|J5qvMQ|ZC&1$KenK0Qt;{aT1%IAVUqwRF-IhGB!jd4{#>2IdI6Mw6Ry=0KxK z9v3|LvM;y-+Y^UH#reV2|K256%WCmDBx8~~=p3nB^^PgG&&kTccM87Q{CN4cU>7)Pi11e^b_}b*rvPg+(A|W3I3bM4&+3CjOlt zTPeEUY?VO5muaQ2E;d4JE7c1G`rtxqo5%{W+S>DD6q%G#-PnNUC{sffPS$+AGE-N= zQK7y)lP|PHUhO2A_o;Hsh2NR6(^n4+t5q8py788!h)4TUo#y5gkh$rfZFU)>JOM!N zf9uB|!$AInq&}isr5_c5E;?&B4{%o$VMu0Aq^1kP7NPpE0S*%%SD1;zK1?wDj`uBP z+MK#oVO!BHwH@Mz26{dUI2m=mW=a{B3ur(!+cg{J{ur*Z-MbwGbhiesDs9sNfBiuY z6K#q?kNksb%UCAJ)Rx)AsMX@DH^G=`ftyIelvAzWKCsPNA}`~8VH%(qyc-zBkvfqz zxZr(}nvoDNbpIEB)Z3;f6;uf70i+I8b3rm~+vUW!aI?1UYLD9Nyaf#JQu>~;*gG|- z{|kfqZ$Ku$6huI!I4QVEt1rRp!|%ljIelzu&vulP(3~S85r_m_1kjuEtPvs zPsCzzpZ@uWbIXa?2ol%W6?fnq6<&GsK_S^$E7MY)*X7=qZ%EjERp1W@lLm%pJ7zCPMV)ecMs`x+e8Kv1$v6TWKGW`sCVul*qUzfdmL&et9I2SJPyzxEfKa~G3 zO#id&-AW#qKHj=&0?2M14Z!J)*MDXApOtwdFgbAh0+@Dv86?VoudRj^4uWNmt()yP z(!XevKg-^2y?w}dw+(Rsv%v~i=U5K?m;JC?naij^M1vRr_^6!&6w?3S34bwQ|Hqv$ vy9wME#wkIRG~k;91lz{}`qo?^d52^C7x-NT-^%n~!0WMl$1M3<~}1i!km#K>fHt9%kL*;JqH6e)=4>DXW}Dou;iErY*vQf|nku$7q< zRroxv2tF6oBjoiFT^djd*Smec6#mioBdOWpMDhc&!=oP8*YBlOI?LWPA@DIu=tmSU zzVgqbugm~6v+7kYStW#SOcJ#> zXEY(iI@Sps1+L<=m0_mC&6@J{i`L1MK)+*&l$RcY>_di7EJWPE?4fR&8rMlY=@cuc zyx=_1^ZVYfI*d{NcqpY}5|8Db?`?iCC6>buBmQI%rd zug{4HL|Vs28(EhwFRmU-Dy#N!Oih+#jX1{NPPyz`qn>b4L+^ITJ6eh(+Upo=CJcfV z?@dR(pbU}?RH)wLy9vp8eXL$ucl%7O(~)ZWg`x2=6jnU&Zj4!P6Yyu+!`p6BRuKPC zry?bFL$yMEn^vzG6Ua9PW)^Q8JeMpWBCxV@SQyf$+$-4cqlyb@Kkue~gF28B@p{sA z_!AU^Dkym3rT)RU5Q8MbScT@bYAE|0?y8+t<7sZ-!`;EtfV$wqfMTfQ=yjU%N*Jx% zvA%4UppYPBDZ)`4DdKrigA};{q(nUC0|xMnz*<8B!M{ zNVMH<$kKhiT@#+JSB8%}N#{b1-4Cm9O@GE$wu*OAa8aE0+66QYG`~?olx`2oar^!T zv^VsmL5wKc8tZ+7^~=KnldqEZl`7t(*`&K+&?kOdCw2A)G(9vqF=hI_6c~pW-~Bq3CAgqEtNMK9W3ktJ|@fH*jNl{P>HSm5~tf+vz!~B?4py z#(CBuEq5&YiBdR|RH2g!*Jlam!?}zu^-?}SnX`DG_}%TgIp?$*$NdDWRt#YobI4G% z5Ns6`f*Q^GrGy_5R^h6BNtP%`M|L_9S1v8)A1C?OB$OZ%@iF@Np z19};xSrephUQv{cPvR`zx_NVZIvJl6E-p-tDohhEiNg^CP-=Pp+lT63lpG!(+MSOY zT-S!>9TS?r(566tKPlgubK`K)p$1N8fX+6w{RY(( z_aJtyhwTT{AYs!MO~V}UWa|bz-((=IEWmQ*`j_k5p0A($xrYZeL-fW<+>mwJ_75~X zqC;N<1DBf7F29&0%3goZH(EXu-qk^8hqqDGC(EWYSndUAwjVI&{Hx4;%W}K9+Rw? z+ZVU}-Oi9=q7S3bthlXMQ$T3#BgIM!mYu$kzN#i4MIl{YUEbE@y)oA-%Hf}z8*KFh zw+}*csrZc^crb3GZJ#M5?hj$H!?F@CD)>PxfO4T!izP9GhUkwRTJucAB8}*-R$pk* zq>bOrFJUg1Jv^|u3(ae`C3i`$o;V^wg#MRAPRzi%j7s3w`XwoNMk|W|5?)299kjnu}a#TEl zzmGQRjcrIOQ#Df62d?%r9Ugn~vXCXI03FozZ^IlTOq?>1cFB((%;XRfXZ=pW4kF~><_+5WxWw?v zSYa#jIxMdVZGSQ0I@d`9W%56U2}hafnbv#$DgDs> zkt64w6p};%?6qst`p-_Pjcfr>rzIS5p_iM6gT9|@sB749ntF}S6zEfi-a@XkDZ#Pb zyPq#f3AQG4^7S?T2P9n?yY&U1mD4;*lmz=Hnq))GKat~6q4d!i+2}b)luCu3i=6_Y z2_K$Sj;%0opfNpmU{)bnwM4E=^5+IQG@z6&hMhTD0ztnu2jOX`e(=Xi`kcu|o)euc zcapFncv=5}PJHb(^p6Y>4%J7ieo5OM9=I1i|H(mG@;*^GzU^p8yHvJYuboG}Mmp%z z_mc7wQcIfcwTOj}IMb=(0_3QonKR^4c+{9~@7IM^xrxU<&DRz@#LZNiGUXy%UzwE4 ztn!-PLhhM+dQfh~7Z*a5Y>a6c8h_C|~jGyWod<3-c=mxr~u3vySOz>#&Q&2 z@|-;YB*<@+*$|D?DG)~5@5 zUUkOv2<6|Mu_?_(bx6Ix{Xz3ufX6m0V?S6lkNL4 z_`*|>e3|w~8uUAJct?jCpT>?3Y4+;}oqmAOTDxW@P{0katO){(BI;DZPR=LTo;y5T zjnNAezTiwC9{n`r81kC0PUTr67Ax`h;@G+5UMI356oyZ_%a*)SEKKb^ zR8_Omv5#r++&*ZrpNIu!yHVC*B#$6xqC}Cb_2cSpbD9WjdMv%DFb{eI z9cihoy(08^0TM0U7X4Xy5GO*q8;>1P%W6Jo6l!|5{q8Rke=0n{YbNn~jBl;#PAT=Rvs3{&} z6U+Jw2fe;;{&J{i6UsbQ!kW!Q;NAEh1o{)hlmORhKW5+(4fxsvl8G37Ng1!c)9Fp( z{;oU~D3yz0{Ha-r+oi8#c(T!2HW|rHFYQ4R56V*y0w`8wUf;^_ujRr9Y_7*=C0cdG zn)t@4q!Xn;&WVdS@R7q(JcwuEog-ZUp~v@(>9XM<&xg)kB^)qJGK#qJ3`#j+!e-*aw&Oj`#jR47Z=< z|L~QL7+3y=7;C$qpERyRY5b{HJM=M9%~ZUC@}1Q(Cu*j@)hcU(ySzJB-^7FQ-B{U< zTy0?DZ0e8FS|NwTYm?G_cI79E;I{~^A7r-uN8y)|;%-pL4NCu|Rk~h1p!u0Y5s*0L zX9^BDU!sqN?@EQ=?yBB_$gg+wF)V6{UvfV0agyoKQ(F;p^@AcDx4W|a5UfkNp43I! za`|g;pZEKof$}gb`pSeLU$` zUiYiggwJn`nJRPA#L5xtO#6c#I{s{hLO%*YQKYxoK_|{VoT@`X z27N;9Mi_Q)-XH<*o|}9KbUWYI9jrN?ru0;rw>?YXRCeWM{IdpMPUF3m3o-6!|Vp8otZzQ~qBe zQ#q+~v0c&P3gO2j0f56^d(o+#XsX06{V?86*hb^CH;Y&|&-Z4Np{`WH)(=-Xyogaf z$5La#Qu2e%Hg!3Dws*y;7t17(kvN}L4m+${$ERHH8w6{F(<~s@WI`UMX4qX8`{=fV zj)3-Mf$Y|BC(F-~pSKD(KFuI|^Yr&M*^SPmS6HLee)uO=E-wS9FsHLwcbXxCbwlG4 zPW$Icee?P-+r?@Xd%fs@Yfo={U>j_{O1jkPr4>(f@{ir+5{QUZ_!F>hRukfrIqb5x zbDuFdP1+CTd_P@lmwPJXCd#P!7Z)X;m;HcRmhq#lxztpU<){?Ks-oD0Y(W@{_k+G@ z6D-Ua3QxK(B&Cz7v?Sjqx>Hzb>~Ru9{bJ4#=24hub+4QWo@Pijys=&o6!tM|O>7O{ zPSG*(tx`>q(^&s0kGo=NYu1NS!q^^Nh2ZkJYE4bQv%Pfq;{&YV(EM* zz~Oi_=kgFnhCe83TlDt&5JW~*k}Rc)Fl*&d3^)Erz1Or5yA7m0S*9v31`-XuljkNJ zdl%lo|6;!Ri=L9~-B_^SLks+1yt=KTq?Aa6Z*x3Oc2i0pPQ0kW>6vA!YNBeXkX9aA zl$9&$WFeHV##tD!y3<#K&igO&dk_HEErOff0EDou(Nz=6rMI~|OyqmCxD6E9ptr8N zKXg}WkT{I>Uo%Zuc=!{_yaVmORzm@lqK^~$`@YJ5!qVM&f`Q`6!d2Ki$o}pnLKxzeg=dcon*fmA0XSH+3 zJWSsNj%B{p!8g6MKxE5kxzs-huzorY_<}&s@V^58 zw0)B2iR!F|sJe&ijIve6x9Ko4A%d}L61;k50%AJr=gQYfv?Vi;`+n*&qpcVmN||F? zTl5h4FdF{Tk)?wkFm%5N%Oys?j5xd|@#KFP&WU;c6OqNBU%it4O-z)srCg2trI#UA z>fY2plTMDgC9g2hR}N;CPZ1to_c`87r(E?mQ|MZVIC1W~Rr^y9uYTkpV7{hNi+<-i zMSU~#U7|~4ldx;x^Z{KE-2Isc&>;JU$qT=U=A&cR^vwJ?;`Bwow1xU9@cI8KgZVAQ z-1~mWq2}?pQ$QISpC*w$>3C0mCpl{x2nS|H?(2=#tkj=sC@?{bG{MvUeRbS=MW1bp z=R<($*V8EzFt}d$lI(N2*#Xbc_8Zi4 zkGH^IxTVvW{;2=SFoPZ|(sT`ANEd}V1AdO&|Ei%!7$Msaw}uTtX8SVFkkS8Q!hmw~ z_J27+JNnMq4%52uA8KfztDH(Ie}6JL><&O|)0U=}RZ~9b{SAVAl%<9B4zP)--QaCS z#%acbZkdXXTzzVz=xI{y#{s%+#xOfoPzi|PP3+{=V{LKI-!)~r0sr|GvCL|* zge0{X_Y7LF5b(wZ-|{sDUpY$9^`0DEh^_ox0N4LpRzA^-SV%z0R^4lX(-RbCPUOG2 zRB4PmP$mC*z_ZW344Q03ZSjWa^V?@iT7u{J;8QAvPhh-1SnWx}UF({sPFsalXQK#S zm<@Je$1Q!uWUGuUUZvd@?_y6k#h$nTrWkRr!E9PqZB^N=jfGflp53y*vXT1?m{gh7 ze$P)ShX>3;eR1D52+^?ATnUT%apN?rS*AC8>URb_ReBa7kpFYDFBsRZqvLC>XX{)5 zN^6}h5`w^K^_MYZD!o8u&mR6uh4}+K^D6CminK(m0pY?%H#&rXJq>U6Q<6b8G0FoF zkkzKOs%rQNCJ^27GU_*a`U1w-XDjA1#wekS=qfdjKViaE{5)4+X z5-jL>%JOr-8)uQKA2To?>w_&^xJgQjVI7@XnRDrjBWd^PLyfFKe-ri@hqzFUM0ul9 zHx$a~_}4q0UGjc{ITFIyzQIVqz=+QQ#VTtaSX^B+Zl=gFSN~mVbdl%FQ~Qbl zZaTG_fD2p1X~oyHL`&7;wd4O1p6r{~U!9E5PqkWA*$}6)_0(9|k@g6{wz*+_6af32 z3c>&wm#Fkznj8*R)3-T<*~eaa&3o>Ct;vs}EeXmJ**;$yd}Rl)Yo|Y#JNUIv^JQ_? zIU60NI~ePWKhiYdOPFDgZ*4Zz;4FNuHpgLur0-yWO3uvP{XVh-l9Burk4)t) zd64W6*i@v6S+9OLRyvvzK~*nVuk}d$E>5kQL<<)n$0^Oim%q}KKSVa|9V-ZGlr0na zBPA(!z*gitTwTV>Rsb4E4K(`o9p6j#I}!m8;}0Xt*``xm|6+2ymQ$P6FOaUm4_4jL zKc)8v!TZn^f0uhUu3}+Rd7L$GqBR_Ip7S;(`7$2G?*XpuD3QTe*u~#YgGrLCAMDZ>9AMV1&m` z4{SFhQck)r)d>1htDJS!U!Pg0FCVrsOa1b1pN6x&;w4V6ft+1?(fpH};_@8t4cSyx zxXhK09JU8G5Lacxa;CkU1xzS9JEJ?#hau080NT{@&oOK*Vkkh(ctoE-@{L_ADk;pR9Psy)RJrebxq-CFNN>8}6Q%(kw9x;K z0jYq`EF4v(N_e&Da~HuAK?$Fr9t?BecLNq*)8BqD>$86^co=~*!S(_Y+UkACC61~#z zlf*46HPldbN}H(5kA6&zI-<7AuhRRRRn*}&4j$+KZ#JL!U%N5MrcP-uJLuj15oYcZ zd2RV^jt9)X|B<@x4tPRWED5y3#Ru|gE1ISVTKiWH%mF{J`Zi^wMo_$FQ7qW zeFf>nyh3o;N0AY9z5m?083}*^t_#^|cL}hUsF4?UD1aKJAIQl6$7XWW1^^kT{-eU4 zy}wYeYOA?hfZ_1cqWx{LpS$s|Gn}4@pSi_nKV}T3RU;mtZ5Ei&-0jaZjkWW;q|?5~ z{kx+`ALjU_yZC$lu%^Af5-icI^&#X=-u5a~j-*fqDNWGmE|H_9CI)TZ(A{^Z(>5y$ z@DeN)e@Q2XUChtiE+wT6)-jVi9$xGPEkw@oE8eIphCtStrX_Zp^S_|mpl7HX#@;!Z zciqLFQR))56A~dZFaWWM20P{zJ?(r4bPOMtZ|I93lc6_bJNGfVXTJ-y;X*rCCf`2d z6+SNHQ^hd4E2w01RCvICj0$)MJ-P6T`0hZ`1T}%P6Kg**&YVN1bpiW9HEAkjO5vw` z1DwcIR_EE&pc+K5Ru9ivW#g7Q{#4?dI2jf&5RdT{U(az5+US~HCxOH}FRO<-MQBF8 zA}_Vp-r5`xLH_??4|oRVL*-Tu9sm^_y;00-tDmBfO?bs&qAcu}JWJiDH=_N%(glsM zA?#Hr@N`VEs?qqf%F-SEaG&4JYQ%l0L8`G$1L0dUe<7G`1L@|&$NpmH;o&D2!19@YGAOBRop$QQ)M8^?Rz+cclBnCTLD#y5WH6-Rz&*&`}Iy5-X4 z>W!Am6$HqfnW7N;wKVar48iZ*j?0qH-uon1>dFT2!zn+OW0Do9mn@DdWIYC0-Xs$^ zQf$;~Gt2M(2Mqv1t(q=pKrIS32CC)zI%7Vk^n!+$B1)wYka3gY;8cl7e*hc-_@Vku zRP_s$YLDarD1d|b0q*{VzkAhfY`*Iotia?er9f%SHN*g7DYdWM;Sp`-1Z~C)I1x2# zuwHLAy>`KC>Wg;&PEQKQTndenWww0GYcR`oJweWOCiUwMS|NPBMUKjD=EAx@??t0h zOhU_Nx>mhfWAnWo9xbe9u6JLeIt6||yWsw=i<*Y;Ha)O;_@a#2Xf63fv3H1^Q^_cW zW3Xfpk#-`o8{Tzkk~h;MsA6%!Zp6i!(jq&qqgYVeuL2(Z5H`S+l~=x$nN7JmHPULZ zEgvE)JMwX0AuWH}n(H>XXEAqK%A1H>yp&s3elH&@7K-@C9_ryi45%eJe8SV$Op(Ta zp3z7|EAG2J_R+3$(IC4sQ(1N@C602)cW2MF2r#x z#q_7Me#mJ%wVCcwnV46myBVHW^>O<3c+V0p)&<#J-SpZ{RR>KqrP@8FlEDnT>Tz6G zG)@khY2gl_ew*mOjf^mT09C;!K>S`lor`8ZwC~%I)_xYAmGoy8jWcK-kj)#|$V7G#k`($D65%RY@CeU(AsDKKjbd(>;_h#5;$P z%c+6xYlG?biurEXihqgY&1JEQT~0*%#?}?4F_Nk)k3(5=iKF z#?NZDRzwAEoMv1k3Aqa~Ach2XkWvEVN-;@EAc<(am;eDn2ni(Ft4?=2JNtA0?2miy zdEfWW`#$Hq=RVIl^XI*3;V(zM3;+oKc=v~&0`RK|Y|dU8ijAz`{aox7Qu1lq`#`2$ z{{%B$VC>wp6M$yHE6WF7#O#-j?536g5Yheg3F$4q`56GOocQ>|otfqIsj;FjHYP=c zS_xgF!OZsIZQ>C3v6Xg_hID2e*zXhGR5=f& zhJmW{Ywv}Cyp8;@;{aIE$Y(|R^LYoKngNq_77GWl={|a%< zRq^JV=4A`f+WAj|@~BF_$l0bTaNsjN<{QbLMkNOdR7_3AI=z{*ODRiZ(*fk+1&={H zQIG4-oxR_VKO=Ou@yXWU7v6B+YT`S~zoStqDRO6VYlA06JY^rV1P5lRM)V9a`9K+9 zXRA<|XFDEkbRO_H*I3UISNLYXYPeX-)y~T};{z74PgY>DTqRJnmOi}75O)DF>^M|W zu*%{_9@+_Is?L$5^vYFAekY(T`@=cQ4@wroG8{yDZ5_qEdEu^kv5w1Kx<)U?$1v8#iCE(dZs#bw=(0hb{X^kaD~KnG};F@3mAyDj}|QCKaWk% z&wIPL?M0~wSB&GJS`P9^=bAM-8ug^26W{MP)%K(;c$kFQfz6MbgMVtG5KDMkNg%`j zX0pg=kkmgJbYwXQ6bJR0qsG;G0eMT z;9N#gPjiCvyhEoH)9&}fb-#&LwG(9w6nPebFLHOWtgcbty7@Q-ouJoZkgobwG+jvz z1%XsS>JG8n&d?%k9sh#6onXlsh40W zrQnv9DRUZT?g?8!_q^udLT+E2q>@1>-8N(1u!NtG{9H^E_yi8VBvLZOfvj4ahz3Il zoat}h;BFa$y1$`_&3|~z&K-8Zw<_)%`pH_^-44$Aaj4GYbKPkpK9MKrRF9G$A~x+-?kjbm2vFZR#j2b^%zGM{#yzn3NUkYVn`_+CBAJEx(*RcI5DDU zFo!nBj3p@@x(*tpfFP?Ne^iReA|M@rD96 zNiqk;F`D$>(Rns$VQWy=Atrg_=GbMzRUN{buz6A26Ww^SUQ}mO)6EjW!g_={d7jX| zL9XmLmZo6bAQRGb>bj341QjRIu`WLep=u=%olrnc=wG7vc2Nf&Cab@P#0kVUxAd~m zbcf(!7NrkMdM7a?vCckY&a9fcRWFH0hvE@2j-kkcTjXrrw=Rf@NPp`fU3L{-?mTGY zSxc6q@9}ZO8EcYGn81GwtaoyW*V`<+*RHiD%_3KctkF$DZZ(MucwY$?!;=B%cx44P z9==-@0l>PquCD-HyD$a=fB*IW$X#(7hGBjqhbKz+kAjYb70DGVyBnRL%9EFRYSrwc zBX{OZPcqak!3fLIFwmi?iijE49J!PHp1?;-OlDlR*znV!iV}J6!3WvVd!13tTmpK* zb09U$8od_tt-TjM0Xo94tfz;La6a2p-2-iL=4T`brv&Wj{Y_Z!K;CwK==@^Lt()(X zMs{wiaSD6Y*%G@hSx-j#AdSQ~Vm_O_jUQZZuC`q7K9%JaklitDObc@>=WW3VGX_jP z#dI+7c58}F$x%ptSoY zA&X&9(em%Fk=OOos(U%wgG~lpKgP7~mh663m2i%cDj;6^PM?_{)}Fl$ZuwxqX|7OVj+jrz-gZRVL`&jI5|mqFhSJ!i+9`p1i_wU>{h{*( zt!P8&y%WVemFa4U#N1$B1Aw!;iYLOMWL3H<0RVF^c13%xZ04`$$`>Z<d+~V71{jr9J2ETP}Pqu zXu!wuP6qI|Tx6$4QSBLaP*tljl;iO2^@v=~Fa}0)R7QZ;(p}N9(b30;&)fx_!dgHS;p)iY-4EGIk^abTyP?B97@q v?3@2qi*NYh6SGdv64wI2%viwd3>KBb)V6gmfrghVu;QKue zzv?-`U@W!JpKXnZbPE{l`kdOuUv9b?&kSf}+Triw$;{gyyb)LJX1h|pC)t%25D~Eh zd#98=g39sG?pOG3mI?p5(zL5bA6Y2qk$?M5_Ko|sKj4>3Mn7*m^Q%rluJNTCwl`a4 z%{#fYoZc6Yd2D#g@ML(u8q;-}Q)ufO#nq zV6geeRU50A^|%CTSptd$1{+lMqBbS0H|0|Ma`hO&-yf5ygTd}c)d}o^!72q=B*05f zI0kKl!Nh}Az{T9uc+8+n4t;;gufNhWlOU2h15p)~X`mVSh9(mJ_DP7RWVtfWU1aO=XqBbHn%2W4l_*ZpXDLbY!((EL9b07qMOP%j_U3AdI3Ddi99mXtKemX;7;5Bu@~} z9xo9*J<^`9C^AMiJ_CJ?v-wEl`?Y7>RH!H^tZlqd|DZZ-_C9ncrepOMG_F3xkB1<) zxB7P>hZ-DYg#=mQgcswo?d^t3&YHxDNiL4q9r1zqfjic3Dc}RxQLb5wWg$c+dukjm z+?N}q-}uHwsdt?#Nc&+$1)~_2ho&WM4gFZSafgA1PGcUBRaxjT0J2043o_UFhfj!a7W9mN4DtOG)<=7zXI%vifZt;JgkUQ6k0>r)hx zQ2gnl-jliGH3#VLIqkAm(v?+`=wRo-C#%jK%F&)|HBs}4*q_vpbeBIgMq>XFQo}Xj zxD+OJ6gr0knOVu~Z#_jU5ijaeUbc(<$^6*)9S)3OxgFhWwf~V&wG*`F*k{sCHynd$ z25c@y6CVPe;)F+%Qg#6`QOkZhn7)COgsPqmG=SB4)bCK2y|fu%4kQ zaoSA6#zev@p#h=Zo_|NTV(>BUX;f}Goc1oK+}{A-luf=I-X;aJsPbMz(5z@pJ`QWr z65RNcwb3X~ycK>LxjNKV`xH0b9&~?vFKq8jZlw3)Dhm*bPdvR)SB-L3x1OyFk4 zL|8cI8abLFtIG1uAJ#7Uu%w1bc7HSWURdFyvUU8PbM0Mv^yMlUYvA^YQ-Rrknb$zZ zq#!-tTB*X@^p5#;_QzSX#b>T&wMlb66H66<$v1b?h_sEBZtoTK8~!NaT*|c$JAn#^ zZ_C<)V9gVm%v$@iwW`F?VpOI-Dk--!I=xd3k@7iet^DkxVmp^<_Fa_&u}rVgGsM?t zKbZ7%>j!0Vw5OPjZ66-3-?-sfq3?EPO+Rc8Og=GJiEKricEwXE;O#^ag@$%Mh1d$8 zguui3C$0;$HT=;@)j^5wYA_`bbWh$IFmXz>iis$g}x#*8-o_%#zr=kXFj z=B09cnB93}<7p+NC~Kp#8utMSA=td%kcsex&Fh{av7YDDCkJ!aPDDq@=lOq<;doqr zsJ>c8ME(VbzUR<)*A8)i9j7{;Sm4i`d+76MbCPLfUb5ZpY}xMHZ9O%r4ewMwq1w9_ z9rUedg1OmYZpxaDK_AVM)!Tg~T1clz;#spMs3b0~K>xtRba5W`V%Z71XWb=p3B59N z?)ijC+d~D^GxXl@_br+vP*-gxa2V$ z@J2>^uF%$-hl4L534wN@W^iOg-^7Z)=&SNc<&WsYDspC)?N%=~qg8b&FPSEURxQiM z{&`HqPQv)W<3o?Z7Z=c_!dlHQ|{{k$03c>ZDWbM52G?9v(@ z&%{I&+CUv_?(IM3XKvf6%=kpLHF`H#I0FIBADI$M-hXOH5)y28X%^xTcu{hg+r}JR z;e&3+!{85jw_h=-8{}ZfiHjeY(t`7<^3(Iv_tC|l{Uerdd-W?X zirT*UrrCM)a|8|MAi^SXpL+inT^|PN0Unql`(_XN+Au(mNtWX^t5K5qpsma5b(#{H zmv<1B=gVt0JHP9Q@lcmIqoKHb5$bnL^ME}A%=jHDp^k?lc44*)7l`p62DkMQ@>=15 zwu!%zIP7fslt6>e9MhotTl2&5E#~|ka8#$x1(FnOP@zuc;d)MA2K2e}t*`qj+iKQ4 zF7WQT*xR?7p<2DaA@DbYR{wxzP?{Q&RQH!l?3bMo*$ESQ&muwPPU%LT)OI?gzh7)$ zc1%~|XRC|WzUEB~T}tN0T)kfYy`)|mB0+6JtbaeUr3yww4mzmX~evBO=Le>4(`AO^k?L-s46k^>$~9^8gE#Jk6N0W4g169n^-7Q$({M$KTk@`seBwq z(%-K;qVctNHQ1z?VdhDi$$X%xy99l7RZDy=sd5JNJ8_YjrLrz%gZ(ykDgu*Q0-_F2 z=C9p3GpTKTEXzc64*=jWue|xVmX_PRr%*nmac`U~fC!um%Yi;C zfe^~b06w;}B?lbrRLEJouGo_uCG>yhrG zqo?6_sR7kbYtEH*KaCjx4UIKp2;D}QTzcc6~`FcEG7J#%~jB{B=GTro~&n1)}3sv!jwO^ zU;-C=V&wZzY3I=@%yB7&Z4V@~i!-tsrSgi2TA@cm^*>|C{xYqST?uo8@ENbpV}rA} zaW?jiqwW)1=DJORH>HrIMzo!wcfFN6zy2`1 zH{ET%;Vd?-$0Td^cax3v^^H(gL4!|T<+1_GLa!8~`mhlOot*akR*KE1> zUeDco$2ZU|6M`fwJYTlKcaVE?{}e5=MN%nF8%DBRp7=G>OM0T%=lfUpGCx%f1OJ5{s|!7X z3DU#v(yS!O3&QSmuetM6t4_^j;#u}AQd8B)GhF0 z40gX(V?*|}52s5nc40CZ)yr~qSHttpWJDuD86Zmebe)Crg7II}Dr9dRf(=^V4O%79 z%m#1~nsa5ta&vSrQON5jGBGJr7_&4ZWB8Wzn(WFU+OT6zz{|4H#ku-4toq~Cz-W_= zQIlsGJ}6$idm(uUKoHb)UU}?pHKe-$SW6g` z;*~B`o0B*3kD@pWkfgEG+_koUisM%fEeVQfIK58002_3`vq;q7ebDoLcTPfOPZ#ik zyn9l3PP9bF#W2wEZaLMCCLW5}J;nLVrGQ6VkaFx%nXvxuHieEIFo)w-P#?RmvoIFO zMP(iOQX14Mq3<_&1tmV zXLR(v^aTs)!Jn`RDu++r_Bz#K7;$EbGu|&n%F~Rho^OzV%xm`8x3YdK6h&2QsZH2$ zU|7FXr!tLXDfERWpvy8DwZ~1=U5KcSm&R+6yWk6()q0G7{e?4Pgy7>*%RF!Yhg?Ks zmiN4}T=OpIzU1)f#`%bpJvSmpGSBsmY)9ev-AZ%$X)BGGwM>l7K&Tu#_Z~nEDhDR> znW5qanj?pPryobDKWU#fYdp$szRtEyC8@v#F+1tox!U+R_Bk%Gpz7QY)?iL=f6XTa zih*Q@Id>1#bi-6vU~^7EdsuT&-|AuJ1qaP~CmE!6%KN*SF%u7UTa6U*WNGj{N(tx zzE0n3>7h_b-XP(fpsj6V4fSm@bx@SkFYX#Q{s0F+VdTf zoHt}Tq(7+mx625Wsi2z{v>WMwb>^srnoi^97Kmw)3w^I*$+Wr=qX}45k6_-;%8GD` z+7->HR0i|tnL>%ShWoJ@2`YDV7?l&n-v8wvhqLcBb(Y@$cx`cJz0WKXt_BgiuGm4q zB4d4s2;3ao@nY21^4D`VcANdh_@V_<;^v|*lR2$pUh2BxJ94{*>Lw=^EO9+E5-FEW zsR8IPKzVt_4Q!q90%~Cj;Be=z+~yvlR<;RTdix;ruQd^{lXkY!Kd0TfoivNs;yu&V z<{}JLtnQo>QdY5=P|=vHQvuuS$X>890QTb$6^Uylt0MMEUI#ERtM_vaC^@&U_yvG{ zqz2Kw;_iCk?@J>)$vn(gltB}a+aIV2w|Hp>VX*iQvHWH$xCc|K$Lk7rz%)mxJ_nqp zrTMDDkTLUz#<#)lkG~PP&0iIWmR?;85O*Bd4&yt?XV&Mr61*G+K&?z0B`GZt;zFhq za9qsn2|qE*dp`?*!*@fi4^8^B>>dak+lmz(>SBP|4^OkTV`e;faiNX!2Nykmy=f8( ztv-Lyk5+FgiB!(2wTt(Yj5RSvFFXC z-e|_<(>P7i3qWaxLQcS{1^9gEkPliHwc5pJr|q6G0R|E(@y3(MWFen9c;3NqJB;V^ zDHaJdyh=wTtf0Gf@zyXGPKyfr)nK0=xJ173GQLi?_zjL)XlQBf#=2<9izxu5;SMek z$pVwD}Yo3z0}bu{q%OrSe6}?55P3PgkD}Gh3MSOOP)z zC6>*1F~O=u>r`5n-MWF+<2t{LU++|+@p}UkEf~L{2Ypk{N_!B~)w$wg z^YMYvXA&~bh`Uxb2~=#0x-fa6g;aJ_r%POZkGdVHZY_;-R;noKqlVwI+Yv7#z9~DO z1H}2X)BP~b3w%TA<7cr}n^i{G^;HEjTXQ?44cMRPhs`OLGPt))GKv>rEh*ER1w;4l zxn*kqXM04E=zKYDlC2%5Ta~{g&#IRG4Y*lrvFi_V^qtdHaKn|nVb7K1?AtpPn>7$y z;c?4(BY)&}s%sCRMG6MGfMEZ^ae6ASE~dBGw>3^vA8;aKrMSnD(*OW+JbOQuJz=c; z`No?uj%TyI_>6CwQ_Ud=3-awophiqDJ)}cB9jC$gka35#Ec@5>OZxdS^f&7@TqU@j zQ8lq8ObTY!yAiBC+)FgI-_8kKM~bGNGn3@J@oxohx<+73uq8+|cyAd>kEQ>2D$j4+ z2RNVA0HI>DHc(7ES9tP#P649Oz2p#Py$Zg9~k|! zQ=)T1sl(nM-#qKdMq}x9}o|!|NZ`z);^T4&3Zkg(rE-__9t9q-myRn#(*5^Y6!I z>Bi|KI`?|z>gqeUZWdJBj=zIMvs(DB(*5qA8UkA*^Uk+Y^4e4}$^Lz@nG*eSw4d*u zBY3Zs7;E)bPc-Lr)f{S?DCnK!y1k~BTRUpbw|7-RFeA7A-leRuV>g3>LWwqf2xl$ObE1td6LWM5n1(Eu-#X???IvsD6+LUuF2%Wd!`e8Q$t4(&OzJmN4ZQ(mr#6SN4U?; zJ_Kb`-oM=^R=Uf?v^+fExItBn9XTkn{fX#s_78O0kih7`b%kR-KEcASZ;s2ppAu?5 z2CF_)#cMX*x{PL#Kw%Fj)bb~WwcYUid^HO($pLfl^`r@oZOjccRvp88Dr&5RAbenx zOzsh|K@T1?62Y_0Gcp&ka-iFekdB1Go@)eh?TJ%mQV*22wnUnLtodZ2+pF9zOnp4I zGw!2lB2V@WSEOaUFRYT_72c)m+cUtIdgO$i_u(gk8R+Ir?St#!` zs>t0qpEJq$Gq{rDrwpNry|BCiW}3#sgT@u!T1;A3GG;h;d6Jv%J`E!%yp3{RZ}vFw ztiaH+*2ZHG>{b*akjq_XW};xL;Z%ux2y}Gm0Z8d*(f0`MK65uQ7$E#R!tWM#6^PG9 zdnWmRFx185Ey<&H!yNqE1;%u|M|CziG~#YhKCP0s=O@q`<<K&FXACe> z-&$n!gy%he4+UmblhC{oJClcr8BtmKMZE}9uHAM5*kr)@Odc!pF#Z9rVy(ByQ4ZCr z7yd9cVI@hqX>|d1D>suxLR%6?a@P(uQ=5s?8NQJpezK(b?Fp)uX&^+Af04pSuuf_{ zENClyEG1|kV^s^g&%^`u=QC5>p|T}sv4(yQ&cS7HiEUXc$3cVB>VcwpCej1 zDZkE$=s(I zHqMH{3;T%WDs3Dw64yPQ9N(`0e#(C1HCU*#%Q^hrujH}gL%JZ17qyxy=hKv64wq!b)!mzr3oG*`&QLpG}xlcLMtsxO?ZCdTaw`x8tQGmf~a6c#|I?k zs_HJ5h&Np8RM!^mn?{}8=T{P8cLO0aE3U@>h~f8PU98Z6a8VK$U3gABguJoO)2QW& zC9z)dhNm0{qv7K$ z624=~Q;Lq}GQ8}M8RKmeffja|j&Gz}8_x=UK@PgTtywr1{BPNth8Nsve@&;mq!@iK z{<&|;%SX#36x#d0dy=2OXDm6(?b4p&vV3Yzhf)qn{>tyij)XQN*UHmY?>X-a)!1$z z^S+TPn3}e!`1eLznOAFcwj~yUE$_8YWoQcqg+Co=nY8n)Kr%`jHIq0iN=}pZ*K7Zx z^t{u|nbzt18coT1Pj(SqR?{Yjw4&zUg(4@T;&UFEqMJ0(;{um#5ER3X?AS4&&*hs+ zG4uz?-x;nQQ`2p}>--CsHhIiuANf7rxAJpN>|tWWQg+Atq+<6>*|g9odGNrb#X7q^ z45nYNHF?W)AkS3tG9Bt`0WQMujBa`rvpU-GHU4ZCDZ9JA<))O$^zYNpMKy>M@uojv z=Z^rwy?=4f4dXyV>!DP&wohPnmO-M>o!$Mvk}AW#D-Pw3H)fh8?>&(BT>VN2 zSk{sKF?yMpiYmL~5XJiq8(}*IL@|9ohrSGnrj=yQ-wX)FV;P!79A8|7)9M{ig#z$^ zTeH#u)FEzwnD0uV@{01)53?tlBKA3d(gSOI#yNah8+13fJA|-}Au^R4SqqD2hJJ7? zjb>(~TNG*JMRs}!@exFW@$Fmh5RHnJzM?){KQyTCJ9Dl-CEnZk1HvcL7wV>|xI9fSdxPw_0A*Wd|A4>2o$JG#5?gR5 z!WyV;=Xbyiho?O86r3v_DXisYzU+b!O9uNRZ4KaH=QDy6wT?&Mj!~{W zQ_gl69JOgPKyaLbe@R{3sHp;Pp$#?8p362?AI+}J^VR6Mz*UAC)G}kHLEm>V!BfOH z#yeH6v=N7l+`75%DZS|2YJK!RW^6?DfA-y5NGsL-I%Ag*+G*&QqK`54F-@mvyUB5! zE_&6;+-Z|jAW_M&x)^5ii)PJ@XPIXF5@-mYp;R75NNbNe^Jhp|)5X-%=0NndzP-7U zI>@Br=#t|G#ubK}Mnu!y%82PunX!0lf%BZewXhS30hhRyTL=jU)tRO3z@#TS-Fhc& zE`Bc064(?4KsMyxm=Ej%VTH=we_a?Yb-pzFvFhymp+@sOSH}yHkDBWRh@}sPLjPcz zU#)CG(dLp}?~b-ReAVGRfe!aOotrX_eQitO>PDnHmD3ro9I*a5pke_UKgZhxy@|(>r6v*s)G+d85=wFhzPy>g5Z9>Sm zQ+$?{s}wJp%jjlxpGC(klx*GG$brKD##&tkK9(@YTc2FhcdL+gU9O&7N4^auf37ReYHxE$>a-le&Wgh5bB~8GHt*L1?q^SBV7T!+%)U-k5zrkhQ}70O!3n` z$N=%!V?{}tA*Q*Z&tKCQBcf4xsrU%H1X}RQRq=H;9Cq(naBa# zLMym{l~b7Ns3LNp9da`2g?vT5;(thT={xKQBD|Td^_|Qlzm^xb_w2sh!D>~UzV+!l zS8+KhicOQo`Kuh>?)jS3Pwkx&S<=34e>Ft@!gZgFX7pUXGUd>OGYli(R^=i%nRBry zs?85J&_pnxC`ts$Uu%uag{)-GW&NU)B9f1%R1pSKS}w{naVE1<=`%} zzN@`;$Nbv0ErXA+13GAJdS^J8^D4V3()_yl_4hzC*|d05#Cq%*K3Hy=X8kiJ@BwAy>a3F| zwX;|(EDHEpIJ>?p_*9FwU{LVI-f5xtLXnn)jWNs$MTy9oymu}Gs`QtBdSy;^*n~d6 z02pw*xX@Zc$PGa_qX^iGUe2TzteV3FL!)45>X;>v2yyMCGbE)5fMBqlzDHy6rELuo zm6>bXBEGBot}a0f(p?L@E|P~C|L`V$10WwK18gL9dbw~9y!0A#vxH4gz0;BsNhQMW zqlYxjM#%FlnZZniqSlp%-=hSJceYVXK=ETzm(zPB9f5Rg?Eg}m3e{lMlAkbFLGaG) zHgK?MKOf{UX?8i>v)J16ZXf(GDDrU#rC_iG+ys8WM_T`tkDRDnqbhMLCNu)SvE`{| z)6*z9{7A&W{dd4ivAm*(_bVA!?>&KavZU5T=5^fG%<|E0bBc@_XtLDd9nH`6W||}K zSLmV>5lywyZSnf@b@gq82ByONP2l^{vmW%A%*EIqyu518UR^&#`-ua9LH2vu~<> z$VD&CIb+odhuTa@Ss9|7AzvETztJYS)orzUA8(^*)?00L(B?diFZIlt6`Am8tx^E%YyGAooEz3fhoj6B*Q7+28&bS$^ZHQ6oacbP zmamy_RdT*H%`>10UaLu34#zL}#EtyT_b!SKs1CDe!+uq(?nzCsH2sAS^j#$y?@qI8 zu&VqH!2K?j56KXENx&#i2f9$7+mL_zMO{4bS3$rHLL5-b@H9^ckqZA5+uTr`0k0Ws zP+(;jgZvJF_e(=IbPDn|Zj`+HE7BiM@uG&AjL}o4ZQXcEEZ#Gm=6nDc?k7z!3%MJI z=#>C!26?l8V15!GBirwUMaS@%^XcAUe3?39Y=D-7>z2d>lvc00XOAG!akF`$zS`R*0UiTx+)yFB`lCk^T zrKDtM;^$0IX3l^&NRR!34(WcXzzWHsK*TiN19PBjvy$JKZsQu^-)KN;q;EE0M0ZzB zge?(oJn=EoAm_Gu;n~71vz_}u)M%0}#4d)}j;n5=h>e-8fjdZc@Ykq7b()gsEe|Jo#;z#`}*gr*cjl?$NNfsB3en?zWu>)`tUfC$Q^2J%uA5*Z zKHy)krpK0mb2wD(BkeS(U0pBc#uB>f_}WhCIX*U2Wz3PWo^vPXAkqCfS-#G2^}L~b z8Bv3!m$|eBH-4?*$?|x^2}Ew&(`6>WX<`-fH!jE%M6=7Mfm)Pa?I_GiAr zwKq(5rn0Y1F;8YNU%~uU6S9D`lkbA!oYOcU2bo^+j*N71s^Y(kQz6MFd(GPqZ~07$ zBdWDiHc*fyW*|*;#6a(6!CwH(|KHqTUmDRAE_+;i(~8nYde%1%IZK?A9Jts)d5Hb( zQ{ZrQhfG(A8hKCl$z2c^R<%}5I9R(?r@}jMkNiK|N(z~Is71dpUJ>|_`M@(+jIm|rH?0$QEg1feba{x-mdB@_0zRs=nYbdzM?Gw1NUAV zA)6_kRkOvt=Sm_9%RtaM;ahHsL$U2&lqEf}weOaQ7T#E!4E!)|a_;{VL9jWJ5e5v= zPOjNF41@c}FzI2niXnJ00xfnsb&cu&x>L+62673Q{5tyIIz9`OO}ABrsF_NZY&TVAG@F{S8*R3dzD< z(#}k<(w~7V@d{_b>QkubqEZ$0k|RE1hV=?kfRzts7_PDpuRb3p&r0|+9jmNRhUAC+ zJT^^l2Ow9gVy5w_oXrMX=--g^;{E5m57+cMpq$Z3Wv|i&)3;x|55xQ&u%210fKX+= z2X&X0> zWOxc_umy%-cHsUDFo}}8r2K##a=UErCUG$1r{@l~c@Xv-#zGit`84cSXw>?YVCE5o zG*srVEi)Xq`?X)@j#i}6PFRnm% z!JwA>1W!nT6pm0ijiIewNX{}OqXXEU~gXlQzfhzq(!Vni#A?> z$Y+3HNAsH4sn5~xnZ8Bl0=Y1s>5;jv{xG#% z$BOEwA8)p8MeT)KZVE{Kkjv@xp6MN=hpc_->)kMQZRwnuN7Iw6E{4-BZWWLr<@IQK z^*R8kGr;Tg1i{}wPZ-TuRkHZ6^L@_vJQW{q$$&mP{53aXwO6yIC!kJ)2Hf_L)uu455w0E!u0MXh!USm5p;slsITg5e-;_p3FpZlL_(cPT8e`+%CgmCGui$JIeqx}zvAGBB^0^A{U3`UwBy z%_tCXax5ViF&Dp*hFMiw4_C_6pphn>-P+JHoO%i;7}~cGS>E-9qwWgG@`B4^@4y2? zQgL%socP9&_;1Av3f=5@=ymx*x222>MxlpV$%>QE>PmJ=a~Ej z;7OyT8DZ1T#Duz1kv4e0&+t1-^PjW5Ba)eev9Z7u1u3KbRBUE9LBTafU7=MKe|z~R z6e9$$(&P(Y*s~xgVp$dwa18n&anK&DJxv$cp=7dZ0+Ysz6cxUn4|%56{z8!A7bn7K zNQ(I80up%uOmw3^t39lWbv-_jED*Im`sQE5jCbi_#+_}ZQcA5@6645FguCI(6+ujj zCdCck35C18WdCmO`KJ`hADsNdP!BUNC{oV1Hklsoz)r_VNSHK4qyg z$1M+VPQ`esr(0qDRZl5_kjvEH3p&Nsxih@#ZNv!RO*0tCnG}OWg1@$8;!gWSg$JDv z#y7#Zw9x4hy>ANYRTCYjO^FK1GgpI$)z{`MOUybab`&-<^FX5-_1=zQP*XLyFs?o2z*MP zogs2geo2FK%v%HN07tH(QRfWE%{9M|+AS1Ke7bjQUm;Rzte4B-_aA;zd~^l0-ZAz} znUrwKe}@I-4`rnxTS58520{x*=KB4d^2fJ>{2$!3{*JiT;F}Jw-{*vkNskU zv60E~v?;+{6|wV<-_4}epWk6G=!t9iszAAmHC+7O15+i106qdaSby7Ggr0z!;x~t+ z`uysq6c3uF-cq5DaD6x6)@{t$HVCvG(Au29VBP&F3ndP&p2bdhg|DbdYYEUa^R{h< zJO0-3K&}owy(ML}jHS6_H~I{LT5onFLxl0enGH5{5uK`ZmM`8NkL4-gc6g3%OMG|= zu%`Em*V2vLrH-c(mGv#D?>xf8w`rl>& z`LG@JA^y$e8zS%~+_KBK*$*v(#l?snFXXlV&G_D0`PVzi@obD(^Ql)|vv;YFs_XQg zSoQf%`dYHdCqumsXy~mB%FfgTyzkYoD&vGkHRqG)qf8)_9MdW)GEj)oQu#VWxAX+k ztrS20$GzvC%c?V;CMRFFRI<7G-d`E7_5YFwQ&nmgUoPXwXrrW5aU_baSe#trRR$xI z0cPr=HIGq9#Y4Lam?+vdfAKB64*bZs_}hk}HG64EVak0@YL9mk%J$ls_B-@vXf|kN za@ct(CUhq0W>|vp(A+I0>)gtJP_pb=3SAHY{E=AXe{LKTdQ68pOYC+G8TG&5Sj2%P~-e+1j^O zUIW)a>=j-1ZcNLi)7ncwbd9$_-!Jo~#2f@bGuQi=NZp%59^wR$`GAa{B>RE$b1J!^ z%!P*sFO8^i^Gnga!8<1#V98iTw@7*IqPyL} z+W*zta3{!UTH6%6_O61srxW+#W?u1Rw!sxMxxaG>O$m*pZ&j=aZYH74f z@%f{fy)K$WX6!8Rwxw^2cR`M~e4eGD-=@1^LgS>Dhd-(|W^^D^zZt==pPCOh8xAlPDPkqV$q&YtmRV_Q$CHB;|M1C>1XhO`O~jC~B%6IQ6TyRA%ZcO@-XU?>#`BI5t5Y0oVaCfDx; z8bpFDPY!T4Aq}npL~m9r^X`#8`EGKrqTPul20c5$;14kZ{%wG3ej7cbYxs|h?paT5 zEm9o^cL=<#(-0bH=`_2-m-3^I&B6WK%#3bOmA3h@)F{8E34b^Be=P^(H3w(!@>;4T z>dyh%)71<#C37bCu?7eC)h`-07q0CxkJ4Sv zTr?GT+-w({aEj(^9i$ug$kJ$yumF$KJrHEf7_W) zZ2{&wm{_-o@&G0Om%igE`FFaQh!*XxS?}A%5w_0=n<~ zenJ2loB-4Aj_OU{y< zUDBILOp(@cfX|~?;5lBZmbR;7n}foqp;3-r#jc{RjDoLcDAX`{B&b|*V?z%-4m>PD z*X0abeLYXd*G}tc^|BklPZlWXY`u+q^OyE2_Ru`uFDaqlAoRndf>oeGpn1O@op1ro zChn`!qDCKMyo)I0DNXyj%)3YQM3v7pj0iwz6g1CQ3mJ~JXOG(@z+bCcU+(=m|65#) zu!{T-I-b;+p64%$lZ-d5>!al_e(jeP4|T%0N&a2&CqW=GOqJK*ZLgvE^)gKp^7*gZ zNAlmdj|&1fRol5b!9tE2xa30Pc8VG;UdUMRFZwK8SROUXy*gU2xU3G%0pWHa2LG2H zh?sS0wTkV3Qu%qM969976+3oA)9E8h&xos0evc~NBm4Yy4rd}r*nr@~chjM;!&Fe9}td;50$tcHTwROg)x}rF|x=_FU#8` z$LapYOJFimPj=*ZtbyN$sqzo{Or0pZ=6`W*be5pBR<&=9v|AN^xR(xTJG_I=n!aPq z*WeC4Aax$>uU&t`vXFRRuhOzKC%)>b;Z@4CvHQxncn`$-fxU~;Nn}Ec@Y^M1@fNc> zDEJ(dR*ak>DCBz+no5C4RGpeoH+^F+4@on}e_k!|edi}UKE${(1QK3{StP2Z7hm{Y zPT7)lyr}Qz)~_Oi{=BG%&-eEaq&^OSsPBcp#L=reRw67mnIGrla$qmqqO!f3B!9kU zrGBi)zogY7VN=HW!u3O#>X#0{s>MK@Ot+JMZ~|R(6Y|780V)efsA#wx0zOBs31*4y zxZuq$>54vO7t{Pi5Z$Yfh8g$?TYVq4isCnuy4nTNli>jF#{2rzqu@+~Z|QV!WJpa? zv9v81ujq3lKw+wv!r_f8er^!;vBNV0&i=(vfo4bNFJ;+rUs$+yC$qsK`CO9ykEwpF z#5S1W^i(&EOq)%sq9Lb9T==+bV6T^=bHNEP7!tW0bG0vps*{p$EnMLS>2R>%ToYNE zG@vaoae<9mW9Vd6rId@iJI+-cKf$NtS^ zhjakm=x+m#D!th)_CoJ2V}(&NE+{lT?d#3|(KCpKS3?XFG!1~y?hX0urpUxENuaDL zL@JKZ#Xo(WLQzCjnSL=+&V4mfh7~tMLr^StV#?U(+%ik|oQa$b%>XYv2&RIL``q=Q zhoqK!c9pnP*?>F>Q??C=nJRnk%4BTA(!=Ruf_k1e>EWi$s4$q$$g47EVq&G702(io zy3_xM=&l0;eeyBnM}ex0(rXso#xFK4hl1GG2)T~l$Gc}T95;$6@zl`&YN!wW*9cYC z^zBLPdz~{D|3h91 zgnQwbnTYP0jLvSO0gPL{kvj5mC)$iCy1r}Q?0e= z3XnhdXl4Kt&H5h#vuxcU2KX?7QPrIn3~|H?DS&)Z{5UkekSgGCyq75TEuGkKk@vCf zXAwK#dw&)|hb|nFbbR)#Oe?I!Zlq2n1u%tEyiVX)jsHCm>&Ub@{KHn2zkp;Z-3M`n z`=o@=4M}m+L%yOIUdsHgl850xKBy`05ype@zVP`Ab^SxsH4e}1WJ36Cj#a6`qC64i zpt8U=jHn32(D;Cc%ICmOP{Xbau5z)ShV@_d2)fXpFGoagLmh3?ax0pY0=59mAq){1 z-5%?DIcjr=JWTa(#Z;gn@H9Q_Dxh;@JIvmcDl8$eAocFtPaoRc2I$GAo%YFY^ObxF zm*O>Hr4K6j%(85>$*-lebbMb~w55S4lMVRAN6VGKW1=UOz?m4rc5sy>aB|I{t|l^Q z!j6aGk(QF%r+|wZU>G9bcq}(k>d`iE+Ql8OBrrj!C)NN4vrD0}h~1pUf#!~Y{qQdO z>$w*e7aa1Z6OTZ~GIAE=ar>1w#~IaSR)D3vdI5@&-hVK^X*~dlMO#KtvuTT()KlII zp(g7?oS2p67nmwq8m$P4zcdas%Q(zD=LRNM{QJKj;W5XfK7yXMdRb|KotlUk1|!uS zn*JAkEH8iygkVP8$Nn6Ht`%XjIPhHM$`~|I^GX!kumO%u33m}i&+-$EF$@j|eGa+{ zZR=0qF_EdW5L2p4pSGi}p(y4U)AG)E+us|MqI=2Z*IQD0|i@>JdKHn_wr z!9nz(Mj&;58KluD7;@CcCsIP)j^4@Wz+{}Z1rdZ@WOj8-GOG1?z+gFb0;zcvw!#Hb zPv}z?tU~U^{djr4VfG<4-I%f7eK5lS@FAgr-cy+$HcbkmArwH#OC;=4-Z}%t;=*7- zpv9IIg%H_(U)?e2l$TuH{GRQwK>%n@$wOa2MOc{ZTB{(Z0T{|!m?>nK_=hw8@}c8 zs5AYyfWpW$t8$&bNV%?%Cu+~K>0jSR&!T?_ zQ9Km@+1vl584hwR5Z&gnJpfX zufT!^tM6auPQbPS%1_{z|$SNq!J}Yc1{b(oHtF^oxUUI%y78gs1;-eYSZ zs6PHHp_d+F`<)&i{;!=<8nn*Do~K_sB{JPXxG$u9^Pr8fvOG|-t(-#Ht{OZ&VFR={ zXnrb8cr)0j3c60L0d!OcbRv~>063w^NVl9+&IwB3<_QA`u&0D#Sr!v{|Q@D3VzPW{bV*dJJD)|KVPoVmV(+KkNZv6gZ z`vJJ0w_))w?;-E&&K?d+2Eev`<+AFe-aU;(sylLU|EV;>cyC1RA6(x*^eSwho*68v za4=+Q3u8&|T-xxOHuv12&hrCy)c#mGhG|(erz$ip%opAOv|hdDl!yctN6$r{47l^^ zDhhzfPYzf++U^11WsbKJfK1Za8Y=)gUq-J1Ah)dz*{f4lmJI;jlA$b{iz^d?(_?=5 zZLPH<0B7H~Mb7tf%K__>TTWklzeIw0I0PUuLAuS`?^n3C=y?v$!-5(cH!_wiW4Q|( zfTmv7rq$W8>tT~|f$ulr0mlBCCHp;YG^uDE_8Xcg|VMH88Kg(9(Hafp85 zm;n2HyR^H3qqp~6?xR?N2ZqjeOtie+TbB$USiaZ(?!}LQ(#5*O&ecxgx3!u>k7G>u zZt0i9bn;fv%BaIA3oZsRekX_3Yj$=e^poWYU$dL(aH*hW??tUJoog5fcH@co0eCKz z1xMltlyD!=O30miD1o+&q`1ZNL~va|HX}HU6U8H#Cc1O(?ZEu=&+PCB9vrspuF19n zv*#YSm2bxnqAOrECBmgcyfcESx77M$&H03#j+k#vGYTsp*2UYRhngh9NL`8sXZuf>tN^sF?ZwrNM1tn85_Z^p%8 z4VV*``6&zFI&mXF$%PzCUO1jWI??tb$)57n4@3j4Ch@_FGxkiEF=Mlp+t(>O$Skya1JV>1AkO} zBZJv;SZ{VVwbV1a`w7RW%R#9ok6Ll6Y~ekT$@ChU{2oaX5jESdz+CI2i7d<3ikrlH zO8won#A1*N4T$Pk8m(QQBdxj@mARP*7plF>)YTXrXNL*JZ)Wc>nLp8hIC@LGPc zFh}NHtI%G57EY2e&bV+CSB;Gn&Hqn5!>nk7L>CW zioZI8TJD|K(YspMAw69%%7Y-7>h;r+)IGda%PlKOBvdRBH?G=<0R2Xuo2wrm>Y$$%Rcl2$JWHkuy+-X2Zwn{Y zY4);>O0QVm_-aP~R3lM>Th_L|%9)2`AzR173aG_VC-oLS#j)8>9|heiF0}-?XY-se zeVE;TEwXqRjb14lk!v3(b*U~<<_~56JG1kJ74_Azfp&{e+sY&1&>3yxZ}Z5SJVd?i0({an^xO*zR2#eYF{*xbDJC@|BKMxrQ8ZU`c6tKAFd?=rFT?WB; zH%$*FJ21&HNAi(s4%vTI=MRf`?jt(wzS@&~`tjzODg$NVhlvzDo&U4S?I9ssf2mO_ z!|jceqqn#2KH7aR?bhOvaBjXcASN%Y756bbRmj*?pHSQ?)kt${-Lbx(l7u(qzPz*Or4XBIU z%ac6fr*}%Rha{0f@SQ{C--nQSlZtOU_Dl>R=%Y&V(nz%kk=&4R+{-RYp3E!;c3O6h zZk8{fVuuX+ELlXNmL9!+-Gyb8VQXKAx%b{(NlttHQrkA@0BHGQnVBZ^Y>6{1OiGDZ zh6g0{Z1#zD!TAnsL_{VthLgoAjnsH|SmqZi1}*QjTIzh1!lkEXqZ!w^^j_xfMTB;* zT&G3Lo?vwUAt6h3-pZ=9;esR)>CBm#c~`DkY8)#j|E$*Q;dek$yNjLm1$rwtoT7MB z4NW~4sYfX5!It*6ZD%_V)XWGuyv;2;b;+H*InDFoIN2JI);()oGF4M+=kCaX>y9TJ z!~2as5gRpM*~)0<@WBoao&d!&s=_rWUSp|H@!%ZD27O*_Xg{d--&w;{P9<$W45Dh+a>+X2agp*0`x$vP!(lQ zeJnbK7FRGE@d$_>w!qu^r|E?MlnSLL-@4=+W2jBxm!Y`Hh`5hv){lY{KTUWn#U34J z(?Khg>#f{me09=gpPF)qbcRLfYjQ}PeJ&3BVvsOFR&(x~FYVoylh7|!3A?89^F2Z4 z8|UO2tBHq5<1pLWwzj^q?Eel5J&Am&uN7G7JzDqI`6`ETPpFHVmcB8w3rz$PYuIuP zu9R4T?mlfa$>k5l&_DX7H*;w%HR7vAHF3fp2v<}x_VMEb@fR%i;nM;4#Z~$U)6lxN ztPg;nav-{A;mZWqL3(Fz@367wR%3+D4dzp-{cixG~Y(j zfz5Jx1krWj_!$7M_)Ucb5yY3y5rpzLyKYmuWz-3&%V_2ReO(C`gEGD^?2>eA5n9oI zXmlR81`S#zE^!au#r-H{H4XUwSc$?XC2^%oXf8I969WcVk77`!b7)>|`Zknb&qnZ6 zzugvHu>(3muqlP7Q(o!b1Ko4?h=H%5F<3Sw8PgUpK&x%8&q|M&m{RksEIoD6b#C0K z&sd)PRE+@_YGiG3CTb;>$22WmOf^`nYBt!S?P$z0KM)UKhYG~7c z-R;NRlOZpy4d7_+PmEHGW7N~T)I}Au?*a~z0QWS{k*?5(SI+Jc5+E~sVtCwmMVla3 zG>B%&q6Y2lV*Ib=!i~{KuCjzPo7QAHJ0;aT5__7`2fvPrAC&48b3ExR+^)`Y8yr`n zrU}GfN~AH=3j@YpDzjK9v!wcvB;LQ}bjN)BY>7f~zbFNd{xre`jJ;M8 z0|n>UT7i0MN;OyYrm!k>`Edr{ET!ZrU|+`{fi=x|z(aeJSZf*DJLF|0qmByG048#B|vW|78paZ156Lhs4CP32< zw0Q%S@_Ktmhr!ElNhkxY?^q!-Xk5Og^-~1l&@h&)uNZ%PT6EujVmM6Y1tkk&j!t(; zp}`fY_(jht@4_~sLh26bgic^=06CHs$kepAt^MjL&gXOW7KE2py~_92jv1dJR;hW| z^6-D#5tlOOyXrySQjO<-8=7Z~8dvZB6=yQIq9*^gEUUv2b96K}%MO6FzFJ)3+sXBJ ztV`55(&}>xGp6Ss-yx1Dz3tl-OVqmgeV0T!GZ5LpWWje?Hr6ZdDDc0QzBo6nXYLQ& WzmK*X$44?990@#ru=-EYfBP57nv}@^ literal 0 HcmV?d00001 -- Gitee From d97a7464c98de8a7ff999c3075ad264f4ecde029 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Tue, 30 Jul 2024 11:43:02 +0800 Subject: [PATCH 04/14] fix bug Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/src/generate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/h2hdf/src/generate.js b/src/cli/h2hdf/src/generate.js index 5495a774..86f25976 100644 --- a/src/cli/h2hdf/src/generate.js +++ b/src/cli/h2hdf/src/generate.js @@ -104,7 +104,7 @@ function genDriverFramework(driverName, out = '') { let frameworkJsonPath = path.join(__dirname, './templete/framework.json'); let frameworkJson = getJsonCfg(frameworkJsonPath); - let frameworkPath = pathJoin(out, 'hdf'); + let frameworkPath = pathJoin(out, driverName + 'hdf'); let namespaceName = driverName.substring(0,1).toUpperCase() + driverName.substring(1, driverName.length); let idlFileName = 'I' + namespaceName + 'Interface'; -- Gitee From 90eb8134a3d6bff76cd747adc93e8ecff384f460 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Tue, 30 Jul 2024 19:28:17 +0800 Subject: [PATCH 05/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/docs/INSTRUCTION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/h2hdf/docs/INSTRUCTION.md b/src/cli/h2hdf/docs/INSTRUCTION.md index 56da00f8..56301165 100644 --- a/src/cli/h2hdf/docs/INSTRUCTION.md +++ b/src/cli/h2hdf/docs/INSTRUCTION.md @@ -109,7 +109,7 @@ cat hdf_devhost.cfg 根据hostName找到对应hostId,如本例的hostName为hello_host,对应找到“name”为“hello_host”那一项,查看“path”的第二个参数,则为hostName对应的hostId,即14,如下所示: -![image-20240724093743837](./figures/pic_show_hostid.png) +![image-20240724093743837](./figures/pic_show_hostId.png) 2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入第一个参数为hostId,第二个参数为hostName;运行命令如下所示: -- Gitee From eda4d808a3f75253c7824b8c8cf91410b98396c9 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Fri, 2 Aug 2024 14:55:18 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E5=9C=A8=E6=A8=A1=E6=9D=BF=E4=B8=AD?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- .../IdlInterfaceTemplete/buildgnTemplete.gen | 14 +++++++- .../DumpExampleTemplete/buildgnTemplete.gen | 2 +- .../DumpExampleTemplete/dumpCTemplete.gen | 2 +- .../DumpExampleTemplete/dumpHTemplete.gen | 2 +- .../HdiServiceTemplete/buildgnTemplete.gen | 2 +- .../HdiServiceTemplete/driverTemplete.gen | 33 ++++++++++++++++++- .../HdiServiceTemplete/logHTemplete.gen | 2 +- .../HdiServiceTemplete/serviceCppTemplete.gen | 18 ++++++++++ .../HdiServiceTemplete/serviceHTemplete.gen | 15 +++++++++ .../PeripheralTemplete/buildgnTemplete.gen | 2 +- 10 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen index d6627116..6ae05cb2 100644 --- a/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen +++ b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/buildgnTemplete.gen @@ -1,6 +1,18 @@ +# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import("//drivers/hdf_core/adapter/uhdf2/hdi.gni") # 编译idl必须要导入的模板 hdi("[driver_name]") { # 目标名称,会生成两个so,分别对应 lib[driver_name]_client_v1.0.z.so 和 lib[driver_name]_stub_v1.0.z.so - # package = "ohos.hdi.[driver_name].v1_0" # 包名,必须与idl路径匹配 module_name = "[driver_name]_service" # module_name控制dirver文件中驱动描 述符(struct HdfDriverEntry)的moduleName sources = [ # 参与编译的idl文件 "[driver_idl_name].idl", # 接口idl diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen index 0001c148..c724378b 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen @@ -1,4 +1,4 @@ -#Copyright (c) 2022-2023 Huawei Device Co., Ltd. +#Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. #Licensed under the Apache License, Version 2.0 (the "License"); #you may not use this file except in compliance with the License. #You may obtain a copy of the License at diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen index 56fcb800..b3f17584 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Huawei Device Co., Ltd. + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen index e3cd4d11..018647c5 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Huawei Device Co., Ltd. + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen index 3b9c6820..c3cbb02e 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2023 Huawei Device Co., Ltd. +# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen index 440777b4..deda4b24 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include #include #include @@ -13,25 +28,37 @@ struct Hdf[driver_inter_name]Host { OHOS::sptr stub; }; -// 处理客户端请求的Dispatch方法 +/* + * 处理客户端请求的Dispatch方法: 处理来自客户端的IO请求 + * client:指向HdfDeviceIoClient结构体的指针,表示发起请求的客户端。 + * cmdId:命令ID,标识了要执行的命令或操作。 + * data:指向HdfSBuf结构体的指针,包含了请求的数据。 + * reply:指向另一个HdfSBuf结构体的指针,用于存放响应的数据。 + */ static int32_t [driver_inter_name]DriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply) { auto *hdf[driver_inter_name]Host = CONTAINER_OF(client->device->service, struct Hdf[driver_inter_name]Host, ioService); + // 声明两个MessageParcel对象,用于序列化和反序列化IPC通信中的数据 OHOS::MessageParcel *dataParcel = nullptr; OHOS::MessageParcel *replyParcel = nullptr; + // 创建一个MessageOption对象,用于设置IPC通信的选项。 OHOS::MessageOption option; + // 响应序列化:将HdfSBuf中的数据转换为MessageParcel对象。如果转换失败,记录错误并返回错误代码。 if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) { HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__); return HDF_ERR_INVALID_PARAM; } + + // 数据序列化:尝试将响应数据的HdfSBuf转换为MessageParcel对象。如果失败,也记录错误并返回错误代码。 if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) { HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__); return HDF_ERR_INVALID_PARAM; } + // 调用stub对象的SendRequest方法,发送请求。这个方法执行实际的IPC调用,将cmdId和序列化后的请求数据dataParcel发送给服务端,并将响应数据反序列化到replyParcel中。 return hdf[driver_inter_name]Host->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); } @@ -46,12 +73,14 @@ static int Hdf[driver_inter_name]DriverInit(struct HdfDeviceObject *deviceObject static int Hdf[driver_inter_name]DriverBind(struct HdfDeviceObject *deviceObject) { HDF_LOGI("%{public}s: driver bind start", __func__); + // 创建对象:该对象是驱动服务的具体实现 auto *hdf[driver_inter_name]Host = new (std::nothrow) Hdf[driver_inter_name]Host; if (hdf[driver_inter_name]Host == nullptr) { HDF_LOGE("%{public}s: failed to create create Hdf[driver_inter_name]Host object", __func__); return HDF_FAILURE; } + // 为ioService结构体设置回调函数:设置的Dispatch函数用于处理IO请求 hdf[driver_inter_name]Host->ioService.Dispatch = [driver_inter_name]DriverDispatch; hdf[driver_inter_name]Host->ioService.Open = NULL; hdf[driver_inter_name]Host->ioService.Release = NULL; @@ -63,6 +92,7 @@ static int Hdf[driver_inter_name]DriverBind(struct HdfDeviceObject *deviceObject return HDF_FAILURE; } + // 使用ObjectCollector的GetOrNewObject方法获取或创建一个Stub对象。Stub对象是服务接口的客户端代理,用于发起远程调用。 hdf[driver_inter_name]Host->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, OHOS::HDI::[driver_namespace_name]::V1_0::[driver_idl_name]::GetDescriptor()); if (hdf[driver_inter_name]Host->stub == nullptr) { @@ -71,6 +101,7 @@ static int Hdf[driver_inter_name]DriverBind(struct HdfDeviceObject *deviceObject return HDF_FAILURE; } + // 将ioService绑定到deviceObject,这样HDF框架就可以通过deviceObject来访问服务 deviceObject->service = &hdf[driver_inter_name]Host->ioService; HDF_LOGI("%{public}s: driver bind end", __func__); return HDF_SUCCESS; diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen index c4f24fef..d186466d 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen index b20b968b..75c39f8a 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "v1_0/[driver_name]_interface_service.h" #include #include "[driver_name]_log.h" @@ -12,13 +27,16 @@ namespace [driver_namespace_name] { namespace V1_0 { extern "C" [driver_idl_name] *[driver_inter_name]ImplGetInstance(void) { + // עhidumper DevHostRegisterDumpHost(Get[driver_namespace_name]Dump); + // [hdf-gen] Todo HDF_LOGI("%{public}s: [driver_idl_name] init", __func__); return new (std::nothrow) [driver_inter_name]Service(); } int32_t [driver_inter_name]Service::Helloworld(const std::string& sendMsg, std::string& recvMsg) { + // [hdf-gen] Todo HDF_LOGI("%{public}s: [driver_namespace_name]Service::Helloworld print", __func__); return HDF_SUCCESS; } diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen index f6c33dbf..804b9d50 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #ifndef OHOS_HDI_[upper_driver_name]_V1_0_[upper_driver_name]INTERFACESERVICE_H #define OHOS_HDI_[upper_driver_name]_V1_0_[upper_driver_name]INTERFACESERVICE_H diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen index 7c92b45a..c93922c2 100644 --- a/src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen +++ b/src/cli/h2hdf/src/templete/PeripheralTemplete/buildgnTemplete.gen @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Huawei Device Co., Ltd. +# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -- Gitee From ff3a40a1e2c9913c558cc60427b121f76361fbf9 Mon Sep 17 00:00:00 2001 From: goujingjing Date: Tue, 6 Aug 2024 09:23:06 +0800 Subject: [PATCH 07/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: goujingjing --- .../{pic_show_hostId.png => pic_show_hostid.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cli/h2hdf/docs/figures/{pic_show_hostId.png => pic_show_hostid.png} (100%) diff --git a/src/cli/h2hdf/docs/figures/pic_show_hostId.png b/src/cli/h2hdf/docs/figures/pic_show_hostid.png similarity index 100% rename from src/cli/h2hdf/docs/figures/pic_show_hostId.png rename to src/cli/h2hdf/docs/figures/pic_show_hostid.png -- Gitee From 1af64b729194a37fbb96b648779d22bc022c68e9 Mon Sep 17 00:00:00 2001 From: goujingjing Date: Thu, 8 Aug 2024 21:15:07 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: goujingjing --- src/cli/h2hdf/docs/INSTRUCTION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/h2hdf/docs/INSTRUCTION.md b/src/cli/h2hdf/docs/INSTRUCTION.md index 56301165..56da00f8 100644 --- a/src/cli/h2hdf/docs/INSTRUCTION.md +++ b/src/cli/h2hdf/docs/INSTRUCTION.md @@ -109,7 +109,7 @@ cat hdf_devhost.cfg 根据hostName找到对应hostId,如本例的hostName为hello_host,对应找到“name”为“hello_host”那一项,查看“path”的第二个参数,则为hostName对应的hostId,即14,如下所示: -![image-20240724093743837](./figures/pic_show_hostId.png) +![image-20240724093743837](./figures/pic_show_hostid.png) 2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入第一个参数为hostId,第二个参数为hostName;运行命令如下所示: -- Gitee From 264b43b8646d60e5eaaf667d19550e3d896f93e9 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Fri, 9 Aug 2024 09:16:58 +0800 Subject: [PATCH 09/14] =?UTF-8?q?=E8=84=9A=E6=9C=AC=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E5=A2=9E=E5=8A=A0=E6=89=93=E5=8D=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/src/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cli/h2hdf/src/main.js b/src/cli/h2hdf/src/main.js index 4423ca5e..506a88a6 100644 --- a/src/cli/h2hdf/src/main.js +++ b/src/cli/h2hdf/src/main.js @@ -26,6 +26,7 @@ let drivername = ops.drivername; // 若drivername不为空,则生成,否则打印错误信息 if (drivername.trim().length !== 0 && checkInput(drivername)) { main.genDriverFramework(drivername); + console.info('Generate Success'); } function checkInput(input) { -- Gitee From d2ab355d9c0de2ce1dc0c511b06322476e1f5dce Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Fri, 9 Aug 2024 09:19:53 +0800 Subject: [PATCH 10/14] =?UTF-8?q?add=E5=B7=A5=E5=85=B7=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- test/h2hdf/hellohdf/HcsConfig/device_info.hcs | 13 ++ .../hellohdf/IdlInterface/hello/bundle.json | 62 ++++++++ .../hellohdf/IdlInterface/hello/v1_0/BUILD.gn | 23 +++ .../hello/v1_0/IHelloInterface.idl | 5 + test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn | 18 +++ .../hellohdf/Peripheral/hello/bundle.json | 46 ++++++ .../hellohdf/Peripheral/hello/hal/BUILD.gn | 51 ++++++ .../Peripheral/hello/hal/hello_dump.c | 114 ++++++++++++++ .../Peripheral/hello/hal/include/hello_dump.h | 37 +++++ .../Peripheral/hello/hdi_service/BUILD.gn | 98 ++++++++++++ .../hdi_service/hello_interface_driver.cpp | 146 ++++++++++++++++++ .../hdi_service/hello_interface_service.cpp | 47 ++++++ .../hdi_service/hello_interface_service.h | 38 +++++ .../hello/utils/interface/hello_log.h | 26 ++++ 14 files changed, 724 insertions(+) create mode 100644 test/h2hdf/hellohdf/HcsConfig/device_info.hcs create mode 100644 test/h2hdf/hellohdf/IdlInterface/hello/bundle.json create mode 100644 test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn create mode 100644 test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/bundle.json create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h create mode 100644 test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h diff --git a/test/h2hdf/hellohdf/HcsConfig/device_info.hcs b/test/h2hdf/hellohdf/HcsConfig/device_info.hcs new file mode 100644 index 00000000..6d9453ec --- /dev/null +++ b/test/h2hdf/hellohdf/HcsConfig/device_info.hcs @@ -0,0 +1,13 @@ +hello :: host { + hostName = "hello_host"; + priority = 50; + hello_device :: device { + device0 :: deviceNode { + preload = 0; + policy = 2; + priority = 100; + moduleName = "libhello_driver.z.so"; + serviceName = "hello_interface_service"; + } + } +} \ No newline at end of file diff --git a/test/h2hdf/hellohdf/IdlInterface/hello/bundle.json b/test/h2hdf/hellohdf/IdlInterface/hello/bundle.json new file mode 100644 index 00000000..ffb2ce0e --- /dev/null +++ b/test/h2hdf/hellohdf/IdlInterface/hello/bundle.json @@ -0,0 +1,62 @@ +{ + "name": "@ohos/drivers_interface_hello", + "description": "hello device driver interface", + "version": "4.1", + "license": "Apache License 2.0", + "publishAs": "code-segment", + "segment": { + "destPath": "drivers/interface/hello" + }, + "dirs": {}, + "scripts": {}, + "component": { + "name": "drivers_interface_hello", + "subsystem": "hdf", + "syscap": [], + "adapted_system_type": ["standard"], + "rom": "675KB", + "ram": "1024KB", + "deps": { + "components": [ + "ipc", + "hdf_core", + "hilog", + "c_utils" + ], + "third_party": [] + }, + "build": { + "sub_component": [ + "//drivers/interface/hello/v1_0:hello_idl_target" + ], + "test": [ + ], + "inner_kits": [ + { + "name": "//drivers/interface/hello/v1_0:libhello_proxy_1.0", + "header": { + "header_files": [ + ], + "header_base": "//drivers/interface/hello" + } + }, + { + "name": "//drivers/interface/hello/v1_0:libhello_stub_1.0", + "header": { + "header_files": [ + ], + "header_base": "//drivers/interface/hello" + } + }, + { + "name": "//drivers/interface/hello/v1_0:hello_idl_headers", + "header": { + "header_files": [ + ], + "header_base": "//drivers/interface/hello" + } + } + ] + } + } + } \ No newline at end of file diff --git a/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn b/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn new file mode 100644 index 00000000..721cea1e --- /dev/null +++ b/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn @@ -0,0 +1,23 @@ +# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//drivers/hdf_core/adapter/uhdf2/hdi.gni") # 编译idl必须要导入的模板 +hdi("hello") { # 目标名称,会生成两个so,分别对应 libhello_client_v1.0.z.so 和 libhello_stub_v1.0.z.so + module_name = "hello_service" # module_name控制dirver文件中驱动描 述符(struct HdfDriverEntry)的moduleName + sources = [ # 参与编译的idl文件 + "IHelloInterface.idl", # 接口idl + ] + language = "cpp" # 控制idl生成c或c++代码 可选择`c`或`cpp` + subsystem_name = "hdf" # 子系统名 + part_name = "drivers_interface_hello" # 组件名 +} diff --git a/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl b/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl new file mode 100644 index 00000000..01d19794 --- /dev/null +++ b/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl @@ -0,0 +1,5 @@ +package ohos.hdi.hello.v1_0; + +interface IHelloInterface { + Helloworld([in] String sendMsg, [out] String recvMsg); +} \ No newline at end of file diff --git a/test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn b/test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn new file mode 100644 index 00000000..c17c0023 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +group("hello_entry") { + deps = [ "hdi_service:hdf_hello_service" ] +} + + diff --git a/test/h2hdf/hellohdf/Peripheral/hello/bundle.json b/test/h2hdf/hellohdf/Peripheral/hello/bundle.json new file mode 100644 index 00000000..97cf1323 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/bundle.json @@ -0,0 +1,46 @@ +{ + "name": "@ohos/drivers_peripheral_hello", + "description": "hello device driver", + "version": "4.1", + "license": "Apache License 2.0", + "publishAs": "code-segment", + "segment": { + "destPath": "drivers/peripheral/hello" + }, + "dirs": {}, + "scripts": {}, + "component": { + "name": "drivers_peripheral_hello", + "subsystem": "hdf", + "features": [ + ], + "syscap": [], + "adapted_system_type": ["standard"], + "rom": "675KB", + "ram": "7400KB", + "deps": { + "components": [ + "ipc", + "hdf_core", + "hilog", + "c_utils", + "drivers_interface_hello", + "hitrace", + "hilog_lite" + ], + "third_party": [ + "bounds_checking_function" + ] + }, + "build": { + "sub_component": [ + "//drivers/peripheral/hello:hello_entry" + ], + "test": [ + ], + "inner_kits": [ + + ] + } + } +} diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn b/test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn new file mode 100644 index 00000000..10b081ed --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn @@ -0,0 +1,51 @@ +#Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +# + +import("//build/ohos.gni") + +config("libhdi_hello_pub_config") { + visibility = [ ":*" ] +} +ohos_shared_library("hdi_hello") { + public_configs = [ ":libhdi_hello_pub_config" ] + sources = [ + "hello_dump.c", + ] + include_dirs = [ + "include", + "../utils/interface", + "//third_party/bounds_checking_function/include", + ] + cflags = [ + "-Wall", + "-Wextra", + "-Werror", + "-fsigned-char", + "-fno-common", + "-fno-strict-aliasing", + ] + install_images = [ chipset_base_dir ] + subsystem_name = "hdf" + part_name = "drivers_peripheral_hello" + if (is_standard_system) { + external_deps = [ + "c_utils:utils", + "hdf_core:libhdf_host", + "hdf_core:libhdf_utils", + "hilog:libhilog", + ] + } else { + external_deps = [ "hilog:libhilog" ] + } +} diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c b/test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c new file mode 100644 index 00000000..88aa0580 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hello_dump.h" +#include +#include +#include "devhost_dump_reg.h" +#include "hdf_base.h" +#include "hello_log.h" + +#define HDF_LOG_TAG uhdf_hello_service + +// -c dump the helloworld info +static const char *g_dumpHelp = + " usage:\n" + " -h, --help: dump help\n" + " -c, --channel: dump the hello channel info\n"; + +static uint32_t ShowHelloworldInfo(struct HdfSBuf *reply) +{ + int32_t ret; + const char *helloWorldMessage = "Hello, World!"; + + ret = HdfSbufWriteString(reply, helloWorldMessage); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: write hello world info failed", __func__); + return HDF_FAILURE; + } + + HDF_LOGI("%{public}s: hellodump: print hello world !", __func__); + + return HDF_SUCCESS; + +} + +static int32_t DumpHelloChannel(struct HdfSBuf *reply) +{ + int32_t ret; + HDF_LOGI("%{public}s: get hello dump channel begin", __func__); + ret = ShowHelloworldInfo(reply); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: show hello world info failed", __func__); + return HDF_FAILURE; + } + + return HDF_SUCCESS; +} + +static int32_t HelloDriverDump(struct HdfSBuf *data, struct HdfSBuf *reply) +{ + uint32_t i; + uint32_t argv = 0; + HDF_LOGI("%{public}s: get hello dump begin xx", __func__); + if (data == NULL || reply == NULL) { + return HDF_FAILURE; + } + + if (!HdfSbufReadUint32(data, &argv)) { + HDF_LOGE("%{public}s: read argv failed", __func__); + return HDF_FAILURE; + } + + if (argv == 0) { + if (!HdfSbufWriteString(reply, g_dumpHelp)) { + HDF_LOGE("%{public}s: write -h failed", __func__); + return HDF_FAILURE; + } + } + + for (i = 0; i < argv; i++) { + const char *value = HdfSbufReadString(data); + if (value == NULL) { + HDF_LOGE("%{public}s value is invalid", __func__); + return HDF_FAILURE; + } + + if (strcmp(value, "-h") == HDF_SUCCESS) { + if (!HdfSbufWriteString(reply, g_dumpHelp)) { + HDF_LOGE("%{public}s: write -h failed", __func__); + return HDF_FAILURE; + } + continue; + } else if (strcmp(value, "-c") == HDF_SUCCESS) { + DumpHelloChannel(reply); + continue; + } + } + + return HDF_SUCCESS; +} + +int32_t GetHelloDump(struct HdfSBuf *data, struct HdfSBuf *reply) +{ + HDF_LOGI("%{public}s: get hello dump begin", __func__); + int32_t ret = HelloDriverDump(data, reply); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: get hello dump failed", __func__); + return HDF_FAILURE; + } + + return HDF_SUCCESS; +} diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h b/test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h new file mode 100644 index 00000000..cd422189 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HELLO_DUMP_H +#define HELLO_DUMP_H + +#include +#include +#include "hdf_sbuf.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +int32_t GetHelloDump(struct HdfSBuf *data, struct HdfSBuf *reply); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* HDI_HELLO_DUMP_H */ \ No newline at end of file diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn new file mode 100644 index 00000000..c019335a --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn @@ -0,0 +1,98 @@ +# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +HDF_CORE_PATH = "../../../hdf_core" +import("//build/ohos.gni") +import("$HDF_CORE_PATH/adapter/uhdf2/uhdf.gni") + +ohos_shared_library("libhello_interface_service_1.0") { + include_dirs = [ + ".", + "../utils/interface", + "../hal/include" + ] + + sources = [ "hello_interface_service.cpp"] + deps = [ "../hal:hdi_hello" ] + + cflags = [ + "-Wall", + "-Wextra", + "-Werror", + "-fsigned-char", + "-fno-common", + "-fno-strict-aliasing", + ] + + if (is_standard_system) { + external_deps = [ + "c_utils:utils", + "drivers_interface_hello:hello_idl_headers", + "hdf_core:libhdf_host", + "hilog:libhilog", + "hitrace:hitrace_meter", + ] + } else { + external_deps = [ "hilog:libhilog" ] + } + external_deps += [ "ipc:ipc_single" ] + + install_images = [ chipset_base_dir ] + subsystem_name = "hdf" + part_name = "drivers_peripheral_hello" +} + +ohos_shared_library("libhello_driver") { + include_dirs = [ + ] + sources = [ "hello_interface_driver.cpp" ] + + cflags = [ + "-Wall", + "-Wextra", + "-Werror", + "-fsigned-char", + "-fno-common", + "-fno-strict-aliasing", + ] + + if (is_standard_system) { + external_deps = [ + "c_utils:utils", + "drivers_interface_hello:libhello_stub_1.0", + "hdf_core:libhdf_host", + "hdf_core:libhdf_ipc_adapter", + "hdf_core:libhdf_utils", + "hdf_core:libhdi", + "hilog:libhilog", + "ipc:ipc_single", + ] + } else { + external_deps = [ + "hilog:libhilog", + "ipc:ipc_single", + ] + } + + shlib_type = "hdi" + install_images = [ chipset_base_dir ] + subsystem_name = "hdf" + part_name = "drivers_peripheral_hello" +} + +group("hdf_hello_service") { + deps = [ + ":libhello_driver", + ":libhello_interface_service_1.0", + ] +} diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp new file mode 100644 index 00000000..73887585 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "v1_0/hello_interface_stub.h" + +#define HDF_LOG_TAG hello_interface_driver + +using namespace OHOS::HDI::Hello::V1_0; + +struct HdfHelloInterfaceHost { + struct IDeviceIoService ioService; + OHOS::sptr stub; +}; + +/* + * 处理客户端请求的Dispatch方法: 处理来自客户端的IO请求 + * client:指向HdfDeviceIoClient结构体的指针,表示发起请求的客户端。 + * cmdId:命令ID,标识了要执行的命令或操作。 + * data:指向HdfSBuf结构体的指针,包含了请求的数据。 + * reply:指向另一个HdfSBuf结构体的指针,用于存放响应的数据。 + */ +static int32_t HelloInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, + struct HdfSBuf *reply) +{ + auto *hdfHelloInterfaceHost = CONTAINER_OF(client->device->service, struct HdfHelloInterfaceHost, ioService); + + // 声明两个MessageParcel对象,用于序列化和反序列化IPC通信中的数据 + OHOS::MessageParcel *dataParcel = nullptr; + OHOS::MessageParcel *replyParcel = nullptr; + // 创建一个MessageOption对象,用于设置IPC通信的选项。 + OHOS::MessageOption option; + + // 响应序列化:将HdfSBuf中的数据转换为MessageParcel对象。如果转换失败,记录错误并返回错误代码。 + if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) { + HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__); + return HDF_ERR_INVALID_PARAM; + } + + // 数据序列化:尝试将响应数据的HdfSBuf转换为MessageParcel对象。如果失败,也记录错误并返回错误代码。 + if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) { + HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__); + return HDF_ERR_INVALID_PARAM; + } + + // 调用stub对象的SendRequest方法,发送请求。这个方法执行实际的IPC调用,将cmdId和序列化后的请求数据dataParcel发送给服务端,并将响应数据反序列化到replyParcel中。 + return hdfHelloInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); +} + +// 驱动自身业务初始化的接口 +static int HdfHelloInterfaceDriverInit(struct HdfDeviceObject *deviceObject) +{ + HDF_LOGI("%{public}s: driver init start", __func__); + return HDF_SUCCESS; +} + +// 将驱动对外提供的服务能力接口绑定到HDF框架 +static int HdfHelloInterfaceDriverBind(struct HdfDeviceObject *deviceObject) +{ + HDF_LOGI("%{public}s: driver bind start", __func__); + // 创建对象:该对象是驱动服务的具体实现 + auto *hdfHelloInterfaceHost = new (std::nothrow) HdfHelloInterfaceHost; + if (hdfHelloInterfaceHost == nullptr) { + HDF_LOGE("%{public}s: failed to create create HdfHelloInterfaceHost object", __func__); + return HDF_FAILURE; + } + + // 为ioService结构体设置回调函数:设置的Dispatch函数用于处理IO请求 + hdfHelloInterfaceHost->ioService.Dispatch = HelloInterfaceDriverDispatch; + hdfHelloInterfaceHost->ioService.Open = NULL; + hdfHelloInterfaceHost->ioService.Release = NULL; + + auto serviceImpl = OHOS::HDI::Hello::V1_0::IHelloInterface::Get(true); + if (serviceImpl == nullptr) { + HDF_LOGE("%{public}s: failed to get of implement service", __func__); + delete hdfHelloInterfaceHost; + return HDF_FAILURE; + } + + // 使用ObjectCollector的GetOrNewObject方法获取或创建一个Stub对象。Stub对象是服务接口的客户端代理,用于发起远程调用。 + hdfHelloInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, + OHOS::HDI::Hello::V1_0::IHelloInterface::GetDescriptor()); + if (hdfHelloInterfaceHost->stub == nullptr) { + HDF_LOGE("%{public}s: failed to get stub object", __func__); + delete hdfHelloInterfaceHost; + return HDF_FAILURE; + } + + // 将ioService绑定到deviceObject,这样HDF框架就可以通过deviceObject来访问服务 + deviceObject->service = &hdfHelloInterfaceHost->ioService; + HDF_LOGI("%{public}s: driver bind end", __func__); + return HDF_SUCCESS; +} + +// 驱动释放资源的接口 +static void HdfHelloInterfaceDriverRelease(struct HdfDeviceObject *deviceObject) +{ + HDF_LOGI("%{public}s: driver release start", __func__); + if (deviceObject->service == nullptr) { + return; + } + + auto *hdfHelloInterfaceHost = CONTAINER_OF(deviceObject->service, struct HdfHelloInterfaceHost, ioService); + if (hdfHelloInterfaceHost != nullptr) { + delete hdfHelloInterfaceHost; + } +} + +/* + * 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。 + */ +struct HdfDriverEntry g_hellointerfaceDriverEntry = { + .moduleVersion = 1, + .moduleName = "hello_service", + .Bind = HdfHelloInterfaceDriverBind, + .Init = HdfHelloInterfaceDriverInit, + .Release = HdfHelloInterfaceDriverRelease, +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * 调用HDF_INIT将驱动入口注册到HDF框架中。 + * 在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + */ +HDF_INIT(g_hellointerfaceDriverEntry); +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp new file mode 100644 index 00000000..0dc14b38 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "v1_0/hello_interface_service.h" +#include +#include "hello_log.h" +#include "devhost_dump_reg.h" +#include "hello_dump.h" + +#define HDF_LOG_TAG hello_interface_service + +namespace OHOS { +namespace HDI { +namespace Hello { +namespace V1_0 { +extern "C" IHelloInterface *HelloInterfaceImplGetInstance(void) +{ + // עᨩdumper + DevHostRegisterDumpHost(GetHelloDump); + // [hdf-gen] Todo + HDF_LOGI("%{public}s: IHelloInterface init", __func__); + return new (std::nothrow) HelloInterfaceService(); +} + +int32_t HelloInterfaceService::Helloworld(const std::string& sendMsg, std::string& recvMsg) +{ + // [hdf-gen] Todo + HDF_LOGI("%{public}s: HelloService::Helloworld print", __func__); + return HDF_SUCCESS; +} + +} // V1_0 +} // Hello +} // HDI +} // OHOS diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h new file mode 100644 index 00000000..3872b699 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_HDI_HELLO_V1_0_HELLOINTERFACESERVICE_H +#define OHOS_HDI_HELLO_V1_0_HELLOINTERFACESERVICE_H + +#include "v1_0/ihello_interface.h" + +namespace OHOS { +namespace HDI { +namespace Hello { +namespace V1_0 { +class HelloInterfaceService : public OHOS::HDI::Hello::V1_0::IHelloInterface { +public: + HelloInterfaceService() = default; + virtual ~HelloInterfaceService() = default; + + int32_t Helloworld(const std::string& sendMsg, std::string& recvMsg) override; + +}; +} // V1_0 +} // Hello +} // HDI +} // OHOS + +#endif // OHOS_HDI_HELLO_V1_0_HELLOINTERFACESERVICE_H \ No newline at end of file diff --git a/test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h b/test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h new file mode 100644 index 00000000..9f6d5c67 --- /dev/null +++ b/test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HELLO_UHDF_LOG_H +#define HELLO_UHDF_LOG_H + +#include "hdf_log.h" + +#ifdef LOG_DOMAIN +#undef LOG_DOMAIN +#endif +#define LOG_DOMAIN 0xD002516 + +#endif //HELLO_UHDF_LOG_H \ No newline at end of file -- Gitee From 4cfdcab4338a209cc13fb20338f1f7bc2389d7df Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Wed, 14 Aug 2024 09:25:59 +0800 Subject: [PATCH 11/14] fix codecheck Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/docs/DEVELOP.md | 2 +- src/cli/h2hdf/docs/INSTRUCTION.md | 2 +- src/cli/h2hdf/src/generate.js | 51 ++++++++----------- .../{device_info.hcs => device_info.hcs.gen} | 0 .../hello/{bundle.json => bundle.json.gen} | 0 .../hello/v1_0/{BUILD.gn => BUILD.gn.gen} | 0 ...oInterface.idl => IHelloInterface.idl.gen} | 0 .../hello/{BUILD.gn => BUILD.gn.gen} | 0 .../hello/{bundle.json => bundle.json.gen} | 0 .../hello/hal/{BUILD.gn => BUILD.gn.gen} | 0 .../hal/{hello_dump.c => hello_dump.c.gen} | 0 .../{hello_dump.h => hello_dump.h.gen} | 0 .../hdi_service/{BUILD.gn => BUILD.gn.gen} | 0 ...ver.cpp => hello_interface_driver.cpp.gen} | 0 ...ce.cpp => hello_interface_service.cpp.gen} | 0 ...ervice.h => hello_interface_service.h.gen} | 0 .../{hello_log.h => hello_log.h.gen} | 0 17 files changed, 24 insertions(+), 31 deletions(-) rename test/h2hdf/hellohdf/HcsConfig/{device_info.hcs => device_info.hcs.gen} (100%) rename test/h2hdf/hellohdf/IdlInterface/hello/{bundle.json => bundle.json.gen} (100%) rename test/h2hdf/hellohdf/IdlInterface/hello/v1_0/{BUILD.gn => BUILD.gn.gen} (100%) rename test/h2hdf/hellohdf/IdlInterface/hello/v1_0/{IHelloInterface.idl => IHelloInterface.idl.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/{BUILD.gn => BUILD.gn.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/{bundle.json => bundle.json.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/hal/{BUILD.gn => BUILD.gn.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/hal/{hello_dump.c => hello_dump.c.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/hal/include/{hello_dump.h => hello_dump.h.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/hdi_service/{BUILD.gn => BUILD.gn.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/hdi_service/{hello_interface_driver.cpp => hello_interface_driver.cpp.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/hdi_service/{hello_interface_service.cpp => hello_interface_service.cpp.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/hdi_service/{hello_interface_service.h => hello_interface_service.h.gen} (100%) rename test/h2hdf/hellohdf/Peripheral/hello/utils/interface/{hello_log.h => hello_log.h.gen} (100%) diff --git a/src/cli/h2hdf/docs/DEVELOP.md b/src/cli/h2hdf/docs/DEVELOP.md index 927e1561..8b26954a 100644 --- a/src/cli/h2hdf/docs/DEVELOP.md +++ b/src/cli/h2hdf/docs/DEVELOP.md @@ -194,7 +194,7 @@ cat hdf_devhost.cfg ![image-20240724093743837](./figures/pic_show_hostid.png) -2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入第一个参数为hostId,第二个参数为hostName;运行命令如下所示: +2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入一个参数为hostId,第二个参数为hostName;运行命令如下所示: ``` ./hdf_devhost 14 hello_host diff --git a/src/cli/h2hdf/docs/INSTRUCTION.md b/src/cli/h2hdf/docs/INSTRUCTION.md index 56da00f8..248a2c3d 100644 --- a/src/cli/h2hdf/docs/INSTRUCTION.md +++ b/src/cli/h2hdf/docs/INSTRUCTION.md @@ -111,7 +111,7 @@ cat hdf_devhost.cfg ![image-20240724093743837](./figures/pic_show_hostid.png) -2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入第一个参数为hostId,第二个参数为hostName;运行命令如下所示: +2.运行可执行文件hdf_devhost,手动拉起host:进入/vendor/bin路径下,运行可执行文件hdf_devhost,传入一个参数为hostId,第二个参数为hostName;运行命令如下所示: ``` ./hdf_devhost 14 hello_host diff --git a/src/cli/h2hdf/src/generate.js b/src/cli/h2hdf/src/generate.js index 86f25976..2681f124 100644 --- a/src/cli/h2hdf/src/generate.js +++ b/src/cli/h2hdf/src/generate.js @@ -12,19 +12,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const path = require('path'); const fs = require('fs'); - -/** - * 获取Json文件内容 - * @returns - */ -function getJsonCfg(jsonFilePath) { - let jsonCfg = null; // json 配置文件 - let jsonFile = fs.readFileSync(jsonFilePath, { encoding: 'utf8' }); - jsonCfg = JSON.parse(jsonFile); - return jsonCfg; -} +const path = require('path'); function replaceAll(s, sfrom, sto) { while (s.indexOf(sfrom) >= 0) { @@ -33,29 +22,19 @@ function replaceAll(s, sfrom, sto) { return s; } -// 创建路径 -function createDirectorySync(directoryPath) { - try { - if (!fs.existsSync(directoryPath)) { - fs.mkdirSync(directoryPath, { recursive: true }); - } - } catch (err) { - console.error(`无法创建文件夹 ${directoryPath}: ${err}`); - } +function getJsonCfg(jsonFilePath) { + let jsonCfg = null; + let jsonFile = fs.readFileSync(jsonFilePath, { encoding: 'utf8' }); + jsonCfg = JSON.parse(jsonFile); + return jsonCfg; } function pathJoin(...args) { return path.join(...args); } -// 写文件 -function writeFile(fn, str) { - fs.writeFileSync(fn, str, { encoding: 'utf8' }); -} - function utf8ArrayToStr(array) { let char2, char3; - let outStr = ''; let len = array.length; let i = 0; @@ -94,6 +73,20 @@ function readFile(fn) { return data; } +function createDirectorySync(directoryPath) { + try { + if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath, { recursive: true }); + } + } catch (err) { + console.error(`无法创建文件夹 ${directoryPath}: ${err}`); + } +} + +function writeFile(fn, str) { + fs.writeFileSync(fn, str, { encoding: 'utf8' }); +} + /* 根据用户输入的driver名字生成framework框架 * drivername:用户输入的驱动名,out:生成框架路径 * 1. 读取json文件模板 @@ -112,7 +105,7 @@ function genDriverFramework(driverName, out = '') { 'driverName': driverName, 'namespaceName': namespaceName, 'idlFileName': idlFileName, - } + }; // 生成Hcs配置文件 genHcsconfigFile(frameworkJson, driverName, frameworkPath); @@ -121,7 +114,7 @@ function genDriverFramework(driverName, out = '') { genInterface(frameworkPath, frameworkJson, rootInfo); // 生成hdi_service - genPeripheral(frameworkPath,frameworkJson, rootInfo); + genPeripheral(frameworkPath, frameworkJson, rootInfo); } function genPeripheral(frameworkPath, frameworkJson, rootInfo) { diff --git a/test/h2hdf/hellohdf/HcsConfig/device_info.hcs b/test/h2hdf/hellohdf/HcsConfig/device_info.hcs.gen similarity index 100% rename from test/h2hdf/hellohdf/HcsConfig/device_info.hcs rename to test/h2hdf/hellohdf/HcsConfig/device_info.hcs.gen diff --git a/test/h2hdf/hellohdf/IdlInterface/hello/bundle.json b/test/h2hdf/hellohdf/IdlInterface/hello/bundle.json.gen similarity index 100% rename from test/h2hdf/hellohdf/IdlInterface/hello/bundle.json rename to test/h2hdf/hellohdf/IdlInterface/hello/bundle.json.gen diff --git a/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn b/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn.gen similarity index 100% rename from test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn rename to test/h2hdf/hellohdf/IdlInterface/hello/v1_0/BUILD.gn.gen diff --git a/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl b/test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl.gen similarity index 100% rename from test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl rename to test/h2hdf/hellohdf/IdlInterface/hello/v1_0/IHelloInterface.idl.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn b/test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn rename to test/h2hdf/hellohdf/Peripheral/hello/BUILD.gn.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/bundle.json b/test/h2hdf/hellohdf/Peripheral/hello/bundle.json.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/bundle.json rename to test/h2hdf/hellohdf/Peripheral/hello/bundle.json.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn b/test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn rename to test/h2hdf/hellohdf/Peripheral/hello/hal/BUILD.gn.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c b/test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c rename to test/h2hdf/hellohdf/Peripheral/hello/hal/hello_dump.c.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h b/test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h rename to test/h2hdf/hellohdf/Peripheral/hello/hal/include/hello_dump.h.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn rename to test/h2hdf/hellohdf/Peripheral/hello/hdi_service/BUILD.gn.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp rename to test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_driver.cpp.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp rename to test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.cpp.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h b/test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h rename to test/h2hdf/hellohdf/Peripheral/hello/hdi_service/hello_interface_service.h.gen diff --git a/test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h b/test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h.gen similarity index 100% rename from test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h rename to test/h2hdf/hellohdf/Peripheral/hello/utils/interface/hello_log.h.gen -- Gitee From 3bf52ea62dee0e9be83965e1e3f3f1e67060b04c Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Wed, 14 Aug 2024 10:25:38 +0800 Subject: [PATCH 12/14] fix codecheck2 Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/src/generate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/h2hdf/src/generate.js b/src/cli/h2hdf/src/generate.js index 2681f124..5c9bda1c 100644 --- a/src/cli/h2hdf/src/generate.js +++ b/src/cli/h2hdf/src/generate.js @@ -99,7 +99,7 @@ function genDriverFramework(driverName, out = '') { let frameworkPath = pathJoin(out, driverName + 'hdf'); - let namespaceName = driverName.substring(0,1).toUpperCase() + driverName.substring(1, driverName.length); + let namespaceName = driverName.substring(0, 1).toUpperCase() + driverName.substring(1, driverName.length); let idlFileName = 'I' + namespaceName + 'Interface'; let rootInfo = { 'driverName': driverName, -- Gitee From 6ffb56de0d4b04182235273d814673f961693872 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Fri, 6 Sep 2024 09:26:39 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E8=AF=84=E5=AE=A1?= =?UTF-8?q?=E6=84=8F=E8=A7=81=E4=BF=AE=E6=94=B9=E5=B7=A5=E5=85=B7=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/docs/DEVELOP.md | 145 +++++++++++------- src/cli/h2hdf/docs/figures/pic_code_frame.png | Bin 33533 -> 37189 bytes src/cli/h2hdf/docs/figures/pic_show_exe.png | Bin 0 -> 855 bytes .../h2hdf/docs/{INSTRUCTION.md => usage.md} | 71 ++++++--- src/cli/h2hdf/package.json | 8 +- src/cli/h2hdf/src/generate.js | 43 +++--- src/cli/h2hdf/src/main.js | 45 +++++- .../{ => v4_1}/bundlejsonTemplete.gen | 0 .../{ => v4_1}/buildgnTemplete.gen | 0 .../{ => v4_1}/buildgnTemplete.gen | 0 .../{ => v4_1}/bundlejsonTemplete.gen | 0 src/cli/h2hdf/src/templete/framework.json | 16 +- 12 files changed, 222 insertions(+), 106 deletions(-) create mode 100644 src/cli/h2hdf/docs/figures/pic_show_exe.png rename src/cli/h2hdf/docs/{INSTRUCTION.md => usage.md} (75%) rename src/cli/h2hdf/src/templete/IdlInterfaceTemplete/{ => v4_1}/bundlejsonTemplete.gen (100%) rename src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/{ => v4_1}/buildgnTemplete.gen (100%) rename src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/{ => v4_1}/buildgnTemplete.gen (100%) rename src/cli/h2hdf/src/templete/PeripheralTemplete/{ => v4_1}/bundlejsonTemplete.gen (100%) diff --git a/src/cli/h2hdf/docs/DEVELOP.md b/src/cli/h2hdf/docs/DEVELOP.md index 8b26954a..c9ef1456 100644 --- a/src/cli/h2hdf/docs/DEVELOP.md +++ b/src/cli/h2hdf/docs/DEVELOP.md @@ -1,18 +1,17 @@ -# Develop guide +# Develop Guide ## h2hdf工具使用场景 在OpenHarmony系统中,上层应用或服务层通过调用HDF框架提供的HDI接口,能够以一种标准化和抽象化的方式与底层硬件设备进行交互。使用h2hdf工具,用户只需提供一个drivername,工具会自动生成整个框架的代码,包含驱动配置文件、idl接口、驱动程序driver和驱动服务框架。 -![image-20240724093743837](./figures/pic_frame.png)) +![image-20240724093743837](./figures/pic_code_frame.png) ## h2hdf工具代码框架说明 ``` -napi_generator/src/cli/h2hdf - h2hdf ├── docs # 文档 +│ ├── figures # 图片资源 │ ├── usage.md # 使用文档 │ ├── develop.md # 设计文档 ├── src @@ -21,21 +20,25 @@ h2hdf │ │ │ ├── hcsconfigTemplete.gen # hcs配置模板 │ │ ├── IdlInterfaceTemplete │ │ │ ├── buildgnTemplete.gen # idl接口BUILD.gn模板 -│ │ │ ├── bundlejsonTemplete.gen # idl接口bundle.json模板 +│ │ │ ├── v4_1 +│ │ │ │ ├── bundlejsonTemplete.gen # idl接口bundle.json模板 │ │ │ ├── idlInterfaceTemplete.gen # idl接口定义文件模板 │ │ ├── PeripheralTemplete │ │ │ ├── DumpExampleTemplete # dump示例 -│ │ │ │ ├── buildgnTemplete.gen # BUILD.gn模板 +│ │ │ │ ├── v4_1 +│ │ │ │ │ ├── buildgnTemplete.gen # BUILD.gn模板 │ │ │ │ ├── dumpCTemplete.gen # dump实现示例模板 │ │ │ │ ├── dumpHTemplete.gen # dump h文件模板 │ │ │ ├── HdiServiceTemplete # hdi_service 模板 -│ │ │ │ ├── buildgnTemplete.gen # BUILD.gn模板 +│ │ │ │ ├── v4_1 +│ │ │ │ │ ├── buildgnTemplete.gen # BUILD.gn模板 │ │ │ │ ├── driverTemplete.gen # driver模板 │ │ │ │ ├── logHTemplte.gen # 日志文件模板 │ │ │ │ ├── serviceCppTemplete.gen # 驱动服务模板 │ │ │ │ ├── serviceHTemplete.gen # 驱动服务 h 文件模板 │ │ │ ├── buildgnTemplete.gen # hdi service BUILD.gn模板 -│ │ │ ├── bundlejsonTemplete.gen # hdi service bundle.json模板 +│ │ │ ├── v4_1 +│ │ │ │ ├── bundlejsonTemplete.gen # hdi service bundle.json模板 │ │ ├── framework.json # 存储模板对应相对路径 │ ├── generate.js # 使用templete中对应的模板生成代码。 │ ├── main.js # 工具入口文件,定义输入参数,调用generate.js来启动代码生成过程。 @@ -46,50 +49,60 @@ h2hdf ![image-20240724093743837](./figures/pic_code_frame.png) -// 脚本重要函数 +main.js为脚本入口,其中使用stdio.getopt获取参数,参数分别为: -n, drivername,例如:hello;-v, 可选参数,版本,默认为4.1;-o, 可选参数,默认为当前目录,指定生成框架代码输出路径。 ``` -// main.js - let ops = stdio.getopt({ // 输入driver name ,输入一个字符串,默认为hello 'drivername': { key: 'n', args: 1, description: 'driver name', default: 'hello' }, + // 输入版本号 + 'version': { key: 'v', args: 1, description: 'source version', default: '4.1' }, // 输出文件夹路径 - 'out': { key: 'o', args: 1, description: 'output directory', default: '.' }, + 'out': { key: 'o', args: 1, description: 'output directory', default: '' }, }); ``` +对输入的参数值进行校验:checkInput对输入的drivername进行校验,输入的drivername必须符合命名规范;isValidValue对输入的版本号进行校验,输入的版本号必须包含在版本号数组中,该数组后续可持续更新: + ``` -// generate.js +... +const allowedVersion = ['4.0', '4.1', '5.0', '5.1']; +function isValidValue(value, allowedVersion) { + return allowedVersion.includes(value); +} -/* 根据用户输入的driver名字生成framework框架 - * drivername:用户输入的驱动名,out:生成框架路径 - * 1. 读取json文件模板 - * 2. 替换模板中的名字并写文件输出 - */ -function genDriverFramework(driverName, out = '') { - // 读取Json文件,获取各模板路径 - let frameworkJsonPath = path.join(__dirname, './templete/framework.json'); - let frameworkJson = getJsonCfg(frameworkJsonPath); +function checkInput(input) { + const regex = /\b[a-zA-Z_][a-zA-Z0-9_]*\b/; + return regex.test(input); +} +``` - let frameworkPath = pathJoin(out, 'hdf'); +使用getJsonCfg读取json文件中hdf框架模板的路径: - let namespaceName = driverName.substring(0,1).toUpperCase() + driverName.substring(1, driverName.length); - let idlFileName = 'I' + namespaceName + 'Interface'; - let rootInfo = { - 'driverName': driverName, - 'namespaceName': namespaceName, - 'idlFileName': idlFileName, - } +``` +function getJsonCfg(jsonFilePath) { + let jsonCfg = null; + let jsonFile = fs.readFileSync(jsonFilePath, { encoding: 'utf8' }); + jsonCfg = JSON.parse(jsonFile); + return jsonCfg; +} +``` +获取到每个模板的路径后,根据路径读取模板文件,并替换模板文件中的drivername等: + +``` +/* 根据用户输入的driver名字生成framework框架 + * drivername:用户输入的驱动名,frameworkJson: 模板内容,out:生成框架路径 + * 替换模板中的名字并写文件输出 + */ +function genDriverFramework(driverName, frameworkJson, version, out = '') { + ... // 生成Hcs配置文件 genHcsconfigFile(frameworkJson, driverName, frameworkPath); - // 生成Idl接口 genInterface(frameworkPath, frameworkJson, rootInfo); - // 生成hdi_service - genPeripheral(frameworkPath,frameworkJson, rootInfo); + genPeripheral(frameworkPath, frameworkJson, rootInfo); } ``` @@ -115,11 +128,14 @@ node main.js -n hello -n, drivername,例如:hello + -v, 可选参数,版本,默认为4.1 + -o, 可选参数,默认为当前目录,指定生成框架代码输出路径。 6.执行成功后在napi_generator/src/cli/h2hdf/src/下生成hellohdf文件夹,文件夹中目录结构如下所示: ``` +hellohdf ├── HcsConfig # hcs配置文件 │ ├── device_info.hcs # 内容配置到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 ├── IdlInterface @@ -148,29 +164,56 @@ node main.js -n hello ### 编译 -1.将hellohdf/Peripheral文件夹下的hello文件夹拷贝到源码drivers/peripheral目录下,将hellohdf/IdlInterface文件夹下的hello文件夹拷贝到源码drivers/interface目录下,将hellohdf/HcsConfig/device_info.hcs中的内容拷贝到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 +1.将hellohdf/Peripheral文件夹下的hello文件夹拷贝到源码drivers/peripheral目录下 -2.配置产品:在源码productdefine/common/inherit/rich.json文件中增加以下代码: +``` +cp hellohdf/Peripheral/hello 源码/drivers/peripheral +``` + +将hellohdf/IdlInterface文件夹下的hello文件夹拷贝到源码drivers/interface目录下 ``` -{ - "component": "drivers_interface_hello", - "features": [] -}, +cp hellohdf/IdlInterface/hello 源码/drivers/interface ``` -其中drivers_interface_hello为drivers/interface/hello/v1_0/BUILD.gn中的part_name。 +将hellohdf/HcsConfig/device_info.hcs中的内容拷贝到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中,如下所示: + +``` + root { + device_info { + ... + hello :: host { + hostName = "hello_host"; + priority = 50; + hello_device :: device { + device0 :: deviceNode { + preload = 0; + policy = 2; + priority = 100; + moduleName = "libhello_driver.z.so"; + serviceName = "hello_interface_service"; + } + } + } + ... + } + } +``` -在源码productdefine/common/inherit/chipset_common.json文件中增加以下代码: +2.配置产品:以rk3568为例,在源码vendor/hihope/rk3568/config.json文件中hdf子系统的components中增加以下内容: ``` { - "component": "drivers_peripheral_hello", - "features": [] - }, + "component": "drivers_interface_hello", + "features": [] +}, +{ + "component": "drivers_peripheral_hello", + "features": [] +} ``` -其中drivers_peripheral_hello为drivers/peripheral/hello/bundle.json中的component。 +注意:drivers_interface_hello为drivers/interface/hello/v1_0/BUILD.gn中的part_name。drivers_peripheral_hello为drivers/peripheral/hello/bundle.json中的component。 3.编译,在源码下执行以下命令进行编译: @@ -200,6 +243,10 @@ cat hdf_devhost.cfg ./hdf_devhost 14 hello_host ``` +![image-20240903114845035](./figures/pic_show_exe.png) + +注意 :不可将进程kill + 3.查看host是否加载:新开一个命令行窗口,hdc进入开发板,执行以下命令查看进程是否拉起: ``` @@ -220,14 +267,6 @@ ps -A | grep host ![image-20240724093543096](./figures/pic_show_host.png) -``` -----------------------------------HdfDeviceServiceManager-------------------------------- -hdf device information in user space, format: -... -hello_host :0xf - device0 :0xf000101 :hello_interface_service -``` - 使用hidumper查看更多信息 ``` diff --git a/src/cli/h2hdf/docs/figures/pic_code_frame.png b/src/cli/h2hdf/docs/figures/pic_code_frame.png index b5af154bd3581d32fcab280f41592c6cf24ea328..1f77649365e6174e8f482ffef45c86f8dc0d9c05 100644 GIT binary patch literal 37189 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 lJKN#^{`r4Q4lK{N{BhB*NA-%6}`?bC= zSsTFdaEqt)<#R`W|EsugGTeHFBg_7{_1F0u&L2}^D5w*xsjok6(eV6_F4OPBJ3q5e zRsFEfD1P?xeRUUi*2KqtzoE2Z{`q;PuOeqYziYU7XYn<^xAQK>rr%HB7ME#URlMkr z=BQ&J?>5r>Hf4h;@H*CwRTnCHcs6$X{~hUyG8SqFP=FwbMK6+r`Mm^ zwCEp5`1Z>a_c^MJL+>10^haabM9cWVuXgh~Q>$KWPA%T%b~Js3@#n-9^{M~7&T~!|b6neBLZ@m1Ha_0K|mp8DU|#j$A4`}0SxJP&()`qSRJzO%P?EPb>}z3b+V zQ$P2JKCOQ9=3b9a@A9n0GkRCw$%(uAqo)5=XOh{vBD;LopLe(YTYqq}KAXb(ANI0y zFPY`^zdHCl;J)y*S3l4Hd#l7S;kXjR0Wb>Kz`+1PB05YA5X9QezyL-L2dSg%N5%U2 Z@jAZ_hH6bHdj!l344$rjF6*2UngDjdp_c#v literal 0 HcmV?d00001 diff --git a/src/cli/h2hdf/docs/INSTRUCTION.md b/src/cli/h2hdf/docs/usage.md similarity index 75% rename from src/cli/h2hdf/docs/INSTRUCTION.md rename to src/cli/h2hdf/docs/usage.md index 248a2c3d..dedb587b 100644 --- a/src/cli/h2hdf/docs/INSTRUCTION.md +++ b/src/cli/h2hdf/docs/usage.md @@ -3,7 +3,7 @@ ## 简介 在OpenHarmony系统中,上层应用或服务层通过调用HDF框架提供的HDI接口,能够以一种标准化和抽象化的方式与底层硬件设备进行交互。使用h2hdf工具,用户只需提供一个drivername,工具会自动生成整个框架的代码,包含驱动配置文件、idl接口、驱动程序driver和驱动服务框架。 -![image-20240724093743837](./figures/pic_frame.png) +![image-20240724093743837](./figures/pic_code_frame.png) ## 约束 系统:建议Ubuntu 20.04或者Windows 10 @@ -32,11 +32,14 @@ node main.js -n hello -n, drivername,例如:hello + -v, 可选参数,版本,默认为4.1 + -o, 可选参数,默认为当前目录,指定生成框架代码输出路径。 6.执行成功后在napi_generator/src/cli/h2hdf/src/下生成hellohdf文件夹,文件夹中目录结构如下所示: ``` +hellohdf ├── HcsConfig # hcs配置文件 │ ├── device_info.hcs # 内容配置到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 ├── IdlInterface @@ -65,29 +68,56 @@ node main.js -n hello ### 编译 -1.将hellohdf/Peripheral文件夹下的hello文件夹拷贝到源码drivers/peripheral目录下,将hellohdf/IdlInterface文件夹下的hello文件夹拷贝到源码drivers/interface目录下,将hellohdf/HcsConfig/device_info.hcs中的内容拷贝到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中 +1.将hellohdf/Peripheral文件夹下的hello文件夹拷贝到源码drivers/peripheral目录下 -2.配置产品:在源码productdefine/common/inherit/rich.json文件中增加以下代码: +``` +cp hellohdf/Peripheral/hello 源码/drivers/peripheral +``` + +将hellohdf/IdlInterface文件夹下的hello文件夹拷贝到源码drivers/interface目录下 ``` -{ - "component": "drivers_interface_hello", - "features": [] -}, +cp hellohdf/IdlInterface/hello 源码/drivers/interface ``` -其中drivers_interface_hello为drivers/interface/hello/v1_0/BUILD.gn中的part_name。 +将hellohdf/HcsConfig/device_info.hcs中的内容拷贝到源码vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs文件中,如下所示: + +``` + root { + device_info { + ... + hello :: host { + hostName = "hello_host"; + priority = 50; + hello_device :: device { + device0 :: deviceNode { + preload = 0; + policy = 2; + priority = 100; + moduleName = "libhello_driver.z.so"; + serviceName = "hello_interface_service"; + } + } + } + ... + } + } +``` -在源码productdefine/common/inherit/chipset_common.json文件中增加以下代码: +2.配置产品:以rk3568为例,在源码vendor/hihope/rk3568/config.json文件中hdf子系统的components中增加以下内容: ``` { - "component": "drivers_peripheral_hello", - "features": [] - }, + "component": "drivers_interface_hello", + "features": [] +}, +{ + "component": "drivers_peripheral_hello", + "features": [] +} ``` -其中drivers_peripheral_hello为drivers/peripheral/hello/bundle.json中的component。 +注意:drivers_interface_hello为drivers/interface/hello/v1_0/BUILD.gn中的part_name。drivers_peripheral_hello为drivers/peripheral/hello/bundle.json中的component。 3.编译,在源码下执行以下命令进行编译: @@ -117,6 +147,10 @@ cat hdf_devhost.cfg ./hdf_devhost 14 hello_host ``` +![image-20240903114845035](./figures/pic_show_exe.png) + +注意 :不可将进程kill + 3.查看host是否加载:新开一个命令行窗口,hdc进入开发板,执行以下命令查看进程是否拉起: ``` @@ -137,14 +171,6 @@ ps -A | grep host ![image-20240724093543096](./figures/pic_show_host.png) -``` -----------------------------------HdfDeviceServiceManager-------------------------------- -hdf device information in user space, format: -... -hello_host :0xf - device0 :0xf000101 :hello_interface_service -``` - 使用hidumper查看更多信息 ``` @@ -157,4 +183,5 @@ hidumper -s HdfDeviceServiceManager -a "-host hello_host -c" #### 静态加载 -// todo 待补充 \ No newline at end of file +// todo 待补充 + diff --git a/src/cli/h2hdf/package.json b/src/cli/h2hdf/package.json index 04b9082f..98faddb5 100644 --- a/src/cli/h2hdf/package.json +++ b/src/cli/h2hdf/package.json @@ -17,18 +17,18 @@ "assets": [ "./src/templete/HcsconfigTemplete/hcsconfigTemplete.gen", "./src/templete/IdlInterfaceTemplete/buildgnTemplete.gen", - "./src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen", + "./src/templete/v4_1/IdlInterfaceTemplete/bundlejsonTemplete.gen", "./src/templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen", - "./src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen", + "./src/templete/PeripheralTemplete/DumpExampleTemplete/v4_1/buildgnTemplete.gen", "./src/templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen", "./src/templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen", - "./src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen", + "./src/templete/PeripheralTemplete/HdiServiceTemplete/v4_1/buildgnTemplete.gen", "./src/templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen", "./src/templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen", "./src/templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen", "./src/templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen", "./src/templete/PeripheralTemplete/buildgnTemplete.gen", - "./src/templete/PeripheralTemplete/bundlejsonTemplete.gen" + "./src/templete/PeripheralTemplete/v4_1/bundlejsonTemplete.gen" ] } } diff --git a/src/cli/h2hdf/src/generate.js b/src/cli/h2hdf/src/generate.js index 5c9bda1c..381f4b01 100644 --- a/src/cli/h2hdf/src/generate.js +++ b/src/cli/h2hdf/src/generate.js @@ -22,13 +22,6 @@ function replaceAll(s, sfrom, sto) { return s; } -function getJsonCfg(jsonFilePath) { - let jsonCfg = null; - let jsonFile = fs.readFileSync(jsonFilePath, { encoding: 'utf8' }); - jsonCfg = JSON.parse(jsonFile); - return jsonCfg; -} - function pathJoin(...args) { return path.join(...args); } @@ -88,20 +81,16 @@ function writeFile(fn, str) { } /* 根据用户输入的driver名字生成framework框架 - * drivername:用户输入的驱动名,out:生成框架路径 - * 1. 读取json文件模板 - * 2. 替换模板中的名字并写文件输出 + * drivername:用户输入的驱动名,frameworkJson: 模板内容,out:生成框架路径 + * 替换模板中的名字并写文件输出 */ -function genDriverFramework(driverName, out = '') { - // 读取Json文件,获取各模板路径 - let frameworkJsonPath = path.join(__dirname, './templete/framework.json'); - let frameworkJson = getJsonCfg(frameworkJsonPath); - +function genDriverFramework(driverName, frameworkJson, version, out = '') { let frameworkPath = pathJoin(out, driverName + 'hdf'); let namespaceName = driverName.substring(0, 1).toUpperCase() + driverName.substring(1, driverName.length); let idlFileName = 'I' + namespaceName + 'Interface'; let rootInfo = { + 'version': version, 'driverName': driverName, 'namespaceName': namespaceName, 'idlFileName': idlFileName, @@ -139,7 +128,11 @@ function genPeripheral(frameworkPath, frameworkJson, rootInfo) { function genBuildFile(peripheralPath, frameworkJson, rootInfo) { // out/hdf/Peripheral/xxx/bundle.json let genBundlejsonPath = pathJoin(peripheralPath, 'bundle.json'); - let bundlejsonPath = path.join(__dirname, frameworkJson.PeripheralTemplete.bundlejsonTemplete); + let bundlejsonPath = ''; + if (rootInfo.version === 'v4_1') { + bundlejsonPath = path.join(__dirname, frameworkJson.PeripheralTemplete.bundlejsonTemplete.v4_1); + } + // let bundlejsonPath = path.join(__dirname, frameworkJson.PeripheralTemplete.bundlejsonTemplete); let bundlejsonContent = readFile(bundlejsonPath); bundlejsonContent = replaceAll(bundlejsonContent, '[driver_name]', rootInfo.driverName); writeFile(genBundlejsonPath, bundlejsonContent); @@ -199,7 +192,11 @@ function genHdiService(peripheralPath, frameworkJson, rootInfo) { // 生成hdi_service下面的BUILD.gn: out/hdf/Peripheral/xxx/hdi_service/ let genHdiServiceGnPath = pathJoin(hdiPath, 'BUILD.gn'); - let serviceGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.buildgnTemplete); + let serviceGnPath = ''; + if (rootInfo.version === 'v4_1') { + serviceGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.buildgnTemplete.v4_1); + } + // let serviceGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.buildgnTemplete); let serviceGnContent = readFile(serviceGnPath); serviceGnContent = replaceAll(serviceGnContent, '[driver_name]', rootInfo.driverName); writeFile(genHdiServiceGnPath, serviceGnContent); @@ -209,7 +206,11 @@ function genExampleDumpfile(peripheralPath, frameworkJson, rootInfo) { let dumpExamplePath = pathJoin(peripheralPath, 'hal'); createDirectorySync(dumpExamplePath); let genDumpExampleGnPath = pathJoin(dumpExamplePath, 'BUILD.gn'); - let dumpExampleGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.buildgnTemplete); + let dumpExampleGnPath = ''; + if (rootInfo.version === 'v4_1') { + dumpExampleGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.buildgnTemplete.v4_1); + } + // let dumpExampleGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.buildgnTemplete); let dumpExampleGnContent = readFile(dumpExampleGnPath); dumpExampleGnContent = replaceAll(dumpExampleGnContent, '[driver_name]', rootInfo.driverName); writeFile(genDumpExampleGnPath, dumpExampleGnContent); @@ -246,7 +247,11 @@ function genInterface(frameworkPath, frameworkJson, rootInfo) { // idl接口bundlejson路径 out/hdf/IdlInterface/foo/bundle.json let genIdlBundlejsonPath = pathJoin(idlPath, 'bundle.json'); - let idlBundlejsonPath = path.join(__dirname, frameworkJson.IdlInterfaceTemplete.bundlejsonTemplete); + let idlBundlejsonPath = ''; + if (rootInfo.version === 'v4_1') { + idlBundlejsonPath = path.join(__dirname, frameworkJson.IdlInterfaceTemplete.bundlejsonTemplete.v4_1); + } + let idlBundlejsonContent = readFile(idlBundlejsonPath); idlBundlejsonContent = replaceAll(idlBundlejsonContent, '[driver_name]', rootInfo.driverName); writeFile(genIdlBundlejsonPath, idlBundlejsonContent); diff --git a/src/cli/h2hdf/src/main.js b/src/cli/h2hdf/src/main.js index 506a88a6..50f5b1c5 100644 --- a/src/cli/h2hdf/src/main.js +++ b/src/cli/h2hdf/src/main.js @@ -12,24 +12,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const fs = require('fs'); +const path = require('path'); const stdio = require('stdio'); const main = require('./generate'); let ops = stdio.getopt({ // 输入driver name ,输入一个字符串,默认为hello 'drivername': { key: 'n', args: 1, description: 'driver name', default: 'hello' }, + // 输入版本号 + 'version': { key: 'v', args: 1, description: 'source version', default: '4.1' }, // 输出文件夹路径 - 'out': { key: 'o', args: 1, description: 'output directory', default: '.' }, + 'out': { key: 'o', args: 1, description: 'output directory', default: '' }, }); +const allowedVersion = ['4.1', '4-1', '4_1', '4.0', '5.0', '5.1']; + let drivername = ops.drivername; +let version = ops.version; // 若drivername不为空,则生成,否则打印错误信息 if (drivername.trim().length !== 0 && checkInput(drivername)) { - main.genDriverFramework(drivername); - console.info('Generate Success'); -} + if (!isValidValue(version, allowedVersion)) { + // 版本号不符合规则 请输入正确的版本号 + console.log('请输入正确的版本号!如:4.1'); + return; + } + // 在这里读取cfg文件 + let frameworkJsonPath = path.join(__dirname, './templete/framework.json'); + let frameworkJson = getJsonCfg(frameworkJsonPath); + if (version === '4.1' || version === '4-1' || version === '4_1') { + version = 'v4_1'; + } else { + console.log('其他版本暂不支持...'); + return; + } + + // 然后再调用templete生成模板 + main.genDriverFramework(drivername, frameworkJson, version, ops.out); + console.log('Generate Success'); +} else { + // 输入的名字不符合规则 + console.log('请输入正确的drivername!'); +} + +function isValidValue(value, allowedVersion) { + return allowedVersion.includes(value); +} function checkInput(input) { const regex = /\b[a-zA-Z_][a-zA-Z0-9_]*\b/; return regex.test(input); } + +function getJsonCfg(jsonFilePath) { + let jsonCfg = null; + let jsonFile = fs.readFileSync(jsonFilePath, { encoding: 'utf8' }); + jsonCfg = JSON.parse(jsonFile); + return jsonCfg; +} diff --git a/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen b/src/cli/h2hdf/src/templete/IdlInterfaceTemplete/v4_1/bundlejsonTemplete.gen similarity index 100% rename from src/cli/h2hdf/src/templete/IdlInterfaceTemplete/bundlejsonTemplete.gen rename to src/cli/h2hdf/src/templete/IdlInterfaceTemplete/v4_1/bundlejsonTemplete.gen diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/v4_1/buildgnTemplete.gen similarity index 100% rename from src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen rename to src/cli/h2hdf/src/templete/PeripheralTemplete/DumpExampleTemplete/v4_1/buildgnTemplete.gen diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/v4_1/buildgnTemplete.gen similarity index 100% rename from src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen rename to src/cli/h2hdf/src/templete/PeripheralTemplete/HdiServiceTemplete/v4_1/buildgnTemplete.gen diff --git a/src/cli/h2hdf/src/templete/PeripheralTemplete/bundlejsonTemplete.gen b/src/cli/h2hdf/src/templete/PeripheralTemplete/v4_1/bundlejsonTemplete.gen similarity index 100% rename from src/cli/h2hdf/src/templete/PeripheralTemplete/bundlejsonTemplete.gen rename to src/cli/h2hdf/src/templete/PeripheralTemplete/v4_1/bundlejsonTemplete.gen diff --git a/src/cli/h2hdf/src/templete/framework.json b/src/cli/h2hdf/src/templete/framework.json index e667f66c..d4979baa 100644 --- a/src/cli/h2hdf/src/templete/framework.json +++ b/src/cli/h2hdf/src/templete/framework.json @@ -2,23 +2,31 @@ "HcsconfigTemplete":"./templete/HcsconfigTemplete/hcsconfigTemplete.gen", "IdlInterfaceTemplete":{ "buildgnTemplete":"./templete/IdlInterfaceTemplete/buildgnTemplete.gen", - "bundlejsonTemplete":"./templete/IdlInterfaceTemplete/bundlejsonTemplete.gen", + "bundlejsonTemplete":{ + "v4_1": "./templete/IdlInterfaceTemplete/v4_1/bundlejsonTemplete.gen" + }, "idlInterfaceTemplete":"./templete/IdlInterfaceTemplete/idlInterfaceTemplete.gen" }, "PeripheralTemplete": { "DumpExampleTemplete": { - "buildgnTemplete":"./templete/PeripheralTemplete/DumpExampleTemplete/buildgnTemplete.gen", + "buildgnTemplete":{ + "v4_1": "./templete/PeripheralTemplete/DumpExampleTemplete/v4_1/buildgnTemplete.gen" + }, "dumpCTemplete":"./templete/PeripheralTemplete/DumpExampleTemplete/dumpCTemplete.gen", "dumpHTemplete":"./templete/PeripheralTemplete/DumpExampleTemplete/dumpHTemplete.gen" }, "HdiServiceTemplete": { - "buildgnTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/buildgnTemplete.gen", + "buildgnTemplete":{ + "v4_1": "./templete/PeripheralTemplete/HdiServiceTemplete/v4_1/buildgnTemplete.gen" + }, "driverTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/driverTemplete.gen", "logHTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/logHTemplete.gen", "serviceCppTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/serviceCppTemplete.gen", "serviceHTemplete":"./templete/PeripheralTemplete/HdiServiceTemplete/serviceHTemplete.gen" }, "buildgnTemplete":"./templete/PeripheralTemplete/buildgnTemplete.gen", - "bundlejsonTemplete":"./templete/PeripheralTemplete/bundlejsonTemplete.gen" + "bundlejsonTemplete":{ + "v4_1": "./templete/PeripheralTemplete/v4_1/bundlejsonTemplete.gen" + } } } \ No newline at end of file -- Gitee From 8de64bceb3de571f5728243351e9b0e61242e3b2 Mon Sep 17 00:00:00 2001 From: chen-zhongwei050 Date: Fri, 6 Sep 2024 10:05:25 +0800 Subject: [PATCH 14/14] fix codecheck Signed-off-by: chen-zhongwei050 --- src/cli/h2hdf/src/generate.js | 135 +++++++++++++++++----------------- 1 file changed, 67 insertions(+), 68 deletions(-) diff --git a/src/cli/h2hdf/src/generate.js b/src/cli/h2hdf/src/generate.js index 381f4b01..f7d6d4e9 100644 --- a/src/cli/h2hdf/src/generate.js +++ b/src/cli/h2hdf/src/generate.js @@ -15,71 +15,6 @@ const fs = require('fs'); const path = require('path'); -function replaceAll(s, sfrom, sto) { - while (s.indexOf(sfrom) >= 0) { - s = s.replace(sfrom, sto); - } - return s; -} - -function pathJoin(...args) { - return path.join(...args); -} - -function utf8ArrayToStr(array) { - let char2, char3; - let outStr = ''; - let len = array.length; - let i = 0; - while (i < len) { - let ch = array[i++]; - switch (ch >> 4) { - case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: - // 0xxxxxxx - outStr += String.fromCharCode(ch); - break; - case 12: case 13: - // 110x xxxx 10xx xxxx - char2 = array[i++]; - outStr += String.fromCharCode(((ch & 0x1F) << 6) | (char2 & 0x3F)); - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - char2 = array[i++]; - char3 = array[i++]; - outStr += String.fromCharCode(((ch & 0x0F) << 12) | - ((char2 & 0x3F) << 6) | - ((char3 & 0x3F) << 0)); - break; - } - } - - return outStr; -} - -function readFile(fn) { - if (!fs.existsSync(fn)) { - return ''; - } - let data = fs.readFileSync(fn); - data = utf8ArrayToStr(data); - return data; -} - -function createDirectorySync(directoryPath) { - try { - if (!fs.existsSync(directoryPath)) { - fs.mkdirSync(directoryPath, { recursive: true }); - } - } catch (err) { - console.error(`无法创建文件夹 ${directoryPath}: ${err}`); - } -} - -function writeFile(fn, str) { - fs.writeFileSync(fn, str, { encoding: 'utf8' }); -} - /* 根据用户输入的driver名字生成framework框架 * drivername:用户输入的驱动名,frameworkJson: 模板内容,out:生成框架路径 * 替换模板中的名字并写文件输出 @@ -132,7 +67,6 @@ function genBuildFile(peripheralPath, frameworkJson, rootInfo) { if (rootInfo.version === 'v4_1') { bundlejsonPath = path.join(__dirname, frameworkJson.PeripheralTemplete.bundlejsonTemplete.v4_1); } - // let bundlejsonPath = path.join(__dirname, frameworkJson.PeripheralTemplete.bundlejsonTemplete); let bundlejsonContent = readFile(bundlejsonPath); bundlejsonContent = replaceAll(bundlejsonContent, '[driver_name]', rootInfo.driverName); writeFile(genBundlejsonPath, bundlejsonContent); @@ -196,7 +130,7 @@ function genHdiService(peripheralPath, frameworkJson, rootInfo) { if (rootInfo.version === 'v4_1') { serviceGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.buildgnTemplete.v4_1); } - // let serviceGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.HdiServiceTemplete.buildgnTemplete); + let serviceGnContent = readFile(serviceGnPath); serviceGnContent = replaceAll(serviceGnContent, '[driver_name]', rootInfo.driverName); writeFile(genHdiServiceGnPath, serviceGnContent); @@ -210,7 +144,6 @@ function genExampleDumpfile(peripheralPath, frameworkJson, rootInfo) { if (rootInfo.version === 'v4_1') { dumpExampleGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.buildgnTemplete.v4_1); } - // let dumpExampleGnPath = path.join(__dirname, frameworkJson.PeripheralTemplete.DumpExampleTemplete.buildgnTemplete); let dumpExampleGnContent = readFile(dumpExampleGnPath); dumpExampleGnContent = replaceAll(dumpExampleGnContent, '[driver_name]', rootInfo.driverName); writeFile(genDumpExampleGnPath, dumpExampleGnContent); @@ -278,6 +211,72 @@ function genHcsconfigFile(frameworkJson, driverName, frameworkPath) { writeFile(genHcsconfigFile, hcsconfigContent); } +function replaceAll(s, sfrom, sto) { + while (s.indexOf(sfrom) >= 0) { + s = s.replace(sfrom, sto); + } + return s; +} + +function pathJoin(...args) { + return path.join(...args); +} + +function utf8ArrayToStr(array) { + let char2; + let char3; + let outStr = ''; + let len = array.length; + let i = 0; + while (i < len) { + let ch = array[i++]; + switch (ch >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + outStr += String.fromCharCode(ch); + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + outStr += String.fromCharCode(((ch & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + outStr += String.fromCharCode(((ch & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + break; + } + } + + return outStr; +} + +function readFile(fn) { + if (!fs.existsSync(fn)) { + return ''; + } + let data = fs.readFileSync(fn); + data = utf8ArrayToStr(data); + return data; +} + +function createDirectorySync(directoryPath) { + try { + if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath, { recursive: true }); + } + } catch (err) { + console.error(`无法创建文件夹 ${directoryPath}: ${err}`); + } +} + +function writeFile(fn, str) { + fs.writeFileSync(fn, str, { encoding: 'utf8' }); +} + module.exports = { genDriverFramework }; \ No newline at end of file -- Gitee