日期:2026-06-23 · 当前任务:operate/control 双进程拆分 · 本专项 = 昨日建议「配置收敛」 范围铁律:只碰 operate↔control 的 7 个共享连接键 + 删 operate 死键。front、Java 微服务、凭据组、oplog-config.json 本轮不动。
operate/control 双进程拆分后,同一台机器上两个进程各自带一份 App.config,其中约 25 个键几乎逐字重复。control 那份(control/ivf_tl_ControlHost/App.config)的注释甚至明写「内容与 operate App.config 保持同步」——这是拆分把早先「operate = 单一数据源」(M5-01/M5-02 治理意图)又打破后留下的伤疤。
真实痛点:换中间件服务器、或调连接参数时,要在 operate + control 两份文件里各改一遍,漏一处 → 两进程连接目标不一致 → 行为打架,且不易发现。
已坐实的现状(codegraph 核实):
control/ivf_tl_Control/AppData.cs:47-133):读全部 13 个换气/CCD 业务键 + 连接组 + 凭据。ivf_tl_Operate/AppData.cs:77-112):只读 urlIp/urlPort/mqttIp/mqttPort/outInter;kfkaIP/kfkaPort 在 App.xaml.cs 读;凭据/cacheDisk/Language/houseEnabled 在登录/路径处读。operate 不读那 13 个换气/CCD 键 → 它们是合并单进程时代的死副本。UnifiedConfigViewModel + AppConfigHelper.SaveAll),编辑连接组+本机+凭据,但 AppConfigHelper.Save 经 OpenExeConfiguration 只写 operate 自己的 config,不知道 control 还有一份 → 这就是单一数据源缺口的代码体现。SetApp/SaveEncrypted + 启动 MigratePlaintextCredentials),control 那份是明文 123456 → 本身已不一致。目标:
非目标(本轮明确不做):
server.ip、oplog-config.json 的收敛。<appSettings file=> 只读合并,写入由 operate 收口。
file= 而非 configSource:file= 属性支持 .. 上级相对路径(control 在 operate 子目录,要指向父目录的共享文件);configSource 禁止 ..。AppConfigHelper。operate 输出根目录/
├─ ivf_tl_Operate.exe
├─ ivf_tl_Operate.dll.config ← 只留 operate 独有键 + 引用共享文件
├─ tl-shared.config ← 【新增·唯一数据源】只放共享连接组
└─ control/
├─ ivf_tl_ControlHost.exe
└─ ivf_tl_ControlHost.dll.config ← 只留 control 独有键 + 引用 ..\tl-shared.config
tl-shared.config(独立 appSettings 片段,这是 file= 期望的格式):
<?xml version="1.0" encoding="utf-8"?>
<appSettings>
<add key="urlIp" value="http://127.0.0.1"/>
<add key="urlPort" value="10010"/>
<add key="mqttIp" value="192.168.0.108"/>
<add key="mqttPort" value="1883"/>
<add key="kfkaIP" value="192.168.0.108"/>
<add key="kfkaPort" value="9092"/>
<add key="outInter" value="0"/>
</appSettings>
operate App.config:<appSettings file="tl-shared.config"> …operate 独有键 inline… </appSettings>
control App.config:<appSettings file="..\tl-shared.config"> …control 独有键 inline… </appSettings>
| 类别 | 键 | 处置 |
|---|---|---|
| 共享组(7) | urlIp, urlPort, mqttIp, mqttPort, kfkaIP, kfkaPort, outInter | 从两份 App.config 删除,只存 tl-shared.config |
| operate 独有 | userName, passWord, engineerPwd, tlNum, cacheDisk, Language, houseEnabled, autoFocus, controlPort, controlExePath | 留 operate config |
| control 独有 | csTime, gbTime, VentNum, VentPre, VentWaitTimeB, VentWaitTimeD, AutoWaitTime, CCDAutoWaitTime, CCDError, CCDFailedNumber, CCDFailedWaitTime, QueuAir, StopPro, cacheDisk, 凭据(现状) | 留 control config |
| operate 死键(删) | csTime, gbTime, VentNum, VentPre, VentWaitTimeB, VentWaitTimeD, AutoWaitTime, CCDAutoWaitTime, CCDError, CCDFailedNumber, CCDFailedWaitTime, QueuAir | 从 operate App.config 删除(operate 不读;实现时再 grep 全 operate 兜底确认无其它消费点) |
注:cacheDisk 两进程都可能用,各自保留 inline(非"换机要改"的连接类,不进共享文件,无漂移风险)。autoFocus/StopPro/QueuAir 等非连接键不进共享文件。死键删除以"operate 侧确无消费点"为前提,实现时逐键 grep 坐实再删。
读(两进程 C# 零改动):
<appSettings file="…tl-shared.config">,.NET 启动时把共享文件的键合并进 ConfigurationManager.AppSettings。AppData/AppConfigHelper.GetString、control 的 AppData 照旧 AppSettings["urlIp"] 即读到。写(只动 operate 侧 AppConfigHelper):
AppConfigHelper 把它们写进 tl-shared.config(用 XDocument 直接读写这个小文件,绕开 ConfigurationManager 写回 file= 的不确定行为),写后 ConfigurationManager.RefreshSection("appSettings") 让本进程即时生效。OpenExeConfiguration 落 operate 自己的 config。AppConfigHelper 内部维护一个"共享键集合",Save(key,value) 按 key 归属分流到 tl-shared.config 或 operate config;SaveAll 不改调用方,只改底层落点。control 侧:只在 ControlHost/App.config 加 file="..\tl-shared.config",C# 一行不改。
tl-shared.config 加进 ivf_tl_ControlHost 或 operate 工程,以 <None><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></None> 随 operate 输出根目录;control 子目录不需要拷贝(运行期靠相对 ..\ 指过去)。
control\ 子目录;tl-shared.config 落在 operate 根 = control 的父目录,..\tl-shared.config 命中。tl-shared.config 不存在,用当前 operate config 里那 7 个共享键的现值生成它。已存在则不动。开发环境/双进程部署指南.md + 连接配置清单-换服务器必读.md 第三节:C# 侧换机由"改 operate+control 两份"改为"只改 tl-shared.config 一份";操作端逻辑与配置全景.md / control-逻辑与配置全景.md 的配置键章节标注共享文件来源。file= 指向不存在文件时 .NET 行为是"忽略外部文件、只用 inline"——故首启迁移要尽早生成它;且 AppConfigHelper.GetString 已有缺键回退默认值(不裸崩)。AppConfigHelper 写操作 try 兜底(沿用现有"写失败不抛出"策略),不阻断 UI/启动。..\tl-shared.config,值为 operate 上次保存的;持久化在磁盘,标准场景成立。AppSettings 是否即时反映合并后的外部文件值 —— 列入真机验证项(若不即时,UI 保存提示"重启生效"即可,不阻断)。<appSettings file="..\x.config"> 在子目录进程能读到父目录共享文件键(真机 Release 编译+跑)。不过则当场退回方案二。AppConfigHelper 共享键读写(写 tl-shared.config→读回一致;非共享键仍落 operate config 不串);死键删除后 operate 编译 0 错。tl-shared.config 的 urlIp/mqtt/kfka → operate 读到新值 + 看门狗/operate 拉起 control 也读到同一新值 → /status 连接正常、operate Release 真外壳登录通;control 启动 0 缺键 NPE / 0 DbException。file= + .. 不被支持(低概率)→ 第一步硬验证拦截,退方案二。file= 合并的即时生效问题 → 用 XDocument 直写 + RefreshSection 规避;真机验证兜底。