final version
This commit is contained in:
+503
@@ -0,0 +1,503 @@
|
||||
# EDFM Simulator 开发者快速入门
|
||||
|
||||
## 1. 先明确当前主线
|
||||
|
||||
这个项目当前的正式 GUI 路线是:
|
||||
|
||||
- 界面文件:`gui_support/app/EDFM_Simulator_App.mlapp`
|
||||
- 控制器:`gui_support/app/EDFMAppController.m`
|
||||
- 统一运行入口:`gui_support/runtime/run_case.m`
|
||||
|
||||
早期 `.m` 版界面:
|
||||
|
||||
- `gui_support/app/EDFMSimulatorApp.m`
|
||||
|
||||
现在只应视为历史过渡版本,不应再作为主要开发方向。
|
||||
|
||||
如果后续要继续开发,请默认以:
|
||||
|
||||
- `.mlapp + controller`
|
||||
|
||||
为主线。
|
||||
|
||||
## 2. 当前架构怎么理解
|
||||
|
||||
建议把项目拆成三层理解。
|
||||
|
||||
### 第一层:界面层
|
||||
|
||||
正式界面在:
|
||||
|
||||
- `EDFM_Simulator_App.mlapp`
|
||||
|
||||
你导出的可读版本在:
|
||||
|
||||
- `gui_support/app/edfm_simulator_app.txt`
|
||||
|
||||
这一层主要负责:
|
||||
|
||||
- 组件摆放
|
||||
- 组件命名
|
||||
- 回调入口
|
||||
|
||||
### 第二层:控制器层
|
||||
|
||||
控制器文件:
|
||||
|
||||
- `gui_support/app/EDFMAppController.m`
|
||||
|
||||
这一层主要负责:
|
||||
|
||||
- startup 初始化
|
||||
- 模板加载
|
||||
- 导入/导出配置
|
||||
- 导入/导出结果
|
||||
- UI 到 `config` 的写回
|
||||
- `config` 到 UI 的回填
|
||||
- 调用 `run_case(config)`
|
||||
- 更新结果页
|
||||
- 处理 wells 页表格逻辑
|
||||
|
||||
### 第三层:后端运行层
|
||||
|
||||
统一运行入口:
|
||||
|
||||
- `gui_support/runtime/run_case.m`
|
||||
|
||||
这一层负责:
|
||||
|
||||
- 根据 `config` 构造运行所需参数
|
||||
- 调用网格、裂缝、流体、井、schedule 相关逻辑
|
||||
- 最终调用原始求解器
|
||||
|
||||
## 3. 开发时应遵循的原则
|
||||
|
||||
以后开发尽量遵循下面的分工。
|
||||
|
||||
### `.mlapp` 负责什么
|
||||
|
||||
- 负责界面布局
|
||||
- 负责组件名称
|
||||
- 负责简单回调入口
|
||||
|
||||
### `EDFMAppController.m` 负责什么
|
||||
|
||||
- 负责业务逻辑
|
||||
- 负责数据同步
|
||||
- 负责调用后端
|
||||
|
||||
### `run_case.m` 负责什么
|
||||
|
||||
- 负责统一组织求解流程
|
||||
- 负责把 `config` 真正转换成后端求解器输入
|
||||
|
||||
简化理解就是:
|
||||
|
||||
- `.mlapp` 负责“长什么样”
|
||||
- `controller` 负责“点了之后做什么”
|
||||
- `run_case` 负责“真正怎么算”
|
||||
|
||||
## 4. 当前 `.mlapp` 已经具备什么
|
||||
|
||||
根据你导出的 `edfm_simulator_app.txt`,当前 `.mlapp` 已经不是空壳,而是已经完成了较完整的绑定:
|
||||
|
||||
- 已有 `Controller EDFMAppController`
|
||||
- 已有 `startupFcn`
|
||||
- startup 时会执行:
|
||||
|
||||
```matlab
|
||||
app.Controller = EDFMAppController(app);
|
||||
app.Controller.startup();
|
||||
```
|
||||
|
||||
- 顶部按钮已绑定到 controller
|
||||
- results 页按钮已绑定到 controller
|
||||
- wells 页表格增删与选中已绑定到 controller
|
||||
- 各类绘图参数变化已绑定到 controller
|
||||
|
||||
这意味着当前开发重点不应再是“重新解释 `.m` 版怎么跑”,而应是:
|
||||
|
||||
- 继续完善 `.mlapp` 布局
|
||||
- 保持组件名稳定
|
||||
- 在 `EDFMAppController.m` 中补逻辑
|
||||
|
||||
## 5. App Designer 中最常见的组件
|
||||
|
||||
下面只讲和当前 `.mlapp` 直接相关的组件。
|
||||
|
||||
### `uidropdown`
|
||||
|
||||
下拉框,用于:
|
||||
|
||||
- 模板选择
|
||||
- 模型选择
|
||||
- 绘图选项选择
|
||||
|
||||
常见属性:
|
||||
|
||||
- `Items`
|
||||
- `Value`
|
||||
- `ValueChangedFcn`
|
||||
|
||||
### `uibutton`
|
||||
|
||||
按钮,用于:
|
||||
|
||||
- 加载模板
|
||||
- 导入导出
|
||||
- 运行
|
||||
- 绘图
|
||||
- 表格行增删
|
||||
|
||||
常见属性:
|
||||
|
||||
- `Text`
|
||||
- `ButtonPushedFcn`
|
||||
|
||||
### `uitable`
|
||||
|
||||
表格控件,当前项目非常关键,尤其在 `Wells` 页。
|
||||
|
||||
当前主要有:
|
||||
|
||||
- `Well1Table`
|
||||
- `Well2Table`
|
||||
- `ScheduleTable`
|
||||
- `FractureWellLocationTable`
|
||||
|
||||
常见属性:
|
||||
|
||||
- `Data`
|
||||
- `ColumnName`
|
||||
- `RowName`
|
||||
- `ColumnEditable`
|
||||
- `CellSelectionCallback`
|
||||
|
||||
### `uitextarea`
|
||||
|
||||
多行文本框,当前仍大量用于输入 MATLAB 表达式,如:
|
||||
|
||||
- 数组
|
||||
- 矩阵
|
||||
- cell
|
||||
- 边界与裂缝文本数据
|
||||
|
||||
### `uieditfield`
|
||||
|
||||
数值编辑框,用于:
|
||||
|
||||
- 求解器参数
|
||||
- 初始条件
|
||||
- 部分单值参数
|
||||
|
||||
### `uiaxes`
|
||||
|
||||
绘图区,用于展示:
|
||||
|
||||
- 时间步图
|
||||
- Newtons vs Time
|
||||
- 部分结果图
|
||||
|
||||
## 6. 回调函数怎么理解
|
||||
|
||||
回调函数就是用户操作组件时触发的函数。
|
||||
|
||||
在当前 `.mlapp` 主线下,推荐让回调函数保持很薄,只做“转发”。
|
||||
|
||||
例如按钮回调:
|
||||
|
||||
```matlab
|
||||
function onRun(app, event)
|
||||
app.Controller.runCurrentConfig();
|
||||
end
|
||||
```
|
||||
|
||||
这种写法的好处是:
|
||||
|
||||
- `.mlapp` 内代码简单
|
||||
- 业务逻辑集中在 controller
|
||||
- 后续换布局时不容易把逻辑打散
|
||||
|
||||
## 7. startup 机制
|
||||
|
||||
当前 `.mlapp` 的关键启动逻辑是:
|
||||
|
||||
```matlab
|
||||
function startupFcn(app)
|
||||
app.Controller = EDFMAppController(app);
|
||||
app.Controller.startup();
|
||||
end
|
||||
```
|
||||
|
||||
App 创建完成后,`runStartupFcn(app, @startupFcn)` 会触发这段逻辑。
|
||||
|
||||
它的作用是:
|
||||
|
||||
1. 给 app 挂上 controller
|
||||
2. 初始化下拉框
|
||||
3. 设置表格
|
||||
4. 加载默认模板
|
||||
5. 刷新界面
|
||||
|
||||
如果启动后界面不正常,先查这条链路,而不是先去看求解器。
|
||||
|
||||
## 8. 组件名为什么这么重要
|
||||
|
||||
`EDFMAppController.m` 大量依赖组件名访问控件,例如:
|
||||
|
||||
```matlab
|
||||
obj.App.(propName)
|
||||
```
|
||||
|
||||
这意味着:
|
||||
|
||||
- 组件名一旦变了
|
||||
- controller 里对应逻辑就可能失效
|
||||
|
||||
所以在 App Designer 里改界面时,第一原则是:
|
||||
|
||||
- 不要随意修改已有组件名
|
||||
|
||||
如果确实要改名,必须同步修改 controller 中所有相关引用。
|
||||
|
||||
## 9. 修改界面的正确方式
|
||||
|
||||
## 9.1 纯布局修改
|
||||
|
||||
如果只是改:
|
||||
|
||||
- 位置
|
||||
- 大小
|
||||
- 标签文字
|
||||
- 页签布局
|
||||
|
||||
那么直接在 App Designer 中改 `.mlapp` 即可。
|
||||
|
||||
这种修改通常不需要动 controller。
|
||||
|
||||
## 9.2 新增一个控件
|
||||
|
||||
如果新增了一个输入控件,通常要做四件事。
|
||||
|
||||
### 第一步:在 `.mlapp` 中添加控件
|
||||
|
||||
例如增加:
|
||||
|
||||
- 一个下拉框
|
||||
- 一个按钮
|
||||
- 一个数值框
|
||||
|
||||
### 第二步:给它起稳定名字
|
||||
|
||||
命名风格建议沿用现有项目,例如:
|
||||
|
||||
- `MyNewDropDown`
|
||||
- `MyNewButton`
|
||||
- `MyNewEditField`
|
||||
|
||||
### 第三步:添加回调
|
||||
|
||||
例如按钮:
|
||||
|
||||
```matlab
|
||||
function MyNewButtonPushed(app, event)
|
||||
app.Controller.doSomething();
|
||||
end
|
||||
```
|
||||
|
||||
### 第四步:在 controller 中补逻辑
|
||||
|
||||
这是关键步骤,不要把逻辑只写在 `.mlapp` 里。
|
||||
|
||||
## 10. 新增一个参数字段时怎么改
|
||||
|
||||
假设新增一个参数,推荐按下面顺序。
|
||||
|
||||
### 1. 先决定它在 `config` 中的位置
|
||||
|
||||
例如:
|
||||
|
||||
- `config.solver.xxx`
|
||||
- `config.wells.xxx`
|
||||
- `config.flow.multi_component.xxx`
|
||||
|
||||
### 2. 修改默认配置
|
||||
|
||||
编辑:
|
||||
|
||||
- `gui_support/config/create_empty_config.m`
|
||||
|
||||
### 3. 修改模板
|
||||
|
||||
编辑:
|
||||
|
||||
- `gui_support/templates/load_case_template_case01.m`
|
||||
- 其他需要的 `caseXX`
|
||||
|
||||
### 4. 在 `.mlapp` 中增加控件
|
||||
|
||||
并给出稳定组件名。
|
||||
|
||||
### 5. 在 controller 中补两处
|
||||
|
||||
必须同时补:
|
||||
|
||||
- `refreshUIFromConfig()`
|
||||
- `pushUIToConfig()`
|
||||
|
||||
这两个函数分别负责:
|
||||
|
||||
- `config -> UI`
|
||||
- `UI -> config`
|
||||
|
||||
如果只改一边,参数就会出现“显示了但不会保存”或者“保存了但不会显示”的问题。
|
||||
|
||||
### 6. 如果运行要用到,再改 `run_case.m`
|
||||
|
||||
只有到这一步,才去改后端运行逻辑。
|
||||
|
||||
## 11. 如何实现一个完整功能
|
||||
|
||||
这里给一个适用于本项目的通用开发流程。
|
||||
|
||||
### 第一步:先明确功能边界
|
||||
|
||||
先回答这几个问题:
|
||||
|
||||
- 用户从哪个页面进入
|
||||
- 输入是什么
|
||||
- 点击哪个按钮
|
||||
- 结果显示在哪里
|
||||
|
||||
### 第二步:落到统一 `config`
|
||||
|
||||
不要直接从控件把值硬塞到求解器里,优先让它进入统一 `config`。
|
||||
|
||||
### 第三步:补齐 UI 和 config 的双向同步
|
||||
|
||||
需要改:
|
||||
|
||||
- `.mlapp`
|
||||
- `refreshUIFromConfig()`
|
||||
- `pushUIToConfig()`
|
||||
|
||||
### 第四步:补齐运行逻辑
|
||||
|
||||
需要改:
|
||||
|
||||
- `run_case.m`
|
||||
- 以及必要的后端函数
|
||||
|
||||
### 第五步:补齐结果显示
|
||||
|
||||
如果功能有结果输出,还需要改:
|
||||
|
||||
- `refreshResults()`
|
||||
- 结果页控件
|
||||
- 可能的绘图逻辑
|
||||
|
||||
## 12. Wells 页是当前最需要小心的地方
|
||||
|
||||
当前 `Wells` 页已经从原来“纯文本输入”往 `uitable` 方向演进,这是比较正确的方向。
|
||||
|
||||
开发时重点注意三件事。
|
||||
|
||||
### 1. 表头要明确
|
||||
|
||||
当前 controller 已统一设置这些表的 `ColumnName`。
|
||||
|
||||
如果你在 `.mlapp` 中重建表格或改字段,记得同步更新 controller 中的表头配置。
|
||||
|
||||
### 2. 默认行也要同步
|
||||
|
||||
新增表格字段后,还要同步修改:
|
||||
|
||||
- `getDefaultRowForTable()`
|
||||
|
||||
否则新增行时默认数据长度会不匹配。
|
||||
|
||||
### 3. 注意字符串和数值混合解析
|
||||
|
||||
例如 `ScheduleTable` 既有:
|
||||
|
||||
- 井名
|
||||
- `open/close`
|
||||
- `inj/pro`
|
||||
- 数值
|
||||
- 可能为空的项
|
||||
|
||||
所以改表结构时,需要同步考虑:
|
||||
|
||||
- `setTableValue`
|
||||
- `getTableValue`
|
||||
- `parseUITableData`
|
||||
- `parseTableCell`
|
||||
|
||||
## 13. 开发者在 App Designer 中最常见的修改场景
|
||||
|
||||
### 场景一:改一个按钮文字
|
||||
|
||||
直接在 App Designer 中改 `Text` 即可。
|
||||
|
||||
### 场景二:改布局
|
||||
|
||||
直接在 App Designer 中调整控件位置、网格和页签。
|
||||
|
||||
### 场景三:加一个按钮
|
||||
|
||||
推荐做法:
|
||||
|
||||
1. 在 `.mlapp` 里加按钮
|
||||
2. 生成回调函数
|
||||
3. 回调函数里调用 controller
|
||||
4. 在 controller 实现业务逻辑
|
||||
|
||||
### 场景四:加一个新表字段
|
||||
|
||||
推荐做法:
|
||||
|
||||
1. 先改 `.mlapp` 里的表展示
|
||||
2. 再改 controller 表头
|
||||
3. 再改默认行
|
||||
4. 再改 config 和模板
|
||||
5. 最后确认后端是否需要这个字段
|
||||
|
||||
## 14. 现在不推荐的做法
|
||||
|
||||
以下做法不推荐继续扩大。
|
||||
|
||||
### 1. 继续把主逻辑写回 `.m` 版老界面
|
||||
|
||||
因为项目主线已经切到 `.mlapp`。
|
||||
|
||||
### 2. 在 `.mlapp` 回调里直接堆业务逻辑
|
||||
|
||||
因为这样会让界面代码越来越难维护。
|
||||
|
||||
### 3. 改了组件名但不改 controller
|
||||
|
||||
这会直接导致绑定失效。
|
||||
|
||||
## 15. 当前最值得先看的文件
|
||||
|
||||
如果你是新接手开发者,建议按下面顺序看代码。
|
||||
|
||||
1. `gui_support/app/edfm_simulator_app.txt`
|
||||
2. `gui_support/app/EDFMAppController.m`
|
||||
3. `gui_support/config/create_empty_config.m`
|
||||
4. `gui_support/templates/load_case_template_case01.m`
|
||||
5. `gui_support/runtime/run_case.m`
|
||||
6. 对应算例目录下的 `main1.m`
|
||||
|
||||
这个顺序能最快建立“界面字段 -> config -> 后端”的对应关系。
|
||||
|
||||
## 16. 一句最实用的开发建议
|
||||
|
||||
以后每次开发,都先问自己三件事:
|
||||
|
||||
1. 这个改动在 `.mlapp` 里对应哪个组件
|
||||
2. 这个组件在 `config` 里对应哪个字段
|
||||
3. 这个字段在 `run_case` 或后端里最终去哪儿
|
||||
|
||||
只要这三层关系始终清楚,项目就不会乱。
|
||||
|
||||
Reference in New Issue
Block a user