HarmonyOS ArkUI实战开发-NAPI异步编程

笔者在前 5 小节里讲述了在 OpenHarmony 上通过 NAPI 的方式实现了 JS 调用 C++的能力,但是这些实现都是同步的,本节笔者简单介绍一下 NAPI 的异步实现。

约定编程规范

ArkUI 开发框架对外提供的 API 命名是需遵守一定规范的,以 @ohos.display 模块提供的 API 为例,源码如下所示:

declare namespace display {function getDefaultDisplay(callback: AsyncCallback<Display>): void;function getDefaultDisplay(): Promise<Display>;function getDefaultDisplaySync(): Display;
}

根据该模块提供的方法,根据方法的命名规则可以得出 2 条规范:

  • 同步调用:

    • 方法名+ Sync 关键字,如:getMd5Sync():string
  • 异步调用:

    • 需要提供 AsyncCallback 和 Promise 的实现,如:getMd5(): Promise<string>getMd5(callback: AsyncCallback<Display>)

因此,我们在 index.d.ts 中声明 NAPI 方法时也按照系统约定的规范来。

定义异步方法

笔者在第 5 小结实现了 MD5 的计算,本节笔者把 MD5 的实现放在异步线程中,先在 index.d.ts 声明 JS 侧的方法,如下所示:

export const add: (a: number, b: number) => number;// 声明异步方法
export function getMd5(value: string, callback: (md5: string) => void): void;
export function getMd5(value: string): Promise<string>;// 声明同步方法
export function getMd5Sync(value: string): string;

getMd5Sync()表示同步实现 MD5 的计算,getMd5() 表示异步实现 MD5 的调用。

实现异步方法

声明完 JS 端的方法后,接着在 hello.cpp 中实现对应的方法,步骤如下:

  • 添加映射

    在 hello.cpp 的 Init() 方法里添加 JS 端的方法映射,代码如下所示:

    static napi_value Init(napi_env env, napi_value exports) {napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},{"getMd5Sync", nullptr, GetMd5Sync, nullptr, nullptr, nullptr, napi_default, nullptr},{"getMd5", nullptr, GetMd5, nullptr, nullptr, nullptr, napi_default, nullptr},};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;}

