【源码阅读】交易池txs_pool

news2024/11/20 6:19:03

代码结构
重要接口:

重要接口
ITxsPool的实现:

type TxsPool struct {
	config      TxsPoolConfig
	chainconfig *params.ChainConfig

	bc common.IBlockChain

	currentState  *state.IntraBlockState
	pendingNonces *txNoncer
	currentMaxGas uint64

	ctx    context.Context //
	cancel context.CancelFunc
	wg     sync.WaitGroup
	mu     sync.RWMutex // lock

	istanbul bool // Fork indicator whether we are in the istanbul stage.
	eip2718  bool // Fork indicator whether we are using EIP-2718 type transactions.
	eip1559  bool // Fork indicator whether we are using EIP-1559 type transactions.
	shanghai bool // Fork indicator whether we are in the Shanghai stage.

	locals   *accountSet
	pending  map[types.Address]*txsList
	queue    map[types.Address]*txsList
	beats    map[types.Address]time.Time
	all      *txLookup
	priced   *txPricedList
	gasPrice *uint256.Int

	// channel
	reqResetCh      chan *txspoolResetRequest
	reqPromoteCh    chan *accountSet
	queueTxEventCh  chan *transaction.Transaction
	reorgDoneCh     chan chan struct{}
	reorgShutdownCh chan struct{}

	changesSinceReorg int

	isRun uint32

	deposit *deposit.Deposit
}

1、Has

Has(hash types.Hash) bool

根据输入的hash值判断交易池中是否有该交易,返回bool值
input:hash
output:bool

2、Pending

Pending(enforceTips bool) map[types.Address][]*transaction.Transaction

函数遍历所有当前可处理的交易,按原始帐户分组并按随机数排序。
input:enforceTips 参数可用于对挂起的交易进行额外的过滤,只返回那些在下一个挂起的执行环境中有效提示足够大的交易。
return pending;返回的交易集是一个副本,可以通过调用代码自由修改。
input:enforceTips
output:pending(账户+交易)

txs := list.Flatten()

使用txpool_list中的Flatten()函数可以实现按照nonce排序,并将排序结果缓存下来,形成副本,用于保存。

pending[addr] = txs

这一行代码实现了按照账户将交易进行分组。

3、GetTransaction

GetTransaction() ([]*transaction.Transaction, error)

获取pending队列里面的交易,返回pending,且err:nil

4、GetTx

GetTx(hash types.Hash) *transaction.Transaction

通过pool对象中的txLookup类型的all,通过其中的pool.all.Get(hash)方法Get(hash types.Hash) *transaction.Transaction可以根据hash值获取对应的交易。

5、AddRemotes

AddRemotes(txs []*transaction.Transaction) []error

将远程交易加入交易池中,调用pool对象中的addTxs()方法func (pool *TxsPool) addTxs(txs []*transaction.Transaction, local bool, sync bool) []error,其中bool local为false,bool sync为false,sync是同步的意思

5.1 addTxs

addTxs(txs []*transaction.Transaction, local bool, sync bool) []error

无需池锁
遍历整个交易列表:

  • 1 交易池中已经有的交易不能要:通过**get(hash)**函数
  • 2 发送者验证不通过的交易不能要:通过pool.validatesender()函数
    上锁之后通过
    addTxsLocked
    得到一些err和地址集
    如果是本地的,可以把交易通过event.GlobalEvent.Send事件
    通过pool.requestPromoteExecutables获得请求的结果。

5.1.1 addTxsLocked

addTxsLocked(txs []*transaction.Transaction, local bool) ([]error, *accountSet)

addTxsLocked 尝试对一批有效的交易进行排队。
必须持有交易池锁。
dirty的定义是调用tx_list中的**newAccountSet()方法实现的。
遍历交易列表,通过调用
pool.add()方法进行处理。如果没有被替换的且没有错误信息,就可以调用addTx(tx)**将交易加入交易池中

5.1.1.1 newAccountSet
func newAccountSet(addrs ...types.Address) *accountSet

newAccountSet 创建一个新地址集,其中包含用于发送者派生的关联签名者。

5.1.1.2 add
func (pool *TxsPool) add(tx *transaction.Transaction, local bool) (replaced bool, err error)

