车载通信协议-列车实时数据协议(TRDP)

news2024/11/28 16:50:36

TCNOPEN是一个铁路行业相关的合作伙伴创建的开源的倡议,其目的是建立一些新的或即将出台的铁路标准的关键部分,通常以TCN命名。

TCN(列车通信网络)是IEC(国际电工委员会)第43工作组制定的一系列国际标准(IEC61375),规定了列车内和车辆间数据通信的通信系统。它目前正在世界上成千上万的列车上使用,以便允许电子设备在同一列车上运行时交换信息。

TCNOPEN遵循开源方案,因为软件是由参与公司共同开发的,根据它们的角色,从而实现更便宜、更快和更好的质量结果。

目标

TCNOPEN的总体目标是提供一个合适的环境,开放的兴趣团体。其中合作伙伴公司可以合作开发符合TCN标准的新组件。

对于每个需求,将启动一个特定的开源项目,该项目将贯穿所有需要的阶段:规范、开发、测试、支持。

第一个项目目前正在进行中,与开发的TRDP模块有关。

TRDP(列车实时数据协议)是TCP或UDP协议与使用网络的应用之间的中间模块。

它包括一个可选附加的安全层(SDT)。SDT是在不可信通信信道上的端到端协议。

SDT实现IEC62280(En50159),并支持安全数据源与一个或多个安全数据接收器之间的安全相关数据的传输。

TRDP项目介绍

需求是在三层交换机上支持TRDP协议,故开始不断研读协议。

源码svn:https://svn.code.sf.net/p/tcnopen/trdp/ 可以下载最新的版本。

编译:

windows下可以使用VS工具直接编译相应代码 ;

linux编译输入一下命令:

cd trdp/config
chmod a+x *
mv buildsettings_posix_TEMPLATE_ buildsetings_%TARGET%
cd ..
source config/buildsettings_%TARGET%
sudo make LINUX_config
sudo make //或者make all //make help查看编译选项
//生成目标文件放在bld/output下

测试用例:(trdp核心代码是为了生成trdp.a库文件,具体使用都需要开发者进行研究)

Example目录下有sendHello和receiveHello的测试代码,VS下也有测试其他功能的代码。

后记:基本介绍就到这里了,了解标准可以看IEC61375-2-3。

TRDP通信协议结构:

以下是技术文档中的:几个重要信息的解释。

Process Data (PD) is data that is cyclically distributed among many applications. Payload size is limited to 1436 bytes (without SDT).

Message Data (MD) is data that is sent event driven from one application to one or more other applications. Payload using UDP can be up to ~64 Kbytes, using TCP up to ~4 GBytes.

The TRDPLight contains the base functionality for PD, UDP-MD and TCP-MD.

The TRDPXML contains the functionality for reading TRDP XML configuration files.

The TRDPSpy contains the wireshark plugin for interpreting TRDP telegrams.

--TRDPSpy 包含解读TRDP报文的wireshark插件。

对于PIS与TMS系统通信来说,只需要了解PD(过程数据)通信结构即可。

在当前铁路的一些车辆中,MVB通信逐渐被淘汰,实时以太网TRDP的应用逐渐的在增多,而我们目前的所用的抓包器在TRDP网络中抓到的数据包显示为UDP,虽然可以看到数据包的内容,但加入这个链接库后,在wireshark就可以看到名为TRDP的数据包,且也可以使用trdp的命令进行抓取特定comid的数据包。本人所使用的Wireshark版本为Wireshark-win64-2.2.7.exe。将trdp_spy.dll放置与wireshark的安装目录下的plugins目录下当前版本号文件夹目录下(如:D:\Program Files\Wireshark\plugins\2.2.4),重启后即可。

TRDP协议介绍

TRDP(Train Real-time Data Protocol)协议是纯正的轨道交通领域的概念。

其也是应用是处于编组网层次,但是该协议并不是处于数据链路层,个人理解其处于应用层。其是基于UDP、TCP的通信协议,在使用过程中终端设备在标准以太网的基础上新增TRDP协议栈即可。

TRDP的软件是完全开源的,具体可以登入https://www.tcnopen.eu/进行查看。

TRDP是一个软实时的实时以太网,其目前已经在地铁车辆中逐步推广使用,替代原有的MVB网络,主要解决MVB网络通信的数据量太小的问题。