"getMd5Sync" 和 GetMd5Sync 分别表示 JS 端和 C++ 端的方法,通过 napi_define_properties() 把他们映射在一起。

  • 方法实现
    getMd5() 的 C++ 端代码如下所示:
    // 定义异步线程执行中需要的上下文环境struct Md5Context {// 异步 workernapi_async_work work;// 对应 JS 端的 callback 函数napi_ref callback;// 对应 JS 端的 promise 对象napi_deferred promise;// 传递进来的参数string params;// 计算后的结果string result;};// 在子线程中执行static void doInBackground(napi_env env, void *data) {Md5Context *md5Context = (Md5Context *)data;// 模拟耗时操作,进行 MD5 计算string md5 = MD5(md5Context->params).toStr();// 计算后的 MD5 字存储到 result 中md5Context->result = md5;// 模拟耗时操作,让当前线程休眠 3 秒钟std::this_thread::sleep_for(std::chrono::seconds(3));}// 切换到主线程static void onPostExecutor(napi_env env, napi_status status, void *data) {Md5Context *md5Context = (Md5Context *)data;napi_value returnValue;if (napi_ok !=napi_create_string_utf8(env, md5Context->result.c_str(), md5Context->result.length(), &returnValue)) {delete md5Context;md5Context = nullptr;napi_throw_error(env, "-111", "napi_create_string_utf8: error");return;}if (md5Context->callback) {// 取出缓存的 js 端的 callbacknapi_value callback;if (napi_ok != napi_get_reference_value(env, md5Context->callback, &callback)) {delete md5Context;md5Context = nullptr;napi_throw_error(env, "-111", "napi_get_reference_value error");return;}napi_value tempValue;// 调用 callback,把值回调给 JS 端napi_call_function(env, nullptr, callback, 1, &returnValue, &tempValue);// 删除 callbacknapi_delete_reference(env, md5Context->callback);} else {// 以 promise 的形式回调数据if (napi_ok != napi_resolve_deferred(env, md5Context->promise, returnValue)) {delete md5Context;md5Context = nullptr;napi_throw_error(env, "-111", "napi_resolve_deferred error");}}// 删除异步任务并释放资源napi_delete_async_work(env, md5Context->work);delete md5Context;md5Context = nullptr;}static napi_value GetMd5(napi_env env, napi_callback_info info) {// 1、从 info 中读取 JS 传递过来的参数放入 args 里size_t argc = 2;napi_value args[2] = {nullptr};if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {napi_throw_error(env, "-1001", "napi_get_cb_info error");return nullptr;}// 2、读取传入的参数类型napi_valuetype stringType = napi_undefined;if (napi_ok != napi_typeof(env, args[0], &stringType)) {napi_throw_error(env, "-1002", "napi_typeof string error");return nullptr;}// 3、传入的 string 如果为 null 或者 undefined 则抛异常if (napi_null == stringType || napi_undefined == stringType) {napi_throw_error(env, "-1003", "input params null or undefined");return nullptr;}// 4、读取传入的 string 内容长度size_t length = 0;if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {napi_throw_error(env, "-1004", "get string length error");return nullptr;}// 5、判断传入的 string 长度是否符合if (0 == length) {napi_throw_error(env, "-1005", "string length can't be zero");return nullptr;}// 6、读取传入的 string 长度读取内容char *buffer = new char[length + 1];if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {delete[] buffer;buffer = nullptr;napi_throw_error(env, "-1006", "napi_get_value_string_utf8 string error");return nullptr;}// 7、读取 JS 有没有传递 callback,如果 callback 为 null 就表示是 promise 的回调方式napi_valuetype callbackType = napi_undefined;napi_status callbackStatus = napi_typeof(env, args[1], &callbackType);if (napi_ok != callbackStatus && napi_invalid_arg != callbackStatus) {delete[] buffer;buffer = nullptr;napi_throw_error(env, "-1004", "napi_typeof function error");return nullptr;}// 8、创建一个异步线程需要的数据 model,把传递过来的参数加入进去做下缓存auto context = new Md5Context();context->params = buffer;napi_value returnValue = nullptr;// 9、判断是 callback 的回调方式还是 promise 的回调方式if (napi_function == callbackType) {// 如果是 callback 的回调方式,需要创建 callback 的引用napi_ref callback;if (napi_ok != napi_create_reference(env, args[1], 1, &callback)) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_reference error");return nullptr;}// 缓存 callbackcontext->callback = callback;// 临时返回一个 undefined 值给 JS 端napi_get_undefined(env, &returnValue);} else {// promise 的回调方式,创建一个 Promise 的引用napi_deferred promise;if (napi_ok != napi_create_promise(env, &promise, &returnValue)) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_promise error");return nullptr;}// 缓存 promisecontext->promise = promise;}napi_value resourceName;if (napi_ok != napi_create_string_utf8(env, "GetMd5", NAPI_AUTO_LENGTH, &resourceName)) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_string_utf8 resourceName error");return nullptr;}// 10、创建一个异步任务napi_async_work asyWork;napi_status status =napi_create_async_work(env, nullptr, resourceName, doInBackground, onPostExecutor, (void *)context, &asyWork);if (napi_ok != status) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_async_work error");return nullptr;}// 11、保存异步任务context->work = asyWork;// 12、添加进异步队列napi_queue_async_work(env, asyWork);return returnValue;}

getMd5() 的代码比较多,笔者添加的注释比较清楚,前 6 个小步骤是对传递进来的参数做基础校验,第 7 步是根据参数判断当前异步执行的回调方式是 Promise 还是 Callback。第 8 步创建了一个 Md5Context 对象,它的作用是把当前相关参数缓存下来目的是接下来在异步线程里使用这些参数,第 9 步根据异步回调的方法创建 Promise 或者 Callback 然后把他们保存在 Md5Context 对象里。第 10 步创建一个异步任务,然后把异步任务添加进异步队列中。

