ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

利用SKILL脚本实现PCB元件封装一键导出与引脚自动统计

利用SKILL脚本实现PCB元件封装一键导出与引脚自动统计

1. 这篇文章真正要解决的问题

如果你是一名使用Cadence Allegro进行PCB设计的工程师,那么下面这个场景你一定不陌生:项目评审会上,硬件负责人指着BOM清单问,这颗关键IC的封装和引脚定义,能否单独导出一份清晰的图纸给结构工程师和测试工程师参考?或者,在布局规划时,你需要快速统计板上某个复杂BGA芯片的电源、地、信号引脚各有多少,以便进行电源平面分割和Fanout规划。面对这些需求,你是选择在庞大的PCB设计文件中手动截图、标注、数引脚,还是希望有一个一键生成、准确无误的自动化方法?

手动操作不仅效率低下,而且极易出错。截图可能遗漏背面的引脚,数引脚时眼花缭乱数错一个,都可能导致后续的装配或测试问题。这正是Cadence SKILL脚本语言大显身手的地方。本文要解决的,就是如何利用SKILL脚本,实现两个非常实用但官方工具并未直接提供的功能:一键导出指定元件的详细封装图纸,以及自动统计指定元件的引脚数量及类型

很多人以为SKILL语言高深莫测,是Tool开发工程师的专属。实际上,对于PCB设计工程师而言,掌握一些基础的SKILL脚本,就像程序员掌握了Excel宏或Python脚本一样,能直接将重复、繁琐的劳动自动化,极大提升设计效率和准确性。本文将从一个实际痛点出发,手把手带你编写两个实用的SKILL脚本,让你不仅“会用”Allegro,更能“驾驭”它。

2. 基础概念与核心原理:SKILL是什么,为什么是它?

在深入代码之前,有必要厘清几个核心概念,这能帮助你理解为什么SKILL是解决此类问题的最佳选择,而不是其他方法。

SKILL语言:它是Cadence公司为其EDA工具套件(包括Allegro、Virtuoso等)开发的一种基于Lisp的交互式脚本语言。Lisp语言以其强大的符号处理和表处理能力著称,非常适合处理EDA工具中复杂的层次化数据(如网表、封装、约束)。SKILL脚本可以直接在Allegro的CIW(Command Interpreter Window)窗口中运行,与设计数据库进行深度交互,访问和操作任何设计对象(如元件、引脚、走线、形状)。

与手动操作、Report功能的对比

  • 手动截图/标注:完全依赖人工,不可靠、不标准、无法复用。
  • Allegro自带报告(Tools -> Reports):功能强大,但输出的是文本列表,不直观。例如,你可以生成元件列表报告,但无法直接生成一个带有引脚编号和网络的独立图纸。报告功能是“数据分析”导向,而非“图纸生成”导向。
  • SKILL脚本:可以精准定位到设计数据库中的任何一个对象(比如名为“U1”的元件),提取其所有属性(位置、角度、封装名),再进一步遍历其所有引脚,获取引脚的编号、坐标、网络名,甚至引脚类型(通过焊盘名或属性判断)。最后,脚本可以控制Allegro生成一个新的设计文件(.dra),只包含这个元件,并添加标注,或者直接在CIW窗口输出统计表格。它是程序化、可定制、可复用的解决方案。

核心原理拆解: 我们的两个脚本都遵循一个相似的逻辑流程:

  1. 目标选择:通过交互式命令或参数,让用户指定一个元件(例如,通过鼠标点击或输入元件位号)。
  2. 数据提取:利用SKILL内置函数(如axlDBGetDesignaxlSelectByNameaxlDBGetPad)深入到Allegro的设计数据库中,获取该元件的所有几何和电气信息。
  3. 逻辑处理
    • 对于导出脚本:创建一个新的设计窗口,将原元件的几何图形(Symbol)、引脚(Pins)、位号(Ref Des)复制过去,并可选地添加引脚编号标注和网络名标注。
    • 对于统计脚本:遍历元件的每一个引脚,根据引脚连接的焊盘名(通常包含“PIN”、“BALL”等信息)或用户自定义属性,将其归类到“电源”、“地”、“信号”等类别,并进行计数。
  4. 结果输出:将处理结果以可视化的图纸(.dra文件)或清晰的文本报告形式呈现给用户。

