2026-06-17-M5-配置监控子计划.md 35 KB

M5 · 配置统一管理 + 只读服务监控 + 通讯稳定子计划

编制日期:2026-06-17 · 里程碑:M5 · 对应需求 7 / 9 / 10(兼顾 12)· 决策 🔶D8(配置分层边界)/ 🔶D10(实时指标) · 风险 R8(配置迁移)/ R9(实时通讯) 父需求:../00-需求总览.md §3(需求 7/9/10)、§5(🔶D8/🔶D10)、§6(R8/R9) · 方案权威:../需求文档/06-参数配置统一管理.md../需求文档/05-实时通讯与服务监控.md 改造对象工程:ivf_tl_operate_2.0/ivf_tl_Operate/(合并后主进程,前台 UI + 设置页)、ivf_tl_control_2.0/ivf_tl_Control/ivf_tl_control_2.0/ivf_tl_ServicesImpl/(后台托管服务,监控数据来源)


Goal

把当前「配置散落四处、明文密码、operate/control 各存一份、运维只能改 XML、断线无 UI 提示、心跳被屏蔽」的状态,改造为:

  1. 参数配置统一管理(需求 9):合并后单进程内收拢配置入口到设置页统一配置页,按「数据库层 / 本地层」合理默认分层(标 🔶D8 待确认),去重(operate/control 两份 App.config 同名键合一、单一数据源)、密码加密(App.config 明文 passWord / MQTT 凭据 / 硬编码 tl13579 纳入治理),原"只能改 XML"的项搬到界面可编辑。
  2. 只读服务监控页(需求 7):设置页新增纯只读"服务运行监控"页,从托管的 control 内存/事件取数,展示各服务/连接/队列/磁盘状态。不新增任何控制/修改/删除(范围边界)。
  3. 链路健康 + 通讯稳定(需求 10):恢复被 return; 屏蔽的 MQTT 保活心跳、链路健康可视化(服务器/MQTT/上报队列/最后成功通讯时间)、断线显著提示(不"假装实时")、图片补传机制补强(落盘队列 + 去重 + 断网累积兜底)。

配置统一只动「读写来源 + 入口聚合 + 加密」,不改既有业务参数语义;监控页只读不写;通讯改造只补强保活/提示/补传,不改 MQTT 主题与上报报文结构。 既有设置项(HEPA/保养时间、对焦时间、帧率、抠图数、舱室/对焦/调试入口、密码校验语义)一律保留(00-需求总览.md §8「既有设置项不删只增统一入口」)。


Architecture(现状核对结论,已用 codegraph/Read 核对存在)

A. 配置散落现状(需求 9 / R8)—— 已逐处核对

来源 精确位置 内容 问题
operate 本地 App.config ivf_tl_operate_2.0/ivf_tl_Operate/App.config:3-29 autoFocus(实为烧录换气)/outInter/userName/passWord(明文123456)/tlNum/urlIp/urlPort/mqttIp/mqttPort/houseEnabled/Language 手改 XML;明文密码;键名词不符义
control 本地 App.config ivf_tl_control_2.0/ivf_tl_ControlTest/App.config:3-46 CCDError/csTime/gbTime/VentNum/VentPre/VentWaitTimeB/D/AutoWaitTime/CCDAutoWaitTime/CCDFailedWaitTime/CCDFailedNumber/QueuAir/StopPro/userName/passWord(明文123456)/cacheDisk/urlIp/urlPort/kfkaIP/kfkaPort/mqttIp/mqttPort/Language 同名键与 operate 重复且默认值不一致(operate urlIp=192.168.1.92 vs control 192.168.0.91;mqttIp 同样不一致)
数据库 TLSetting(服务器下发) operate SettingPageView.xaml.cs:52 SettingCommonApi:116 CommonUpdateApi;字段见 tLSettingCommon.autoFocusTime/videoFps/cropNum/heapDate/keepDate/cleanSurfaceData/tmpDir 对焦时间/帧率/抠图数/换气/HEPA/保养/清理天数/缓存盘 等 部分现场可改(设置页),但与本地配置来源割裂
硬编码口令 SettingPageView.xaml.cs:109 MiMa()textBox.Password.Trim() == "tl13579" 全设备统一明文工程师口令,7 处入口共用(:167/190/213/237/256/291/323) 明文、不可改、无权限分级
下位机 EEPROM / calibration.json 见 03 / M2 计划 well 水平位/Z 零点/灯光/阀门时间;自动对焦标定 本计划只纳入只读展示,写入归 03/M2,不在 M5 动

配置读取方式(核对到的精确调用点)

