python+嵌入式——串口通信篇(收发解包)
目录
- 前言
- 安装pyserial
- pyserial大致概括
- 整体流程
- 硬件连接
- 例子(简单版)
- 详细使用
- serial初始化参数
- 发包
- 收包
- 收包
- 检查包并解包
- python struct模块
- 结语
前言
这几年,自己也做了一些嵌入式机器人。在整个开发的过程中,调通信通常会花费一段比较长的时间,串口通信就是这样的一个部分。
而现在在百度上进行搜索,发现对python串口通信的博客讲解,都有点太笼统了,这其中,应该与python在处理硬件底层速度较慢导致用的人少有关系。
这里把python串口通信的部分进行一下个人使用过程中的总结。既是自我总结,也让未来开发更快。
文章参考官方文档:
https://pyserial.readthedocs.io/
python进行串口通信,依赖的包就是pyserial
,因此,本文是基于这个包进行总结。
总结过程中难免有理解不到位,或者错误的地方,欢迎大家提出问题并一起交流
安装pyserial
pip安装
python -m pip install pyserial
# 或者直接pip安装
pip install pyserial
conda安装
conda install pyserial
#or
conda install -c conda-forge pyserial
pyserial大致概括
整体流程
对于串口通信,我们的大致流程如下:
硬件连接
发包
收包
硬件连接
首先需要将串口设备与电脑相连,并查看自己的串口的端口号是多少
串口的端口号就是将你的串口命名,让程序或者系统能够快捷的寻找
对于windows设备
打开设备管理器 -> 端口 -> 寻找你的设备号