add 验证事务并将其插入到不可执行队列queue中以供稍后挂起的提升和执行。 如果该交易是已待处理或排队交易的替代交易,则如果其价格较高,它将覆盖前一交易。
如果新添加的交易被标记为本地local,则其发送帐户将被添加到白名单中,以防止任何关联交易因定价限制而被从池中删除。

  • 1 通过**pool.all.Get(hash)**判断是否在交易池中,如果已经存在,返回replaced:false和错误信息。
  • 2 local本地的判断
 isLocal := local || pool.locals.containsTx(tx)

如果它来自本地源或来自网络但发送者之前被标记为本地,则将其视为本地事务。

  • 3 通过pool.validateTx来进行验证
  • 4 如果交易池已经满了,价格过低的交易可以丢弃
  • 5 非本地的新交易的价格过低,不接受
  • 6 回滚重组的个数应该不大于总GlobalSlots的25%,否则有错误信息
  • 7 新的交易更好的话,为其腾出空间;如果是本地事务,则强制丢弃所有可用事务;否则,如果我们无法为新的空间腾出足够的空间,则中止操作。通过txs_list.go里面的**Discard()**函数实现。
  • 8 通过pool.removeTx淘汰定价过低的远程交易
  • 9 452-468行实现如果交易是已待处理交易,则覆盖前一交易
  • 10 通过pool.enqueueTx加入到queue中
  • 11 480-484实现本地local将会被添加到白名单中
5.1.1.2.1 validateTx
func (pool *TxsPool) validateTx(tx *transaction.Transaction, local bool) error

完成对交易的验证

  • 1 eip-2718/2930和eip-1559不支持
  • 2 交易大小大于最大限制txMaxSize,这可以限制DOS攻击
  • 3 交易数量不为负值
  • 4 gas过大
  • 5 确认类似gasprice、gastipcap这样的数据的位数是正确的
  • 6 GasFeeCap要大于等于GasTipCap
    GasFeeCap是指消息发送者针对每个GasUnit愿意支付的最高价格,以特定单位衡量。同时,GasFeeCap与另一个参数GasLimit一起,确定了发送者为消息支付的最大费用金额。这保证了发送者发送消息的费用不会超过GasLimit和GasFeeCap的乘积值。
    GasTipCap是交易设置中的一个参数,对应于交易中的最大 小费,决定了在网络拥堵时,用户愿意支付的最高小费价格。同时,可以通过调用某些API函数,例如SuggestGasTipCap来获取到系统建议的GasTipCap值。这个参数的设置对于交易的执行速度有一定影响,因为如果设置了较高的GasTipCap,那么在网络拥堵的时候,你的交易可能会优先得到处理。不过需要注意的是,这并不意味着你可以无限制地提高GasTipCap,因为过高的费用可能会导致交易失败。
  • 7 非本地交易不能降低到我们自己接受的最低汽油价格或小费之下
  • 8 随机数nonce要排序
  • 9 要有足够的余额来支付
  • 10 交易的gas要比基本的汽油费要大
5.1.1.2.2 removeTx
func (pool *TxsPool) removeTx(hash types.Hash, outofbound bool)

removeTx 从队列中删除单个事务,将所有后续事务移回未来队列。

  • 1 获取要删除的交易
  • 2 通过pool.all.Remove(hash)从已知队列中删除
    outofbound
  • 从pending队列中删除并且重置nonce,通过txs_list.go里面的pending.Remove(tx)进行删除
  • 如果队列中没有剩余的交易,就删除list

6、AddLocal

AddLocal(tx *transaction.Transaction) error

通过调用pool.AddLocals([]*transaction.Transaction{tx})将本地交易加入交易池

6.1 AddLocals

func (pool *TxsPool) AddLocals(txs []*transaction.Transaction) []error

这里调用pool.addTxs(txs, !pool.config.NoLocals, true),其中的sync是异步。参考5.1

7、Stats

Stats() (int, int, int, int)

通过遍历pending和queue中的交易获得输出
主要有四个输出:
pendingAddresses:待处理的地址数量
pendingTxs:待处理的交易数量
queuedAddresses:排队等待处理的地址数量
queuedTxs:排队等待处理的交易数量

8、Nonce

Nonce(addr types.Address) uint64

