洌⊿ubtopologies)設(shè)計(jì)模式:模塊化組件分組、配置覆蓋與核心子拓?fù)鋵?shí)戰(zhàn)指南)
F′ 子拓?fù)銼ubtopologies設(shè)計(jì)模式模塊化組件分組、配置覆蓋與核心子拓?fù)鋵?shí)戰(zhàn)指南【免費(fèi)下載鏈接】fprimeF′ - A flight software and embedded systems framework項(xiàng)目地址: https://gitcode.com/GitHub_Trending/fpr/fprime子拓?fù)銼ubtopology是 FPP 建模語言提供的一種可復(fù)用設(shè)計(jì)模式把一組組件實(shí)例與組件間連接打包成一個(gè)獨(dú)立單元隨后導(dǎo)入到更大的部署拓?fù)渲袕?fù)用從而避免在每個(gè)項(xiàng)目中手工重復(fù)接線。本文以 F′ 倉庫中的 CdhCore、ComCcsds、ComFprime、FileHandling、DataProducts 等核心子拓?fù)錇閷?shí)例講解子拓?fù)涞亩x、導(dǎo)入、參數(shù)化配置與構(gòu)建期覆蓋機(jī)制讀者學(xué)完后可在自己的部署中直接導(dǎo)入、定制并接線這些官方子拓?fù)洹J裁词亲油負(fù)湓?FPPF′ Prime Prime中拓?fù)鋞opology是組件實(shí)例與連接的集合而**子拓?fù)鋝ubtopology**則是其中可以被整體復(fù)用的一小塊。它的核心理念與 UML/SysML 中的 composite block復(fù)合塊類似把“設(shè)計(jì)上就要協(xié)同工作”的一組組件封裝成一個(gè)可導(dǎo)入的單元上層拓?fù)渲恍枰淮蝘mport即可自動(dòng)帶入該單元內(nèi)部的全部實(shí)例與連接。子拓?fù)鋷淼闹苯邮找婺K化功能邊界清晰例如“命令與數(shù)據(jù)處理CDH”“CCSDS 通信棧”“文件處理”各自成塊可復(fù)用同一子拓?fù)淇稍诙鄠€(gè)部署中反復(fù)導(dǎo)入無需重寫內(nèi)部接線易管理復(fù)雜系統(tǒng)的組件與連接被組織成有名字、有文檔的結(jié)構(gòu)而非一張龐大的扁平網(wǎng)。一個(gè)最小示例ManagerWorker 子拓?fù)潢P(guān)聯(lián)文檔使用 ManagerWorker 示例說明子拓?fù)涞幕拘螒B(tài)該示例位于 fprime-examples 倉庫的 FlightExamples/ManagerWorkermodule ManagerWorker { # Defining the instances to be used in subtopology instance manager: ManagerWorker.Manager base id ManagerWorkerSubtopologyConfig.ManagerWorkerSubtopology_BASE_ID 0x0000 \ queue size ManagerWorkerSubtopologyConfig.Defaults.QUEUE_SIZE \ stack size ManagerWorkerSubtopologyConfig.Defaults.STACK_SIZE \ priority ManagerWorkerSubtopologyConfig.Priorities.manager instance worker: ManagerWorker.Worker base id ManagerWorkerSubtopologyConfig.ManagerWorkerSubtopology_BASE_ID 0x1000 \ queue size ManagerWorkerSubtopologyConfig.Defaults.QUEUE_SIZE \ stack size ManagerWorkerSubtopologyConfig.Defaults.STACK_SIZE \ priority ManagerWorkerSubtopologyConfig.Priorities.worker Subtopology for connecting manager/worker topology Subtopology { # Instantiation in subtopology instance manager instance worker connections ManagerWorker { manager.startWorker - worker.startWork manager.cancelWorker - worker.cancelWork worker.workDone - manager.doneRecv } } # end Subtopology } # end ManagerWorker這個(gè)示例展示了子拓?fù)涞膬蓚€(gè)核心組成部分實(shí)例定義manager與worker兩個(gè)組件實(shí)例在模塊ManagerWorker中聲明。注意它們的base id、隊(duì)列大小、棧大小、優(yōu)先級(jí)都引用了一個(gè)獨(dú)立的配置模塊ManagerWorkerSubtopologyConfig中的常量——這正是后面要講的“參數(shù)化配置”思想的雛形。子拓?fù)涠xtopology Subtopology中列出要納入的實(shí)例instance manager、instance worker并通過connections ManagerWorker塊描述三者之間的連接關(guān)系startWorker、cancelWorker、workDone三個(gè)端口。定義完成后設(shè)計(jì)好的“管理/工作”組件對(duì)就可以像搭積木一樣被整體引入任何上層拓?fù)鋞opology ExamplesDeployment { import ManagerWorker.Subtopology [...] }導(dǎo)入后上層拓?fù)渫ㄟ^**限定名qualified name**引用子拓?fù)鋬?nèi)部的實(shí)例與端口topology ExamplesDeployment { import ManagerWorker.Subtopology instance otherComponent connections Other { otherComponent.cancelAll - ManagerWorker.worker.cancelWork } }從源碼結(jié)構(gòu)看這種“模塊內(nèi)實(shí)例 Subtopology拓?fù)? 上層import 限定名引用”的四步模式正是 F′ 官方所有核心子拓?fù)渥裱慕y(tǒng)一約定。子拓?fù)涞膮?shù)化配置機(jī)制配置總覽子拓?fù)湓谠O(shè)計(jì)中就預(yù)留了“可配置性”可以在子拓?fù)涠x層定義參數(shù)并在用戶實(shí)例化子拓?fù)鋾r(shí)覆蓋這些參數(shù)。這一能力依賴 F′ 構(gòu)建系統(tǒng)提供的register_fprime_configAPI見 cmake/API.cmake它允許子拓?fù)溟_發(fā)者提供默認(rèn)配置文件項(xiàng)目在構(gòu)建時(shí)可選地覆蓋這些默認(rèn)文件。register_fprime_config的底層實(shí)現(xiàn)fprime_add_config_build_target見 cmake/API.cmake會(huì)執(zhí)行一次“配置替換”把配置源文件拷貝進(jìn)構(gòu)建緩存用拷貝后的版本替代原始源文件參與編譯。因此只要覆蓋文件的模塊名與常量名保持不變子拓?fù)湓创a在編譯時(shí)就會(huì)自動(dòng)使用新值——這正是整個(gè)覆蓋機(jī)制能夠生效的原理。從實(shí)現(xiàn)看該函數(shù)還支持CONFIGURATION_OVERRIDES、BASE_CONFIG將配置鏈接進(jìn)全局接口目標(biāo)、CHOOSES_IMPLEMENTATIONS等指令并以FPRIME_CONFIG_MODULES全局屬性登記所有配置模塊。三類可配置要素關(guān)聯(lián)文檔以 Svc.CdhCore 子拓?fù)錇槔龤w納出三種典型的可配置要素1. 可配置參數(shù)Configurable parametersCdhCoreConfig/CdhCoreConfig.fpp中存放了隊(duì)列大小、BASE_ID等配置常量子拓?fù)鋵?shí)例定義直接引用它們例如 CdhCore.fpp 中的instance events: Svc.EventManager base id CdhCoreConfig.BASE_ID 0x001000 # notice the use of BASE_ID覆蓋BASE_ID或隊(duì)列大小會(huì)直接影響組件實(shí)例的 ID 布局與運(yùn)行時(shí)資源占用。2. 組件實(shí)例Component instances組件實(shí)例本身也可以是配置的一部分。CdhCore 使用的tlmSend實(shí)例并不定義在子拓?fù)渲魑募卸嵌x在CdhCoreConfig/CdhCoreTlmConfig.fpp中。查看該文件CdhCoreTlmConfig.fpp可見默認(rèn)實(shí)現(xiàn)是Svc.TlmChan文件內(nèi)還以注釋形式保留了切換到Svc.TlmPacketizer的備選實(shí)例定義。用戶只需在項(xiàng)目中覆蓋該配置文件即可替換整個(gè)遙測(cè)發(fā)送實(shí)例。3. 可配置 C 代碼Configurable C覆蓋機(jī)制同樣作用于 C 源文件與頭文件用于定制組件在拓?fù)渲械臉?gòu)造與裝配方式。典型例子是 Svc.ComFprime 子拓?fù)淦銫omFprimeConfig/CMakeLists.txt見 CMakeLists.txt通過SOURCES/HEADERS引入ComFprimeSubtopologyConfig.cpp/.hpp其中提供了內(nèi)存分配器ComFprime::Allocation::memAllocator而 ComFprime.fpp 中的configComponents階段把該分配器傳入comQueue.configure(...)從而讓項(xiàng)目可以選擇自己的內(nèi)存分配方式。四步配置覆蓋流程關(guān)聯(lián)文檔給出了在部署中覆蓋子拓?fù)渑渲玫耐暾襟E以下結(jié)合倉庫實(shí)際文件逐條展開。Step 1創(chuàng)建配置目錄在部署目錄下為自定義配置模塊建一個(gè)目錄mkdir MyDeployment/MyCdhCoreConfigStep 2創(chuàng)建配置覆蓋文件拷貝與原配置文件同名的文件到該目錄覆蓋依賴“同名 同模塊名”機(jī)制。例如覆蓋CdhCoreConfig.fpp# File: MyDeployment/MyCdhCoreConfig/CdhCoreConfig.fpp module CdhCoreConfig { constant BASE_ID 0x01000000 module QueueSizes { constant cmdDisp 10 constant events 10 constant tlmSend 10 } ... }對(duì)照倉庫默認(rèn)值CdhCoreConfig.fpp默認(rèn)BASE_ID 0x01000000隊(duì)列大小cmdDisp/events/tlmSend 10、$health 25棧大小統(tǒng)一64 * 1024字節(jié)優(yōu)先級(jí)cmdDisp 35、$health 24、events 23、tlmSend 22CPU 親和性默認(rèn)Os.TASK_DEFAULT不綁定核心。覆蓋時(shí)建議對(duì)照這些默認(rèn)值逐項(xiàng)調(diào)整避免遺漏。Step 3注冊(cè)配置模塊在配置模塊自己的CMakeLists.txt中調(diào)用register_fprime_config()第一個(gè)參數(shù)配置名必須唯一且不能是config# File: MyDeployment/MyCdhCoreConfig/CMakeLists.txt register_fprime_config( MyCdhCoreConfig CONFIGURATION_OVERRIDES ${CMAKE_CURRENT_LIST_DIR}/CdhCoreConfig.fpp EXCLUDE_FROM_ALL INTERFACE )CONFIGURATION_OVERRIDES聲明哪些文件屬于“配置覆蓋”構(gòu)建系統(tǒng)據(jù)此執(zhí)行源文件替換EXCLUDE_FROM_ALL該模塊不作為默認(rèn)構(gòu)建目標(biāo)的一部分只在被依賴時(shí)構(gòu)建INTERFACE聲明為純接口配置不產(chǎn)生編譯產(chǎn)物若同時(shí)提供SOURCES/AUTOCODER_INPUTS則會(huì)構(gòu)成靜態(tài)庫參見 cmake/API.cmake 中關(guān)于庫類型的自動(dòng)判定邏輯。作為對(duì)照官方子拓?fù)涞呐渲媚K如 CdhCore/CdhCoreConfig/CMakeLists.txt 同時(shí)列出了三個(gè)AUTOCODER_INPUTSCdhCoreConfig.fpp、CdhCoreFatalHandlerConfig.fpp、CdhCoreTlmConfig.fpp說明一個(gè)配置模塊可以包含多個(gè) FPP 配置源文件。Step 4加入部署依賴將配置模塊注冊(cè)為部署拓?fù)淠K的依賴# File: MyDeployment/Top/CMakeLists.txt register_fprime_module( AUTOCODER_INPUTS ${CMAKE_CURRENT_LIST_DIR}/instances.fpp ${CMAKE_CURRENT_LIST_DIR}/topology.fpp SOURCES ${CMAKE_CURRENT_LIST_DIR}/ExampleTopology.cpp DEPENDS MyCdhCoreConfig )關(guān)聯(lián)文檔指出ExampleCdhCoreConfigfprime-examples 倉庫中的參考實(shí)現(xiàn)正是通過這四步把 CdhCore 的遙測(cè)發(fā)送從TlmChan替換為TlmPacketizer。結(jié)合 CdhCoreTlmConfig.fpp 中預(yù)留的切換注釋可以印證這套流程的實(shí)際用途。F′ 核心子拓?fù)渌儆[F′ 在 Svc/Subtopologies 目錄下提供了一批可配置的官方子拓?fù)淦渲卸鄶?shù)會(huì)出現(xiàn)在fprime-util new --deployment生成的默認(rèn)部署中。目錄注冊(cè)見 Svc/Subtopologies/CMakeLists.txt其中DpCompression因依賴 zlib 而按需加入find_package(ZLIB)失敗時(shí)會(huì)跳過并打印提示。Svc.CdhCore核心命令與數(shù)據(jù)處理CdhCore 打包了幾乎所有 F′ 部署都需要的核心服務(wù)命令分發(fā)Svc.CommandDispatcher、事件管理Svc.EventManager、健康監(jiān)控Svc.Health、版本與配置上報(bào)Svc.Version、文本日志Svc.PassiveTextLogger、斷言轉(zhuǎn)致命錯(cuò)誤Svc.AssertFatalAdapter以及兩個(gè)可配置實(shí)例tlmSend遙測(cè)發(fā)送與fatalHandler致命錯(cuò)誤處理。其實(shí)例清單與配置鉤子詳見其設(shè)計(jì)文檔 Svc/Subtopologies/CdhCore/docs/sdd.md。從其拓?fù)涠xCdhCore.fpp可以看到 CdhCore 的完整結(jié)構(gòu)實(shí)例cmdDisp、events、$health、version、textLogger、fatalAdapter、tlmSend、fatalHandler內(nèi)部連接僅有一條故障保護(hù)鏈路events.FatalAnnounce - fatalHandler.FatalReceive暴露的拓?fù)涠丝趕eqCmdBuff/seqCmdStatus命令輸入/狀態(tài)回傳、eventsPktSend事件包輸出、tlmSendPktSend/tlmSendRun遙測(cè)包輸出與調(diào)度、cmdDispRun/healthRun/eventsRun各組件調(diào)度輸入。值得注意的細(xì)節(jié)$health與version的實(shí)例定義中內(nèi)嵌了phase Fpp.ToCpp.Phases.configComponents/startTasks等自動(dòng)代碼生成階段的 C 片段用于注入健康 ping 表與版本上報(bào)邏輯詳見 CdhCore.fpp。Svc.ComCcsdsCCSDS 通信棧使用 CCSDS 協(xié)議棧實(shí)現(xiàn)上下行通信內(nèi)部組件包括ComQueue、FrameAccumulator配CcsdsTcFrameDetector、TmFramer/SpacePacketFramer、Deframer、路由器等并通過ComStub對(duì)外暴露 ByteStream 驅(qū)動(dòng)接口。其配置模塊 ComCcsdsConfig.fpp 除常規(guī)的BASE_ID/隊(duì)列/棧/優(yōu)先級(jí)/CPU 親和性外還包含QueueDepths/QueuePriorities事件、遙測(cè)、文件三條隊(duì)列的深度與優(yōu)先級(jí)事件優(yōu)先級(jí)最高取值 0遙測(cè) 2文件 1Aggregator.enablePacketSpanning是否跨傳輸幀拆裝分組包默認(rèn)false需要 TmFramer 與地面解幀器配合BuffMgr幀累加器大小、通信緩沖大小/數(shù)量、緩沖管理器 ID 等。Svc.ComCcsdsSdls帶 SDLS 加密/解密的 CCSDS 棧在 CCSDS 通信?;A(chǔ)上疊加 SDLSSpace Data Link Security加密/解密層配置模塊見 Svc/Subtopologies/ComCcsdsSdls/ComCcsdsSdlsConfig。Svc.ComFprime輕量 F′ 協(xié)議通信棧使用 F′ 自有輕量協(xié)議組幀/解幀。其結(jié)構(gòu)值得細(xì)看ComFprime.fpp內(nèi)部定義了兩個(gè)端口枚舉Ports_ComPacketQueueEVENTS/TELEMETRY與Ports_ComBufferQueueFILE上層拓?fù)溆盟鼈兯饕齝omPacketQueueIn/bufferQueueIn數(shù)組端口FramingSubtopology子拓?fù)浞庋b了comQueue、frameAccumulator、commsBufferManager、deframer、framer、fprimeRouter并明確注釋導(dǎo)入方需要與實(shí)現(xiàn)了Svc.Com接口的組件建立 5 條連接framer 數(shù)據(jù)出、frameAccumulator 數(shù)據(jù)返回出、framer 數(shù)據(jù)返回入、framer 狀態(tài)入、frameAccumulator 數(shù)據(jù)入外層Subtopology再import FramingSubtopology并加入comStub形成完整的“F′ 協(xié)議 ComStub”組合對(duì)外暴露commandOut、fileUplinkOut、comPacketQueueIn、bufferQueueIn、drvReceiveIn、drvSendOut、comQueueRun等端口組件裝配大量使用 C phase 片段comQueue.configure()配置三級(jí)隊(duì)列事件/遙測(cè)/文件深度與優(yōu)先級(jí)來自 ComFprimeConfig.fpp 的QueueDepths/QueuePrioritiesframeAccumulator.configure()與commsBufferManager.setup()配置緩沖池BuffMgr模塊并在tearDownComponents階段cleanup()。Svc.DataProducts數(shù)據(jù)產(chǎn)品處理打包數(shù)據(jù)產(chǎn)品Data Product相關(guān)服務(wù)dpMgrDpManager服務(wù)客戶端的產(chǎn)品獲取/請(qǐng)求/發(fā)送、dpWriterDpWriter寫入文件系統(tǒng)、dpCatDpCatalog編目并按優(yōu)先級(jí)下行、dpBufferAccumulatorBufferAccumulator產(chǎn)品緩沖累加、dpBufferManagerBufferManager緩沖分配被動(dòng)組件。內(nèi)部接線包括dpMgr.bufferGetOut - dpBufferManager.bufferGetCallee、dpMgr.productSendOut - dpBufferAccumulator.bufferSendInFill、dpWriter.dpWrittenOut[0] - dpCat.addToCat等完整說明見 Svc/Subtopologies/DataProducts/docs/sdd.md。其配置模塊 DataProductsConfig.fpp 除實(shí)例屬性外還定義BufferAccumulator分配器 ID 301、最多緩存 10 個(gè)緩沖、BuffMgr產(chǎn)品緩沖存儲(chǔ)大小 10000、數(shù)量 10、管理器 ID 300與Paths產(chǎn)品目錄./DpCat、編目狀態(tài)文件./DpCat/DpState.dat。Svc.DpCompression數(shù)據(jù)產(chǎn)品無損壓縮對(duì)數(shù)據(jù)產(chǎn)品做無損壓縮處理依賴 zlibSvc/Subtopologies/CMakeLists.txt 中按可用性條件注冊(cè)通過dpWriterProcOut處理鉤子接入 DataProducts 管線。Svc.FileHandling文件處理打包文件上行FileUplink、文件下行FileDownlink、板載文件管理FileManager與基于文件系統(tǒng)的參數(shù)管理PrmDb。配置模塊 FileHandlingConfig.fpp 在實(shí)例屬性之外還定義Paths.prmDbFile參數(shù)數(shù)據(jù)庫存儲(chǔ)文件名默認(rèn)PrmDb.datPaths.sandboxDir文件訪問沙箱目錄默認(rèn)/即不限制DownlinkConfig下行冷卻時(shí)間1000 ms、下行周期1000 ms、文件隊(duì)列深度10。[!WARNING] 安全提示該子拓?fù)淠J(rèn)并非安全配置。默認(rèn)sandboxDir /意味著任何進(jìn)程可訪問的絕對(duì)路徑都可能被地面指令讀寫或加載底層Os::SandboxedFile在未配置時(shí)是 fail-closed 的而該子拓?fù)渲鲃?dòng)將其配置為開放。需要限制文件安全時(shí)必須在拓?fù)鋯?dòng)代碼中自動(dòng)生成的configComponents階段之后對(duì)fileUplink、fileManager、fileDownlink、prmDb分別調(diào)用configure(directory)/configureSandbox(directory)收緊目錄或覆蓋Paths::sandboxDir為單一受限目錄。相關(guān)細(xì)節(jié)與../路徑穿越拒絕行為見 Svc/Subtopologies/FileHandling/docs/sdd.md。實(shí)戰(zhàn)接線如何在部署拓?fù)渲薪M合多個(gè)子拓?fù)潢P(guān)聯(lián)文檔以 CdhCore 為例說明了子拓?fù)湓诟笸負(fù)渲械挠梅?。倉庫自帶的 Ref 參考部署TestDeploymentsProject/Ref/Top/topology.fpp給出了同時(shí)組合四個(gè)官方子拓?fù)涞耐暾独菍W(xué)習(xí)子拓?fù)浣泳€的第一手素材。1. 實(shí)例化子拓?fù)鋓nstance CdhCore.Subtopology instance ComCcsds.Subtopology instance FileHandling.Subtopology instance DataProducts.Subtopology #instance DpCompression.Subtopology2. 聲明模式連接pattern connections子拓?fù)鋬?nèi)部的組件通過“限定實(shí)例名”參與模式連接command connections instance CdhCore.cmdDisp event connections instance CdhCore.events telemetry connections instance CdhCore.tlmSend text event connections instance CdhCore.textLogger health connections instance CdhCore.$health param connections instance FileHandling.prmDb time connections instance posixTime3. 通過暴露端口接線調(diào)度Rate Groups把速率組輸出接到子拓?fù)浔┞兜恼{(diào)度端口例如CdhCore.Subtopology.tlmSendRun、CdhCore.Subtopology.healthRun、CdhCore.Subtopology.cmdDispRun、CdhCore.Subtopology.eventsRun、FileHandling.Subtopology.fileDownlinkRun、ComCcsds.Subtopology.comQueueRun、DataProducts.Subtopology.dpMgrSchedIn等跨子拓?fù)鋽?shù)據(jù)流ComCcsds ? CdhCoreCdhCore.Subtopology.eventsPktSend - ComCcsds.Subtopology.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.EVENTS] CdhCore.Subtopology.tlmSendPktSend - ComCcsds.Subtopology.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.TELEMETRY] ComCcsds.Subtopology.commandOut - CdhCore.Subtopology.seqCmdBuff CdhCore.Subtopology.seqCmdStatus - ComCcsds.Subtopology.cmdResponseIn跨子拓?fù)鋽?shù)據(jù)流ComCcsds ? FileHandling ? DataProducts文件下行包經(jīng)bufferQueueIn[FILE]進(jìn)入通信隊(duì)列文件上行從fileUplinkOut流入fileUplinkBufferSendIn數(shù)據(jù)產(chǎn)品編目后經(jīng)dpCatFileOut - FileHandling.Subtopology.fileDownlinkSendFile下行見 topology.fpp。4. 連接外部基礎(chǔ)設(shè)施ComDrivercomDriver.$recv - ComCcsds.Subtopology.drvReceiveIn ComCcsds.Subtopology.drvSendOut - comDriver.$send comDriver.ready - ComCcsds.Subtopology.drvConnected從以上接線可以看出子拓?fù)涞摹岸丝诩雌跫s”設(shè)計(jì)每個(gè)官方子拓?fù)浒研枰獠拷尤氲狞c(diǎn)調(diào)度、數(shù)據(jù)流、驅(qū)動(dòng)、緩沖顯式暴露為命名端口上層拓?fù)渲恍璋疵诌B接內(nèi)部組件完全隔離這正是其模塊化價(jià)值所在。小結(jié)子拓?fù)涫?F′ 系統(tǒng)建模中實(shí)現(xiàn)“分而治之”的關(guān)鍵設(shè)計(jì)模式它讓組件實(shí)例與連接以可復(fù)用單元的形式存在通過import與限定名引用集成進(jìn)部署拓?fù)渑浜蟫egister_fprime_config構(gòu)建期配置覆蓋機(jī)制開發(fā)者既可以使用官方子拓?fù)涞哪J(rèn)行為也可以在保持內(nèi)部接線不變的前提下替換參數(shù)、組件實(shí)例乃至 C 裝配邏輯。F′ 倉庫在 Svc/Subtopologies 下提供了覆蓋 CDH、CCSDS/SDLS 通信、F′ 協(xié)議通信、數(shù)據(jù)產(chǎn)品、壓縮、文件處理等場(chǎng)景的官方子拓?fù)銻ef 參考部署 則是將它們組合使用的完整范例可直接作為新部署的起點(diǎn)?!久赓M(fèi)下載鏈接】fprimeF′ - A flight software and embedded systems framework項(xiàng)目地址: https://gitcode.com/GitHub_Trending/fpr/fprime創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考