napi_create_async_work() 方法的第 3 、 4 个参数需要注意,doInBackground() 方法是在异步线程中执行的,onPostExecutor() 方法在异步线程结束后切换到主线程中执行。

  • 完整代码
    hello.cpp 全部代码如下所示:
    #include <cstddef>#include <cstring>#include "napi/native_api.h"#include <js_native_api.h>#include <js_native_api_types.h>#include <node_api.h>#include <node_api_types.h>#include <string>#include <thread>#include "./md5/md5.h"// 定义异步线程执行中需要的上下文环境struct Md5Context {// 异步 workernapi_async_work work;// 对应 JS 端的 callback 函数napi_ref callback;// 对应 JS 端的 promise 对象napi_deferred promise;// 传递进来的参数string params;// 计算后的结果string result;};static void doInBackground(napi_env env, void *data) {Md5Context *md5Context = (Md5Context *)data;// 模拟耗时操作,进行 MD5 计算string md5 = MD5(md5Context->params).toStr();// 计算后的 MD5 字存储到 result 中md5Context->result = md5;// 模拟耗时操作,让当前线程休眠 3 秒钟std::this_thread::sleep_for(std::chrono::seconds(3));}static void onPostExecutor(napi_env env, napi_status status, void *data) {Md5Context *md5Context = (Md5Context *)data;napi_value returnValue;if (napi_ok !=napi_create_string_utf8(env, md5Context->result.c_str(), md5Context->result.length(), &returnValue)) {delete md5Context;md5Context = nullptr;napi_throw_error(env, "-111", "napi_create_string_utf8: error");return;}if (md5Context->callback) {// 取出缓存的 js 端的 callbacknapi_value callback;if (napi_ok != napi_get_reference_value(env, md5Context->callback, &callback)) {delete md5Context;md5Context = nullptr;napi_throw_error(env, "-111", "napi_get_reference_value error");return;}napi_value tempValue;// 调用 callback,把值回调给 JS 端napi_call_function(env, nullptr, callback, 1, &returnValue, &tempValue);// 删除 callbacknapi_delete_reference(env, md5Context->callback);} else {// 以 promise 的形式回调数据if (napi_ok != napi_resolve_deferred(env, md5Context->promise, returnValue)) {delete md5Context;md5Context = nullptr;napi_throw_error(env, "-111", "napi_resolve_deferred error");}}// 删除异步任务并释放资源napi_delete_async_work(env, md5Context->work);delete md5Context;md5Context = nullptr;}static napi_value GetMd5(napi_env env, napi_callback_info info) {// 1、从 info 中读取 JS 传递过来的参数放入 args 里size_t argc = 2;napi_value args[2] = {nullptr};if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {napi_throw_error(env, "-1001", "napi_get_cb_info error");return nullptr;}// 2、读取传入的参数类型napi_valuetype stringType = napi_undefined;if (napi_ok != napi_typeof(env, args[0], &stringType)) {napi_throw_error(env, "-1002", "napi_typeof string error");return nullptr;}// 3、传入的 string 如果为 null 或者 undefined 则抛异常if (napi_null == stringType || napi_undefined == stringType) {napi_throw_error(env, "-1003", "input params null or undefined");return nullptr;}// 4、读取传入的 string 内容长度size_t length = 0;if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {napi_throw_error(env, "-1004", "get string length error");return nullptr;}// 5、判断传入的 string 长度是否符合if (0 == length) {napi_throw_error(env, "-1005", "string length can't be zero");return nullptr;}// 6、读取传入的 string 长度读取内容char *buffer = new char[length + 1];if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {delete[] buffer;buffer = nullptr;napi_throw_error(env, "-1006", "napi_get_value_string_utf8 string error");return nullptr;}// 7、读取 JS 有没有传递 callback,如果 callback 为 null 就表示是 promise 的回调方式napi_valuetype callbackType = napi_undefined;napi_status callbackStatus = napi_typeof(env, args[1], &callbackType);if (napi_ok != callbackStatus && napi_invalid_arg != callbackStatus) {delete[] buffer;buffer = nullptr;napi_throw_error(env, "-1004", "napi_typeof function error");return nullptr;}// 8、创建一个异步线程需要的数据 model,把传递过来的参数加入进去做下缓存auto context = new Md5Context();context->params = buffer;napi_value returnValue = nullptr;// 9、判断是 callback 的回调方式还是 promise 的回调方式if (napi_function == callbackType) {// 如果是 callback 的回调方式,需要创建 callback 的引用napi_ref callback;if (napi_ok != napi_create_reference(env, args[1], 1, &callback)) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_reference error");return nullptr;}// 缓存 callbackcontext->callback = callback;// 临时返回一个 undefined 值给 JS 端napi_get_undefined(env, &returnValue);} else {// promise 的回调方式,创建一个 Promise 的引用napi_deferred promise;if (napi_ok != napi_create_promise(env, &promise, &returnValue)) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_promise error");return nullptr;}// 缓存 promisecontext->promise = promise;}napi_value resourceName;if (napi_ok != napi_create_string_utf8(env, "GetMd5", NAPI_AUTO_LENGTH, &resourceName)) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_string_utf8 resourceName error");return nullptr;}// 10、创建一个异步任务napi_async_work asyWork;napi_status status =napi_create_async_work(env, nullptr, resourceName, doInBackground, onPostExecutor, (void *)context, &asyWork);if (napi_ok != status) {delete[] buffer;delete context;buffer = nullptr;context = nullptr;napi_throw_error(env, "-11", "napi_create_async_work error");return nullptr;}// 11、保存异步任务context->work = asyWork;// 12、添加进异步队列napi_queue_async_work(env, asyWork);return returnValue;}static napi_value GetMd5Sync(napi_env env, napi_callback_info info) {// 1、从info中取出JS传递过来的参数放入argssize_t argc = 1;napi_value args[1] = {nullptr};if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {napi_throw_error(env, "-1000", "napi_get_cb_info error");return nullptr;}// 2、获取参数的类型napi_valuetype stringType;if (napi_ok != napi_typeof(env, args[0], &stringType)) {napi_throw_error(env, "-1001", "napi_typeof error");return nullptr;}// 3、如果参数为null或者undefined,则抛异常if (napi_null == stringType || napi_undefined == stringType) {napi_throw_error(env, "-1002", "the param can't be null");return nullptr;}// 4、获取传递的string长度size_t length = 0;if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {napi_throw_error(env, "-1003", "napi_get_value_string_utf8 error");return nullptr;}// 5、如果传递的是"",则抛异常if (length == 0) {napi_throw_error(env, "-1004", "the param length invalid");return nullptr;}// 6、读取传递的string参数放入buffer中char *buffer = new char[length + 1];if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {delete[] buffer;buffer = nullptr;napi_throw_error(env, "-1005", "napi_get_value_string_utf8 error");return nullptr;}// 7、计算MD5加密操作std::string str = buffer;str = MD5(str).toStr();// 8、把C++数据转成napi_value并返回napi_value value = nullptr;const char *md5 = str.c_str();if (napi_ok != napi_create_string_utf8(env, md5, strlen(md5), &value)) {delete[] buffer;buffer = nullptr;napi_throw_error(env, "-1006", "napi_create_string_utf8 error");return nullptr;}// 9、资源清理delete[] buffer;buffer = nullptr;return value;}static napi_value Add(napi_env env, napi_callback_info info) {size_t requireArgc = 2;size_t argc = 2;napi_value args[2] = {nullptr};napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);napi_valuetype valuetype0;napi_typeof(env, args[0], &valuetype0);napi_valuetype valuetype1;napi_typeof(env, args[1], &valuetype1);double value0;napi_get_value_double(env, args[0], &value0);double value1;napi_get_value_double(env, args[1], &value1);napi_value sum;napi_create_double(env, value0 + value1, &sum);return sum;}EXTERN_C_STARTstatic napi_value Init(napi_env env, napi_value exports) {napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},{"getMd5Sync", nullptr, GetMd5Sync, nullptr, nullptr, nullptr, napi_default, nullptr},{"getMd5", nullptr, GetMd5, nullptr, nullptr, nullptr, napi_default, nullptr},};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;}EXTERN_C_ENDstatic napi_module demoModule = {.nm_version = 1,.nm_flags = 0,.nm_filename = nullptr,.nm_register_func = Init,.nm_modname = "entry",.nm_priv = ((void *)0),.reserved = {0},};extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {napi_module_register(&demoModule); }