TRDP协议中传输的PD(Process-Data)和MD(Message-Data)。

PD主要用于列车控制,传输命令和状态信息,数据量大,要求高可靠性、实时性和确定性,一般为周期性传送。

MD主要用于故障和诊断信息,数据量长短不一,一般都是按需传送,需要确保实时性。

PD和MD通信方式都是基于了生产者/消费者模型,包含了PUSH和PULL操作,设备角色有publisher、subscriber和requester。

PD消息PUSH操作一般是publisher周期性发布消息,subscriber接收此PD消息;PULL操作是subscriber或者requester请求消息,publisher发布需求的COMID消息,在这里requester可以是subscriber。

MD消息通常请求者发出请求,已经监听的应答者做出回复,请求者可以再确认收到应答。

/***************************************************************************************************/

我下载的是1.4.0.0的发布版,通过目录可以基本先了解大致结构。

源码位于src目录下,/api提供了基本对外的API接口,/common包含了协议处理的核心代码,/vos包含了与操作系统对接的代码,支持了linux、VxWorks、Windows,操作系统接口大致为socket网络通信处理、内存管理以及任务调度。

/test 、/example 目录下主要是测试代码,

/spy目录下是wireshark的抓包工具的插件,可以针对trdp报文进行解析,

/bld是存放编译代码生成的目标文件,linux下可以在/linux-rel目录里执行测试的.exe文件。

TRDP协议代码的函数也是具有较高的辨识度,tlc表示处理通用接口,tlm表示处理MD数据接口,tlp表示处理PD数据接口。

分析/example目录示例代码,trdp通用流程大致为:

start->tlc_init->tlc_openSession->tlp_subscribe/tlp_publish->while(tlc_process)->end.

TRDP还有很多的扩展功能,因此协议可以根据业务的需求,编写适合不同场景的代码。

现在总结一下PD通信方式。

PD一般是周期性传输数据,协议规定通信方式为UDP。之前学习基于socket通信时,也只是知道UDP和TCP相比,UDP是面向无连接的,具体还怎么使用过,这次PD的收发包模型可以好好学习一波了!!!

UDP,用户数据包协议,属于传输层协议。传输数据时,不与对面先连接,而是直接把数据传送给对方。因此,UDP适用于传送数据量少,对可靠性要求不高的场景。例如比较常见的SNMP协议,TFTP协议,DNS协议等。如果SNMP协议每次请求MIB数据都需要先三次握手的话,那效率也太低了。

UDP协议通信流程非常简单。Client一般只要socket()一个socket,从而使用sendto()就可以向目的地址发送数据报了。Server一般socket()一个socket,对这个socket进行bind(),绑定端口号和IP,使用recvfrom就可以收到发送给自己的数据报了,如果需要回应,使用sendto()就可以发送了。当然,最后我们都需要close()申请的socket。

#include <sys/types.h>          
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain参数用于设置网络通信的域,AF_INET,IPV4,   AF_INET6, IPV6,其余的还有很多,一般只需牢记这两个。
type参数用于指定数据类型,
SOCK_STREAM         Provides sequenced, reliable, two-way, connection-based byte streams.   //用于TCP
SOCK_DGRAM          Supports datagrams (connectionless, unreliable messages ). //用于UDP
SOCK_RAW            Provides raw network protocol access.  //RAW类型,用于提供原始网络访问
protocol参数指定协议类型,默认可以置0,UDP是17.
返回值成功时返回socket文件描述符,失败则返回-1
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
              const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:正在监听端口的套接口文件描述符,通过socket获得
数buf:发送缓冲区,往往是使用者定义的数组,该数组装有要发送的数据
len:发送缓冲区的大小,单位是字节
flags:填0即可
dest_addr:指向接收数据的主机地址信息的结构体,也就是该参数指定数据要发送到哪个主机哪个进程
addrlen:表示第五个参数所指向内容的长度
返回值:成功:返回发送成功的数据长度
       失败: -1
