23/05/05记录之前的一些问题;简单总结
前言
23年4月的一些琐碎知识总结
一、dart简单语法
1. await Future.wait(_params.map((e) => _saveHwWalletInfo(e.coin, e.xpubkey)));
这行代码意味着你在等待每个_saveHwWalletInfo()
函数都执行完成后才能继续往下执行,这也就是await
的作用。如果你希望_saveHwWalletInfo()
函数并行执行(即同时执行多个_saveHwWalletInfo()
函数而不是一个一个的执行),可以将map
转换为Future.wait()
中的一个List对象,然后使用await Future.wait()
,这样所有的函数都会同时启动执行,返回的结果也会按照原始的顺序组成一个List返回给你。
List<Future> futures = _params.map((e) => _saveHwWalletInfo(e.coin, e.xpubkey)).toList();
await Future.wait(futures);
这里的futures
实际上是一个List<Future>
对象,其中每个元素都是一个Future对象,表示一个异步的_saveHwWalletInfo()函数的执行结果。Future.wait()
方法接收一个Future对象的List作为输入,等待这些Future对象全部执行完成,然后返回一个按照原始Future对象顺序的结果List。因此,我们可以使用await Future.wait()
等待所有异步函数执行完成。
很重要的一点,Future.wait()会按照顺序执行下去,但是如果按顺序执行的函数中包含异步函数,那会统一放到最后一起执行;所以只是非异步函数的顺序执行,想要实现异步函数的 每个步骤是一整个函数完成才到下一阶段,那么您可以尝试 使用for-in
循环结构和 async/await
方式 来处理每个元素。这样,每个元素都将在同一个循环中进行处理,并且将在迭代完所有元素后一次性完成。以下是示例代码:
Future<void> saveHwWalletInfoList(List<WalletInfo> params) async {
for (final param in params) {
await saveHwWalletInfo(param.coin, param.xpubkey);
}
}
Future<void> saveHwWalletInfo(String coin, String xpubkey) async {
// 完整的函数实现
}
在这个示例代码中,saveHwWalletInfoList
循环处理每个元素并依次调用saveHwWalletInfo
函数,这意味着每个元素都将在同一个循环中完成。由于每个saveHwWalletInfo
函数都是异步的,因此应使用await
等待其完成。
2. dart中返回一个函数
Dart的语法不支持以下方式的return语句:
return () => {
subscription.cancel();
Log().debug('remove characteristic monitor: $characteristic.uuid');
};
因为在Dart中,{}
与()
不是等价的。{}
用于表示代码块,而()
用于表示函数调用时的参数列表或者函数指示符。因此,如果您希望返回一个匿名函数,可以使用以下方式:
return () {
subscription.cancel();
Log().debug('remove characteristic monitor: $characteristic.uuid');
};
也可以使用箭头函数语法:
return () => subscription.cancel()
..Log().debug('remove characteristic monitor: $characteristic.uuid');
这里使用..
运算符在同一个表达式中执行两个操作。但是请注意,使用这种方式时需要确保返回值是函数类型。
3. 字符串替换
parsePath(String path){
return path.replaceAll("'", "").split('/').sublist(path.split('/').length - 2).map((e) => int.parse(e)).toList();
}
这样,如果传入的是 "m/44'/5353'/0'/0/0"
,则解析结果为 [0, 0]
。当然,如果传入的字符串中含有其他的单引号'
,也会被替换为两个单引号''
二、C语言
1..h
和 .c
后缀不代表类型或结构体定义,用于区分头文件和源文件。
.h
后缀:通常用于头文件,头文件包含了函数、变量和宏的声明,头文件可以被其他源文件和头文件包含,以便在程序中使用里面定义的内容。头文件中不应该包含变量或函数的实现和定义,以及任何需要引用其他头文件的声明。使用 #include
预编译指令可以将头文件包含到源文件中。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
-
/* demo.h */ /* 声明一个函数 */ int func1(int a, int b); /* 定义一个宏 */ #define MAX_NUM 100 /* demo.c */ #include "demo.h" // 包含头文件 /* 实现函数 */ int func1(int a, int b) { return a + b; }
-
.c
后缀:通常用于源文件,源文件包含了函数和变量的定义和实现,以及其他源代码。应该只有一个源文件是定义主函数的文件,而其他文件应该是按照需要实现的功能进行划分的。源文件中可以包含多个函数的实现和定义,可以包含其他头文件,也可以包含宏和全局变量的定义。
4. c当中的一些宏定义方法
-
#ifndef H_GSW_BLE
#define H_GSW_BLE- 定义了宏
H_GSW_BLE
表示这个头文件只应被编译一次。
- 定义了宏
- #define MODLOG_DFLT(ml_lvl_, ...)
MODLOG_DFLT_ ## ml_lvl_(VA_ARGS)
以上是一个宏定义,使用了预处理符号
##
和可变参数...
。这个宏定义的作用是根据传入的日志级别符号(ml_lvl_)和可变参数(...)来调用相应的日志函数进行输出。
其中,
##
用于连接两个符号,将ml_lvl_
和__VA_ARGS__
的内容组合成一个新的符号。例如,当ml_lvl_
的值为CRITICAL
时,##
将MODLOG_DFLT_
和CRITICAL
这两个符号组合成了一个新的符号MODLOG_DFLT_CRITICAL
。因此,这个宏定义在预处理时将根据
ml_lvl_
的不同来调用不同的日志函数进行输出。具体使用时,比如想要输出一条日志,日志级别为
DEBUG
,内容为Error occurred in function foo
,则可以通过以下方式调用宏定义:MODLOG_DFLT(DEBUG, "Error occurred in function foo");
这个宏实际上会被替换成:
MODLOG_DFLT_DEBUG("Error occurred in function foo");
从而调用
MODLOG_DFLT_DEBUG()
函数输出一条日志。
2.一些字符大小用法
event.response = *(uint16_t *)msg
:将消息的前两个字节解析成一个uint16_t
类型的整数,然后赋值给event.response
属性,可能表示消息的响应信息。
memcpy(event.param, &msg[4], event.param_size)
:将消息中包含的参数复制到event.param
数组中,&msg[4]
表示参数在消息中的位置,event.param_size
表示参数的长度,memcpy
函数用于将数据从一个内存地址复制到另一个内存地址。-
rt_uint8_t
、rt_uint16_t
、rt_uint32_t
是实时操作系统(RTOS)中用来定义无符号整数变量类型的宏定义。rt_uint8_t
表示8位(1字节)的无符号整数, 取值范围为0~255。rt_uint16_t
表示16位(2字节)的无符号整数, 取值范围为0~65535。rt_uint32_t
表示32位(4字节)的无符号整数, 取值范围为0~4294967295。
3. extern
进行声明或引用
-
在当前文件中引用另一个文件中已定义的变量或函数。
在当前文件中,使用
extern
可以声明一个已在其他文件中定义的变量或函数。这样,编译器就知道这个变量或函数的类型和名称,从而可以正确地编译当前文件的代码。例如,在 current.c 文件中引用了另一个文件 funcs.c 中定义的
add()
函数:/* current.c */ extern int add(int, int); int main() { int a = 1, b = 2; int c = add(a, b); return 0; }
-
在其他文件中引用当前文件中已定义的变量或函数。
当前文件中定义的变量或函数也可以使用
extern
进行声明,表示在其他文件中也需要使用该变量或函数。例如,在 current.c 文件中定义了一个
result
变量,其他文件也需要使用这个变量:/* current.c */ extern int result = 0; int main() { result = 1; return 0; }
然后,在另一个文件中也可以使用
extern
引用该变量:/* other.c */ extern int result;
需要注意的是,extern
只是声明而非定义,就是它不会给变量或函数分配内存空间,只是告诉编译器它已经定义在其他地方了。定义变量或函数的语句一般通常在函数外或宏定义中进行,这样全局才直接起效。
4. memcpy/strcpy之类的函数
用于将一个内存地址的数据复制到另一个内存地址。
void *memcpy(void *destination, const void *source, size_t num);
其中,destination是目标地址,source是源地址,num是要复制的字节数。该函数返回指向destination的指针。
memset()
的语法如下:
void *memset(void *s, int c, size_t n);
其中,s
参数表示要设置为指定值的内存空间的起始地址,c
参数表示要设置的值,n
参数表示要设置的字节数。
因此,该代码的含义是将 addr_data
这块内存空间的前 100 个字节全部设置为 0。
assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
此处用 assert 函数验证
ctxt->op
是否为BLE_GATT_ACCESS_OP_WRITE_CHR
,如果不是则终止程序,对错误进行提示。这个语句的作用是确保代码出现意外时程序可以在这里中断,方便开发调试。如果程序正常执行这里不会产生任何结果。char *msg = rt_malloc(ctxt->om->om_len + 2);
定义一个名为
msg
的char
类型指针,申请长度为ctxt->om->om_len + 2
字节的空间。*(uint16_t *)msg = conn_handle;
将
conn_handle
的值存入msg
指向的内存空间的前两个字节中。这里使用了指针类型强制转换运算符来将msg
指向的空间解释为uint16_t
类型,并且将conn_handle
强制转换为uint16_t
后赋值给它。rc = os_mbuf_copydata(ctxt->om, 0, ctxt->om->om_len, &msg[2]); if (rc != 0){ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; }
将
ctxt->om
指向的数据缓冲区(os_mbuf
)的内容,从第0个字节开始,拷贝到msg
指针指向的内存空间(从第3个字节开始)中。os_mbuf_copydata()
函数返回拷贝的字节数量。如果出现拷贝异常,返回错误信息。MODLOG_DFLT(INFO, "Write info om-len = %d opcode = %d, conn_handle = %d", ctxt->om->om_len, msg[0], conn_handle);
使用
MODLOG_DFLT()
函数输出日志信息。这里输出由ctxt
、msg
和conn_handle
的值构成的字符串,其中{}
用于格式化输出内容。例如,om-len = 20, opcode = 5, conn_handle = 0
。
三、协议、具体业务相关
1. 发送的蓝牙信息Protocol Buffers
消息的类型字段是指代具体消息的类型,消息的大小字段是指代消息的大小,方便接收方进行处理。消息的内容是一个经过 Protocol Buffers 编码的内容,这是因为 Protocol Buffers 是一种轻量级的序列化工具,可以方便地序列化和反序列化结构数据,而且生成的数据非常小,可以节省网络带宽。魔数常量这些是用来辅助接收方正确解析出消息的数据类型和结构的标识。
2. BLE GAP
BLE GAP (Generic Access Profile) 是蓝牙低功耗无线通信技术中的一个协议层,用于处理设备间的连接管理和通信。它定义了在设备间进行连接、匹配和交换数据的过程。
BLE GAP 协议包含了以下内容:
-
角色定义:定义了连接设备之间所扮演的角色,包括广播、扫描、主要设备和次要设备等。
-
连接参数定义:定义了连接建立和维护的参数,例如连接间隔、超时、数据包长度和传输速率等。
-
安全性定义:定义了数据传输的安全性要求和密钥管理。
-
数据格式和协议定义:定义了数据交换的格式和协议,例如广播数据和 GATT (Generic Attribute Profile) 数据。
BLE GAP 协议的实现可以通过各种操作系统的 API 接口来实现。在实际应用中,BLE GAP 协议可以帮助设备管理连接、维护状态、安全交换数据等功能,实现低功耗的无线通信约束的设备之间的互联互通。
四、一些命名方式/含义
1. 蓝牙服务
BLE_GATT_SVC_TYPE_PRIMARY
- 定义了该服务为主服务。BLE_UUID16_DECLARE
- 定义 16 位 UUID..type
,.uuid
,.characteristics
- SCV(服务)、UUID(唯一标识码)、特征值等结构体成员。.access_cb
- 回调函数 HOOK..flags
- 表示特征值的读/写等属性信息。struct ble_gatt_access_ctxt
- BLE GATT 属性上下文,包含与访问相关的信息。conn_handle
- BLE 连接句柄,用于标识需要操作的 BLE 设备。attr_handle
- 将操作应用于的 GATT 特征值的句柄。ctxt
- 访问上下文上下文选项,包括对 BLE GATT 属性进行访问所需的所有信息。
2. 字符大小变量命名
static char handler_stack[10240];表示定义了一个长度为10240字节的 handler_stack
用来存储一个任务的堆栈。一个任务的堆栈大小需要根据任务的实际需求来决定。
rt_uint8_t msg_pool[4096];
表示定义了一个 rt_uint8_t
类型的 msg_pool
数组 消息队列,数组的长度是 4096 字节,即 4KB。
rt_thread gsw_event_handler;,gsw_event_handler
是一个 rt_thread
类型的变量,用于表示一个线程。
m[1] = event->param[1] << 8 | event->param[2];
:将事件结构体的参数数组中的第二个和第三个元素左移 8 位后进行位或运算,结果存储到m[1]
中。