理解了这套“选择-提取-处理-输出”的范式,你就能举一反三,开发出更多自动化脚本来解决日常工作中的痛点。

3. 环境准备与前置条件

在开始编写和运行SKILL脚本之前,请确保你的工作环境已就绪。

  1. 软件与版本

    • 必需:Cadence Allegro PCB Designer。本文演示基于 Allegro 17.4 版本,但核心SKILL函数在16.6及以上版本中基本通用。请注意,不同小版本间个别函数名或行为可能有细微差异,但总体思路一致。
    • 推荐:具备文本编辑器,如Notepad++、VS Code或UltraEdit,用于编写和修改脚本文件(.il文件)。
  2. 设计文件

    • 打开一个完整的PCB设计文件(.brd)。脚本需要在活跃的Allegro设计会话中运行。
  3. SKILL脚本加载方式: SKILL脚本文件通常以.il为后缀。要让Allegro识别并运行你的脚本,有几种常用方法:

    • 方法一:直接加载:在Allegro的CIW窗口中,输入命令load("你的脚本路径/script.il")。每次重启Allegro都需要重新加载。
    • 方法二:自动加载:将脚本文件放入Allegro的本地或系统SKILL路径下,并在allegro.ilinit文件中添加load语句。这样每次启动Allegro都会自动加载该脚本,并可以将其绑定到菜单或快捷键上(进阶用法)。
    • 本文演示:为了清晰和便于调试,我们将采用第一种方法,在CIW中直接加载写好的脚本文件。
  4. 权限与备份

    • 脚本会读取和创建设计数据。在运行任何脚本,尤其是涉及创建新文件或修改数据的脚本前,请务必保存并备份当前的设计文件(.brd)。
    • 确保你对脚本文件所在的目录有读写权限。

4. 核心流程拆解:从想法到实现

让我们将两个功能拆解成具体的、可执行的步骤。

4.1 功能一:导出单颗零件图纸

这个脚本的目标是生成一个只包含目标元件的新.dra封装文件。

  1. 用户输入:脚本提示用户在PCB上点击选择需要导出的元件。
  2. 获取元件对象:通过选择操作,获取该元件在数据库中的唯一标识(DBID)。
  3. 提取元件信息:从该DBID中提取关键信息,如元件位号(RefDes)、所在的封装名(Symbol)、位置和旋转角度。
  4. 创建新设计:在内存中创建一个新的设计窗口(axlDMOpenDesign)。
  5. 复制几何图形:将原元件的封装图形(Symbol Geometry)复制到新设计中。
  6. 复制引脚与属性:将元件的所有引脚(Pins)和位号丝印(Ref Des Text)复制到新设计中。
  7. (可选)添加标注:遍历每个引脚,在其旁边添加文本标注,显示引脚编号和/或网络名称。
  8. 保存文件:将新设计保存为.dra文件(axlDMSSave),通常以元件位号或封装名命名。
  9. 清理与提示:关闭临时设计窗口,并在CIW中提示用户文件保存路径。

4.2 功能二:统计引脚数量

这个脚本的目标是在CIW窗口或一个文本文件中输出目标元件的引脚分类统计表。

  1. 用户输入:同样,提示用户选择目标元件。
  2. 获取元件对象与引脚列表:获取元件DBID,并进一步获取该元件下的所有引脚对象列表。
  3. 遍历与分类:遍历每一个引脚对象。对于每个引脚:
    • 获取其焊盘名(Padstack Name)。
    • 根据焊盘名中的关键词(如包含“PWR”、“VDD”、“GND”、“AGND”、“PIN”、“BALL”等)或引脚的NET属性,将其初步归类。
    • (更精确的方法)检查引脚是否具有PINUSE属性(如POWERGROUNDSIGNAL),这是最权威的分类依据。
  4. 计数与汇总:为“电源”、“地”、“信号”、“未定义”等类别设置计数器,在遍历过程中进行累加。
  5. 格式化输出:将统计结果以清晰易读的表格形式在CIW中打印出来,包括总引脚数、各类别引脚数及占比。
  6. (可选)输出到文件:将统计结果同时写入一个.txt.csv文件,便于存档和分享。

5. 完整示例与代码实现

下面我们将提供两个完整的、可运行的SKILL脚本示例。请将以下代码分别保存为两个.il文件,例如export_component.ilcount_pins.il