Index.ets 的测试代码如下:

    import testNapi from 'libentry.so';@Entry @Component struct Index {@State message: string = 'Hello,OpenHarmony'build() {Column({ space: 10 }) {Text(this.message).fontSize(20)Button("同步回调").onClick(() => {this.message = testNapi.getMd5Sync("Hello, OpenHarmony")})Button("异步 Callback 回调").onClick(() => {this.message = "计算中...";testNapi.getMd5("Hello, OpenHarmony", (md5: string) => {this.message = md5;});})Button("异步 Promise 回调").onClick(() => {this.message = "计算中...";testNapi.getMd5("Hello, OpenHarmony").then((md5: string) => {this.message = md5;}).catch((error: Error) => {this.message = "error: " + error;})})}.padding(10).width('100%').height("100%")}}

样例运行结果如下图所示:

小结

本节笔者简单讲述了 NAPI 的异步实现方式,下一小节笔者从源码的角度给大家讲解一下 NAPI 的实现原理,敬请期待……

码牛课堂也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线。大家可以进行参考学习:https://qr21.cn/FV7h05

①全方位,更合理的学习路径
路线图包括ArkTS基础语法、鸿蒙应用APP开发、鸿蒙能力集APP开发、次开发多端部署开发、物联网开发等九大模块,六大实战项目贯穿始终,由浅入深,层层递进,深入理解鸿蒙开发原理!