读取点 写回点
operate urlIp/urlPort/mqttIp/mqttPort/outInter ivf_tl_operate_2.0/ivf_tl_Operate/AppData.cs:79-82 构造函数
operate userName/passWord/tlNum LoginWindow.xaml.cs:36-38(回填登录框) AppData.cs:159-167 SetApp(...)(OpenExeConfiguration) + :169-197 setAppConfig(...)(直接改 ivf_tl_Operate.dll.config XML)
operate Language App.xaml.cs:57
operate cacheDisk(合并后) MainWindow.xaml.cs:96 启动托管 control 时读 → PathHelper.pan
control 全部业务参数 + urlIp/kfkaIP/mqttIp/mqttPort ivf_tl_control_2.0/ivf_tl_Control/AppData.cs:49-74 构造函数(ConfigurationManager.AppSettings[...]
control userName/passWord/cacheDisk ivf_tl_ControlTest/Window1.xaml.cs:53-55,91-92(原 control 登录窗,M1 已去 UI;合并后由 MainWindow.xaml.cs:96 单登录透传)

去重关键:M1 合并后两进程变一进程,ConfigurationManager.AppSettings 实际只读主进程(operate)的 ivf_tl_Operate.dll.config——control AppData.cs:49-74 在同进程内读到的将是 operate 的 config,control 那份 ControlTest/App.config 在合并后不再生效。因此「去重」不是手工比对两文件,而是:把 control 独有键并入 operate config(单一文件),并补回 control 需要、operate config 现在缺的键(kfkaIP/kfkaPort/csTime/gbTime/VentNum/... 等约 16 个),否则 int.Parse/.ToString() 会 NPE(R8「改一处漏一处导致服务起不来」的具体形态)。

B. 实时通讯 / 服务监控现状(需求 7/10 / R9)—— 已逐处核对

关注点 现状(核对到的精确位置) M5 处理
MQTT 保活心跳 ivf_tl_control_2.0/ivf_tl_ServicesImpl/MqttServices/MqttService.cs:79-80Task.Run(ClientMqtt); return;——return; 后的 TryPingAsync 保活循环(:81-113)整段死代码不执行 M5-04:恢复保活心跳
断线补连 MqttService.cs:197-208 _mqttClient_DisconnectedAsync 已在断开回调里 ClientMqtt() 重连;但 :65 WithCleanSession() 断线期间漏发不补 M5-04:保留重连 + 评估 CleanSession(标 D10/M7)
断线 UI 提示 断线仅 MqttAlarm?.Invoke(...) 写日志(:202-203),operate/control 均无 UI 失联提示 M5-03/04:链路健康卡片 + 显著失联提示
在线态来源割裂 operate MainPageViewModel.StartThread:741-773 HTTP 5s 轮询 TlInfoTimeApi(运行/保养/HEPA) + 报警计数;舱室态走 MQTT RecMqttMessage:512。broker 掉线 HTTP 仍正常 → 显"在线"但数据停更 M5-03:链路健康同页区分两源 + 最后通讯时间
control 实时上报线程 ivf_tl_Control/AppData.cs:273 StartUpLoadImage(图片)、:316 StartSendMqttMsg(舱室态1s)、:372 StartSendHistoryMsg:435 StartPushMessageThread;均 StartAsync():221 内拉起 监控数据来源(只读取状态,不改逻辑)
图片补传现状 AppData.cs:273-311 StartUpLoadImage:每 5s 扫描落盘目录 *.jpgKafkaUploadImageAsync:1258-1345:成功才 File.Delete(:1313-1317),失败 KafkaAlarm 保留文件下轮重扫;imageDTO 为空则 File.Move 到 error 目录(:1290-1293) M5-04:已具「失败保留+重扫重试」雏形,但无显式队列/无去重防重传/无断网累积上限——补强为落盘队列可视化 + 去重 + 兜底(05 §2.5「无明确补传队列」)
监控取数桥 M1 已在 operate 进程内托管 control:MainWindow.xaml.cs:103-125 new StartMain().StartRun()ivf_tl_Control.AppData.Instance 单例同进程可直接访问 M5-02:监控页直接读 ivf_tl_Control.AppData.Instance 暴露的只读状态
监控可用数据源(control 内存/事件,已核对存在) AppData.cs:1461 GetDiskInfo(磁盘)、MqttService.cs:263 MqttIsConnected()(MQTT)、AppData.cs:759 HouseBin_GetCCDServiceEvent/:652 HouseBinPhotoStateEvent(CCD/拍照)、:164 ImageDTODic+扫目录(上传队列)、各 HouseBin*/BufferBottleBin(温度/气体态)、:172 HttpService(服务器通信) M5-02:聚合为只读监控快照

来源割裂的本质(05 §1):HTTP online 与 MQTT 舱室态两条链独立,任一断、另一未断时界面"假装实时"。M5 不合并两源,而是在链路健康页把两源的「最后成功通讯时间 + 连接态」并排显式呈现,让运维一眼看出哪条断了。


Tech Stack

  • C# / WPF / .NET 6 · MVVM(operate 设置页/监控页)
  • 配置:System.Configuration.ConfigurationManager(沿用现有读取链,仅扩键 + 集中封装);写回沿用 OpenExeConfiguration + RefreshSection(已存在于 AppData.cs:159-205
  • 加密:.NET 内置 System.Security.Cryptography.ProtectedData(DPAPI,LocalMachine 作用域,零外部依赖、与本机绑定,契合「本机凭据」语义)——存储时加密、读取时解密、界面脱敏显示
  • 监控页:只读 WPF UserControl + ViewModel + DispatcherTimer 轮询 ivf_tl_Control.AppData.Instance 只读快照(不订阅写事件、不持有可写引用)
  • 心跳/补传:沿用现有 MQTTnet TryPingAsync/ConnectAsync、现有落盘扫描线程,只补强(解屏蔽 + 队列状态 + 去重标记)

范围与约束(必读)

  1. 本地不可构建/运行:本机 dotnet6、项目实为 net6,且 M1 合并代码尚未整体编译。本计划任何任务都不含"构建/运行/连服务/连 broker/真机"步骤;每步完成后只做「代码完成 → 登记待验证项」,运行级验证(需运行 / 服务 / 网络 / broker / 断网恢复)统一后置到 M7。计划内不出现假装能跑的命令。
  2. 🔶D8 依赖未决:配置分层边界(数据库/本地分层原则、无网兜底、权限分级)待你拍板。为不阻塞 M5,本计划按「合理默认」落地并标注待确认
    • 本地层(与本机/现场绑定):urlIp/urlPort/mqttIp/mqttPort/kfkaIP/kfkaPortcacheDiskLanguageoutIntertlNumuserName/passWord加密)、houseEnabled
    • 数据库层(多端一致/审计):现已走 TLSettingautoFocusTime/videoFps/cropNum/heapDate/keepDate/cleanSurfaceData 等保持数据库来源。
    • 暂留本地、待 D8 定归属:换气/电机/CCD 类业务参数(csTime/gbTime/VentNum/VentPre/VentWaitTime*/AutoWaitTime/CCDAutoWaitTime/CCDFailedNumber/CCDFailedWaitTime/QueuAir/StopPro)——06 §2 建议这些进数据库,但现状在 control App.config。M5 先原样保留在本地层并集中展示,迁移到数据库登记为 [D8] 待确认项,不在 M5 强行搬迁(避免 R8 起不来)。
    • 无网兜底 / 权限分级:本地缓存只读副本、普通运维 vs 工程师权限——均标 [D8],M5 仅预留结构不实装强约束。
  3. 🔶D10 依赖未决:实时指标具体值(刷新延迟上限、断线检出时间、重连最大时延)待定。M5 链路健康页呈现「最后成功通讯时间 / 连接态」原始数据,阈值上色与告警门限做成可配置占位,具体阈值标 [D10] 留 M7 校准。
  4. 只读边界(需求 7 / 00-需求总览.md §8):服务监控页只读——只 get 状态、不 set、不下发指令、不新增控制按钮。代码上监控 ViewModel 只接受 AppData.Instance 的只读快照 DTO,不持有可调用控制方法的引用。
  5. 不删既有设置项(00-需求总览.md §8)SettingPageView 现有 7 个入口与业务项全部保留,M5 只新增「统一配置」与「服务监控」两个入口,不改既有 MiMa() 校验语义、不改既有 UpdataTlSetting() 流程。
  6. 不改通讯协议/报文:MQTT 主题(TL/House/surface/{tlSn}TL/House/collecting-data)、上报报文结构、Kafka topic 一律不动;M5 只补强保活/提示/补传可靠性。

待验证清单约定

每步产出登记到统一清单(沿用 M1 的 V-xxx,本计划用 V-M5-xx)。需运行/服务/网络/broker/断网恢复的项标 [M7];依赖 D8 配置分层拍板的标 [D8];依赖 D10 指标拍板的标 [D10]


子任务总览

任务 主题 需求 步骤数
M5-01 配置统一:去重合并(两份 App.config → 单一来源)+ 集中读写封装 9 5
M5-02 配置统一:密码/口令加密 + 设置页统一配置入口(界面化)+ 旧值迁移 9 5
M5-03 只读服务监控页 + 链路健康可视化(服务/连接/队列/磁盘 + 最后通讯时间) 7/10 5
M5-04 通讯稳定:恢复 MQTT 保活心跳 + 断线显著提示 + 图片补传补强 10 4

共 4 子任务 / 19 步骤。建议执行序:M5-01 → M5-02 → M5-04 → M5-03(先把配置单一化与加密落定,再补强通讯可靠性,最后做监控页——监控页要展示心跳/补传状态,依赖 M5-04 暴露的状态字段)。M5-03 与 M5-04 状态字段对齐后可并行细化。


M5-01 · 配置去重合并 + 集中读写封装(需求 9,🔶D8)

目标:合并后单进程只有 operate 一份 App.config 生效,把 control 独有键并入,消除「operate/control 各存一份、默认值不一致」;新增统一读取封装类,杜绝 AppSettings[...] 散落直读。

M5-01-1 把 control 独有键并入 operate App.config(单一数据源)

  • 文件:ivf_tl_operate_2.0/ivf_tl_Operate/App.config:3-29(目标合并文件);参照 ivf_tl_control_2.0/ivf_tl_ControlTest/App.config:3-46(来源)
  • 改什么:在 operate App.config<appSettings>补入 control 现有、operate 缺失的键(保留注释说明,纠正词不符义):CCDError/csTime/gbTime/VentNum/VentPre/VentWaitTimeB/VentWaitTimeD/AutoWaitTime/CCDAutoWaitTime/CCDFailedWaitTime/CCDFailedNumber/QueuAir/StopPro/cacheDisk/kfkaIP/kfkaPort。同名键(userName/passWord/urlIp/urlPort/mqttIp/mqttPort/Language只保留 operate 一份,值以 operate 为准(IP 默认值统一为现场实际值,登记 [M7] 现场核对)。
  • 为何:M1 合并后 ConfigurationManager.AppSettings 只读主进程 config,control AppData.cs:49-74 同进程读到的是 operate config;不补全键,control 构造函数 int.Parse/.ToString() 立刻 NPE(R8)。这是「去重 = 单一数据源」的落地形态,不是手工比对两文件。
  • 与现状衔接:ControlTest/App.config 合并后不再生效,标注其为历史文件(保留只读供迁移比对,见 M5-02-5)。
  • 分层归属:本步并入的换气/CCD 类键按约束 2 暂留本地层,归属待 [D8]。
  • 待验证:V-M5-01 [M7] 合并 config 在真机启动时 control 构造函数全部键解析成功、无 NPE;[M7] IP/端口默认值与现场一致。

M5-01-2 新增统一配置读取封装 AppConfigHelper

  • 文件:新增 ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/AppConfigHelper.cs(命名沿用 operate 现有 Converts/Helpers/ 风格,若无 Helpers/ 目录则新建)
  • 写什么:静态类,封装强类型读取(GetString(key, default)/GetInt(key, default)/GetEncrypted(key)),内部仍走 ConfigurationManager.AppSettings,但集中容错(键缺失返回默认值而非 NPE)+ 分层标注(注释每键属本地层/数据库层/待 D8)。预留 Save(key,value) 走现有 OpenExeConfiguration+RefreshSection(沿用 AppData.cs:159-167 写法)。
  • 为何:现状 AppSettings[...] 散落 14 文件直读(见 Architecture A),易漏改、易 NPE。集中封装是去重与后续加密的单一接管点。
  • 与现状衔接:不一次性重写所有调用点(避免大改面);本步只建封装并在 M5-01-3/M5-02 改造点逐步切换,老直读点保持可用。
  • 待验证:V-M5-02 [M7] 封装容错(缺键取默认)随合并进程启动验证。

M5-01-3 control 构造函数容错读取(防缺键起不来)

  • 文件:ivf_tl_control_2.0/ivf_tl_Control/AppData.cs:49-74
  • 改什么:把 ConfigurationManager.AppSettings["xxx"].ToString() 直读(:51-52 mqttIp/mqttPort:49-50 urlIp/kfkaIP)改为经 AppConfigHelper(或就地加 ?? 默认值 容错),消除「键缺失 → .ToString() NPE」。:54-74int.TryParse 已容错,保留。
  • 为何:M5-01-1 合并 config 若漏键,:51 AppSettings["mqttIp"].ToString() 直接崩,control 后台线程起不来(R8)。
  • 与现状衔接:仅加容错,不改字段语义与默认值。
  • 待验证:V-M5-03 [M7] control 在 config 缺任一键时降级用默认值、不崩(M7 故意删键测)。

M5-01-4 校准 operate config 启动直读点容错

  • 文件:ivf_tl_operate_2.0/ivf_tl_Operate/AppData.cs:79-82App.xaml.cs:57MainWindow.xaml.cs:96Windows/LoginWindow.xaml.cs:36-38
  • 改什么:这些点现状 AppSettings["xxx"].ToString() 直读,缺键会崩。逐点改为 AppConfigHelper 容错读取(语义不变)。LoginWindow:36-38 回填登录框的 userName/passWord/tlNum 改走解密读取(接 M5-02-2)。
  • 为何:统一接管点 + 防缺键崩 + 为加密读取铺路。
  • 与现状衔接:不改登录流程、不改语言切换、不改缓存盘透传逻辑。
  • 待验证:V-M5-04 [M7] operate 启动各直读点容错、登录框回填正常。

M5-01-5 标注分层归属表(落文档不落代码强约束)

  • 文件:本计划文档 + App.config 注释(ivf_tl_operate_2.0/ivf_tl_Operate/App.config
  • 写什么:在 config 每键注释标「本地层 / 数据库层 / 待 D8」;换气/CCD 类键注明「现状本地,06 §2 建议入库,待 D8 拍板」。
  • 为何:D8 未决下先把分层意图固化为可审阅清单,拍板后按注释迁移,不阻塞 M5。
  • 待验证:V-M5-05 [D8] 分层归属待 D8 确认后定稿(尤其换气/电机/CCD 类是否入库、无网兜底、权限分级)。

M5-02 · 密码加密 + 设置页统一配置入口 + 旧值迁移(需求 9,🔶D8)

目标:明文密码/凭据加密存储 + 界面脱敏;把"只能改 XML"的本地项搬到设置页统一配置页;硬编码 tl13579 纳入治理;旧明文值平滑迁移。

M5-02-1 凭据加密工具 CryptoHelper(DPAPI)

  • 文件:新增 ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/CryptoHelper.cs
  • 写什么:Encrypt(plain)/Decrypt(cipher)/IsEncrypted(value) 基于 System.Security.Cryptography.ProtectedData(DPAPI,DataProtectionScope.LocalMachine),密文 Base64 存 config;IsEncrypted 用前缀标记(如 enc:)区分明文/密文,支持「读时若为明文则视为旧值待迁移」。
  • 为何:06 §2/§5 要求本机凭据加密;DPAPI 零依赖、与本机绑定,契合「本机凭据」语义,不引第三方密钥管理。
  • 与现状衔接:仅新增工具类,调用点在后续步骤接入。
  • 待验证:V-M5-06 [M7] 加解密往返一致(同机);[D8] 是否需跨机可迁移密钥(DPAPI LocalMachine 不可跨机)——若 D8 要求跨机,改用对称密钥+固定盐,登记待确认。

M5-02-2 passWord / MQTT 凭据加密读写

  • 文件:ivf_tl_operate_2.0/ivf_tl_Operate/App.configpassWord 项);读取 Windows/LoginWindow.xaml.cs:37AppData.cs(MQTT 凭据);写回 AppData.cs:159-167 SetApp / :169-197 setAppConfig
  • 改什么:写回时对 passWordCryptoHelper.Encrypt;读取(LoginWindow:37 回填、登录透传)先 IsEncrypted 判断——密文解密、明文(旧值)直接用并触发一次「读后回写为密文」迁移。MQTT 凭据(control AppData.cs:156-157 现为硬编码 aivfo/aivfo,operate MqttUserName/MqttPassword 字段)若纳入 config 则同样加密。
  • 为何:消除 App.config 明文密码(R8 核心坑),界面脱敏。
  • 与现状衔接:登录校验、MQTT 连接逻辑不变,只在「存/取边界」加解密。
  • 待验证:V-M5-07 [M7] 旧明文 config 首次启动自动迁移为密文、登录仍成功;密文 config 解密登录成功。

M5-02-3 硬编码 tl13579 纳入治理

  • 文件:ivf_tl_operate_2.0/ivf_tl_Operate/View/SettingPageView.xaml.cs:106-112 MiMa()(口令源 :109
  • 改什么:把硬编码 "tl13579" 比对改为读取加密存储的工程师口令(config 新增 engineerPwd 本地层键,CryptoHelper 加密;首次无值则回退默认 tl13579 并提示迁移)。保留 MiMa(PasswordBox) 方法签名与 7 处调用点不变:167/190/213/237/256/291/323),只换口令来源。
  • 为何:06 §5 要求 tl13579 纳入治理(加密 + 可改);签名不变 = 不触碰既有入口(00-需求总览.md §8)。
  • 与现状衔接:7 个入口(HEPA/保养/系统参数/对焦/舱室调试/缓冲调试/退出)密码校验语义完全不变。
  • 分层/权限:是否「普通运维 vs 工程师」分级口令标 [D8]——M5 先单口令加密,分级待拍板。
  • 待验证:V-M5-08 [M7] 改口令后 7 入口校验生效;[D8] 权限分级是否实装。

M5-02-4 设置页新增「统一配置」入口与配置页

  • 文件:ivf_tl_operate_2.0/ivf_tl_Operate/View/SettingPageView.xaml新增入口控件,与现有 7 入口同构)+ SettingPageView.xaml.cs(新增 UnifiedConfig_MouseUp 事件,仿 :211 HouseSetting_MouseUpMiMa 进入);新增 View/UnifiedConfigView.xaml(.cs) + ViewModel/UnifiedConfigViewModel.cs
  • 写什么:统一配置页分组展示并可编辑本地层项(连接:urlIp/urlPort/mqttIp/mqttPort/kfkaIP/kfkaPort;本机:cacheDisk/tlNum/Language/houseEnabled;凭据:userName/passWord 脱敏;口令:engineerPwd 脱敏);编辑经 AppConfigHelper.Save + 加密项经 CryptoHelper;每组带说明文案(纠正词不符义,如 autoFocus 注明"实为烧录换气开关")+ 改后生效提示(沿用现有 MessagePrompt)。数据库层项(对焦时间/帧率/抠图数等)维持现有 SettingPageViewTLSetting 编辑路径,统一配置页对其做「只读引用+跳转」,不重复写入口
  • 为何:需求 9「界面化」——原只能改 XML 的本地项搬到界面;统一入口而非重做既有项(00-需求总览.md §8)。
  • 与现状衔接:纯新增入口与页面,触控/键盘样式套用 M4 框架(数字/密码键盘见 M4-04);既有设置项与入口零改动。
  • 待验证:V-M5-09 [M7] 统一配置页改本地项→config 落盘(密文)→重启生效;既有设置项入口不受影响。

M5-02-5 旧值导入 + 去重比对(R8 迁移)

  • 文件:迁移逻辑入 AppConfigHelper(或一次性 ConfigMigrator 静态方法,operate 启动 App.xaml.cs 调用一次);比对来源 ivf_tl_control_2.0/ivf_tl_ControlTest/App.config(历史只读)
  • 写什么:启动时一次性迁移——(a) passWord/engineerPwd 明文→密文回写;(b) 若 operate config 缺 control 独有键,从历史 ControlTest/App.config 导入默认值(保证 M5-01-1 未手填的键有兜底);(c) 同名键不一致时以 operate 为准并写日志(去重比对留痕)。迁移幂等(已迁移则跳过)。
  • 为何:R8「旧值导入 + 去重比对 + 改后生效验证」,避免改一处漏一处。
  • 与现状衔接:迁移只在首次启动跑一次,不改运行期逻辑。
  • 待验证:V-M5-10 [M7] 用旧明文 + 缺键 config 启动,迁移后键齐全、密文化、日志记录去重比对结果;[D8] 无网兜底「本地只读副本」是否需要(待 D8)。

M5-03 · 只读服务监控页 + 链路健康可视化(需求 7/10,🔶D10)

目标:设置页新增纯只读"服务运行监控"页,从托管的 control 内存/事件取只读快照,展示各服务/连接/队列/磁盘 + 链路健康(服务器/MQTT/上报队列/最后成功通讯时间)+ 断线显著提示。不新增任何控制

M5-03-1 control 暴露只读监控快照 DTO

  • 文件:新增 ivf_tl_control_2.0/ivf_tl_Control/MonitorSnapshot.cs(只读 DTO)+ ivf_tl_Control/AppData.cs 新增 GetMonitorSnapshot() 只读方法
  • 写什么:DTO 聚合(只读取,不改任何状态):MQTT 连接态(MqttService.cs:263 MqttIsConnected() / :23 IsConnected)、服务器通信态(HttpService 最近调用成功标记,新增只读字段)、CCD/拍照态(HouseBin_GetCCDServiceEvent/HouseBinPhotoStateEvent 已有的 CCD 状态字段聚合)、上传队列长度(ImageDTODic.Count + 落盘目录 *.jpg 待传数)、磁盘(GetDiskInfo(PathHelper.pan)AppData.cs:1461)、各舱温度/气体态(HouseBin*/BufferBottleBin 只读字段)、各链路「最后成功通讯时间」(新增只读时间戳字段,见 M5-03-2)。
  • 为何:需求 7「来自 control 内存/事件」「只读」;DTO 隔离 = 监控页拿不到可写引用(强制只读边界,约束 4)。
  • 与现状衔接:只新增聚合读方法,不动既有采集/上报线程逻辑。
  • 待验证:V-M5-11 [M7] 快照各字段在 control 运行时取值正确(需运行)。

M5-03-2 control 记录各链路「最后成功通讯时间」

  • 文件:ivf_tl_control_2.0/ivf_tl_Control/AppData.csStartSendMqttMsg:316-367 MQTT 上报成功后、KafkaUploadImageAsync:1298-1312 Kafka 上传成功后、HttpService 调用成功后)
  • 写什么:各成功点更新只读时间戳字段(LastMqttOkAt/LastKafkaOkAt/LastHttpOkAt),供 DTO 与链路健康呈现「最后成功通讯时间」、判定"过期/失联"。
  • 为何:需求 10「最后成功通讯时间」「不假装实时」;解决来源割裂(一条链停更可见)。
  • 与现状衔接:仅在已有成功分支加时间戳赋值,不改上报逻辑、不改报文。
  • 待验证:V-M5-12 [M7] 断某条链后对应时间戳停更、其他链继续(需运行/断网模拟)。