5.1 脚本一:导出单颗零件图纸 (export_component.il)

;----------------------------------------------------------------------------- ; 脚本名称: export_component.il ; 功能: 导出当前设计中选中的单个元件到一个新的 .dra 文件 ; 使用方法: 在CIW中输入 load("export_component.il"), 然后输入 EXPORT_COMP ;----------------------------------------------------------------------------- (defun EXPORT_COMP () (let (comp compDBID symName refDes newDesignName) ; 步骤1: 提示用户选择元件 (axlClearSelSet) (axlSetFindFilter ?enabled `("NOALL" "COMPONENTS") ?onButtons `("COMPONENTS")) (printf "请在PCB上点击选择要导出的元件...\n") (setq comp (axlSelect 'axlSingleSelectPoint)) (when comp ; 步骤2 & 3: 获取元件信息 (setq compDBID (car comp)) (setq symName (axlDBGetProperty compDBID "SYM_NAME")) (setq refDes (axlDBGetProperty compDBID "REFDES")) (setq newDesignName (strcat refDes "_export.dra")) (printf "选中元件: %s, 封装: %s\n" refDes symName) (printf "正在创建导出文件: %s\n" newDesignName) ; 步骤4: 创建新的设计 (axlDMOpenDesign newDesignName nil "temp_export_lib" t) ; 步骤5 & 6: 复制元件图形和引脚 (这里简化处理,实际需复制symbol) ; 注意: 直接复制一个已放置的元件实例到新设计比较复杂。 ; 更稳健的方法是:打开元件原始封装库(.dra),另存为新文件,然后添加标注。 ; 以下代码展示核心思路,可能需要根据实际情况调整。 (axlDBCopyDesign (axlDBGetDesign) (axlDMGetCurrentDesign) `(,compDBID)) ; 步骤7: 添加引脚编号标注 (示例:在引脚旁添加文字) ; 这里需要遍历元件的引脚,并获取其位置来放置文本。 ; (pinList = axlDBGetPins compDBID) ... 遍历pinList添加文本 ; 由于篇幅,详细标注代码略,可根据需要扩展。 ; 步骤8: 保存文件 (axlDMSSave (axlDMGetCurrentDesign)) (printf "文件已保存至: %s\n" (axlDMGetCurrentDesignPath)) ; 步骤9: 清理 (不关闭窗口,让用户查看) ; (axlDMCloseDesign (axlDMGetCurrentDesign)) (printf "导出完成!\n") ) (printf "未选择有效元件。\n") ) ) ; 将函数注册为Allegro命令 (axlCmdRegister "export_comp" 'EXPORT_COMP)

代码关键逻辑解释

  • axlSetFindFilteraxlSelect:用于设置选择过滤器并让用户交互式选择对象。
  • axlDBGetProperty:从数据库对象中获取属性,如位号(REFDES)和封装名(SYM_NAME)。
  • axlDMOpenDesign:创建或打开一个设计。
  • axlDBGetDesignaxlDMGetCurrentDesign:获取源设计和目标设计的数据库对象。
  • axlDBCopyDesign:尝试复制设计数据。请注意:直接复制一个已实例化的元件到空白设计可能不会完全成功,因为缺少库链接。更可靠的方法是直接打开元件的原始封装文件进行操作。此示例提供了核心框架。

5.2 脚本二:统计引脚数量 (count_pins.il)

;----------------------------------------------------------------------------- ; 脚本名称: count_pins.il ; 功能: 统计选中元件的引脚数量,并按类型(电源、地、信号)分类 ; 使用方法: 在CIW中输入 load("count_pins.il"), 然后输入 COUNT_PINS ;----------------------------------------------------------------------------- (defun COUNT_PINS () (let (comp compDBID pinList totalPins powerPins gndPins signalPins otherPins pin) ; 步骤1: 提示用户选择元件 (axlClearSelSet) (axlSetFindFilter ?enabled `("NOALL" "COMPONENTS") ?onButtons `("COMPONENTS")) (printf "请在PCB上点击选择要统计引脚的元件...\n") (setq comp (axlSelect 'axlSingleSelectPoint)) (when comp ; 步骤2: 获取元件引脚列表 (setq compDBID (car comp)) (setq pinList (axlDBGetPins compDBID)) (setq totalPins (length pinList)) (printf "\n=== 引脚统计报告 ===\n") (printf "元件位号: %s\n" (axlDBGetProperty compDBID "REFDES")) (printf "总引脚数: %d\n\n" totalPins) ; 初始化计数器 (setq powerPins 0) (setq gndPins 0) (setq signalPins 0) (setq otherPins 0) ; 步骤3: 遍历并分类引脚 (foreach pin pinList (let (padstackName netName pinUse) (setq padstackName (axlDBGetProperty pin 'PADSTACK_NAME)) (setq netName (axlDBGetProperty pin 'NET)) (setq pinUse (axlDBGetProperty pin 'PINUSE)) ; 获取PINUSE属性,最准确 ; 分类逻辑:优先使用PINUSE属性,其次根据网络名或焊盘名关键词判断 (cond ((equal pinUse "POWER") (setq powerPins (1+ powerPins))) ((equal pinUse "GROUND") (setq gndPins (1+ gndPins))) ((equal pinUse "SIGNAL") (setq signalPins (1+ signalPins))) (t ; 如果PINUSE未定义,则根据网络名或焊盘名推测 (cond ((netName && (rexMatchp “.*[Vv][DdCc][DdAa].*|.*PWR.*|.*PVDD.*” netName)) (setq powerPins (1+ powerPins))) ((netName && (rexMatchp “.*[Gg][Nn][Dd].*|.*GND.*|.*AGND.*|.*DGND.*” netName)) (setq gndPins (1+ gndPins))) ((padstackName && (rexMatchp “.*PIN.*|.*BALL.*” padstackName)) ; 假设信号引脚焊盘名含PIN/BALL (setq signalPins (1+ signalPins))) (t (setq otherPins (1+ otherPins))) ) ) ) ) ) ; 步骤5: 格式化输出 (printf "分类统计结果:\n") (printf " 电源引脚 (POWER): %4d (%.1f%%)\n" powerPins (* (/ (float powerPins) totalPins) 100.0)) (printf " 接地引脚 (GROUND): %4d (%.1f%%)\n" gndPins (* (/ (float gndPins) totalPins) 100.0)) (printf " 信号引脚 (SIGNAL): %4d (%.1f%%)\n" signalPins (* (/ (float signalPins) totalPins) 100.0)) (printf " 未分类引脚: %4d (%.1f%%)\n" otherPins (* (/ (float otherPins) totalPins) 100.0)) (printf "==========================\n") ; (可选) 步骤6: 输出到文件 ; (outfile outport “pin_report.txt” “w”) ; (fprintf outport “Pin Report for %s\n” refDes) ... ; (close outport) ) (printf "未选择有效元件。\n") ) ) ; 将函数注册为Allegro命令 (axlCmdRegister "count_pins" 'COUNT_PINS)

代码关键逻辑解释

  • axlDBGetPins:获取元件下的所有引脚对象列表。
  • axlDBGetProperty:用于获取引脚的PADSTACK_NAME(焊盘名)、NET(网络名)和PINUSE(引脚用途)属性。
  • PINUSE属性:这是Allegro中用于定义引脚电气类型的标准属性(POWER, GROUND, SIGNAL, UNSPEC等)。在创建封装时若已正确定义,这是最可靠的分类依据。
  • rexMatchp:SKILL中的正则表达式匹配函数,用于判断字符串是否包含特定模式(如“VDD”、“GND”)。
  • cond:Lisp中的条件判断语句,类似于if-else if-else链。
  • 分类策略采用了优先级:先检查权威的PINUSE属性;若未定义,则根据网络名关键词推测;若网络名也无特征,则根据焊盘名是否为常规信号引脚焊盘(含“PIN”)来归类;最后归为“未分类”。

6. 运行结果与效果验证

6.1 运行脚本

  1. 将上述两个.il文件保存到本地目录,例如C:\skill_scripts\
  2. 打开你的PCB设计文件(.brd)。
  3. 在Allegro的CIW(Command Interpreter Window)中,输入加载命令:
    load("C:/skill_scripts/export_component.il") load("C:/skill_scripts/count_pins.il")
    注意:路径中的反斜杠\在SKILL中可能需要转义或使用正斜杠/
  4. 加载成功后,CIW会提示t表示成功。
  5. 运行命令:
    • 输入export_comp并回车,然后根据提示在PCB上点击一个元件(如U1)。
    • 输入count_pins并回车,然后根据提示在PCB上点击一个元件(如U1)。

6.2 预期输出与验证

  • 对于count_pins脚本: 成功运行后,你将在CIW窗口中看到类似如下的输出:

    === 引脚统计报告 === 元件位号: U1 总引脚数: 256 分类统计结果: 电源引脚 (POWER): 12 (4.7%) 接地引脚 (GROUND): 20 (7.8%) 信号引脚 (SIGNAL): 220 (85.9%) 未分类引脚: 4 (1.6%) ==========================

    如何验证准确性?

    1. 交叉核对:在Allegro中,手动高亮显示该元件的所有电源网络(如VCC3V3)和地网络(GND),查看高亮的引脚数量是否与脚本统计的“电源引脚”和“接地引脚”数量大致相符。
    2. 检查未分类引脚:脚本输出的“未分类引脚”值得关注。你可以修改脚本,让它在遇到未分类引脚时,打印出该引脚的焊盘名和网络名,帮助你判断是分类规则不完善,还是封装定义本身有问题(如PINUSE属性缺失)。
  • 对于export_comp脚本: 成功运行后,脚本会提示文件保存路径,例如U1_export.dra。你可以在Allegro中通过File -> Open打开这个.dra文件进行验证。验证要点

    1. 打开的文件是否只包含目标元件U1的图形?
    2. 元件的引脚、丝印层(Ref Des, Value)是否完整?
    3. (如果实现了标注功能)引脚编号和网络名标注是否清晰、位置是否合适?

如果运行失败

  • 第一步:仔细查看CIW窗口中的错误信息。SKILL的错误提示通常比较直接,会指出是哪一行代码出了问题。
  • 常见原因:文件路径错误、函数名拼写错误、在当前设计环境下某些函数不适用(例如在非布局模式下调用布局函数)。
  • 调试建议:可以在脚本中关键步骤后添加printf语句,打印出变量的值,帮助定位问题。

7. 常见问题与排查思路

问题现象可能原因排查方式解决方案
运行load命令后提示nil或错误1. 脚本文件路径错误。
2. 脚本文件编码格式不对(如UTF-8 BOM)。
3. 脚本中存在语法错误。
1. 检查路径,尝试使用绝对路径。
2. 用文本编辑器将文件另存为ANSI或UTF-8无BOM格式。
3. 查看CIW中具体的错误行号,检查该行及附近的括号、引号、函数名。
修正路径或文件编码。逐行检查语法,确保括号配对。
选择元件时脚本无反应或选不中1. 选择过滤器(axlSetFindFilter)设置不正确。
2. 设计处于非编辑模式(如仅查看模式)。
1. 检查axlSetFindFilter参数,确保COMPONENTS?onButtons列表中。
2. 确认Allegro处于可编辑状态(如Layout模式)。
调整选择过滤器参数。切换到正确的设计模式。
count_pins统计结果全部为“未分类”1. 元件的引脚缺少PINUSE属性。
2. 网络名/焊盘名关键词匹配规则不适用于当前设计。
1. 在Allegro中查看一个引脚的属性,确认是否有PINUSE
2. 打印几个引脚的NETPADSTACK_NAME属性,观察其命名规律。
1. 在封装库中为引脚添加PINUSE属性。
2. 根据实际命名规则,修改脚本中的正则表达式关键词。
export_comp导出的.dra文件为空或只有部分内容1.axlDBCopyDesign函数可能无法直接复制实例化元件到空白设计。
2. 复制范围设置不正确。
1. 查看生成的.dra文件,确认缺失什么内容。
2. 尝试使用axlDMCreateOpenShape等函数直接绘制,或改用打开原始封装库的方法。
采用更稳健的方法:先通过axlDBGetProperty获取元件的SYM_NAME,然后去库路径下找到对应的.dra文件进行复制和修改。
脚本在别人的电脑或新版本上不工作1. SKILL函数在不同版本间有变更或废弃。
2. 环境变量或库路径不同。
1. 查阅对应版本Allegro的SKILL API文档。
2. 检查是否有函数被标记为obsolete(过时)。
1. 使用版本兼容性更高的通用函数。
2. 在脚本开头添加版本判断逻辑,或提供替代方案。

8. 最佳实践与工程建议

将SKILL脚本从“能用”提升到“好用”和“耐用”,需要遵循一些工程化实践。

  1. 脚本健壮性

    • 错误处理:使用catchthrowerror处理来捕获异常,避免脚本意外崩溃导致Allegro不稳定。例如,在文件操作、选择操作外围添加错误判断。
    • 输入验证:在函数开始处,验证输入参数的有效性。例如,检查用户是否真的选择了一个元件 (when comp ...)。
    • 资源释放:如果脚本打开了临时设计或文件,确保在函数结束或出错时正确关闭它们。
  2. 代码可维护性

    • 添加注释:像示例中那样,在文件头、函数定义和关键逻辑处添加清晰注释,说明用途、参数和返回值。
    • 模块化函数:将复杂功能拆分成小函数。例如,将“分类引脚”的逻辑单独写成一个函数classifyPin(pin),提高代码可读性和复用性。
    • 定义常量:将魔法数字或字符串(如分类关键词“POWER”、“GND”)定义为文件开头的常量,便于统一修改。
  3. 用户交互友好性

    • 进度提示:对于遍历大量引脚的操作,可以使用axlUIWPrint或定期printf来显示进度,让用户知道脚本正在运行。
    • 结果可视化:除了在CIW输出,可以考虑用axlMsgPut在图形界面弹出提示框,或者将统计结果生成一个简单的文本文件保存在固定位置。
    • 参数化:允许用户通过对话框输入参数。例如,让用户选择导出图纸的层、标注的字体大小,或者指定统计报告的输出路径。可以使用axlForm函数创建简单的GUI。
  4. 集成到Allegro环境

    • 菜单集成:在allegro.ilinit文件中,使用axlMenuInsert等函数将你的脚本命令添加到Allegro的菜单栏中,方便点击使用。
    • 快捷键绑定:为常用脚本分配快捷键,进一步提升效率。
    • 自动加载:将调试好的脚本放入PCENV目录或通过allegro.ilinit自动加载,使其成为你的个人定制化工具集的一部分。
  5. 版本管理与分享

    • 使用Git等版本控制工具管理你的SKILL脚本,记录修改历史。
    • 在团队内部分享脚本时,务必附带一个简明的README.txt,说明功能、依赖、使用方法及已知限制。

9. 总结与后续学习方向

通过本文,我们不仅完成了“导出单颗零件”和“统计引脚数量”两个具体脚本的编写,更重要的是,我们建立了一套利用SKILL解决PCB设计自动化问题的通用思路:识别重复痛点 -> 分析数据对象 -> 使用SKILL API访问 -> 处理逻辑 -> 输出结果

这两个脚本是起点,而不是终点。基于这个框架,你可以尝试更多自动化任务:

  • 批量操作:修改脚本,使其能批量处理多个选中的元件,甚至整个BOM中的元件。
  • 增强导出功能:在导出的图纸中,自动添加引脚列表表格、元件尺寸标注、或特定的层叠信息。
  • 更智能的统计:结合约束管理器(Constraint Manager)的数据,统计特定电压域的引脚数,或高速信号(如差分对、DDR)的引脚数。
  • 设计检查:编写脚本自动检查常见设计问题,如未连接的电源引脚、间距违规的元件、缺失的阻焊桥等。

要深入学习SKILL,建议:

  1. 查阅官方文档:Cadence安装目录下的doc文件夹里通常有SKILL语言参考和API文档(如skill.pdfskillref.pdf)。这是最权威的资料。
  2. 利用内置帮助:在Allegro CIW中,输入helphelp ‘function-name可以查看函数说明。
  3. 分析现有脚本:Cadence自带了许多SKILL脚本(通常以.il结尾),在安装目录中搜索并阅读它们,是学习高级用法的绝佳途径。
  4. 实践与调试:从解决身边的小问题开始,多写多试。利用CIW的交互模式,可以逐行执行代码片段,观察结果,这是快速掌握SKILL的秘诀。

将SKILL脚本融入你的日常工作流,就像为Allegro装上了一副“机械外骨骼”,能让你从繁琐的重复操作中解放出来,专注于更有创造性的设计工作。建议你将本文的示例脚本保存、修改并应用到实际项目中,体会自动化带来的效率提升。

返回列表