获取地址的pending随机数标识符列表
pool.mu.RLock() 是一个用于获取读锁的方法,通常用于在多线程环境中保护共享资源。当一个线程需要读取共享资源时,它会先获取读锁,其他线程在此期间无法修改共享资源。当线程完成读取操作后,它会释放读锁,以便其他线程可以访问共享资源。
defer关键字用于延迟执行一个函数或方法,直到包含它的函数返回。因此,defer pool.mu.RUnlock()表示在当前函数返回之前,pool.mu.RUnlock()方法来释放读锁。这通常用于处理资源池、互斥锁等需要手动释放的资源。通过使用defer关键字,可以确保即使出现异常情况,也能正确地释放资源。
pool.pendingNonces.get(addr) 是一个用于获取指定地址的待处理随机数的方法。在以太坊智能合约中,随机数生成器通常使用一个待处理的随机数集合来存储待处理的随机数。当合约需要生成一个新的随机数时,它会将该随机数添加到待处理集合中,并返回一个唯一的随机数标识符。当合约完成随机数的使用后,它会从待处理集合中删除该随机数标识符。
通过调用pool对象中的 pool.pendingNonces.get(addr) 方法,可以获取指定地址的待处理随机数标识符列表。这有助于确保每个地址只生成一次随机数,并且可以防止重复生成相同的随机数。

9、Content

Content() (map[types.Address][]*transaction.Transaction, map[types.Address][]*transaction.Transaction)

获取pending和queue,其中调用了tx_List的对象list,对其中的数据进行了排序,Flatten 根据松散排序的内部表示创建随机数排序的事务切片。 排序结果将被缓存,以备在对内容进行任何修改之前再次请求时使用。其中也需要进行读取锁和释放锁。

10、SetDeposit

SetDeposit(deposit *deposit.Deposit)

这是一个名为SetDeposit的函数,它接受一个指向deposit.Deposit类型的指针作为参数。这个函数的作用是将传入的存款对象设置到交易池中。

pool.deposit = deposit

11、promoteTx

func (pool *TxsPool) promoteTx(addr types.Address, hash types.Hash, tx *transaction.Transaction) bool

PromotionTx 将交易添加到待处理(可处理)的交易列表中,并返回该交易是否已插入或较旧的更好。
注意,此方法假设持有池锁!

  • 1 如果pending[addr]==nil,创建交易列表
  • 2 调用txs_list中的list对象的add方法inserted, old := list.Add(tx, pool.config.PriceBump)。Add 尝试将新交易插入列表中,返回该交易是否被接受,如果是,则返回它替换的任何先前交易。如果新交易被接受到列表中,列表的成本和气体阈值也可能会更新。
if old.GasFeeCapCmp(tx) >= 1 || old.GasTipCapCmp(tx) >= 1 {return false, nil}

这种情况下,交易不被接受。

  • 3 如果不被接受,则说明old更好,就需要通过txs_list中的**pool.all.Remove()和pool.priced.Removed()**丢弃该交易,并返回false。
  • 4 如果被接受,就要丢弃原来的旧交易
  • 5 设置新nonce
  • 6 记录心跳

12、promoteExecutables

func (pool *TxsPool) promoteExecutables(accounts []types.Address) []*transaction.Transaction 

PromotionExecutables 将已变得可处理的事务从未来队列移动到待处理事务集pending。 在此过程中,所有无效交易(低随机数、低余额)都将被删除。

  • 1 操作遍历整个queue
  • 2 删除过旧的交易(nonce过小),其中调用list.Foward进行筛选和**remove(hash)**进行删除
  • 3 删除昂贵的交易(balance不足或者gas耗尽),通过list.Filter进行筛选和**remove(hash)**进行删除
  • 4 将所有可执行交易通过promoteTx(参考11)添加到可处理的交易列表中
  • 5 删除超出了硬性limit的queue交易,通过list.Cap进行筛选(得到超出了limit的所有交易)和**remove(hash)**进行删除
  • 6 对于空队列,可以将其delete

13、truncatePending

func (pool *TxsPool) truncatePending()