M5-03-3 监控页 UI(只读)

  • 文件:新增 ivf_tl_operate_2.0/ivf_tl_Operate/View/ServiceMonitorView.xaml(.cs) + ViewModel/ServiceMonitorViewModel.cs
  • 写什么:只读卡片网格展示 DTO 各项(服务器通信/CCD/拍照/温度/气体/上传队列/MQTT/磁盘,05 §3 清单);DispatcherTimer(默认 2s,阈值占位 [D10])调 ivf_tl_Control.AppData.Instance.GetMonitorSnapshot() 刷新;页面无任何按钮/输入/下发控件(约束 4)。
  • 为何:需求 7 只读服务监控;合并后 control 无界面,把后台状态可视化给运维。
  • 与现状衔接:取数走 M1 同进程 AppData.InstanceMainWindow.xaml.cs:103 已托管);触控/自适应套 M4 框架。
  • 待验证:V-M5-13 [M7] 监控页只读刷新、各卡片实时取值(需运行);确认无任何写控件(只读边界)。

M5-03-4 链路健康可视化 + 显著失联提示

  • 文件:ServiceMonitorView.xaml(.cs) + ServiceMonitorViewModel.cs(同 M5-03-3)
  • 写什么:链路健康区并排呈现「服务器连接 / MQTT / 上报队列 / 最后成功通讯时间」四态;按「最后成功通讯时间」超阈值([D10] 占位)显显著失联色/告警标识,区分 HTTP online 与 MQTT 舱室态两源(解决 05 §1 来源割裂"假装实时")。提示为只读呈现(变色/图标/文案),不弹可操作控件。
  • 为何:需求 10「链路健康可视化」「断线显著提示」「不假装实时」。
  • 与现状衔接:与需求 10 打通同页(05 §3「服务监控页含链路健康」)。
  • 待验证:V-M5-14 [M7][D10] 断 MQTT/HTTP/Kafka 任一时对应链显失联、阈值上色;阈值具体值待 D10。

