ARTICLE DETAIL

资讯详情

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

AFT二次开发教程(10):Start Batch Run 工程化——批处理清单、后台运行与容错

AFT二次开发教程(10):Start Batch Run 工程化——批处理清单、后台运行与容错 AFT二次开发教程10Start Batch Run 工程化——批处理清单、后台运行与容错版本与事实声明产品与版本AFT Fathom 15 / AFT Impulse 12当前本篇流程引用官方Fathom 13帮助页Running Models in Batch ModeImpulse 侧同构当前版以官方文档为准。语言/环境Python 3.x标准库subprocess/os/globAFT 桌面端。本文目标读完能为大规模批跑写出清单生成 后台运行 超时熔断 残留清理的可复用编排脚本。铁律 3 贯彻本文不写死 AFT 可执行文件名一律从第 02 篇的环境清单取路径。所有模型名与数值为示例性。一句话结论File Start Batch Run的Batch Run Type有两类Scenarios in Current Model/Models from Different Files后者可用Add Model Files逐个添加或从Batch File列出要运行的模型的文本文件载入还有Save List to File、Run Batch in Background、Export Only (Do Not Run)等选项批跑顺序打开每个场景/模型、只运行 Analysis Setup 完整定义者、每个跑完自动保存输出Cancel后已跑完的输出仍保留——而工程化的关键是给它套上超时 释放 残留清理铁律 8。〇、本篇要解决的认知问题Q1Batch Run Type两类分别什么时候用选错了会丢什么能力Q2Batch File 是什么它和Save List to File/Load Existing Batch File怎么配合Q3Run Batch in Background与Cancel的语义是什么已跑输出保留有什么工程价值Q4Export Only (Do Not Run)适合什么场景它有什么明确限制Q5为什么批跑一定要套超时 释放 残留清理三步怎么写一、机制解析10.1 官方八步流程原文顺序官方Running Models in Batch Mode给的流程是八步我按中文重述并标注工程要点步官方动作工程要点1File 菜单点Start Batch Run打开 Batch Run 对话框2选Batch Run Type当前模型无场景时只有Models from Different Files可用3若为Scenarios in Current Model点Add Scenarios选场景只勾叶场景第 08 篇4若为Models from Different FilesAdd Model Files逐个加或Load Existing Batch File从清单载入Remove Selections移除Save List to File存清单清单可版本管理5可选Save Using Excel Export Manager独立表 / 独立工作簿仅当前模型场景可选须先配好导出项6Output Options输出存文件、和/或打印、和/或Adobe PDFPDF 交付的官方出口7Run Batch in Background后台跑不弹 Solution Progress8Start Run开始批跑行为官方原文要点按顺序打开每个场景/模型文件自动运行那些 Analysis Setup 已完整定义的场景/模型结果送往指定目的地批处理期间产生的错误信息在最后一个场景/模型结束时统一显示每个场景/模型运行完输出文件自动保存再进下一个。两条工程含义顺序执行官方批跑是顺序的不是并发。想跑得更快不能靠多开 grep只能靠缩短单场景时长简化网络、减少输出表或多机。只跑定义完整的场景未完整定义者被跳过。所以批跑前必须有一步场景完整性自检——否则你会得到一份看起来跑完了、其实跳了一半的结果。10.2 Batch File把要跑哪些模型变成资产官方描述Batch File 是一个文本文件列出所有你想运行的模型配套三个动作——Add Model Files逐个加、Load Existing Batch File从清单载入、Save List to File把当前清单存盘以及Remove Selections移除选中。诚实边界官方未公开Batch File 的逐字节格式我们不知道它有表头、有编码声明还是纯路径列表。所以本篇给的方法论是——先手工做一次在 GUI 里Add Model Files加几个模型 →Save List to File存一份→ 打开这份文件看清它的真实格式→再让脚本按这个格式读写。这就是铁律 3先探测后编码在清单文件上的应用。代码 10-1 会把这个格式基准作为输入参数而不是硬编码猜测。10.3 后台运行与取消Run Batch in Background让所有选中场景/模型在不显示 Solution Progress 窗口的情况下运行官方说 Fathom 会在后台运行、不打断你在其他程序里的工作。Cancel整个批跑可随时取消在 Solution Progress 窗口点 Cancel官方明确——已经跑完的场景输出仍然保留。已跑输出保留的工程价值极高它意味着批跑是部分成功可接受的。你半夜跑 200 个场景第 137 个崩了前 136 个的结果已经在磁盘上。所以你的编排脚本应当开跑前记录本轮目标清单结束含异常退出后盘点实际产出的输出只对缺输出的场景补跑用第 09 篇的Apply门控 第 10 篇的清单。这就是断点续跑在 AFT 里最朴素的实现方式。10.4 Export Only (Do Not Run)Batch Run 内有Export Only (Do Not Run)只导出、不重跑复用已有输出。明确限制官方原文如果某个场景没有输出Export Only不会为它导出任何数据因为不运行就不产生输出。适用场景你改了 Excel Export Manager 的导出映射比如新加一列 KPI不需要重算水力只想把已有输出按新映射重导一遍——这时 Export Only 能省下全部计算时间。这是本系列最省时间的一个技巧。10.5 铁律 8批跑必须套的三步回到第 02 篇的席位机制崩溃/强杀 → 席位不归还 → 最长 2 小时 checkout interval → 可能 orphaned。所以任何驱动 AFT 应用的编排都必须三步① 超时熔断 ② try/finally 释放 ③ 残留进程清理 kill after T graceful close kill leftovers by name超时熔断给整个批跑设一个上限时长按场景数 × 单场景经验耗时 × 安全系数超了先尝试正常关闭再强杀。try/finally 释放无论正常结束还是异常都在finally里尝试正常关闭应用正常关闭才归还席位。残留进程清理正常关闭失败时按从环境清单探测到的可执行文件名清理残留进程。注意顺序先礼后兵——先正常关闭能归还席位再强杀会进入 2 小时兜底。很多团队直接强杀结果天天锁席位根因就是跳过了第 ② 步。二、完整代码与逐行剖析代码 10-1make_batch_file.py清单读写格式以 GUI 产出为准# -*- coding: utf-8 -*- make_batch_file.py —— 生成/读取 AFT Batch File列出要运行的模型的文本文件 诚实声明官方未公开 Batch File 逐字节格式本模块的格式以 GUI 的 Save List to File 产物为基准铁律3先探测后编码。 运行 python make_batch_file.py --selftest python make_batch_file.py --models a.fth,b.fth --out batch.txt importargparseimportosimportsysdefwrite_batch_file(models:list,path:str,encodingutf-8-sig):一行一个模型路径写出后可先在 GUI 里 Load 验证一次再批量使用。withopen(path,w,encodingencoding,newline\n)asf:forminmodels:f.write(os.path.abspath(m)\n)returnpathdefread_batch_file(path:str,encodingutf-8-sig)-list:读回清单跳过空行与 # 注释行若你探测到 GUI 产物含注释则相应调整。out[]withopen(path,encodingencoding)asf:forlineinf:sline.strip()ifsandnots.startswith(#):out.append(s)returnoutdefselftest():models[demo_a.fth,demo_b.fth,demo_c.fth]write_batch_file(models,batch_selftest.txt)backread_batch_file(batch_selftest.txt)assertlen(back)3,backassertall(os.path.isabs(p)forpinback),back# 一律绝对路径print(SELFTEST OK写 3 行、读回 3 行、全部为绝对路径。)print(提醒首次使用前请用 GUI 的 Save List to File 产出一份样本与本格式比对。)defmain():apargparse.ArgumentParser()ap.add_argument(--models,help逗号分隔的模型路径)ap.add_argument(--out,defaultbatch.txt)ap.add_argument(--read,help读回并打印一个清单)ap.add_argument(--selftest,actionstore_true)aap.parse_args()ifa.selftest:selftest()return0ifa.read:print(\n.join(read_batch_file(a.read)))return0ifnota.models:ap.error(给出 --models 或 --read 或 --selftest)write_batch_file([m.strip()formina.models.split(,)],a.out)print(f已写出清单{a.out})return0if__name____main__:sys.exit(main())逐行剖析用os.path.abspath()强制绝对路径批跑时应用的工作目录可能不是你启动脚本的目录相对路径极易失效。清单里永远只用绝对路径。encodingutf-8-sig与 GUI 产物对齐Office 系工具常见带 BOM提高被 GUI 正确读取的概率。同时给出若探测到 GUI 产物含注释则调整的口子。文件头注释里那句首次使用前请用 GUI 的 Save List to File 产出一份样本比对不是客套——它就是铁律 3 的操作指令。本文不编格式只给探测—对齐的方法。selftest()断言写 3 读 3、全绝对路径把契约量化。代码 10-2batch_guardian.py铁律 8超时 释放 残留清理# -*- coding: utf-8 -*- batch_guardian.py —— 批跑看门狗超时熔断 try/finally 正常关闭 残留进程清理 exe 路径从第 02 篇的环境清单 (aft_env.json) 取绝不写死铁律3、铁律8。 运行python batch_guardian.py --selftest importargparseimportjsonimportosimportsignalimportsubprocessimportsysimporttime GRACE_SEC60# 超时后给应用正常关闭的宽限时间经验值defpick_exe(env_json:str,hint:str)-str:从环境清单里挑一个可执行文件路径挑不到就报错让用户去探测。withopen(env_json,encodingutf-8)asf:envjson.load(f)cands[]forexesinenv.get(install_dirs,{}).values():cands[eforeinexesifhint.lower()inos.path.basename(e).lower()]ifnotcands:raiseFileNotFoundError(f在{env_json}中未找到含 {hint} 的可执行文件请先跑 probe_env.py 探测安装目录铁律3)returnsorted(cands)[0]defrun_with_timeout(cmd:list,timeout_sec:int)-dict:启动子进程超时则先正常关闭、再强杀返回执行报告。report{cmd:cmd,timed_out:False,returncode:None,elapsed:0.0}t0time.time()psubprocess.Popen(cmd)try:p.wait(timeouttimeout_sec)report[returncode]p.returncodeexceptsubprocess.TimeoutExpired:report[timed_out]True# ① 先礼尝试正常关闭Windows 上 terminate 为终止请求能归还席位的是优雅退出p.terminate()try:p.wait(timeoutGRACE_SEC)exceptsubprocess.TimeoutExpired:# ② 后兵强杀会触发席位 2 小时兜底尽量避免走到这里p.kill()p.wait()finally:report[elapsed]round(time.time()-t0,1)returnreportdefcleanup_residual(name_hint:str)-int:③ 残留清理按进程名关键词清理用 tasklist/taskkillWindows 自带。try:outsubprocess.run([tasklist,/FO,CSV,/NH],capture_outputTrue,textTrue).stdoutexceptFileNotFoundError:return0killed0forlineinout.splitlines():ifname_hint.lower()inline.lower():pidline.split(,)[1].strip()if,inlineelseifpid.isdigit():subprocess.run([taskkill,/PID,pid,/F],capture_outputTrue)killed1returnkilleddefselftest():# 用必定超时的 Python 子进程验证熔断路径reprun_with_timeout([sys.executable,-c,import time; time.sleep(30)],timeout_sec2)assertrep[timed_out]isTrue,repassertrep[elapsed]2,rep# 清理函数在无匹配时返回 0不应报错assertcleanup_residual(___no_such_process___)0print(SELFTEST OK超时熔断生效timed_outTrue残留清理函数安全返回 0。)defmain():apargparse.ArgumentParser()ap.add_argument(--env,defaultaft_env.json)ap.add_argument(--hint,default,help可执行文件名关键词探测所得)ap.add_argument(--timeout,typeint,default3600)ap.add_argument(--selftest,actionstore_true)aap.parse_args()ifa.selftest:selftest()return0exepick_exe(a.env,a.hint)reprun_with_timeout([exe],a.timeout)killedcleanup_residual(os.path.basename(exe).split(.)[0])print(json.dumps({**rep,residual_killed:killed},ensure_asciiFalse,indent2))return0if__name____main__:sys.exit(main())逐行剖析pick_exe()从环境清单里按关键词挑可执行文件——这就是把铁律 3 落成代码路径来自探测不来自记忆。run_with_timeout()的三段结构正是铁律 8TimeoutExpired里先terminate()先礼给GRACE_SEC宽限仍不退才kill()后兵。注释里点明能归还席位的是优雅退出——强杀是最后手段。cleanup_residual()用 Windows 自带tasklist/taskkill不依赖 psutil工业现场很多机器装不了额外包。按关键词匹配而非全名匹配是为了兼容不同版本的命名。selftest()用必定超时的子进程sleep 30timeout 2来验证熔断路径——这比 mock 更真实且不依赖 AFT 存在。一个必须注意的点cleanup_residual的关键词一定要够独特如产品名不要用python之类通用词否则会误杀你自己的脚本进程。三、常见报错与排查报错 10-1批跑跑完了但一半场景没结果。现象日志显示部分场景瞬间跳过。根因官方只运行Analysis Setup 已完整定义的场景。解法批跑前做场景完整性自检把未完整定义的场景补全或从清单移除。报错 10-2脚本半夜崩了早上发现公司席位全被占别人打不开软件。现象席位不释放。根因没做铁律 8 的第 ② 步正常关闭直接强杀或崩溃。解法加try/finally优雅退出已发生后同账号同机重开再正常关闭通常可释放否则等 2 小时兜底。报错 10-3Batch File 在 GUI 里 Load 后清单为空或报错。现象脚本生成的清单 GUI 读不了。根因格式与 GUI 产物不一致编码、是否绝对路径、是否含额外列。解法先用 GUI 的 Save List to File 产样本比对后按样调整这是先探测后编码。报错 10-4想省时间改导出映射后重导结果 Export Only 什么都没导。现象Export Only 后导出件为空。根因官方限制——没有输出的场景不会被导出不跑就没输出。解法先确保这些场景已有输出跑过一次否则改为正常批跑。报错 10-5以为多开会更快结果多个实例互相抢工作簿、席位也不够。现象速度更慢、文件锁冲突。根因批跑本质是顺序的并发还会撞席位池与文件锁。解法单机顺序批跑可后台如确需并行多机 每机独立工作目录/输出目录 足够席位。四、动手练习练习 1清单往返跑python make_batch_file.py --selftest再--models demo_a.fth,demo_b.fth --out batch.txt。判定自检通过batch.txt内每行是一个绝对路径用--read batch.txt能原样读回 2 行。练习 2格式比对在 GUI 里Add Model Files加 3 个模型并Save List to File与脚本产物逐字符比对。判定写出至少 1 处格式差异如编码/是否 BOM/路径风格并据此修正脚本。练习 3看门狗跑python batch_guardian.py --selftest。判定输出SELFTEST OKtimed_outTruecleanup_residual返回 0无残留时。练习 4断点续跑演练故意在第 N 个场景处Cancel。判定确认前 N-1 个场景的输出仍在磁盘记录它们的清单下一次只针对剩余场景配置清单用第 09 篇门控验证只补跑缺口。五、小结与下一篇预告本篇把执行通道讲透了Batch Run Type两类、Batch File 清单格式以 GUI 产物为基准先探测后编码、Run Batch in Background、Export Only (Do Not Run)的省时价值与限制、Cancel后已跑输出保留带来的断点续跑能力以及最重要的——铁律 8 三步法超时熔断 try/finally 正常关闭 残留进程清理。你的batch_guardian.py从此可以安全地跑一夜。第 11 篇《结果取数与结构化落盘》我们转向数据面的下半场——把 Excel Export Manager 的导出件清洗成(scenario, object, parameter, unit, value)长表讲 wide→long、单位拆列、parquet/sqlite 落盘与幂等 upsert让重跑不产生重复数据。FAQ与第〇节一一对应Q1Batch Run Type 两类分别什么时候用AScenarios in Current Model 用于在同一模型文件内的多场景批跑且只有它才能配合 Save Using Excel Export Manager 导出Models from Different Files 用于跨文件的多个模型批跑当当前模型没有场景时只有后者可选。Q2Batch File 是什么怎么配合三个动作使用ABatch File 是列出所有要运行的模型的文本文件用 Add Model Files 逐个添加模型用 Load Existing Batch File 从清单载入用 Save List to File 把当前清单存盘、Remove Selections 移除选中官方未公开其逐字节格式应先用 GUI 存一份样本再让脚本按该格式读写。Q3Run Batch in Background 与 Cancel 的语义是什么ARun Batch in Background 让选中场景/模型在不显示 Solution Progress 窗口的情况下后台运行、不打断其他工作Cancel 可随时取消批跑且已跑完的场景输出仍然保留因此批跑是部分成功可接受的可据此做断点续跑。Q4Export Only (Do Not Run) 适合什么场景限制是什么A适合改动了 Excel Export Manager 导出映射但不需要重算水力的情形可复用已有输出直接重导、省下全部计算时间限制是若某场景没有输出则不会为它导出任何数据因为不运行就不产生输出。Q5为什么批跑必须套超时/释放/残留清理三步A因为应用崩溃或被强杀时授权席位不会正常归还会进入最长 2 小时的 checkout interval 并可能被标记为 orphaned导致其他同事打不开软件所以要先超时熔断、再在 try/finally 里正常关闭以归还席位、最后才按探测到的进程名清理残留。
返回列表