Aspen HYSYS 二次开发系列教程(16):Excel/数据库数据交换——方向性约定与列命名纪律
版本声明块
- 软件:aspenONE V15(基线,同第 1 篇);文档现实:Customization Guide V7.3(2011-03)为公开手册基线
- 语言/环境:Python(openpyxl/数据库驱动) + COM 自动化;未证实成员名以 dir() 守卫处理(铁律 7)
- 本文目标:把"数据交换"从功能描述升级为可复现的工程约定——方向、命名、所有权三条纪律
一句话结论:HYSYS 与 Excel/数据库的数据交换是双向的——Excel→HYSYS 送输入参数与工况表,HYSYS→Excel/数据库回写结果;批量场景的三条纪律是:固定列命名与方向注释、一个 case 只允许一个写入源(第 10 篇双写入源冲突)、每条回写行携带审计链八列(第 14 篇)——缺任何一条,报表错位或结果不可复现。
〇、本篇要解决的认知问题
- 数据交换的两个方向在脚本里分别对应哪些操作?"当前值 vs 基线值"口径陷阱在哪一步出现?
- 列命名纪律的最小格式是什么——为什么"哪列是输入哪列是输出"必须写进文件本身?
- 双写入源冲突的工程后果是什么——如何检测与防护(第 10 篇机制的数据层延伸)?
- 数据库回写时 run_ref、ts、version 三列如何保证可追责(第 14 篇八列的落库形态)?
- 批量扫描表的可复现格式是什么——第三方重跑必须能对齐哪些字段?
一、机制解析
1.1 两个方向的脚本映射(价值锚点:方向混淆是"输入列被当输出读"的第一根因)
方向A(输入): Excel/DB → COM 对象写入(仅输入侧成员——第 9 篇输入/解边界) 方向B(回写): COM 对象读取(仅 status OK 时——铁律 6)→ Excel/DB 记录行(含审计链八列——第 14 篇)口径陷阱出现点:扫描过程中"当前值 vs 基线值"——Excel 表里同一物理量既有基线又有扫描覆盖时,若方向注释缺失,回写可能把"扫描前的基线"当"结果"记录(第 10 篇机制在数据层的复现)。判定标准(练习用):任取一行能指出该 value 来自哪一次 Run(run_ref 列可还原——第 14 篇)。
1.2 列命名纪律的最小格式
col_name, direction, member_confirmed(unit_set), notes T_in, INPUT→HYSYS, dir() confirmed or 未证实, ℃/K 口径记录(铁律 7) purity, HYSYS→OUTPUT, status OK only, mole basis — record explicitly为什么必须写进文件本身:报表错位事故里最常见的场景是"两个团队各自解读同一列"——方向与口径在文件内自描述才是可复现性(第 14 篇八列的表头化)。经验法则:回写前先做一次手工抽查(随机抽 3 行对照 GUI/日志)——这是数据交换层的输出层断言(第 13 篇合流)。
1.3 双写入源冲突的检测与防护
| 场景 | 后果 | 防护 |
|---|---|---|
| ASW 与脚本同时改同一对象 | 谁最后写入谁生效,结果不可复现(第 10 篇) | 一个 case 单一写入源;所有权登记(第 19 篇) |
| 两个脚本实例并发回写同一表 | 行级交错、run_ref 与实际写入者不一致 | 串行化或按 case 分表;审计日志记录写入源类型与时刻(第 13/15 篇) |
判定线:任一 FAIL 行能还原"哪一次 Run、哪个口径、哪个版本、哪个写入源"——四要素齐即可追责(第 14 篇八列在此增加 writer_source)。
1.4 数据库回写的落库形态(可追责三列)
run_ref(对应哪一次 Run 或哪个检查)、ts(含时区)、version(实际运行版本——铁律 1);加上实例模式与许可形态记录(第 15 篇部署侧)。判定标准:数据库任意一行可还原四要素——缺列即不可追责(1.3)。
二、完整代码与逐行剖析
2.1 带方向声明与审计链的 Excel 回写管线(复制即跑;参数为示例值)
importwin32com.client,datetime,csv# ——方向声明表(1.2 纪律的文件化形态——列自描述)——COLS=[("T_in","INPUT→HYSYS"),("purity","HYSYS→OUTPUT")]defexchange_row(sim,stream_name,t_value):row={"param":t_value,"value":None,"status":"OK","ts":datetime.datetime.now(datetime.timezone.utc).astimezone().isoformat(),"version":"<actual running version — record from machine>",# 铁律 1(第 15 篇)"unit_set":"<record>","basis":"<record>","run_ref":None,"writer_source":"script",# 所有权列(1.3)"direction":"HYSYS→OUTPUT"}stream=sim.Flowsheet.MaterialStreamFlowsheetStreams.Item(stream_name)# 名称契约(第 19 篇)# ——方向A:写入仅允许输入侧成员(第 9 篇边界——把"解"当输入写会被求解覆盖)——if"Temperature"notindir(stream):row.update(status="CODE_ERROR",value="member not confirmed (铁律 7)");returnrow stream.Temperature.Value=t_valuetry:sim.Run()# 成员名以本机为准(铁律 7)exceptExceptionase:row.update(status="CODE_ERROR",value=str(e));returnrow props=dir(sim)forcandin("ErrorList","Messages"):# 未证实成员——存在才检查(铁律 7)ifcandinprops:err=getattr(sim,cand)cnt=getattr(err,"Count",None)ifcntandcnt>0:row.update(status="FAIL",run_ref=cand);returnrow# 业务失败不取值(铁律 6)# ——方向B:回写仅 status OK 时(输出层断言——第 13/14 篇合流)——row["value"]=getattr(stream.Temperature,"Value",None)row["run_ref"]="clean run"returnrow app=win32com.client.Dispatch("HYSYS.Application")# 位数一致(铁律 3);实例模式记录进文档(第 15 篇)sim=app.ActiveDocument# 服务场景用 Documents 显式绑定(第 2 篇)rows=[exchange_row(sim,"Feed",t)fortin(20,25,30)]withopen("exchange.csv","w",newline="")asf:w=csv.DictWriter(f,fieldnames=list(rows[0].keys()));w.writeheader();w.writerows(rows)# ——手工抽查判定线(1.2):随机抽 3 行对照 GUI/日志——本脚本把抽查结果写入同一文件注释区——print("sample check:",[(r["param"],r["value"])forrinrows[:3]])逐行要点:方向 A/B 在代码里显式分开——输入侧写入与输出侧取值各对应一条边界纪律;writer_source列把所有权从口头约定变成可查询字段(1.3)。
三、常见报错与排查
- 输入列被当输出读(报表错位)—— 现象:回写值与预期不符。根因:方向注释缺失或"当前值 vs 基线值"口径混淆(1.1)。解法:列自描述表(1.2);回写前手工抽查 3 行。
- 双写入源冲突—— 现象:同一对象值不可复现、run_ref 与写入者不一致(1.3)。根因:ASW/脚本或两脚本实例并发写同一表。解法:一个 case 单一写入源;按 case 分表或串行化;所有权登记进可复现性文档(第 19 篇)。
- 把"解"当输入写入—— 现象:Run 后值被覆盖(第 9 篇机制在数据层的复现)。根因:方向 A 越界。解法:仅输入侧成员可写——代码里用 dir() 确证后再访问(铁律 7)。
- 数据库行不可追责—— 现象:某行无法还原来自哪一次 Run。根因:run_ref/ts/version 三列缺失(1.4)。解法:八列+writer_source 齐备后复跑;缺列即判定不通过。
四、动手练习
- 方向与抽查复现:按 2.1 完成并随机抽 3 行对照 GUI/日志。判定标准:三行都能还原 run_ref;列自描述表(1.2)随文件交付——第三方重跑可对齐字段即达标。
- 双写入源实验:制造一次 ASW 与脚本并发写同一对象。判定标准:能观察到"谁最后写入谁生效"的不可复现现象;恢复单一写入源后连续 10 次输出一致——写出所有权登记的最小三列(writer_source、ts、case)。
- 思考题(无标准答案):为什么"基线值 vs 当前值"在扫描场景必须显式区分?验证要点清单:① 写出扫描覆盖与基线的两个存储位置;② 举一个回写把基线当结果的场景;③ 给出判定线——什么条件下允许省略基线列(单点、无覆盖时)。
五、小结与下一篇预告
本篇把数据交换升级为三条工程纪律:方向自描述、单一写入源、可追责八列。第 17 篇把闭环五步接上外层优化器——黑盒不可微的代价、惩罚值与差分路线的选择;第 18 篇进入数字孪生接口——实时数据接入时"输入=测量、输出=估计"的口径分离;第 19 篇收拢可复现性三件套与回归测试体系。
本篇认知问题回显(FAQ)
Q1:HYSYS 与 Excel/数据库的数据交换两个方向分别对应什么脚本操作?
A:方向 A——Excel/DB 向 HYSYS 送输入参数与工况表(仅允许写输入侧成员);方向 B——HYSYS 向 Excel/DB 回写结果(仅 status OK 时读取,回写行携带审计链八列)。
Q2:"当前值 vs 基线值"口径陷阱出现在哪一步?
A:扫描过程中同一物理量既有基线又有扫描覆盖时——若方向注释缺失,回写可能把扫描前的基线当结果记录;判定标准是任取一行能指出该 value 来自哪一次 Run(run_ref 列可还原)。
Q3:双写入源冲突的工程后果是什么?如何防护?
A:同一对象被 ASW 与脚本(或两个脚本实例)同时写入时谁最后写入谁生效,结果不可复现、审计链断裂;防护是一个 case 只允许一个写入源、按 case 分表或串行化,并把 writer_source、时刻、case 登记进可复现性文档。
Q4:列命名纪律的最小格式是什么?为什么必须写进文件本身?
A:每列声明 direction(INPUT→HYSYS 或 HYSYS→OUTPUT)、成员确证状态与口径——方向与口径在文件内自描述才能避免两个团队各自解读同一列;回写前随机抽 3 行对照 GUI/日志是数据交换层的输出层断言。
Q5:数据库回写的可追责三列是什么?
A:run_ref(对应哪一次 Run 或哪个检查)、ts(含时区)、version(实际运行版本)——加上 writer_source 与实例模式、许可形态记录,任意一行可还原"哪一次 Run、哪个口径、哪个版本、哪个写入源"才叫可追责。