M5-03-5 设置页新增「服务监控」只读入口

  • 文件:ivf_tl_operate_2.0/ivf_tl_Operate/View/SettingPageView.xaml(新增入口控件)+ SettingPageView.xaml.cs(新增 ServiceMonitor_MouseUp,加载 ServiceMonitorView
  • 写什么:仿 :337 About_MouseUp(无密码、直接 LoadPage)新增只读入口——服务监控只读、无需工程师口令亦可,或按 [D8] 权限分级决定是否要口令;进入即 LoadPage(new ServiceMonitorView(...))
  • 为何:需求 7「设置页新增服务运行监控入口」。
  • 与现状衔接:纯新增入口,既有 7 入口不动(00-需求总览.md §8)。
  • 待验证:V-M5-15 [M7] 设置页入口可进监控页;[D8] 监控页是否需口令/权限分级待确认。

M5-04 · MQTT 保活心跳恢复 + 断线提示 + 图片补传补强(需求 10)

目标:恢复被 return; 屏蔽的保活心跳、断线写入失联状态供监控页显示、图片补传由"裸扫目录"补强为有去重/有累积兜底的可靠补传。

M5-04-1 恢复 MQTT 保活心跳(解屏蔽 TryPing 循环)

  • 文件:ivf_tl_control_2.0/ivf_tl_ServicesImpl/MqttServices/MqttService.cs:79-80
  • 改什么::79 Task.Run(() => ClientMqtt()); 之后的 :80 return; 是把 :81-113TryPingAsync 保活循环整段屏蔽成死代码。改为保活心跳与首连接并存——保留 ClientMqtt() 首连,删除/绕过 :80 return; 使 :81-113 保活循环生效(循环内 TryPingAsync 失败则 ConnectAsync 重连,10s 间隔)。注意避免与 :206 _mqttClient_DisconnectedAsync 的重连重复并发——以 IsConnected/IsDispose 做幂等护栏。
  • 为何:05 §1「TryPing 循环被 return; 屏蔽」、§2.3「恢复被屏蔽的保活心跳」;R9 核心缓解。
  • 与现状衔接:不改连接参数/凭据/订阅主题;只解屏蔽既有保活逻辑 + 防重连竞态。
  • 待验证:V-M5-16 [M7] broker 断后心跳检出并重连、无重复连接风暴(需 broker / 断网模拟)。

M5-04-2 评估 CleanSession 漏发补偿

  • 文件:ivf_tl_control_2.0/ivf_tl_ServicesImpl/MqttServices/MqttService.cs:65 WithCleanSession()
  • 改什么:评估去掉 WithCleanSession()(改持久会话 + QoS≥1)以减少断线期间漏发;因涉及 broker 端会话与重复投递语义,M5 不直接改默认行为,仅记录评估结论与改法,留 M7 真机/broker 验证后定。当前舱室态走 1s 周期重发(StartSendMqttMsg:359),漏一帧影响小;临床图片走 Kafka 独立链(M5-04-3),故 CleanSession 风险主要在状态类。
  • 为何:05 §1「WithCleanSession() 断线漏发不补」;但持久会话改动风险高,需 broker 验证,不在不可构建环境下贸然改。
  • 与现状衔接:本步只评估+登记,不改代码(或仅加注释 TODO)。
  • 待验证:V-M5-17 [M7] broker 持久会话方案验证后决定是否去 CleanSession(需 broker)。

M5-04-3 图片补传补强(去重 + 断网累积兜底)

  • 文件:ivf_tl_control_2.0/ivf_tl_Control/AppData.cs:273-311 StartUpLoadImage:1258-1345 KafkaUploadImageAsync
  • 改什么:现状已「成功才删、失败保留下轮重扫」(:1313-1317 删、:1301-1305 失败保留)——补强三点:(a) 去重防重传:上传中文件加 in-flight 集合标记,避免 5s 重扫期间同名文件并发重传(现 ImageDTODic lock 仅护 DTO 缓存,未护"正在上传");(b) 断网累积兜底:MQTT/Kafka 断时落盘目录会持续累积,新增待传数与磁盘水位联动告警(接 M5-03 队列长度 + GetDiskInfo),超阈值([D10])显著提示,不静默堆积;(c) 补传可见:待传队列长度、最后成功上传时间纳入监控快照(M5-03-1/2)。保留"落盘即不丢、成功才删"的现有不丢语义,不引入新的删图风险。
  • 为何:05 §2.5「图片直接落盘 tmpDir、无明确补传队列…需落盘队列+断点续传+去重」;临床数据完整性。
  • 与现状衔接:基于现有扫目录线程补强,不改 Kafka 上传报文、不改落盘路径(PathHelper.GetAutoFocusSaveDirectory/GetEmbryoPicSaveDirectory)。
  • 待验证:V-M5-18 [M7] 断网期间图片累积不丢、恢复后全部补传且无重复入库;并发重扫无重传(需运行/断网/Kafka)。

M5-04-4 断线状态写入供监控显示(HTTP 链路同步)

  • 文件:ivf_tl_operate_2.0/ivf_tl_Operate/ViewModel/MainPageViewModel.cs:741-773 StartThread(HTTP 5s 轮询)
  • 改什么:HTTP 轮询 TlInfoTimeApi(:754) 成功/失败时记录 operate 侧「最后成功 HTTP 通讯时间」与连接态(新增只读字段),供链路健康页区分「HTTP online 链」与「MQTT 舱室态链」——解决 05 §1「broker 掉线 HTTP 正常仍显在线」的来源割裂。失败时 :755 if (a == null) continue; 现状静默跳过,补一处只读失联标记(不改轮询节奏)。
  • 为何:需求 10「不假装实时」,两源连接态都要可见。
  • 与现状衔接:不改轮询周期、不改 Init() 刷新逻辑,只加只读状态标记。
  • 待验证:V-M5-19 [M7] HTTP 断时 operate 侧失联标记置位、监控页可见(需运行/网络)。

待验证清单汇总(V-M5-xx)

编号 内容 标签
V-M5-01 合并 config 真机启动 control 全键解析无 NPE;IP/端口与现场一致 [M7]
V-M5-02 AppConfigHelper 缺键取默认容错 [M7]
V-M5-03 control 构造函数缺键降级不崩 [M7]
V-M5-04 operate 启动直读点容错 + 登录回填正常 [M7]
V-M5-05 配置分层归属定稿(换气/CCD 入库?无网兜底?权限分级?) [D8]
V-M5-06 DPAPI 加解密往返一致;是否需跨机密钥 [M7][D8]
V-M5-07 明文 config 自动迁移密文 + 登录成功;密文登录成功 [M7]
V-M5-08 engineerPwd 改后 7 入口校验生效;权限分级是否实装 [M7][D8]
V-M5-09 统一配置页改本地项落盘密文重启生效;既有项不受影响 [M7]
V-M5-10 旧值导入+去重比对+缺键兜底;无网只读副本是否需要 [M7][D8]
V-M5-11 监控快照各字段运行时取值正确 [M7]
V-M5-12 各链路最后成功通讯时间断链停更、其他续 [M7]
V-M5-13 监控页只读刷新;确认无写控件 [M7]
V-M5-14 断 MQTT/HTTP/Kafka 任一显失联+阈值上色;阈值具体值 [M7][D10]
V-M5-15 设置页服务监控入口可进;是否需口令/权限 [M7][D8]
V-M5-16 MQTT 心跳断后检出重连、无连接风暴 [M7]
V-M5-17 CleanSession 持久会话方案 broker 验证后定 [M7]
V-M5-18 断网图片累积不丢、恢复全补传、无重复、无并发重传 [M7]
V-M5-19 HTTP 断时 operate 失联标记置位、监控可见 [M7]

与其他里程碑衔接

  • 依赖 M1:监控页取数依赖 M1 已把 control 托管进 operate 进程(MainWindow.xaml.cs:103-125ivf_tl_Control.AppData.Instance 同进程可访问)。配置去重依赖 M1 合并后单一 config 生效的事实。
  • 依赖 M4:统一配置页/监控页的自适应+触控+数字/密码键盘套用 M4 框架(M4-01/04)。
  • 关联 M2calibration.json/EEPROM 标定为只读纳入展示,写入归 M2/03,不在 M5 改。
  • 后置 M7:全部 V-M5-xx 的运行/服务/网络/broker/断网恢复验证集中在 M7(含 D10 指标校准)。
  • 待 D8 拍板:配置分层归属(换气/CCD 类是否入库)、无网兜底、权限分级——M5 已按合理默认落地并标注,拍板后按 config 注释迁移,不阻塞。