这里要注意的是,在准备数据时,一般不会直接使用struct sockaddr类型,而是用srtuct sockaddr_in ,在sendto时强制转换类型。这里是因为,底层会再次类型转换回去。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:正在监听端口的套接口文件描述符,通过socket获得
buf:接收缓冲区,往往是使用者定义的数组,该数组装有接收到的数据
len:接收缓冲区的大小,单位是字节
flags:填0即可
src_addr:指向发送数据的主机地址信息的结构体,也就是我们可以从该参数获取到数据是谁发出的
addrlen:表示第五个参数所指向内容的长度
返回值:成功:返回接收成功的数据长度
       失败: -1
 
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
sockfd:正在监听端口的套接口文件描述符,通过socket获得
my_addr:需要绑定的IP和端口
addrlen:my_addr的结构体的大小
返回值:成功:0
       失败:-1

#include <unistd.h>

int close(int fd);

高级UDP编程【参考了大佬https://www.cnblogs.com/skyfsm/p/6287787.html】

其实,在UDP中,也有connect()函数,但是仅仅只是为了确定对方的地址,没有其他的含义。

因为UDP自身的特点,决定了UDP会相对于TCP存在一些难以解决的问题。第一个就是UDP报文缺失问题。

在UDP服务器客户端的例子中,如果客户端发送的数据丢失,服务器会一直等待,直到客户端的合法数据过来。如果服务器的响应在中间被路由丢弃,则客户端会一直阻塞,直到服务器数据过来。

防止这样的永久阻塞的一般方法是给客户的recvfrom调用设置一个超时,大概有这么两种方法:

1)使用信号SIGALRM为recvfrom设置超时。首先我们为SIGALARM建立一个信号处理函数,并在每次调用前通过alarm设置一个5秒的超时。如果recvfrom被我们的信号处理函数中断了,那就超时重发信息;若正常读到数据了,就关闭报警时钟并继续进行下去。

使用select为recvfrom设置超时

设置select函数的第五个参数即可。

UDP发送报文可能会产生乱序问题,乱序就是发送数据的顺序和接收数据的顺序不一致,例如发送数据的顺序为A、B、C,但是接收到的数据顺序却为:A、C、B。产生这个问题的原因在于,每个数据报走的路由并不一样,有的路由顺畅,有的却拥塞,这导致每个数据报到达目的地的顺序就不一样了。UDP协议并不保证数据报的按序接收。

解决这个问题的方法就是发送端在发送数据时加入数据报序号,这样接收端接收到报文后可以先检查数据报的序号,并将它们按序排队,形成有序的数据报。

UDP流量控制问题

总所周知,TCP有滑动窗口进行流量控制和拥塞控制,反观UDP因为其特点无法做到。UDP接收数据时直接将数据放进缓冲区内,如果用户没有及时将缓冲区的内容复制出来放好的话,后面的到来的数据会接着往缓冲区放,当缓冲区满时,后来的到的数据就会覆盖先来的数据而造成数据丢失(因为内核使用的UDP缓冲区是环形缓冲区)。因此,一旦发送方在某个时间点爆发性发送消息,接收方将因为来不及接收而发生信息丢失。

解决方法一般采用增大UDP缓冲区,使得接收方的接收能力大于发送方的发送能力。

int n = 220 * 1024; //220kB

setsocketopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));

这样我们就把接收方的接收队列扩大了,从而尽量避免丢失数据的发生。

经过一段时间的学习,基于以太网交换机的TRDP PD开发调试结果已经正常了,故此,记录一下。

TRDP基于生产者、消费者模型。

TRDP PD 通信模式有两种,push和pull。在这两种模式中,网络设备又可以分为三种角色:publisher、subscriber、requester.

publisher是数据的提供者,在push和pull模式中负责发送注册的comId的数据,即所谓的生产者;

subscriber是数据的接受者,这push和pull模式中负责接收注册的comid的数据,即所谓的消费者;

requester是数据的请求者,在pull模式中负责对publisher发起请求,pd-pdu的头部中会带有相应的reply IP和reply comID,如果头部中没有指定需要reply的IP和comID,则publisher会把接收到的报文的源作为reply的目的给发送回去。

在pull模式中,requester也可以是subscriber。

/************************************************************************************************/

tlc_ 提供了trdp的通用接口,因此在验证功能时非常好用。

//trdp 协议栈初始化

//初始化打印函数和VOS的内存管理