如果池高于挂起限制,truncatePending 将从挂起队列中删除事务。 对于具有许多待处理交易的所有账户,该算法尝试将交易计数减少大约相同的数量。 该函数的主要目的是在交易池中对待处理的交易进行修剪,以确保交易池中的交易数量不超过全局插槽数。如果某个地址的待处理交易数量超过了阈值,那么将该地址的所有待处理交易减少到阈值以下。。

  • 1 首先计算所有待处理事务的总数量,并将其存储在变量pending中。然后,它检查pending是否小于等于全局插槽数pool.config.GlobalSlots,如果是,则直接返回,不执行任何操作。
  • 2 调用github.com/prometheus/common/prque中的函数prque.New创建一个优先级队列,用于存储需要惩罚的大交易者。
  • 3 遍历所有的pending,将满足条件的地址及其待处理交易数量添加到spammers队列中。(每个地址是否包含在本地地址列表中(!pool.locals.contains(addr)),以及该地址对应的待处理事务数量是否大于账户插槽数(uint64(list.Len()) > pool.config.AccountSlots
  • 4 从spammers队列中取出下一个违规者,并将其添加到offenders列表中,直到所有待处理交易的数量都不超过全局插槽数或spammers队列为空。
for pending > pool.config.GlobalSlots && !spammers.Empty()
  • 5 检查是否有多个违规者if len(offenders) > 1(即待处理交易数量超过全局插槽数的地址)。如果有多个违规者,那么计算所有违规者的阈值,即他们的待处理交易数量。
  • 6 迭代地减少所有违规者的待处理交易,直到他们的待处理交易数量都低于阈值或低于全局插槽数。
    在每次迭代中,代码会遍历所有违规者的待处理交易,通过list.Cap进行筛选,并将它们从全局交易池中通过remove移除。同时,还会更新违规者的nonce,以确保其后续的交易不会受到干扰。
  • 7 记录已移除的交易数量,并更新价格池的状态。pool.priced.Removed(len(caps))

14、truncateQueue

func (pool *TxsPool) truncateQueue()

如果池高于全局队列限制,则 truncate Queue 会删除队列中最旧的事务。

  • 1 计算所有queue的总数量,并存在变量queued中。检查是否小于全局队列限制pool.config.GlobalQueue,如果是则直接返回,不执行任何操作。
  • 2 创建一个adddressByHeart的对象,其中有len、less、swap三种方法
  • 3 按照heartbeat进行排序,其中本地accounts不需要被丢弃if !pool.locals.contains(addr)
  • 4 放弃交易,直到queue总数低于限制pool.config.GlobalQueue或仅保留本地
    for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0;
  • 5 如果交易少于溢出drop,就通过pool.removeTx删除所有的交易
if size := uint64(list.Len()); size <= drop {
	for _, tx := range list.Flatten() {
		hash := tx.Hash()
		pool.removeTx(hash, true)
	}
	drop -= size
	continue
}
  • 6 否则,通过list.Flatten排序之后通过pool.removeTx就删除最新的几个交易

15、demoteUnexecutables

func (pool *TxsPool) demoteUnexecutables()

demoteUnexecutables 从池可执行/待处理队列中删除无效和已处理的事务,并且任何变得不可执行的后续事务都将移回到未来队列中。
注意:交易不会在价目表中标记为已删除,因为重新堆总是由 SetBaseFee 显式触发,并且此函数触发重新堆是不必要且浪费的
这段代码是以太坊中的一部分,它定义了一个名为demoteUnexecutables的函数。这个函数的主要目的是处理待处理交易池中的不可执行交易。这个函数主要用于确保交易池中的交易都是可执行的,并且保持了交易的有效性和顺序。

    1. 遍历所有账户和pending待处理交易列表。
    1. 对于每个账户,通过pool.currentState.GetNonce(addr)获取其当前的nonce值。
    1. 删除所有被视为太旧的交易(即nonce值过低的交易),通过list.Forward筛选,通过pool.all.remove进行删除。
    1. 删除所有无法支付的交易(即余额过低或gas不足的交易),通过list.Filter筛选,通过pool.all.remove进行删除。
    1. 通过pool.enqueueTx将无效的交易排队以供后续处理。
    1. 如果本地账户包含某个地址,则不进行任何操作。
    1. 如果在某个地址前面存在间隙(这种情况不应该发生),则将所有交易推迟处理。
    1. 如果某个地址的待处理交易列表变为空,则从待处理交易池中删除该地址的所有条目。

16、blockChangeLoop

func (pool *TxsPool) blockChangeLoop()

作用是监听区块高度的变化,并在新的区块被插入时重置交易池。

    1. 使用defer pool.wg.Done()确保在函数退出时调用pool.wg.Done()来标记工作完成。
    1. 创建一个名为highestBlockCh的通道,用于接收最新的区块高度信息。
highestBlockCh := make(chan common.ChainHighestBlock)
    1. 使用defer close(highestBlockCh)确保在函数退出时关闭通道。
    1. 订阅全局事件event.GlobalEvent,并将highestBlockCh作为回调函数传递给它。
highestSub := event.GlobalEvent.Subscribe(highestBlockCh)
    1. 使用defer highestSub.Unsubscribe()确保在函数退出时取消订阅。
    1. 获取当前区块的高度,并将其存储在变量oldBlock中。oldBlock := pool.bc.CurrentBlock()
  1. 进入一个无限循环,使用select语句监听以下事件:
    • highestSub.Err():如果发生错误,则返回并结束函数执行。
    • pool.ctx.Done():如果上下文被取消,则返回并结束函数执行。
    • highestBlockCh:如果从通道中接收到新的区块高度信息,则进行以下操作:
      • 如果新的高度有效且已插入,则调用pool.requestReset(oldBlock, pool.bc.CurrentBlock())方法重置交易池,并更新oldBlock为当前区块的高度。
case highestBlock, ok := <-highestBlockCh:
	if ok && highestBlock.Inserted {
		pool.requestReset(oldBlock, pool.bc.CurrentBlock())
		oldBlock = pool.bc.CurrentBlock()
	}

16.1 requestReset

func (pool *TxsPool) requestReset(oldBlock block.IBlock, newBlock block.IBlock) <-chan struct{}

该方法接收两个参数:oldBlocknewBlock,它们都是block.IBlock类型的接口。这个方法的作用是向交易池发送一个重置请求,并等待重置操作完成或发生异常情况。
方法内部使用了select语句来处理不同的事件。

    1. 它将一个txspoolResetRequest类型的请求对象发送到pool.reqResetCh通道中,该请求对象包含了旧区块和新区块的信息。然后,它等待从pool.reorgDoneCh通道中接收一个信号,表示重置操作已经完成。
    1. 如果上下文pool.ctx被取消,则返回上下文的完成信号。

17、scheduleLoop

func (pool *TxsPool) scheduleLoop()
    1. 使用defer pool.wg.Done()确保在函数退出时调用pool.wg.Done()来标记工作完成。
curDone:一个非空的通道struct chan,当runReorg处于活动状态时,该通道不为空。
nextDone:一个空的通道,用于等待下一个操作完成。
launchNextRun:一个布尔值,表示是否启动下一个运行。
reset:一个指向txspoolResetRequest类型的指针,用于存储重置请求的信息。
dirtyAccounts:一个指向accountSet类型的指针,用于存储脏账户信息。
queuedEvents:一个映射,将地址映射到指向txsSortedMap类型的指针,用于存储排队的事件。
// Launch next background reorg if needed
if curDone == nil && launchNextRun {
	// Run the background reorg and announcements
	go pool.runReorg(nextDone, reset, dirtyAccounts, queuedEvents)

	curDone, nextDone = nextDone, make(chan struct{})
	launchNextRun = false

	reset, dirtyAccounts = nil, nil
	queuedEvents = make(map[types.Address]*txsSortedMap)
}

这段代码是用于在需要时启动下一个后台重组(background reorg)的。如果当前没有正在进行的重组(curDone == nil),并且需要启动下一个运行(launchNextRun),则执行以下操作:

  1. 使用go关键字启动一个新的协程来运行后台重组和公告(pool.runReorg)。
  2. 将nextDone赋值给curDone,并创建一个新的空通道(make(chan struct{}))赋值给nextDone。
  3. 将launchNextRun设置为false,表示不再需要启动下一个运行。
  4. 将reset、dirtyAccounts和queuedEvents重置为nil或空值,以便下一次使用。
    对于select中的每一个case
    1. req := <-pool.reqResetCh: 从pool.reqResetCh通道中接收重置请求。如果当前没有正在进行的重置(reset == nil),则将请求赋值给reset变量;否则,将新块更新到reset变量中。然后将nextDone发送到pool.reorgDoneCh通道,表示重置已完成。
    1. req := <-pool.reqPromoteCh: 从pool.reqPromoteCh通道中接收提升请求。如果当前没有正在进行的提升(dirtyAccounts == nil),则将请求赋值给dirtyAccounts变量;否则,将请求合并到dirtyAccounts变量中。然后将nextDone发送到pool.reorgDoneCh通道,表示提升已完成。
    1. tx := <-pool.queueTxEventCh: 从pool.queueTxEventCh通道中接收交易事件。获取交易的发送地址,并将交易添加到与该地址关联的队列中。如果该地址尚未在队列中,则创建一个新的排序映射并将其分配给该地址。
    1. <-curDone: 等待当前运行完成。如果curDone不为空,则等待其完成。
    1. case <-pool.ctx.Done():: 等待当前运行完成。如果curDone不为空,则等待其完成。然后关闭nextDone通道并返回。

这段代码的主要目的是处理不同类型的请求,并根据请求类型执行相应的操作。

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

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

相关文章

密码学(一)

文章目录 前言一、Cryptographic Primitives二、Cryptographic Keys2.1 Symmetric key cryptography2.2 asymmetric key cryptography 三、Confidentiality3.1 Symmetric key encryption algorithms3.2 asymmetric key block ciphers3.3 其他 四、Integrity4.1 secure hashing …

【语义解析:连接自然语言与机器智能的桥梁】

语义解析&#xff1a;连接自然语言与机器智能的桥梁 语义解析技术可以提高人机交互的效率和准确性&#xff0c;在自然语言处理、数据分析、智能客服、智能家居等领域都有广泛的应用前景。特别是在大数据时代&#xff0c;语义解析能够帮助企业更快速地从大量的数据中获取有用的…

Helix QAC 2023.4 新版支持C++20语言,带来更多性能提升!

Helix QAC 2023.4 新增功能 Helix QAC 2023.4全面支持MISRA C:2023规则&#xff0c;涵盖100%的指南。此版本还加强了对C20语言的支持&#xff0c;改进了数据流分析性能&#xff0c;并在整个产品中增加了多项用户体验改进。 增强的C20支持 此版本新增了对以下语言特性的支持&a…

永久关闭Windows更新的5种方法

很多家用电脑&#xff0c;如果系统自动更新的话&#xff0c;会变得越来越卡顿&#xff0c;且硬件型号兼容也并不完美。那么我们该如何彻底关闭Win11的自动更新呢&#xff1f;以下准备了5种方法&#xff0c;您可以根据自身实际情况选择合适的方法&#xff01; 一&#xff1a;使…

imgaug库指南(13):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应运而生&#xff0c;成为了解决这一问题的…

【SPDK】【NoF】使用SPDK实现NVMe over Fabrics Target

本文使用两台PC&#xff0c;一台做NVMe over Fabrics Target&#xff08;服务端&#xff09;&#xff0c;一台做NVMe over Fabrics initiator&#xff08;客户端&#xff09;。首先使用SoftRoCE来实现底层的rdma传输&#xff0c;然后使用SPDK来实现NVMe over Fabrics Target。 …

首家通过中国信通院数据库迁移工具专项测试,亚信安慧AntDB受到认可!

亚信安慧数据库数据同步平台经过中国信通院第17批“可信数据库”数据库迁移工具专项测试&#xff0c;成功成为首家符合《数据库迁移工具能力要求》的产品。该平台广泛适用于多种数据迁移场景&#xff0c;具备高性能实时数据处理、断点续作、一键迁移、可视化运维等核心优势。此…

Linux第22步_安装CH340驱动和串口终端软件MobaXterm

开发板输出信息通常是采用串口&#xff0c;而计算机通常是USB接口&#xff0c;为了让他们之间能够交换数据&#xff0c;我们通常采用USB转串口的转换器来实现。目前市场上的串口转换器大多是采用CH340芯片来实现的&#xff0c;因此我们需要在计算中安装一个CH340驱动程序&#…

echarts——折线图实现不同区间不同颜色+下钻/回钻功能——技能提升

echarts——折线图实现不同区间不同颜色下钻/回钻功能——技能提升 需求场景解决步骤1&#xff1a;安装echarts插件解决步骤2&#xff1a;html代码解决步骤3&#xff1a;封装option配置和initChart渲染方法解决步骤4&#xff1a;回钻功能 需求场景 最近在写后台管理系统时&…

conda环境下Torch not compiled with CUDA enabled解决方法

1 问题描述 在运行wav2lip模型训练时&#xff0c;报如下错误&#xff1a; Traceback (most recent call last):File "D:\ml\Wav2Lip\preprocess.py", line 32, in <module>fa [face_detection.FaceAlignment(face_detection.LandmarksType._2D, flip_inputF…

线性代数——(期末突击)矩阵(下)-习题篇(初等变换求逆矩阵、矩阵乘法、求矩阵方程、求线性方程组、解齐次线性方程组)

目录 初等变换求逆矩阵 矩阵乘法 求矩阵方程 求线性方程组 解齐次线性方程组 带有未知数的方程组 初等变换求逆矩阵 如果,则A可逆&#xff0c;且 例题&#xff1a; &#xff0c;求A的逆矩阵。 矩阵乘法 求AB&#xff0c;BA. 矩阵之间的乘法是行乘以列&#xff0c;以这…

基于ssm的教材管理系统论文

基于SSM的教材管理系统的设计与实现 摘 要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。以前学校对于教材信息的管理和控制&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这种以…

沈阳数字孪生赋能工业智能制造,助力制造业企业数字化转型

沈阳数字孪生赋能工业智能制造&#xff0c;助力制造业企业数字化转型。在数字经济时代&#xff0c;数字孪生作为实现各行各业智能化、数字化的重要手段之一&#xff0c;受到了各方的广泛重视。随着各项关键使能技术的不断发展&#xff0c;数字孪生的应用价值有望得到进一步释放…

MFC 多文档视图架构

目录 多文档视图架构 模仿多文档视图架构 执行流程 多文档视图架构 一个多文档视图架构运行后会是下面的样子&#xff1a; 内部的子框架窗口就相当于一个单文档视图架构&#xff0c;多文档视图架构就相当于在外面套一层框架窗口。 特点&#xff1a;可以管理多个文档(可以有…

【hcie-cloud】【21】容器详解【容器网络说明、容器存储说明、容器镜像说明、dockerfile详述、缩略词】【下】

文章目录 容器介绍&#xff0c;容器工作机制、容器常用命令说明容器网络容器网络简介容器常用网络类型 - Bridge容器常用网络类型 - Host容器常用网络类型 - None其他容器网络类型【Macvlan、Overlay、IPvlan】容器网络相关配置 容器存储容器中应用数据的存储容器持久化存储配置…

猫粮的选择:买主食冻干猫粮要注意什么

由于猫咪是肉食动物&#xff0c;对蛋白质的需求很高&#xff0c;如果摄入的蛋白质不足&#xff0c;就会影响猫咪的成长。而冻干猫粮本身因为制作工艺的原因&#xff0c;能保留原有的营养成分和营养元素&#xff0c;所以冻干猫粮蛋白含量比较高&#xff0c;营养又高&#xff0c;…

控制障碍函数(Control Barrier Function,CBF) 三、代码

三、代码实现 3.1、模型 这是一个QP问题&#xff0c;所以我们直接建模 这其实还是之前的那张图&#xff0c;我们把这个大的框架带入到之前的那个小车追击的问题中去&#xff0c;得到以下的一些具体的约束条件 CLF约束 L g V ( x ) u − δ ≤ − L f V ( x ) − λ V ( x ) …

速学python·输入输出

和用户交互 程序与用户交互工程中 用户把信息传送给程序的过程是 输入 程序把结果展示给用户的过程是 输出 输入输出的最简单的方法是利用控制台 例如 和 都是控制台,进行输入和输出的作用 但是: 我们常见的交互界面,例如QQ,浏览器,Wegame等,都不需要输入命令,大大简化了操…

热度不减!一周61篇,二区以上近一半!| 孟德尔随机化周报(12.27-01.02)

欢迎报名2024年孟德尔随机化方法高级班课程&#xff01; 郑老师团队开设的孟德尔随机化高级班2024年1月20-21日开课&#xff0c;欢迎报名 孟德尔随机化,Mendilian Randomization&#xff0c;简写为MR&#xff0c;是一种在流行病学领域应用广泛的一种实验设计方法&#xff0c;利…

selenium爬取多个网站及通过GUI界面点击爬取

selenium爬取代码 webcrawl.py import re import time import json from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options from selenium.common.exceptions import TimeoutException, Stale…