②多层次,更多的鸿蒙原生应用
路线图将包含完全基于鸿蒙内核开发的应用,比如一次开发多端部署、自由流转、元服务、端云一体化等,多方位的学习内容让学生能够高效掌握鸿蒙开发,少走弯路,真正理解并应用鸿蒙的核心技术和理念。

③实战化,更贴合企业需求的技术点
学习路线图中的每一个技术点都能够紧贴企业需求,经过多次真实实践,每一个知识点、每一个项目,都是码牛课堂鸿蒙研发团队精心打磨和深度解析的成果,注重对学生的细致教学,每一步都确保学生能够真正理解和掌握。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:https://qr21.cn/FV7h05

如何快速入门:

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr21.cn/FV7h05

大厂鸿蒙面试题::https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/640367.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【面试八股总结】Linux系统下的I/O多路复用

参考资料 &#xff1a;小林Coding、阿秀、代码随想录 I/O多路复用是⼀种在单个线程或进程中处理多个输入和输出操作的机制。它允许单个进程同时监视多个文件描述符(通常是套接字)&#xff0c;一旦某个描述符就绪&#xff08;一般是读就绪或者写就绪&#xff09;&#xff0c;能够…

SLS 查询新范式:使用 SPL 对日志进行交互式探索

作者&#xff1a;无哲 引言 在构建现代数据和业务系统的过程中&#xff0c;可观测性已经变得至关重要&#xff0c;日志服务&#xff08;SLS&#xff09;为 Log/Trace/Metric 数据提供了大规模、低成本、高性能的一站式平台服务&#xff0c;并提供数据采集、加工、投递、分析、…