EXT_DECL TRDP_ERR_T tlc_init (
    const TRDP_PRINT_DBG_T  pPrintDebugString,
    void                    *pRefCon,
    const TRDP_MEM_CONFIG_T *pMemConfig);

//打开一个trdp 协议栈session入口,这里主要是进行协议栈的配置,返回pAppHandle,这个至关重要

EXT_DECL TRDP_ERR_T tlc_openSession (
    TRDP_APP_SESSION_T              *pAppHandle,
    TRDP_IP_ADDR_T                  ownIpAddr,
    TRDP_IP_ADDR_T                  leaderIpAddr,
    const TRDP_MARSHALL_CONFIG_T    *pMarshall,
    const TRDP_PD_CONFIG_T          *pPdDefault,
    const TRDP_MD_CONFIG_T          *pMdDefault,
    const TRDP_PROCESS_CONFIG_T     *pProcessConfig);

1、PD publisher验证

数据的发布者需要先准备发送的数据,将comID、destip、timeout等信息作为一个实体添加到协议栈的发送队列中

EXT_DECL TRDP_ERR_T tlp_publish (
    TRDP_APP_SESSION_T      appHandle,
    TRDP_PUB_T              *pPubHandle,
    const void              *pUserRef,
    TRDP_PD_CALLBACK_T      pfCbFunction,
    UINT32                  comId,
    UINT32                  etbTopoCnt,
    UINT32                  opTrnTopoCnt,
    TRDP_IP_ADDR_T          srcIpAddr,
    TRDP_IP_ADDR_T          destIpAddr,
    UINT32                  interval,
    UINT32                  redId,
    TRDP_FLAGS_T            pktFlags,
    const TRDP_SEND_PARAM_T *pSendParam,
    const UINT8             *pData,
    UINT32                  dataSize);
 
TRDP_ERR_T tlp_put (
    TRDP_APP_SESSION_T  appHandle,
    TRDP_PUB_T          pubHandle,
    const UINT8         *pData,
    UINT32              dataSize);

这里的每一个实体都是不重复的。因此,在周期推送数据的过程中,trdp 会 依次遍历自己的发送队列,将信息发送给目的地址。

这里的tlp_put()的功能就是将所需要发送的信息作为pd-pdu的dataset组织好数据报,放入发送队列中相应的节点存储。利用linux的select()函数来进行非阻塞的等待,周期性推送timeout的数据,即马上要发送的数据。

2、subscriber验证

subscriber是数据的接受者,需要先注册接收的信息,包含了源IP范围、comID,然后调用tlp_subscribe来注册到协议栈session的接收队列中。

EXT_DECL TRDP_ERR_T tlp_subscribe (
    TRDP_APP_SESSION_T  appHandle,
    TRDP_SUB_T          *pSubHandle,
    const void          *pUserRef,
    TRDP_PD_CALLBACK_T  pfCbFunction,
    UINT32              comId,
    UINT32              etbTopoCnt,
    UINT32              opTrnTopoCnt,
    TRDP_IP_ADDR_T      srcIpAddr1,
    TRDP_IP_ADDR_T      srcIpAddr2,
    TRDP_IP_ADDR_T      destIpAddr,
    TRDP_FLAGS_T        pktFlags,
    UINT32              timeout,
    TRDP_TO_BEHAVIOR_T  toBehavior)

协议栈根据timeout时间来接收绑定的socket数据,对接收到的数据进行校验,例如CRC、TOPOCOUNT、SRCIP、COMID。根据pdu的msgType来判断是否是Pr类型的数据,pr类型也就是请求包,收到这样的包需要查看发送队列是否存在comid相同的节点,如果存在,需要马上将数据作为msgType==Pp推送给reply IP,并且通知trdp user层(打印函数)。

3、requester验证

请求数据和publisher很相似,不过这里是调用了tlp_request来进行msgType==Pr数据的准备。

EXT_DECL TRDP_ERR_T tlp_request (
    TRDP_APP_SESSION_T      appHandle,
    TRDP_SUB_T              subHandle,
    UINT32                  comId,
    UINT32                  etbTopoCnt,
    UINT32                  opTrnTopoCnt,
    TRDP_IP_ADDR_T          srcIpAddr,
    TRDP_IP_ADDR_T          destIpAddr,
    UINT32                  redId,
    TRDP_FLAGS_T            pktFlags,
    const TRDP_SEND_PARAM_T *pSendParam,
    const UINT8             *pData,
    UINT32                  dataSize,
    UINT32                  replyComId,
    TRDP_IP_ADDR_T          replyIpAddr)