对于Windows来说,串口设备端口号一般都是COMn
,这里COM8使用CH340串口芯片来进行通信
对于Linux设备(这里以Ubuntu为例)
ls /dev/ttyUSB*
返回的就是你所有串口设备的设备号,如果你具有多个设备,则可以通过拔插之后,查看哪个设备增删来确定你的设备名
问题
- 针对Ubuntu22.04可能出现找不到设备端口号的问题,解决方法如下:
Ubuntu22.04没有ttyUSB(无法访问 ‘/dev/ttyUSB‘: 没有那个文件或目录)问题解决
- 针对windows找不到端口,一般来说都是驱动问题,解决方法可参考下面博客
笔记本W10找不到端口(com口)及单片机串口连接的问题(附51开发板的CH340串口芯片的驱动程序安装包)
例子(简单版)
找到了设备的端口号之后,就可以使用python来进行通信了。
我贴一个csdn上搜python串口通信的的第一个博客的代码例子。
代码来源:
https://blog.csdn.net/weixin_43217958/article/details/109782000
import serial#导入串口通信库
from time import sleepser = serial.Serial()def port_open_recv():#对串口的参数进行配置ser.port='com3'ser.baudrate=9600ser.bytesize=8ser.stopbits=1ser.parity="N"#奇偶校验位ser.open()if(ser.isOpen()):print("串口打开成功!")else:print("串口打开失败!")
#isOpen()函数来查看串口的开闭状态def port_close():ser.close()if(ser.isOpen()):print("串口关闭失败!")else:print("串口关闭成功!")def send(send_data):if(ser.isOpen()):ser.write(send_data.encode('utf-8'))#编码print("发送成功",send_data)else:print("发送失败!")if __name__ == '__main__':port_open_recv()while True:a=input("输入要发送的数据:")send(a)sleep(0.5)#起到一个延时的效果,这里如果不加上一个while True,程序执行一次就自动跳出了
但是,这个例子,并不适合直接拿来到嵌入式中进一步开发,因为一些重要的参数和解包的方法并没有说明出来,并且在python中,涉及一些数据类型的转换也是比较复杂的(也是让我无数次抓狂的地方😡)。
详细使用
针对串口通信,我们可以将其分为发包和收包。而想要比较方便的使用python来实现通信,我们需要使用到python一个非常强大的功能包struct
,使用的讲解流程,我将按照官网参数文档介绍
- > 例子
这样的顺序来解释。
serial初始化参数
class serial.Serial
init(port=None, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False, inter_byte_timeout=None, exclusive=None)
参数
- port – 串口名字(COMn或者/dev/ttyUSB)或者
None
- baudrate (int) – 波特率,比如9600或者115200
- bytesize – 数据位数,可能的参数值有:
FIVEBITS
,SIXBITS
,SEVENBITS
,EIGHTBITS
- parity – 奇偶校验,可能的参数值:
PARITY_NONE
,PARITY_EVEN
,PARITY_ODD
,PARITY_MARK
,PARITY_SPACE
- stopbits – 停止位的比特数. 可能的参数值:
STOPBITS_ONE
,STOPBITS_ONE_POINT_FIVE
,STOPBITS_TWO
- timeout (float) – 设置pyserial持续读取数据的最长时间(s)
- xonxoff (bool) – 是否启动软件流控制
- rtscts (bool) – 是否启动硬件(RTS/CTS)流控制
- dsrdtr (bool) – 是否启动硬件(DSR/DTR)流控制
- write_timeout (float) – 设置pyserial最长写入串口数据的时间(s)
- inter_byte_timeout (float) – 字符间超时, 没有则禁止(默认禁止).
- exclusive (bool) – 设置独占访问模式(仅POSIX)。 如果端口已经以独占访问模式打开,则不能以独占访问模式打开端口。
异常退出
- ValueError –如果一些参数不在允许参数内,则返回
ValueErro
,比如波特率设置 - SerialException – 如果设备无法被找到或者被设置,则返回
SerialException
说明
当我们初始化串口的时候,open()
函数会被调用,串口就会被打开。
timeout
参数会影响到read()
函数的使用,这个timeout
参数非常重要,直接影响到我们对串口数据的读取。
timeout
=None
: 一直等待,直到设置的接收字节数满后退出
timeout
=0
: 非阻塞模式,在任何情况下都立即返回,返回零或更多,最多为请求的字节数
timeout
=x
:当请求的字节数可用时,将timeout设置为x秒(允许浮动)立即返回,否则等待超时到期,并返回在此之前收到的所有字节。
而对于wrtie()
(发包函数)而言,默认为阻塞,除非设置了write_timeout。
针对硬件流控制而言,这个得观察嵌入式设备了,我之前使用stm32和python通信的时候使用过一次,得需要结合硬件连接和原理图说明,但是我并没有完全搞透,且其他时候用的也比较少,这里就不展开叙述了。
发包
在嵌入式中,我们使用发包,一般是将我们的状态数据,或者是控制指令通过转码为符合设备的通信协议的格式后,将其发出。
因此,我们在编写发包函数前,需要先熟读通信协议,并理解我们需要发送什么样的指令,一般协议是16进制的一串数据。
pyserial
中发包函数为write()
write(data):
参数
: 需要发送的数据
返回值
: 写入的字节数
返回值类型
: int
异常值返回
– 如果为端口配置了写入超时并且超过了时间。
将字节数据写入端口。这应该是字节类型(或兼容的,如bytearray
,memoryview
)。必须对Unicode字符串进行编码(例如“hello”.encode(“utf-8”)。
下面是参考实例
# Usart Library
import serial
import struct
import binascii
# Init serial port
Usart = serial.Serial(port = '/dev/ttyUSB0', # 串口baudrate=115200, # 波特率timeout = 0.001 )# 判断串口是否打开成功
if Usart.isOpen():print("open success")
else:print("open failed")# 使用优雅的方式发送串口数据
# 这里的数据可以根据你的需求进行修改
send_data = [0xA4,0x03,0x08,0x23,0xD2] #需要发送的串口包send_data=struct.pack("%dB"%(len(send_data)),*send_data) #解析成16进制
print(send_data)
Usart.write(send_data) #发送
收包
针对收包,我们一般流程就是 收包
-> 检查包
-> 解包
,先查看官网中收包的函数文档
read(size=1)
参数
: size – 读取字节数
返回值
: 串口读取得到的字节
返回值类型
: bytes
从串行端口读取字节。如果设置了超时,则返回的字符数可能少于请求的字符数。如果没有超时,它将阻塞,直到读取请求的字节数。
read_until(expected=LF, size=None)
参数
: expected
– 预期需要的字节。 size
– 读多少字节。返回值
: 串口读取到的字节数
返回值类型
: bytes
读取数据,直到找到预期的序列(默认为“\n”)、超过大小或超时。如果设置了超时,则返回的字符可能少于请求的字符。如果没有超时,它将阻塞,直到读取请求的字节数。
in_waiting()
获得input buffer中缓存字节数
返回值类型
: int
收包
我们首先需要从串口中读取缓存数据
# Usart Library
import serial
import struct
import binascii# Init serial port
Usart = serial.Serial(port = '/dev/ttyUSB0', # 串口baudrate=115200, # 波特率timeout = 0.001 ) # 由于后续使用read在未收全数据的时候,会按照一个timeout周期时间读取数据# 波特率115200返回数据时间大概是1ms,9600下大概是10ms# 所以读取时间设置0.001s# 判断串口是否打开成功
if Usart.isOpen():print("open success")
else:print("open failed")# ----读取串口数据-----------------------------------
try:count = serial.inWaiting()if count > 0:# 初始化数据Read_buffer = []# 接收数据至缓存区Read_buffer=serial.read(40) # 我们需要读取的是40个寄存器数据,即40个字节# Read_data() # 前面两行可以注释,换成后面这个函数
except KeyboardInterrupt:if serial != None:print("close serial port")serial.close()#--------------------------------------------------------
这里如果没有在0.001s的时间内读到40字节的包,就会退出。因此我们需要结合通信协议来判断我们收到的包是否正确。
检查包并解包
这里根据我之前使用的一款IMU(GY-95T)为例子,来演示用状态机判断包是否正确。
同时在函数中,我也将解包函数放入,后面再说明
def Read_data(self):'''Author: Liu Yuxiang Time: 2022.12.13description: 读取串口数据'''# 初始化数据counter = 0 Recv_flag = 0Read_buffer = []# 接收数据至缓存区Read_buffer=serial.read(40) # 我们需要读取的是40个寄存器数据,即40个字节# 状态机判断收包数据是否准确while(1):# 第1帧是否是帧头ID 0xA4if (counter == 0):if(Read_buffer[0] != 0xA4):break # 第2帧是否是读功能码 0x03 elif (counter == 1):if(Read_buffer[1] != 0x03):counter=0break# 第3帧判断起始帧 elif (counter == 2):if(Read_buffer[2] < 0x2c):start_reg=Read_buffer[2]else:counter=0 # 第4帧判断帧有多少数量 elif (counter == 3):if((start_reg+Read_buffer[3]) < 0x2C): # 最大寄存器为2C 大于0x2C说明数据肯定错了len=Read_buffer[3]else:counter=0break else:if(len+5==counter):#print('Recv done!')Recv_flag=1# 收包完毕if(Recv_flag):Recv_flag = 0sum = 0#print(Read_buffer) # Read_buffer中的是byte数据字节流,用struct包解包data_inspect = str(binascii.b2a_hex(Read_buffer)) # data是将数据转化为原本的按照16进制的数据try: # 如果接收数据无误,则执行数据解算操作for i in range(2,80,2): # 根据手册,检验所有帧之和低八位是否等于末尾帧sum += int(data_inspect[i:i+2],16)if (str(hex(sum))[-2:] == data_inspect[80:82]): # 如果数据检验没有问题,则进入解包过程#print('the Rev data is right')# 数据低八位在前,高八位在后#print(Read_buffer[4:-1]) unpack_data = struct.unpack('<hhhhhhhhhBhhhhhhhh',Read_buffer[4:-1])# 切片并将其解析为我们所需要的数据,切出我们所需要的数据部分```比如:ACC_X = unpack_data[0]/2048 * 9.8 # unit m/s^2ACC_Y = unpack_data[1]/2048 * 9.8 ACC_Z = unpack_data[2]/2048 * 9.8```except:print("Have Error in receiving data!!")counter=0 breakelse:counter += 1 # 遍历整个接收数据的buffer
而解包函数,最关键的就是unpack_data = struct.unpack('<hhhhhhhhhBhhhhhhhh',Read_buffer[4:-1])
这一行代码的含义就是将Read_buffer[4:-1]
的这部分数据按照小端的顺序,解析成hhhhhhhhhBhhhhhhhh
的排列顺序,h代表short
类型,B代表unsigned char
类型。
具体怎么根据你的情况使用,就可以看一下python的struct
模块。
python struct模块
tips:
一个byte
=8bit
\xa4 这样一个数据代表了16bit
这部分内容是我在学习过程中借鉴很多网上写的好的帖子而做的笔记,具体的帖子链接由于做笔记而找不着了,就把自己的笔记贴出来
Python没有专门处理字节的数据类型。但由于b'str'
可以表示字节即bytes
,所以,字节数组=二进制str。在C语言中,我们可以很方便地用struct、union来处理字节,以及字节和int,float的转换。
而在python中却会比较的麻烦,但是python是提供了struct
模块来解决bytes
和其他二进制数据类型的转换
struct模块的两个函数,struct.unpack
和struct.pack
函数
struct.pack(format, v1, v2, …)
Return a bytes object containing the values v1, v2, … packed according to the format string format. The arguments must match the values required by the format exactly.
struct.unpack(format, buffer)
Unpack from the buffer buffer (presumably packed by
pack(format, ...)
) according to the format string format. The result is a tuple even if it contains exactly one item. The buffer’s size in bytes must match the size required by the format, as reflected bycalcsize()
.https://docs.python.org/3/library/struct.html#module-struct
format也就是你需要将后面的数据转换为什么类型的数据,而这个format主要有两部分组成,指定打包数据的方式+数据转换后的类型,具体的表格参考如下:
format可用于指示打包数据的字节顺序、大小和对齐方式,如下表所示:
Character | Byte order | Size | Alignment |
---|---|---|---|
@ | native | native | native |
= | native | standard | none |
< | little-endian | standard | none |
> | big-endian | standard | none |
! | network (= big-endian) | standard | none |
大端(big-endian) | 小端(little-endian) |
---|---|
较低的有效字节存放在较高的存储器地址中,较高的有效字节存放在较低的存储器地址 | 较高的有效字节存放在较高的存储器地址中,较低的有效字节存放在较低的存储器地址 |
比如数据是0x1234
低字节是0x12 高字节是0x34
小端读出来0x34 0x12 大端读出来就是0x12 0x34
而第二个部分也就是后面的数据要转换成什么类型。
Format | C Type | Python type | Standard size(byte) | Notes |
---|---|---|---|---|
x | pad byte | no value | (7) | |
c | char | bytes of length 1 | 1 | |
b | signed char | integer | 1 | (1), (2) |
B | unsigned char | integer | 1 | (2) |
? | _Bool | bool | 1 | (1) |
h | short | integer | 2 | (2) |
H | unsigned short | integer | 2 | (2) |
i | int | integer | 4 | (2) |
I | unsigned int | integer | 4 | (2) |
l | long | integer | 4 | (2) |
L | unsigned long | integer | 4 | (2) |
q | long long | integer | 8 | (2) |
Q | unsigned long long | integer | 8 | (2) |
n | ssize_t | integer | (3) | |
N | size_t | integer | (3) | |
e | (6) | float | 2 | (4) |
f | float | float | 4 | (4) |
d | double | float | 8 | (4) |
s | char[] | bytes | (9) | |
p | char[] | bytes | (8) | |
P | void* | integer | (5) |
这么说会有点抽象,
用具体的例子来看就是
import struct
struct.pack('>I', 10240099)>>> b'\x00\x9c@c'
意思就是说,使用>
代表的字节顺序即big-endian
(网络序),来将后面的数据转化成I
代表的数据类型(4字节无符号整型)
(官网例子)
>>>from struct import *
>>>pack(">bhl", 1, 2, 3)
b'\x01\x00\x02\x00\x00\x00\x03'
>>>unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
>>>calcsize('>bhl')
7
这里和前面的例子是一个道理,使用>
对齐的方式,来将后面三个数据依次打包成b
,h
,l
所代表的数据,解包也是这样,将一串byte
数据按照>
对齐格式转换成b
h
l
对对应的数据。
注意:可以使用b4这样的写法来代表有一个数组
计算所选择的数据格式有多少字节
struct.calcsize(format)
Return the size of the struct (and hence of the bytes object produced by
pack(format, ...)
) corresponding to the format string format.
结语
差不多这样就可以比较优雅的利用python接收和读取串口数据了。如有问题,欢迎评论区提出。
相关文章:

python+嵌入式——串口通信篇(收发解包)
目录前言安装pyserialpyserial大致概括整体流程硬件连接例子(简单版)详细使用serial初始化参数发包收包收包检查包并解包python struct模块结语前言 这几年,自己也做了一些嵌入式机器人。在整个开发的过程中,调通信通常会花费一段比较长的时间ÿ…...

剖析G1 垃圾回收器
简单回顾 在Java当中,程序员在编写代码的时候只需要创建对象,从来不需要考虑将对象进行释放,这是因为Java中对象的垃圾回收全部由JVM替你完成了(所有的岁月静好都不过是有人替你负重前行)。 而JVM的垃圾回收由垃圾回收器来负责,在…...

如何打造一款专属于自己的高逼格电脑桌面
作为一名电脑重度使用者,你是否拥有一款属于你自己的高逼格电脑桌面呢?你是不是也像大多数同学一样,会把所有的内容全部都堆积到电脑桌面,不仅找东西困难,由于桌面内容太多还会导致C盘空间不足,影响电脑的反…...

【C++】string的使用及其模拟实现
文章目录1. STL的介绍1.1 STL的六大组件1.2 STL的版本1.3 STL的缺陷2. string的使用2.1 为什么要学习string类?2.2 常见构造2.3 Iterator迭代器2.4 Capacity2.5 Modifiers2.6 String operations3. string的模拟实现3.1 构造函数3.2 拷贝构造函数3.3 赋值运算符重载和…...
怀念在青鸟的日子
时间过的可真快,一转眼来到了2023年!我初中上完就没有在念,下了学门步入社会,那时的我一片迷茫,不知道该去干什 么,父母说要不去学挖掘机、理发、修车...我思考再三,一个都没有我喜欢的…...
学习记录---Python内置类型
文章目录字符串split()列表常见操作列表相减字典创建普通创建eval(s)添加或更新元素d[t] 1d.update({c: 3}){**d1, **d2} **字典解包装运算符删除元素 d.pop(c)属性d.items()d.keys()d.values()访问元素d[Name]d.get(score)遍历字典for key in dictfor key, values in dict.it…...

Python笔记 -- 列表
文章目录1、列表简介2、修改、添加、删除元素2.1、添加2.2、删除3、排序、倒序4、遍历列表5、创建数值列表6、列表切片7、列表复制8、元组1、列表简介 在Python中用方括号[]表示列表,用逗号隔开表示其元素 通过索引访问列表 names [aa,bb,cc,dd]print(names[0]) …...
谈谈UVM中的uvm_info打印
uvm_info宏的定义如下: define uvm_info(ID,MSG,VERBOSITY) \begin \if (uvm_report_enabled(VERBOSITY,UVM_INFO,ID)) \uvm_report_info (ID, MSG, VERBOSITY, uvm_file, uvm_line); \end 从这里可以看出uvm_info由两部分组成:uvm_report_enabled(VER…...
矩阵理论1 集合上的等价关系(equivalence relations on a set S)
定义 对于一个集合S, 如果集合E⊂SS\mathcal{E} \subset S\times SE⊂SS满足以下条件 自反性: 对于∀s∈S,都有(s,s)∈E\forall s\in S, 都有 (s, s) \in \mathcal{E}∀s∈S,都有(s,s)∈E对称性: (s,t)∈E⇔(t,s)∈E(s,t) \in \mathcal{E} \Leftrightarrow (t,s)\in \mathcal…...

【网络监控】Zabbix详细安装部署(最全)
文章目录Zabbix详细安装部署环境准备安装依赖组件访问初始化配置Zabbix详细安装部署 Zabbix 是一个高度集成的网络监控解决方案,可以提供企业级的开源分布式监控解决方案,由一个国外的团队持续维护更新,软件可以自由下载使用,运作…...

阿里云轻量服务器--Docker--Nacos安装(使用外部Mysql数据存储)
前言:docker 安装nacos 如果不设置外部的mysql 默认使用内嵌的内嵌derby为数据源,这个时候如果,重新部署nacos 则会造成原有数据丢失情况; 1 默认安装的nacos 启动后使用的是内嵌的存储: 2 使用外部mysql 作为存储&a…...

unity开发知识点小结01
unity对象生命周期函数 Awake():最早调用,所以可以实现单例模式 OnEnable():组件激活后调用,在Awake后调用一次 Stat():在Update()之前,OnEnable…...
软件系统[软件工程]
What’s the link? They all involve outdated (legacy) software technology. All have had huge socio-economical impact. Prompting national lockdowns. Spreadsheet workflow error led to thousands of preventable infections and deaths. Huge losses of citizen dat…...

电力系统稳定性的定义与分类
1电力系统稳定性的定义与分类 IEEE给出电力系统稳定性定义:电力系统稳定性是指电力系统这样的一种能力—对于给定的初始运行状态,经历物理扰动后,系统能够重新获得运行平衡点的状态,同时绝大多数系统变量有界,因此整个…...

基于java的俱乐部会员管理系统
技术:Java、JSP等摘要:随着科学技术的飞速发展,科学技术在人们日常生活中的应用日益广泛,也给各行业带来发展的机遇,促使各个行业给人们提供更加优质的服务,有效提升各行业的管理水平。俱乐部通过使用一定的…...

线程池执行父子任务,导致线程死锁
前言, 一次线程池的不当使用,导致了现场出现了线程死锁,接口一直不返回。而且由于这是一个公共的线程池,其他使用了次线程池的业务也一直阻塞,系统出现了OOM,不过是幸好是线程同事测试出来的,没…...

Ubuntu系统新硬盘挂载
Ubuntu系统新硬盘挂载 服务器通常会面临存储不足的问题,大部分服务器都是ubuntu系统,该篇博客浅浅记载一下在ubuntu系统上挂载新硬盘的步骤。本篇博文仅仅记载简单挂载一块新的硬盘,而没有对硬盘进行分区啥的。如果需要更加完善的教程&#…...

【亲测】Centos7系统非管理(root)权限编译NCNN
前言 由于使用的是集群,自己不具有管理员权限,所以以下所有的情况均在非管理员权限下进行安装,即该安装策略仅适用于普通用户构建自己的环境。 什么是NCNN ncnn是一款非常高效易用的深度学习推理框架,支持各种神经网络模型&#x…...
四种常见的异步请求方式
四种常见的异步请求方式 一、xhr异步老祖 XMLHttpRequest(简称XHR)是一种在JavaScript中创建异步请求的技术。XHR对象可以向服务器发送请求,并获取服务器返回的数据,而不会使页面刷新。 XHR对象的创建方式通常是通过构造…...

Linux操作系统学习(进程间通信)
文章目录进程间通信进程通信的意义进程通信的方式1.基于文件的方式匿名管道命名管道2.基于内存的通信方式共享内存验证内核相关的数据结构了解进程间通信 进程通信的意义 当我们和另一个人打电话时两部手机都是独立的,通过基站传递信号等等复杂的过程就实现了通…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...