24 KiB
fquiz ATP 目录化模型管理改造方案
For Hermes: 后续若按此方案实施,优先按阶段拆分为独立任务执行;每阶段完成后先做接口/数据契约核对,再进入下一阶段。
Goal: 把当前基于单个 atp_text 文本的 ATP 模型管理,升级为“按电压等级 / 塔型 / 场景 / 工况目录管理的 ATP 资料包管理系统”,由 MinIO 承载完整目录树,最终在运行时将指定资料包同步到本地 Wine 允许运行根目录,再调用 tpbig.exe / rjtzl.exe 等程序执行。
Architecture: 复用现有 file_storage(VFS/S3-MinIO)作为对象存储底座,新增 ATP 资料包元数据层、导入任务层、运行物化层三层模型。管理端不再把 ATP 模型等价为一段 atp_text,而是把它视为“一个可索引、可发布、可运行的目录包”;运行时通过 Celery 任务将目录包物化到 wine_allowed_root 下,再按规则调用 Wine/ATP 程序并回收结果。
Tech Stack: FastAPI + SQLAlchemy + PostgreSQL + MinIO(S3) + Celery + Wine + Ant Design + 现有文件管理模块。
1. 背景与现状判断
1.1 真实业务目标
用户期望的 ATP 模型管理,不是现在这种:
- 一个模型
- 对应若干版本
- 每个版本核心是一段
atp_text
而是类似真实目录 /mnt/d/fl/解压目录/ATP 的管理方式:
- 按电压等级:
35/66/110/220/330/500/750/800/1000 - 按塔型/线路类型:
ganzi/guxing/jiubei/maotou/sihuita/... - 按场景:
fanji/raoji1/raoji2/raoji3 - 按避雷器/工况组合:
M1/M12/M123/M13/M2/M23/M3/noM/... - 目录内混合存在:
work.atp*.lis/*.pl4/*.log/*.dbg*.lib/*.dat/*.pchEGM输入/结果exepy预处理/后处理脚本txt/docx/zip说明与归档
1.2 fquiz 当前 ATP 模块的主要问题
当前文件:
api/app/models/atp_model.pyapi/app/services/atp_model_service.pyapi/app/api/v1/atp_models.pyweb/src/app/admin/power-lines/atp-viewer/page.tsx
现状特征:
AtpModelVersion的核心字段是:atp_textgraph_jsonartifact_manifest_json
- 运行逻辑
_build_run_command()也是把version.atp_text写到本地文件后直接执行。 - 这套设计只能覆盖“单文件 ATP 文本模板”,不适合覆盖真实目录资料包。
- 当前没有把:
- 电压等级
- 塔型
- 场景
- 工况组合
- EGM/ATP 双运行器 纳入一等公民模型。
1.3 可直接复用的基础设施
当前仓库已经具备以下可复用能力:
对象存储与文件索引
api/app/models/file_storage.pyapi/app/services/storage_driver.pyapi/app/services/file_service.py
已支持:
- VFS / S3(MinIO)
- mount 抽象
- 目录列举
- 上传/下载
- 重命名/移动/删除
- index entry 同步
MinIO 环境
.envdeploy/dev-deploy/compose.ymldeploy/pro-deploy/compose.ymlseed_service.py
已支持:
files.s3.default- bucket 自动初始化
mainmount 默认挂载
Wine 执行能力
api/app/services/wine_service.py
已支持:
- 在
wine_allowed_root下执行 exe - Celery 异步任务
- 日志/退出码记录
legacy ATP 目录执行样板
api/app/services/legacy_atp_adapter.py
已支持:
- 解析模板目录
- 复制模板目录到运行目录
- 对目录内模型文件做特征绑定
- 运行
tpbig.exe/rjtzl.exe - 回读文本结果
大文件/目录导入异步样板
api/app/services/elevation_service.py
已支持成熟链路:
- 上传到对象存储暂存区
- 记录 import job
- Celery 异步解压/整理/导入
- 结果落库
结论: 本方案不需要重造存储、任务、执行底座,只需要把 ATP 模型层升级成“目录包元数据 + 目录包导入/发布/运行编排”。
2. 目标架构
2.1 三层模型
A. ATP 资料包主表(业务维度)
用于表达“一个业务上的 ATP 模型族”。
示例:
- 220kV 四回塔 绕击3
- 500kV 干字塔 反击
- 330kV 酒杯塔 无避雷器
这个层级只关心业务分类与检索,不关心实际目录文件细节。
B. ATP 场景版本表(发布维度)
用于表达“一个可执行的具体版本/场景”。
一个版本会绑定:
- 电压等级
- 塔型
- 场景
- 工况组合
- 运行器类型(ATP / EGM / HYBRID)
- 存储中的目录根路径
- 入口文件
- 可选结果文件规则
- 可选预处理/后处理脚本
这个层级才是真正供运行选择的对象。
C. ATP 工件清单表(文件维度)
用于表达一个场景版本内有哪些关键文件。
典型包括:
work.atpwork.lisrjtzl.exetpbig.exeEGM/*.txtdelete_others.py说明.docx
这个层级负责:
- 展示
- 校验
- 关键文件定位
- 运行时解析入口
D. ATP 导入任务表(流程维度)
用于表达一个 zip/目录包从上传到发布的过程。
2.2 运行时链路
目标运行链路:
- 管理端在 MinIO 中维护 ATP 资料包目录树
- 用户在后台选择一个“ATP 场景版本”发起运行
- API 创建 run 记录
- Celery 任务:
- 从 MinIO 读取该版本目录树
- 物化到本地
wine_allowed_root下的运行目录 - 如有预处理脚本则执行
- 调用
wine tpbig.exe或wine rjtzl.exe - 回收结果文件 / stdout / stderr
- 如有后处理脚本则执行
- 写回 run 结果与产物索引
- 前端查看运行结果、下载关键输出文件
3. 数据模型设计
3.1 新表设计
3.1.1 atp_asset
表示 ATP 业务模型族。
建议字段:
idvarchar(32) pkcodevarchar(64) uniquenamevarchar(255)descriptiontextstatusvarchar(20)draft/enabled/disabled/archivedvoltage_levelvarchar(16) 可选冗余主标签,如220/500tower_typevarchar(64) 可选冗余主标签,如sihuita/ganziscene_typevarchar(32) 可选冗余主标签,如fanji/raoji3tags_jsonjsoncreate_date/create_user/update_date/update_user
说明:
atp_model可保留兼容,但长期建议新建atp_asset,避免语义继续混乱。- 如果不想新建表,也可将现
atp_model语义提升为asset,但字段命名会继续误导开发者。
3.1.2 atp_asset_release
表示一个可执行发布版本。
建议字段:
idvarchar(32) pkasset_idfk ->atp_asset.idrelease_nointrelease_tagvarchar(64)statusvarchar(20)draft/released/archivedvoltage_levelvarchar(16) 非空tower_typevarchar(64) 非空scene_typevarchar(32) 非空scenario_codevarchar(64) 如M1/noM/M123runner_kindvarchar(20)atp/egm/hybrid/wine_scriptstorage_mount_codevarchar(64) 默认mainstorage_root_pathvarchar(2048) 目录包根路径entry_filevarchar(255) 如work.atpresult_filevarchar(255) 可空egm_subdirvarchar(255) 可空egm_result_filevarchar(255) 可空preprocess_scriptvarchar(255) 可空,如1 delete_others.pypostprocess_scriptvarchar(255) 可空manifest_jsonjsonvalidation_jsonjsoncontent_hashvarchar(64)is_activeboolcreate_date/create_user/update_date/update_user
说明:
storage_root_path指向 MinIO 挂载中的目录根,例如:/atp-library/220/sihuita/raoji3/M1
manifest_json保存导入扫描结果:- 文件总数
- 关键文件是否存在
- 子目录结构
- 可执行文件定位
3.1.3 atp_asset_file
表示一个发布版本的关键文件索引。
建议字段:
idbigint pkrelease_idfk ->atp_asset_release.idrelative_pathvarchar(2048)namevarchar(255)file_rolevarchar(32)entry_atpresult_textexe_tpbigexe_rjtzlpreprocess_scriptpostprocess_scriptlibrarydocbinaryother
mime_typevarchar(255)sizebigintetagvarchar(255)storage_keyvarchar(2048)is_requiredboolcreate_date/update_date
3.1.4 atp_asset_import_job
表示导入任务。
建议字段:
idvarchar(32) pkasset_idfk 可空release_idfk 可空mount_codevarchar(64)stage_pathvarchar(2048)source_filenamevarchar(255)statusvarchar(20)pending/queued/importing/validating/completed/failed/cancelled
uploaded_file_countintextracted_file_countintwarning_countintwarnings_jsonjsonstaged_files_jsonjsonmanifest_jsonjsontask_idvarchar(128)error_messagetextcreate_date/create_user/update_date/update_user
3.1.5 atp_asset_run
建议替代或扩展现有 atp_simulation_run。
如果保留现有表,建议至少新增:
release_idstorage_mount_codestorage_root_pathmaterialized_root_pathrunner_kindpreprocess_statuspostprocess_statusoutput_manifest_jsonresult_summary_json
说明:
- 现有
version_id更像文本版本语义,不够表达目录包。
4. 存储设计(MinIO)
4.1 目标目录布局
建议在 main mount 下单独规划 ATP 资料根:
/atp-library/imports/:上传暂存区/atp-library/releases/:正式发布区/atp-library/runs/:运行结果归档(可选)
推荐正式目录格式:
/atp-library/releases/{asset_code}/r{release_no}/...原始目录内容...
例如:
/atp-library/releases/atp-220-sihuita-raoji3/r1/220/sihuita/raoji3/M1/...
或者更扁平一点:
/atp-library/releases/atp-220-sihuita-raoji3/r1/...
建议选择扁平方案,即导入时把用户上传目录整体挂到该 release 根下,不强依赖固定内部第一层路径名。
4.2 为什么不要直接把原始 ATP 总目录一比一挂到运行配置
因为运行时真正需要的是:
- 一个明确可执行的 release 根
- 明确入口文件
- 明确结果文件
- 明确 runner 类型
而不是整个海量总库。总库适合归档,不适合直接供运行任务临时扫描。
5. 导入流程设计
5.1 导入入口
后台提供两种导入方式:
- 上传 zip
- 从文件管理已有路径选择目录并注册
优先建议先做 zip 导入,因为最容易闭环。
5.2 zip 导入流程
Step 1: 上传到对象存储暂存区
复用 elevation_service 的 staging 模式:
driver.write_file()到/atp-library/imports/{job_id}/source.zip
Step 2: Celery 异步解压
任务:
- 读出 zip
- 解压到
/atp-library/imports/{job_id}/expanded/... - 统计文件数、目录数、关键文件
Step 3: 自动校验
校验规则:
- 是否存在
work.atp或配置指定入口 - 是否存在
tpbig.exe/rjtzl.exe或可通过系统默认路径补足 - 是否存在
EGM子目录(若 runner_kind=egm/hybrid) - 是否存在
*.lis/ 结果文本(若声明需要) - 是否存在预处理脚本
- 是否包含潜在危险脚本(例如
.bat/.cmd/.ps1)
Step 4: 写 release 记录
生成:
atp_assetatp_asset_releaseatp_asset_filemanifest_json
Step 5: 发布到正式区
将暂存目录移动或复制到:
/atp-library/releases/{asset_code}/r{release_no}/
Step 6: 前端展示导入报告
包括:
- 识别出的电压等级/塔型/场景/工况
- 关键文件清单
- 告警项
- 是否允许激活
6. 运行流程设计
6.1 物化策略
MinIO 不能直接供 Wine exe 当本地目录运行,因此必须“物化”到本地:
- 本地根:
settings.wine_allowed_root - ATP 运行根建议新增子目录:
./data/wine/atp-runtime/releases/..../data/wine/atp-runtime/runs/{run_id}/...
运行前动作
- 根据
release.storage_root_path枚举 MinIO 下文件 - 全量同步到:
wine_allowed_root/atp-runtime/runs/{run_id}/payload/
- 校验入口文件存在
- 如配置了
preprocess_script,先执行该脚本 - 调用对应运行器
6.2 运行器策略
runner_kind = atp
- 调
tpbig.exe - 输入:
entry_file - 工作目录:release 物化目录
runner_kind = egm
- 调
rjtzl.exe - 工作目录:
egm_subdir - 结果文件按
egm_result_file
runner_kind = hybrid
- 先跑
egm - 再按配置决定是否跑
atp - 或按 legacy adapter 的 contract 合并结果
runner_kind = wine_script
- 允许指定一个 exe + 参数模板
- 仅作为扩展位,第一期不必开放 UI
6.3 后处理
如果有:
postprocess_script
则运行结束后执行,用于:
- 清理参数块
- 汇总结果文件
- 生成前端展示摘要
6.4 结果回收
回收内容:
- stdout/stderr
- exit_code
- 关键结果文件列表
- 输出目录 manifest
- 结果摘要 json
可选增强:
- 将结果目录再回传到 MinIO:
/atp-library/runs/{run_id}/...
这样可支持:
- 后台下载结果
- 历史运行审计
- 二次分析
7. API 改造方案
7.1 保留现接口但升级语义(不推荐)
当前 /api/v1/atp/models 的命名会继续误导为“单文本模型”。
7.2 新接口(推荐)
建议新增一组目录化接口:
资料包主资源
GET /api/v1/atp/assetsPOST /api/v1/atp/assetsGET /api/v1/atp/assets/{id}PATCH /api/v1/atp/assets/{id}DELETE /api/v1/atp/assets/{id}
release 资源
GET /api/v1/atp/assets/{id}/releasesPOST /api/v1/atp/assets/{id}/releases/importGET /api/v1/atp/releases/{release_id}PATCH /api/v1/atp/releases/{release_id}POST /api/v1/atp/releases/{release_id}/activateGET /api/v1/atp/releases/{release_id}/filesGET /api/v1/atp/releases/{release_id}/preview
import job
GET /api/v1/atp/import-jobsGET /api/v1/atp/import-jobs/{job_id}
run
POST /api/v1/atp/releases/{release_id}/runsGET /api/v1/atp/releases/{release_id}/runsGET /api/v1/atp/runs/{run_id}GET /api/v1/atp/runs/{run_id}/outputsGET /api/v1/atp/runs/{run_id}/outputs/download
engine
GET /api/v1/atp/engine/status
7.3 兼容策略
第一期可保留旧接口 /api/v1/atp/models,但:
- 页面入口迁到新资源
- 旧接口标记为 legacy
- 若必须兼容,则让旧
AtpModelSummary映射到新asset的摘要视图
8. 前端改造方案
8.1 页面定位
当前页面:
web/src/app/admin/power-lines/atp-viewer/page.tsx
它现在混合承载:
- 模型台账
- 模板文本编辑
- 仿真运行
- ATP 文本可视化
这不适合目录化模型管理。
8.2 拆页建议
建议拆成 4 个页面:
A. ATP 资料包管理页
路由:/admin/atp-models
展示:
- 资料包列表
- 电压等级筛选
- 塔型筛选
- 场景筛选
- 状态筛选
- 当前激活 release
B. ATP Release 详情页
路由:/admin/atp-models/[assetId]
展示:
- release 列表
- 关键元数据
- 文件清单
- 校验结果
- 激活/归档按钮
C. ATP 导入页/抽屉
功能:
- 上传 zip
- 选择 mount 中现有目录
- 填写:电压等级/塔型/场景/工况/runner
- 显示导入校验报告
D. ATP 运行页
路由可继续沿用子面板或独立页
展示:
- 选择 release
- 运行参数
- 任务状态
- 结果摘要
- 输出文件下载
8.3 ATP 文本预览功能保留方式
现有“ATP 文本转换与预览”不应删除,但应降级为工具页:
- 输入:从 release 文件清单中选择
work.atp - 读取后本地渲染
- 不再承担“模型主编辑器”角色
9. 分阶段实施计划
Phase 1:目录化元数据落库(最小闭环)
Objective: 能在后台录入 ATP 资料包和 release 元数据,但先不做 zip 自动导入。
Files:
- Create:
api/app/models/atp_asset.py - Create:
api/app/schemas/atp_asset.py - Create:
api/app/services/atp_asset_service.py - Create:
api/app/api/v1/atp_assets.py - Modify:
api/app/api/router.py - Modify:
api/app/services/seed_service.py - Create:
web/src/app/admin/atp-models/page.tsx - Create:
web/src/app/admin/atp-models/[id]/page.tsx
Deliverables:
- 资料包 CRUD
- release CRUD
- release 激活
- 文件清单只存元数据,不自动导入
Acceptance:
- 能录入一个 release,指向 MinIO 中某目录
- 能在前端展示该 release
- 能激活切换 release
Phase 2:zip 导入 + manifest 扫描
Objective: 能上传 zip 到 MinIO,异步解压并生成 release。
Files:
- Create:
api/app/tasks/atp_asset_tasks.py - Create:
api/app/services/atp_asset_import_service.py - Modify:
api/app/services/storage_driver.py(如需补目录递归遍历辅助) - Modify:
api/app/services/file_service.py(如需复用目录打包逻辑) - Create:
web/src/components/atp-asset-import-modal.tsx
Deliverables:
- upload zip
- import job
- manifest
- 关键文件识别
Acceptance:
- 能导入一个 ATP 目录 zip
- 能识别
work.atp/EGM/ exe / 说明文件 - 能生成 release + file 清单
Phase 3:运行时物化到 Wine 根目录
Objective: 运行时不再依赖 atp_text,改为从 MinIO 物化目录后执行。
Files:
- Modify:
api/app/services/atp_model_service.py(或拆分为新atp_run_service.py) - Create:
api/app/services/atp_runtime_materializer.py - Modify:
api/app/services/wine_service.py(如需复用执行封装) - Modify:
api/app/services/legacy_atp_adapter.py
Deliverables:
- release -> local runtime dir
- preprocess -> run -> postprocess
- result harvest
Acceptance:
- 指定 release 可成功同步到本地运行目录
- 调用
tpbig.exe或rjtzl.exe - run 记录中能看到目录来源、工作目录、结果清单
Phase 4:结果产物回写与下载
Objective: 支持结果文件长期归档与下载。
Files:
- Modify:
api/app/services/atp_asset_service.py - Modify:
api/app/api/v1/atp_assets.py - Modify:
web/src/app/admin/atp-models/[id]/page.tsx
Deliverables:
- 结果同步回 MinIO
- 结果文件清单下载
- 历史运行追溯
Acceptance:
- 后台能下载某次 run 的关键输出文件
- 结果可回溯到 release 版本
Phase 5:迁移旧 ATP 文本模型
Objective: 把现有 atp_model/atp_model_version 迁移到新体系或降级为 legacy。
策略:
- 短期保留旧表
- 新页面只读 legacy 数据或不展示
- 如确实有价值,把旧
atp_text版本导出成目录包(单文件场景)迁入新 release
10. 关键实现细节
10.1 目录递归枚举能力
当前 storage_driver 公开的是 list_dir(path),如果要做 release manifest 扫描,需要补一个服务层递归工具:
walk_virtual_tree(driver, root_path)
要求:
- 返回相对路径列表
- 限制最大文件数/最大深度,避免超大目录拖垮任务
10.2 本地物化安全边界
运行物化目录必须严格限制在:
settings.wine_allowed_root
例如:
./data/wine/atp-runtime/runs/{run_id}
所有同步、脚本执行、exe 调用都只能在这个根内进行,防止目录穿越与任意执行。
10.3 脚本执行白名单
真实 ATP 目录里可能带:
.py.bat.cmd
为了安全,第一期建议:
- 仅允许执行
.py - 且脚本路径必须位于 release 根目录内
- 且必须在 release 元数据里显式配置为
preprocess_script/postprocess_script - 默认不自动执行任意发现到的脚本
.bat/.cmd/.ps1 第一阶段不要自动执行。
10.4 入口文件发现规则
导入时的默认入口发现:
优先级:
- 用户手工指定
- 根目录下
work.atp - 唯一的
*.atp - 未发现则标记需人工补充
EGM 结果文件类似:
- 用户手工指定
result.txt- 唯一的
*_result*.txt
10.5 分类字段规范化
建议新增枚举/规范字典:
voltage_level:35/66/110/220/330/500/750/800/1000scene_type:fanji/raoji1/raoji2/raoji3/otherrunner_kind:atp/egm/hybrid
tower_type 和 scenario_code 先保持字符串自由录入,避免第一期过度建模。
11. 与当前 legacy ATP 适配器的关系
11.1 短期
不要删除 legacy_atp_adapter.py。
应改造为:
- 既能吃老配置
- 也能吃新
release解析结果
让它从“配置驱动目录执行器”,演进为“目录包运行引擎适配层”。
11.2 中期
把以下逻辑上提为公共运行层:
- 运行目录准备
- 模板目录复制
- 脚本执行
- 结果解析
让 legacy_atp_adapter.py 只保留:
- 特征绑定逻辑
- EGM/ATP 结果合并逻辑
12. 风险与影响
风险 1:对象存储目录大、文件多,导入慢
应对:
- 走 import job 异步链路
- 限制单次导入 zip 大小、文件数
- manifest 预扫描超限直接失败
风险 2:Wine 运行依赖本地目录语义,MinIO 无法直跑
应对:
- 明确采用“本地物化后再执行”
- 运行完成后可选回写产物
风险 3:目录内脚本存在安全风险
应对:
- 第一阶段仅白名单
.py - 必须显式配置
- 严格限制在
wine_allowed_root
风险 4:旧 ATP 文本模型与新目录包模型并存,前后端易混淆
应对:
- 新接口使用
assets/releases命名 - 旧
models标记 legacy - UI 迁移后默认只展示新模型
风险 5:真实 ATP 目录结构不完全统一
应对:
- release 允许手工修正:
entry_fileresult_fileegm_subdirpreprocess_script
- 不强依赖固定目录模板
13. 推荐落地顺序(最务实)
如果要最快闭环,建议按以下顺序推进:
- 先不上来推翻旧 ATP 模块
- 先新建
atp assets/releases模型与接口 - 先支持“手工录入 storage_root_path”的目录化 release
- 打通“从 MinIO 目录 -> 本地物化 -> Wine 执行”
- 跑通后再补 zip 导入/manifest 自动识别
- 最后再考虑迁移旧
atp_text
这样可以最快得到一个能用版本,而不是一上来做完整导入系统导致战线过长。
14. 最小可交付版本(MVP)定义
MVP 应满足:
- 后台可创建 ATP 资料包
- 后台可为资料包创建一个 release
- release 可绑定 MinIO 中一个目录根路径
- 可手工配置:
- runner_kind
- entry_file
- result_file / egm_subdir
- preprocess_script
- 发起运行时:
- 能把整个目录同步到本地
- 能运行 Wine + exe
- 能回收 stdout/stderr/exit_code
- 后台能看到运行记录
MVP 不要求:
- 自动识别所有目录分类
- 自动解析所有结果格式
- 完整图形化编辑 ATP 文本
- 完整历史迁移旧模型
15. 本方案对应的仓库改造重点
后端新增重点
atp_asset/atp_asset_release/atp_asset_file/atp_asset_import_job- 目录递归扫描
- MinIO -> 本地物化
- release 运行器
- import job
前端新增重点
- 资料包列表页
- release 详情页
- 导入弹窗
- 运行记录与结果下载
现有代码复用重点
- 文件存储驱动
- MinIO 部署
- Wine 执行
- legacy ATP 目录执行逻辑
- elevation 异步导入样板
16. 验收标准
功能验收
- 能上传或绑定一个 ATP 目录包到 MinIO
- 能在后台按电压/塔型/场景检索 release
- 能运行指定 release
- 能拿到运行结果
- 能查看和下载关键输出
架构验收
- 不再要求核心执行必须依赖
atp_text - 目录包是系统一等公民
- MinIO 是资料包权威存储
- Wine 只消费本地物化运行目录
可维护性验收
- API 命名明确区分
asset/release/run - legacy 与新体系边界清晰
- 关键目录路径、入口文件、运行器类型可配置
17. 一句话结论
推荐把 fquiz 的 ATP 模型管理,从“单 ATP 文本文档管理”升级为“MinIO 承载的目录化 ATP 资料包管理 + 本地物化执行架构”;复用现有文件存储、MinIO、Wine、Celery、legacy ATP 适配器与高程导入链路,按 asset -> release -> file -> import_job -> run 五层模型落地。