tlp_request会生成一个新的实体,放入trdp session的发送队列中去。如果收到相应的Pp报文,则需要通知trdp user层。

/******************************************************************************************/

PD数据的推拉模式,源码中已经写的比较独立和清晰,需要关注的点是和操作系统的对接,主要也就是socket编程。PD使用的是UDP协议,IANA 分配的port为17224,iec61375-2-3中规定的是20548。在UDP对接中,不同的系统的难度是不一样的,源码可以再Windows系统和linux系统直接生成相应的执行文件,适配网络平台则需要慢慢调试,验证相应的收发功能。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++学习/温习:新型源码学编程(一)

写在前面 本文约15页&#xff0c;如觉得文章过长请通过左侧边栏或知识点概览目录浏览食用面向初学者撰写专栏&#xff0c;个人原创的学习C/C笔记&#xff08;干货&#xff09;编程练习所作源代码输出内容为中文&#xff0c;便于理解如有错误之处请各位读者指正请读者评论回复、…

Go语言基础突破(一)——基础语法快速上手

文章目录一、本次学习重点内容&#xff1a;二、详细知识点介绍&#xff1a;1、什么是Go语言特点&#xff1a;2、为什么字节跳动全面使用go语言:3、开发环境——安装Golang&#xff1a;4、推荐编辑器&#xff1a;VScode、Goland5、基础语法1、HolleWorld2、基础语法——变量声明…

Ubuntu 玩机笔记

键盘Fn无法切换功能键与多媒体键 终端输入&#xff1a; echo 2 | sudo tee /sys/module/hid_apple/parameters/fnmode然后再长按 FnXL 约四秒即可成功使用Fn切换功能键与多媒体键。 永久生效 以上方法只是输入命令后生效&#xff0c;想要永久生效&#xff0c;可以进行如下操…

深度学习-吴恩达(C5)训练词向量

2.1 词汇表征 上周我们学习了RNN、GRU单元和LSTM单元。本周你会看到我们如何把这些知识用到NLP上&#xff0c;用于自然语言处理&#xff0c;深度学习已经给这一领域带来了革命性的变革。其中一个很关键的概念就是词嵌入&#xff08;word embeddings&#xff09;&#xff0c;这…

手机提供GMS支持(适用安卓和鸿蒙系统)

手机提供GMS支持 - 适用安卓和鸿蒙系统前言方式1&#xff1a;安装GMS套件&#xff08;不推荐&#xff09;方式2&#xff1a;安装OurPlay&#xff08;推荐&#xff09;方式3&#xff1a;安装Gspace&#xff08;推荐&#xff09;前言 本文提供多种为手机&#xff08;安卓和鸿蒙系…

数据库系统概论①——数据库系统基本概念

文章目录1、数据库的相关术语1.1 数据(Data)1.2 数据库(Database,简称DB)1.2.1 数据库的定义&#xff1a;数据库是长期储存在计算机内、有组织的、可共享的数据集合。1.2.2 数据库的特征1.3 数据库管理系统&#xff08;DBMS&#xff09;1.3.1 DBMS的主要功能1.4 数据库系统&…

自己动手写一个操作系统——MBR(4)_调试_elf_bin

文章目录前言elf 文件指定代码入口地址添加调试信息代码检验调试小结前言 通过 GDB 我们可以跟踪程序的执行&#xff0c; 并且能够看到寄存器的状态&#xff0c; 但是&#xff0c;上面这种调试方式&#xff0c;没有和代码对应起来&#xff0c;以致于我们无法直观地知道 CP…

CodeSYS之一EtherNET驱动禾川电机

硬件:HCFA Q0-1100系列CPU X3EB 驱动器, SV-X2MHO40A伺服电机,24V开关电源。 硬件连接: 电机三相线和编码器线接X3EB驱动器,24V电源接X3EB驱动器,驱动器IN网口接 CPUPort2,CPU接24V电源,Port1 接电脑。 这部分有问题可以直接进禾川官网 一、驱动器调试 接好先后,按驱动…

边缘检测、Padding、stride、三维卷积

目录1.边缘检测(edge detection)当我们做物体识别的时候&#xff0c;一般神经网络的前几层会进行边缘检测&#xff0c;然后检测到物体的一部分&#xff0c;最后检测到整个物体。边缘检测例子&#xff1a;垂直边缘检测器&#xff1a;中间的一个3x3的矩阵&#xff0c;我们称之为过…

力扣sql简单篇练习(六)

力扣sql简单篇练习(六) 1 变更性别 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # set子句中也是可以使用if函数的 UPDATE Salary SET sexif(sexf,m,f)1.3 运行截图 2 销售员 2.1 题目内容 2.1.1 基本题目信息1 2.1.2 基本题目信息2 2.1.3 示例…

代码随想录--二叉树章节总结 Part I

代码随想录–二叉树章节总结 Part I 1.Leetcode144 前序遍历二叉树 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历 解题思路1&#xff1a;使用递归解决。 public List<Integer> preorderTraversal(TreeNode root) {ArrayList<Integer> list …

c中对宏的理解(面试题)

1、gcc的编译过程&#xff1a;预处理、编译、汇编、链接 预处理&#xff1a;宏替换、删除注释、头文件包含、条件编译 -E &#xff08;不会发生报错&#xff09;生成预编译文件 将 01_code.c文件使用 gcc -E 01_code.c -o 01.i生成预编译文件01.i 可以10行的源文件看见生成800…

【c语言进阶】枚举与联合体的基本知识大全

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我…

1600_Cmake学习笔记_Cmake实践学习

全部学习汇总&#xff1a; GreyZhang/g_cmake: my learning notes for cmake tool. (github.com) 之前考虑过是否要学习Cmake&#xff0c;经过各种对比之后放弃了&#xff0c;我选择了去学习scons。在实际的工作以及学习中scons也的确给了我很大的受益。当时出于各种方面的原因…

PyQt5开发环境搭建 1.1 软件安装

写在前面的话&#xff08;1&#xff09;相对而言&#xff0c;python&#xff0c;PyQt5安装还是比较快的。Qt这个又大又慢。Eric也是需要比较长的时间。&#xff08;2&#xff09;安装失败很正常&#xff0c;多尝试几次&#xff0c;多查查&#xff0c;努力装好软件。安装和配置是…

【Linux】VM与Linux的安装

1.1 VMWare安装 1&#xff09;VMware Workstation Pro安装向导 2&#xff09;VMware Workstation安装的许可协议。 3&#xff09;VMware Workstation安装路径。 4&#xff09;VMware Workstation增强型键盘功能。 5&#xff09;VMware Workstation软件检查更新和帮助完善 6&am…

Linux常见的进程间通信

目录管道pipe匿名管道接口介绍示例代码fifo命名管道接口介绍代码示例匿名管道与命名管道的区别shm共享内存接口介绍相关指令代码示例特点总结信号信号量socket套接字管道 管道是一种较老的&#xff0c;半双工通信方式&#xff0c;即数据只能向一个方向流动&#xff08;即一个进…

力扣 2303. 计算应缴税款总额

题目 给你一个下标从 0 开始的二维整数数组 brackets &#xff0c;其中 brackets[i] [upperi, percenti] &#xff0c;表示第 i 个税级的上限是 upperi &#xff0c;征收的税率为 percenti 。税级按上限 从低到高排序&#xff08;在满足 0 < i < brackets.length 的前提…

spring笔记下(AOP、事务管理)

一、AOP概述 1. AOP介绍 AOP(Aspect Oriented Programming)&#xff1a;面向切面编程&#xff0c;一种编程范式&#xff0c;指导开发者如何组织程序结构&#xff0c;是oop的延续。&#xff08;OOP面向对象编程&#xff09; AOP作用&#xff1a;在不惊动原始设计的基础上为其进…

LeetCode刷题模版:221 - 230

目录 简介221. 最大正方形222. 完全二叉树的节点个数223. 矩形面积224. 基本计算器【未理解】225. 用队列实现栈226. 翻转二叉树227. 基本计算器 II【未理解】228. 汇总区间229. 多数元素 II230. 二叉搜索树中第K小的元素结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有…