Files
SystemSimulationApp/docs/standard/backend-interface-version-spec-v1.md
T

272 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 后端接口版本与定义规范 v1
修订日期:2026-09-12;核对代码基线:`22579e5`。本版按代码补齐存储/执行、表达式、流式结果、浏览器保存和任务生命周期边界。
本文统一说明 SystemSimulationApp 后端的接口边界、版本编号和事实来源。规范版本为
`1.2.0`。这个编号只表示本文档自身的修订版本,不等同于 HTTP API、System XML、
组件目录或单个模型的版本。
## 1. 当前版本基线
后端没有一个可以替代所有子版本的“总版本号”。调用方必须按所使用的数据合同读取
对应版本:
| 版本轴 | 当前值 | 作用范围 | 机器可读事实来源 |
| --- | --- | --- | --- |
| HTTP API | 未版本化 | `/api/...` 路由、请求和响应 | `app/main.py`、FastAPI OpenAPI |
| 后端应用发布版本 | 未定义 | 整个后端部署产物 | 当前没有包元数据或运行时常量 |
| 组件目录 Schema | `1` | `GET /api/components/catalog` | `schemas/component-catalog-v1.schema.json` |
| System XML Schema | `3` | XML 校验、编译和仿真输入 | `schemas/system-simulation-v3.xsd` |
| 组件库版本 | 各库独立 | 一个组件库的发布边界 | 各库 `library.py` 的 `version` |
| 模型合同版本 | 各模型独立 | 单个 `MODEL_TYPE` 的物理和数据合同 | 模型类的 `MODEL_VERSION` |
| ReactFlow 工程 JSON | `2`(兼容读 `1`) | 编辑器存档 | `projectSchemaVersion`、`ReactFlowProjectPayload` 和前端读取代码 |
| 结果文件格式 | `1` | 前端导入、导出的结果快照 | `SimulationResultsView.tsx` |
因此,`schemaVersion=3` 只能说明文件是 System XML v3,不能说明“后端 API 是
v3”;`MODEL_VERSION=0.2.0` 也只描述对应模型合同。
FastAPI 自动生成的 OpenAPI 当前可能显示默认 `info.version=0.1.0`;后端没有显式
声明这个值,因此它不是正式 API 或应用发布版本。
## 2. 事实来源和优先级
接口定义冲突时,按以下优先级处理:
1. 可执行代码和机器可读 Schema;
2. 自动化合同测试;
3. 当前版本规范文档;
4. 示例、调研记录和历史说明。
各类合同的唯一事实来源如下:
| 合同 | 唯一事实来源 |
| --- | --- |
| 公开模型类型和版本 | 模型类的 `MODEL_TYPE`、`MODEL_VERSION` |
| 端口物理合同 | 模型类的 `PORTS` 和 `app/simulation/core/ports.py` |
| 参数、结果及单位 | 模型类的 `PARAMETERS`、`RESULT_VARIABLES` |
| 组件库及分类 | 各库 `library.py` |
| 组件目录 JSON | `build_component_catalog()` 与目录 JSON Schema |
| System XML | v3 XSD、`app/system_xml.py` |
| 网络最终连接检查 | `SimulationNetwork.connect()` |
| HTTP 路由和请求模型 | `app/main.py` |
前端兜底目录、画布布局和示例 XML 不能反向定义后端物理合同。
## 3. 组件接口合同
每个公开组件必须显式声明:
```python
MODEL_TYPE = "example_component"
MODEL_VERSION = "1.0.0"
PORTS = (...)
PARAMETERS = (...)
RESULT_VARIABLES = (...)
DISPLAY = ...
```
并提供统一的 `create()` 入口。各字段含义如下:
- `MODEL_TYPE` 是工程、XML、目录和结果元数据共同使用的稳定机器标识;
- `MODEL_VERSION` 使用 `主版本.次版本.修订版本`;
- `PORTS` 定义端口名称、种类、物理域、名义角色、变量和连接规则;
- `PARAMETERS` 定义 SI 单位、默认值、范围和离散选项;
- `RESULT_VARIABLES` 定义结构化结果元数据;
- `DISPLAY` 中图形、布局、分组不定义物理方程;但 `role="amesimGasMediumDefinition"` 已用于前端及 XML 介质引用检查。网络构造还依据 `AmesimGasMediumDefinitionComponent` 的继承关系识别介质定义,不能只加 role 就得到介质能力。
物理流变量统一以进入组件为正。物理连接的两个端点无方向;信号方向由注册端口的
`output/input` 合同决定。
## 4. 组件目录接口
`GET /api/components/catalog` 返回组件库、分类和模型合同。响应顶层必须为:
```json
{
"schemaVersion": 1,
"libraries": []
}
```
目录结构由 `schemas/component-catalog-v1.schema.json` 约束。库必须携带
`version`,模型必须携带 `modelVersion`。目录 Schema 的整数版本只描述目录 JSON
结构;库和模型版本仍分别使用三段式版本号。
当前三段版本只接受 `数字.数字.数字`,不接受 prerelease 或 build metadata,不能直接
等同于完整 SemVer 实现。目录 Schema 对对象使用 `additionalProperties=false`,因此
新增字段也必须先评估严格消费者,不能只因字段可选就默认兼容。
目录响应是前端生成组件面板、参数编辑器和端口快照的来源。修改响应结构时必须同时
更新 JSON Schema、前端解析和合同测试。
## 5. System XML v3
System XML v3 是当前唯一支持的 XML 求解输入。根元素固定使用:
```xml
<System schemaVersion="3" unitSystem="SI">
```
每个组件必须包含 `id`、`type`、`modelVersion` 和完整 SI 参数;连接只包含两个
`Endpoint(component, port)`。端口合同由 `type + port` 从注册表恢复,XML 不保存
端口快照和画布布局。
`modelVersion` 必须与当前注册模型完全一致。版本不一致时返回
`COMPONENT_MODEL_VERSION_MISMATCH`,不会静默使用当前模型解释旧输入。完整结构见
`docs/standard/system-xml-v3.md` 和 `schemas/system-simulation-v3.xsd`。
## 6. HTTP API
FastAPI 当前直接注册未版本化的 `/api/...` 路由,没有 `/api/v1` 命名空间,也没有
运行时 `apiVersion`。主要业务接口如下:
| 方法与路径 | 请求合同 | 响应合同 |
| --- | --- | --- |
| `GET /api/components/catalog` | 无 | 组件目录 JSON v1 |
| `GET /api/reactflow/projects` | 无 | 已保存工程摘要列表 |
| `GET /api/reactflow/projects/{id}` | 工程 ID | 经 Pydantic 存储结构检查的原始 JSON;不保证可执行或可被浏览器导入 |
| `POST /api/reactflow/projects/{id}` | `ReactFlowProjectPayload` | 保存摘要 |
| `POST /api/reactflow/system-xml` | `ReactFlowProjectPayload` | `application/xml`,System XML v3 |
| `POST /api/reactflow/compile-model` | `ReactFlowProjectPayload` | 网络结构 JSON;不生成或编译 C |
| `POST /api/system-xml/validate` | 原始 XML v3 | 三层校验报告 |
| `POST /api/system-xml/parse` | 原始 XML v3 | 规范化执行模型 |
| `POST /api/system-xml/compile-model` | 原始 XML v3 | 校验报告、设置和网络;不生成或编译 C |
| `POST /api/system-xml/simulate` | 原始 XML v3 | 同步仿真结果 |
| `POST /api/system-xml/simulate-stream` | 原始 XML v3 | `application/x-ndjson` 事件流 |
| `GET /api/system-xml/simulations/{id}` | 路径 ID | 任务快照 |
| `POST /api/system-xml/simulations/{id}/cancel` | 取消原因 JSON | 取消受理状态 |
| `POST /api/simulation-results/csv` | 结构化结果 JSON | UTF-8 CSV 附件 |
两个固定算例接口 `simulate-testmodel` 和 `simulate-test-mql` 仍属于测试/基线能力,不能
视为任意拓扑仿真合同。
FastAPI 会生成 `/openapi.json`、`/docs` 和 `/redoc`。Pydantic JSON 请求体能够进入
OpenAPI,但当前多数 JSON 响应仍以 `dict[str, object]` 构造,XML、CSV 和 NDJSON
也使用原始响应类型。因此 OpenAPI 目前不是完整的响应合同;代码、Schema 和合同测试
仍是必要依据。
### 6.1 工程存储与执行入口
`ReactFlowProjectPayload` 顶层禁止未知字段,节点 data 和边 data 保留额外编辑元数据。存储不等同于执行校验:可保留缺失/不同的组件版本供查看。浏览器要求有效布局、端口对象与 `data.isContactEdge`;信号端口 `positiveFlowDirection: null` 仍应省略,这部分精简规则未改。
**工程 JSON v2**:参数数值、十进制数字字符串、数学表达式,全部按 `parameterUnits[name]` 指定的单位解释;缺省使用目录 SI 单位。缺失参数采用目录 SI 默认值,不随显示单位二次换算。例如单位选 `bar` 时,`2.5`、`"2.5"`、`"=2.5"`、`"2+0.5"` 均得到 `250000 Pa`。温度使用仿射换算,`20 degC = 293.15 K`;压力是绝压,不添加大气压偏移。
**旧工程 JSON v1**:为保持已有模型物理输入不变,普通数值/十进制数字字符串仍按 SI,表达式按显示单位解释。不可只把版本号 1 改成 2。旧文件和旧浏览器存档继续兼容读取并提示旧规则;后续手动保存、自动保存和 JSON 导出共用转换逻辑,将 SI 数字转换回所选单位,写出 v2。若某参数经过显示单位往返会改变浮点末位,则该参数改用 SI 单位保存,保持求解输入逐值精确不变;JSON 导出会报告此类参数数量。只有编辑器内存与结果快照继续使用内部 v1/SI 数字表示。不能转换的未知组件、参数或单位会使保存/导出明确失败:保留编辑器数据和此前的有效浏览器存档,不写出伪 v2,不自动下载旧格式文件;修复后再保存。
| 入口 | 输入处理 | 数值执行边界 |
| --- | --- | --- |
| 网页参数面板与 JSON v2 | 数值与表达式均使用所选单位;导出 XML 前求值 | 完整、有限 SI 数值和当前模型版本 |
| HTTP JSON→XML / compile-model | `prepare_project()` 共享表达式解析和单位换算,返回版本警告 | 严格数值化后调用原 XML/网络构造器 |
| 原生 CLI JSON 输入 | 与 HTTP 复用 `prepare_project()`,警告写入 stderr | 同上;XML 输入仍不允许表达式 |
| XML / 内部数值构造器 / C | 不接收显示单位或表达式;数字字符串也应在输入适配层转为数字 | 仅有限 SI 数值;版本与端口/参数合同严格校验 |
语法统一为有界算术 `+ - * / ^ **`、括号、科学计数法、常量 `pi/e`(不区分大小写)及函数 `sqrt abs sin cos tan asin acos atan exp ln log log10 min max pow`。幂右结合,`-2^2=-4`;`ln/log` 均是自然对数。最多 512 字符、256 词元/运算、32 层嵌套、16 个函数参数;禁止代码执行、未知变量、除零和非有限结果。离散编辑器参数只允许合法数值选项,不接受表达式。仿真时间设置同样可输入表达式,单位固定 s。
组件版本检查使用当前目录内存索引,导入时集中提示;运行/生成 XML 时再次警告,单纯版本不同或缺失不阻止执行。组件缺失、类型冲突、端口不兼容、未知参数、非法数值仍阻止执行。浏览器导出 JSON 时,只有通过组件兼容与参数校验的节点更新为当前版本;未通过者保留原版本。该过程不是旧方程的复现,也不能证明物理语义兼容,警告须明确可能失败或结果与实际不符。
HTTP XML 导出在 `X-Component-Version-Warnings` 返回 URL 编码 JSON(代码、说明、总数、最多前 10 个组件,头部限制 3800 字节);compile-model 在响应 `warnings` 中返回完整清单。没有版本差异时无需该响应头。
参数单位换算表唯一来源为 [`schemas/parameter-units.json`](../../schemas/parameter-units.json),网页和 Python 共用;表达式语法用跨语言同一组用例验收。
### 6.2 流式结果、保存与 CSV
C 程序把结果写为 JSON。数值数组使用 Ryu 的 binary64 往返编码,结合缓冲写出;流式路径附带字节索引,Python 读取较小元数据并直接拼接 series 字节,避免将全部曲线转成 Python 浮点列表再编码。HTTP 数据仍是普通 JSON 数组,没有改成二进制数组协议。
`simulate-stream` 的心跳是带 `heartbeat: true` 的 `event="progress"`,队列无消息时约每 5 秒发送;最终事件为 `result` 或 `error`。一个 NDJSON 事件可能分成多段字节 yield,消费者须按换行组装,不能按 HTTP chunk 一段一对象处理。同步 `/simulate` 默认仍物化完整曲线;任务 GET 遇到保留的原生 series 会以分段 JSON 返回,外部对象结构相同。
正式网页在接收和解析后将结果交给界面,`resultPersistence.ts` 再异步把曲线打包为 Float64Array 写入 IndexedDB,事务完成后才发布 sessionStorage 指针。因此“结果可查看”“浏览器持久化完成”和“下载文件保存”是不同事件。`.simresult` 仍通过 JSON 编码保存;不能把数值往返精度等同于所有导出文本逐字节一致。
网页 CSV 由 `resultCsvExport.ts` 向 Web Worker 分块传输数据并生成 Blob,默认不调用仍保留的 `/api/simulation-results/csv`。列顺序来自结果元数据,数值使用原始 SI,与图表显示单位分开;后端 CSV 接口为另一个可用入口。普通网页只能知道已生成 Blob/触发下载,无法通用地确认操作系统已将文件落盘,自动化测量需要额外下载完成信号。
### 6.3 任务生命周期
流式接口接受可选 `X-Simulation-Id`,省略时生成 ID,并在响应头返回。状态、取消标志与最终结果保存在当前 Python 进程内存中;服务重启即丢失,不是持久任务队列。终态任务超过 600 秒后,在注册新任务时清理,不是精确到时删除。取消 reason 为 `user/stalled`,断开事件流会请求 `stalled` 取消。
原生执行默认超时 300 秒;C 可返回部分结果,进程超过时限再加 5 秒,或收到取消后 5 秒仍未退出,Python 才强制结束并报错。不能把所有取消都承诺为立即终止且一定有完整结果文件。
## 7. 数据命名、单位和错误
- XML 属性和目录 JSON 主要使用 `camelCase`;
- ReactFlow 仿真设置保留现有 `t_start/t_stop/step/max_step` 名称;
- 不允许调用方自行猜测或转换字段命名;
- XML 和求解参数统一使用 SI 基准值;
- 实例 ID 和机器标识必须稳定,显示名称不能代替机器标识。
System XML 校验问题统一包含:
```json
{
"severity": "error",
"layer": "semantic",
"code": "ENDPOINT_PORT_UNKNOWN",
"message": "...",
"path": "...",
"line": 1
}
```
其中 `severity/layer/code/message` 是核心字段,`path/line` 可选。同步 HTTP 错误目前既
可能是 FastAPI 字符串 `detail`,也可能是带 `message/issues/diagnostics` 的结构化
`detail`;流式接口则使用 `event=error` 的 NDJSON 事件。统一错误响应模型尚未实现,
调用方必须同时处理这三种现有形状。
## 8. 版本变更规则
### 8.1 模型和组件库
- 修订版本:修复实现,不改变输入、端口和结果合同;
- 次版本:向后兼容地增加有默认值的参数、结果或能力;
- 主版本:删除或改名端口/参数,或者改变既有物理语义。
修改 `MODEL_TYPE` 视为新模型,不得用原标识承载不兼容合同。
以上版本含义用于分类变更影响;当前 System XML v3 对 `modelVersion` 执行完整字符串
精确匹配。因此即使只是修订或次版本变化,旧 XML 也会被拒绝,不能把“向后兼容”
理解成解析器会自动接受旧版本。
### 8.2 目录和 XML Schema
- 兼容澄清不改变 Schema 版本;
- 新增可选字段前必须验证旧消费者行为;
- 删除字段、改名或改变既有语义必须提高 Schema 版本;
- Schema、解析器、导出器、文档和合同测试必须在同一修改中更新。
### 8.3 HTTP API
当前 HTTP API 未版本化,因此不得在原路径上直接发布破坏性变更。需要破坏现有请求
或响应合同前,应先单独设计 API 版本命名空间、兼容周期和下线规则;该机制不在本文
本次整理范围内。
## 9. 旧版本和迁移边界
当前后端只接受 System XML v3:
- 不按 `schemaVersion` 自动选择 v1/v2 解析器;
- 不提供 v1/v2 到 v3 的自动迁移器;
- 不对不匹配的 `modelVersion` 做自动升级;
- 旧版本值只用于验证“不受支持输入应被拒绝”的边界测试。
ReactFlow 工程 JSON 接受 v1/v2,导出使用 v2 的统一单位规则。输入适配层允许版本警告后选择当前模型,内部执行 XML 仍精确匹配。LMECHN1/FORC 的既有显式迁移由网页和 Python 输入层对齐;PNVO 的布局兼容留在网页。不得把一般版本提示解释为自动保留旧模型的物理语义。
工程结构解析先于目录恢复。当前信号端口的 `positiveFlowDirection` 应缺省,不能从目录复制 `null`;连线须有 `data.isContactEdge` 布尔字段。后端请求模型接受的数据不一定通过前端 `parseProjectPayload()`,应使用实际浏览器导出的结构并验证往返,见[目录与工程协议](component-library-spec-v1.md)。旧 XML v1/v2 不属于上述型号专用处理的范围。
MECMAS21 的 `useFriction`、`strib` 等 AMESim 选项统一使用目录声明的原生编码:
`1` 表示“否/禁用”,`2` 表示“是/启用”。工程 JSON v1/v2、组件目录、System XML v3
和模型构造器不再接受或自动换算旧的 `0/1` 编码,也不再使用
`amesimParameterEncodingVersion` 触发猜测式转换。
## 10. 接口修改完成条件
任何接口合同变更至少应同时完成:
1. 更新唯一事实来源;
2. 根据兼容性决定是否提高对应版本;
3. 更新机器可读 Schema;
4. 更新当前规范和示例;
5. 增加请求、响应、拒绝边界和前后端联调测试;
6. 明确说明未实现的兼容或迁移能力。
## 2026-09-12 / 1.2.0 修订
实现工程组件版本警告、外部 JSON v2 一致单位语义、网页/HTTP/CLI 表达式归一化;XML v3、数值内核模型版本与求解精度未改变。验收记录见 `docs/other/2026-09-12-project-contract-acceptance.md`。