前端点击按钮触发复制文本

1. 效果展示&#xff1a; 点击复制小图标进行内容的复制 在这里我们先不考虑适用插件的情况&#xff0c;因为如果只是简单的复制&#xff0c;则不需要插件 2. 绑定事件 这里我们以vue为例子&#xff0c; 原生和react我后面补上 <i slot"prefix" class"i…

JAVA基础之垃圾收集器

一 JVM垃圾收集 分代收集思想 当前虚拟机的垃圾收集一般采用分代收集算法&#xff0c;这种算法本身没有创新性&#xff0c;只是根据对象存活周期的不同将内存分为几块。一般将java堆内存分为新生代和老年代&#xff0c;这样我们就可以根据不同年龄到的特点选择不同的垃圾收集…

JavaScript作用域

一&#xff0c;作用域 就是代码名字&#xff08;变量&#xff09;在某个范围起作用和效果 1.全局作用域 整个script标签 &#xff0c;或一个js文件 2.局部作用域&#xff08;函数作用域&#xff09; 这个名字只在函数内部起作用和效果 二&#xff0c;变量作用域 1.全局变…

实战 | 无视杀软使用远控工具进行横向移动Tips

实战 | 无视杀软使用远控工具进行横向移动Tips。 在有杀软拦截&#xff0c;CS无法上线的情况下&#xff0c;经常用到todesk和向日葵这两个远控工具进行横向移动。不过这两个工具现在好像不怎么好用了。不过无所谓&#xff0c;用其他的就是了&#xff0c;听说最近GotoHTTP很火&…

PLC_博图系列☞P_TRIG:扫描 RLO 的信号上升沿

PLC_博图系列☞P_TRIG&#xff1a;扫描 RLO 的信号上升沿 文章目录 PLC_博图系列☞P_TRIG&#xff1a;扫描 RLO 的信号上升沿背景介绍P_TRIG&#xff1a; 扫描 RLO 的信号上升沿说明参数示例 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 P_TRIG 背景介绍 这是一…

展商企业【广东伟创科技开发有限公司】| 2024水科技大会暨技术装备成果展

企业介绍 广东伟创科技开发有限公司成立于2006年&#xff0c;位于广东省江门市。公司是华南理工大学造纸与污染控制国家工程研究中心科技成果转化单位&#xff1b;是华南理工大学产学研合作单位&#xff1b;是广东省高新技术企业&#xff1b;是江门市现代信息服务业重点企业&am…

路由策略实验

一.实验要求 1、按照图示配置 IP 地址&#xff0c;R1&#xff0c;R3&#xff0c;R4 上使用 loopback 口模拟业务网段 2.R1 和 R2 运行 RIPV2在 RIP R2&#xff0c;R3 和 R4运行 OSPF&#xff0c;各自协议内部互通 3.在RIP和 OSPF 间配置双向路由引入&#xff0c;要求除 R4 上…

Macs Fan Control Pro for Mac:全面优化Mac风扇控制软件

Macs Fan Control Pro for Mac是一款专为苹果电脑用户设计的风扇控制软件&#xff0c;旨在通过精确的风扇速度调节&#xff0c;全面优化Mac的散热性能&#xff0c;确保系统始终运行在最佳状态。 Macs Fan Control Pro for Mac中文版下载 该软件具备实时监控功能&#xff0c;能够…

让流程图动起来

我们平时画流程&#xff0c;然后贴到文档&#xff0c;就完事了。但是过程演示的时候&#xff0c;如果只是一张静态图&#xff0c;很难吸引到听众的注意力&#xff0c;表达效果并不太好。常用的方法是可以用PPT进行动态演示&#xff0c;做PPT也是需要花一些时间&#xff0c;同时…

kaggle 泰坦尼克使用xgboost 得分0.73684

流程 导入所要使用的包引入kaggle的数据集csv文件查看数据集有无空值填充这些空值提取特征分离训练集和测试集调用模型 导入需要的包 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarni…