Browse Source

feat(house-fault): H-08 operate 监控页"舱故障"区——读 /status Faults 红色展示(代码完成+逻辑/契约验证)

纯 operate 侧加展示,不碰 control(Faults 第一阶段已透出 MonitorSnapshot.Faults)。
- 新增 ServiceMonitorFaultMapper(纯静态/不依赖WPF:8枚举名→中文+未知码兜底/HouseText/AtText UTC→本地/IsolatedText)
- ServiceMonitorViewModel 加 Faults 集合+汇总(HasFaults/Summary)+显隐(直给 Visibility,不用未注册的布尔转换器)+BuildFaultRows+HouseFaultRowVm
- ServiceMonitorView.xaml 在"舱室状态"与"受护栏停止"间插红色故障区(无故障绿条/有故障逐条红底:舱号/类型/原因/阶段/时间/隔离态)
- 验证3层:operate Release 编译0错(XAML→BAML绑定全对)/curl control pid10360 /status 证 Faults 字段透出(基线空·字段名匹配)/映射 harness(临时文件/FaultMapperTest 链入真源码)14检查全过(含 UTC 01:30→本地09:30 东八区+8)
- 残:WPF像素渲染待清僵尸真重启;有故障端到端=H-07物理拔相机门控
- 文档同步:交接卡/进度状态.yaml/进度数据.js/待验证清单 H-08/工作计划表

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 1 ngày trước cách đây
mục cha
commit
f35fc23cbb

+ 36 - 0
ivf_tl_operate_2.0/ivf_tl_Operate/View/ServiceMonitorView.xaml

@@ -158,6 +158,42 @@
                     </ItemsControl.ItemTemplate>
                 </ItemsControl>
 
+                <!-- 舱故障(H-08:读 /status 的 Faults,启动排除 + 运行期突发,红色高亮) -->
+                <TextBlock Text="{Binding FaultSummaryText}" FontSize="48" FontWeight="Bold" Foreground="{Binding FaultSummaryBrush}" Margin="0 30 0 10"/>
+                <!-- 无故障:绿色提示一行 -->
+                <Border Margin="0 6" Padding="30 20" Background="#EAF7EE" CornerRadius="10"
+                        Visibility="{Binding NoFaultVisibility}">
+                    <TextBlock Text="未发现舱室故障,所有已连接舱室运行正常。" FontSize="36" Foreground="#2EA043" VerticalAlignment="Center"/>
+                </Border>
+                <!-- 有故障:逐条红底列表 -->
+                <ItemsControl ItemsSource="{Binding Faults}"
+                              Visibility="{Binding FaultListVisibility}">
+                    <ItemsControl.ItemTemplate>
+                        <DataTemplate>
+                            <Border Margin="0 6" Padding="20 14" Background="#FBE9E7" CornerRadius="8" BorderBrush="#D0322D" BorderThickness="2">
+                                <Grid>
+                                    <Grid.ColumnDefinitions>
+                                        <ColumnDefinition Width="300"/>
+                                        <ColumnDefinition Width="360"/>
+                                        <ColumnDefinition/>
+                                        <ColumnDefinition Width="240"/>
+                                    </Grid.ColumnDefinitions>
+                                    <StackPanel Grid.Column="0" VerticalAlignment="Center">
+                                        <TextBlock Text="{Binding HouseText}" FontSize="38" FontWeight="Bold" Foreground="#D0322D"/>
+                                        <TextBlock Text="{Binding AtText, StringFormat=时间 {0}}" FontSize="28" Foreground="#959596"/>
+                                    </StackPanel>
+                                    <StackPanel Grid.Column="1" VerticalAlignment="Center">
+                                        <TextBlock Text="{Binding FaultTypeText}" FontSize="36" FontWeight="Bold" Foreground="#D0322D"/>
+                                        <TextBlock Text="{Binding Stage, StringFormat=阶段 {0}}" FontSize="28" Foreground="#959596"/>
+                                    </StackPanel>
+                                    <TextBlock Grid.Column="2" Text="{Binding Reason}" FontSize="32" Foreground="#5A5A5A" VerticalAlignment="Center" TextWrapping="Wrap"/>
+                                    <TextBlock Grid.Column="3" Text="{Binding IsolatedText}" FontSize="38" FontWeight="Bold" Foreground="{Binding IsolatedBrush}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
+                                </Grid>
+                            </Border>
+                        </DataTemplate>
+                    </ItemsControl.ItemTemplate>
+                </ItemsControl>
+
                 <!-- 阶段2 §5.4 受护栏停止:二次确认 + 工程师口令 → control 安全停机(唯一写操作) -->
                 <Border Margin="0 30 0 10" Padding="30 20" Background="#FBE9E7" CornerRadius="10">
                     <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

+ 42 - 0
ivf_tl_operate_2.0/ivf_tl_Operate/ViewModel/ServiceMonitorFaultMapper.cs

@@ -0,0 +1,42 @@
+using System;
+
+namespace ivf_tl_Operate.ViewModel
+{
+    /// <summary>
+    /// 舱故障(H-08)展示映射:把 control 经 /status 透出的 HouseFaultRow 原始字段
+    /// (英文枚举名 / 舱号 / UTC 时间 / 隔离布尔) 转成现场可读中文文案。
+    ///
+    /// 纯静态、不依赖 WPF / control 程序集,签名只收基础类型,便于独立 harness / 单测验证
+    /// (ServiceMonitorViewModel.BuildFaultRows 调用它构造展示行,颜色 Brush 留在 ViewModel)。
+    /// </summary>
+    public static class ServiceMonitorFaultMapper
+    {
+        /// <summary>HouseFaultType 枚举名(control 端 ToString 透出的英文)→ 现场可读中文。未知码原样返回。</summary>
+        public static string FaultTypeZh(string type)
+        {
+            switch (type)
+            {
+                case "CameraReadFailed": return "相机SN读取异常";
+                case "CameraDuplicateSn": return "相机SN重复";
+                case "HouseSnDuplicate": return "舱号重复";
+                case "CcdSnMissing": return "相机SN缺失(相机半坏)";
+                case "CcdSnDuplicate": return "舱相机SN重复";
+                case "SerialReadException": return "串口读异常";
+                case "InitException": return "舱初始化异常";
+                case "RuntimeFault": return "运行期突发故障";
+                default: return string.IsNullOrEmpty(type) ? "未知故障" : type;
+            }
+        }
+
+        /// <summary>舱号文案:&gt;0=具体舱(可被剔除);否则=相机/串口级、未定位到具体舱。</summary>
+        public static string HouseText(int houseSn) =>
+            houseSn > 0 ? $"{houseSn}号舱" : "未定位(相机/串口级)";
+
+        /// <summary>发生时刻:control 端存 UtcNow,展示转本地时间;default=未知。</summary>
+        public static string AtText(DateTime atUtc) =>
+            atUtc == default(DateTime) ? "—" : atUtc.ToLocalTime().ToString("MM-dd HH:mm:ss");
+
+        /// <summary>隔离态文案:已被剔除/已停=已隔离;否则仅提示(舱号未知的相机/串口级故障不剔除某舱)。</summary>
+        public static string IsolatedText(bool isolated) => isolated ? "已隔离" : "仅提示";
+    }
+}

+ 62 - 0
ivf_tl_operate_2.0/ivf_tl_Operate/ViewModel/ServiceMonitorViewModel.cs

@@ -31,6 +31,7 @@ namespace ivf_tl_Operate.ViewModel
         public ServiceMonitorViewModel()
         {
             Houses = new ObservableCollection<HouseMonitorRowVm>();
+            Faults = new ObservableCollection<HouseFaultRowVm>();
             Refresh();
             _timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
             _timer.Tick += (s, e) => Refresh();
@@ -62,7 +63,21 @@ namespace ivf_tl_Operate.ViewModel
         [ObservableProperty] private string diskText;
         [ObservableProperty] private Brush diskBrush;
 
+        // —— 舱故障(H-08:读 /status Faults,红色高亮展示)——
+        /// <summary>是否存在舱故障(内部逻辑用)。</summary>
+        [ObservableProperty] private bool hasFaults;
+        /// <summary>故障区标题文案(有故障="舱故障(N)" / 无故障="舱室均正常")。</summary>
+        [ObservableProperty] private string faultSummaryText;
+        /// <summary>故障区标题色(有故障=红 / 无故障=绿)。</summary>
+        [ObservableProperty] private Brush faultSummaryBrush;
+        /// <summary>故障列表显隐(有故障可见)。直接给 Visibility,避免依赖未注册的布尔转换器。</summary>
+        [ObservableProperty] private Visibility faultListVisibility = Visibility.Collapsed;
+        /// <summary>"无故障"绿条显隐(无故障可见)。</summary>
+        [ObservableProperty] private Visibility noFaultVisibility = Visibility.Visible;
+
         public ObservableCollection<HouseMonitorRowVm> Houses { get; }
+        /// <summary>舱故障只读行(启动排除 + 运行期突发,来源 snap.Faults)。</summary>
+        public ObservableCollection<HouseFaultRowVm> Faults { get; }
 
         private static readonly Brush Green = new SolidColorBrush(Color.FromRgb(0x2E, 0xA0, 0x43));
         private static readonly Brush Red = new SolidColorBrush(Color.FromRgb(0xD0, 0x32, 0x2D));
@@ -158,6 +173,15 @@ namespace ivf_tl_Operate.ViewModel
                         CapturePausedBrush = h.CapturePausedByGate ? Amber : Green,
                     });
                 }
+
+                // 舱故障(H-08):启动排除 + 运行期突发,红色高亮;无故障显示绿色"舱室均正常"。
+                Faults.Clear();
+                foreach (var vm in BuildFaultRows(snap.Faults)) Faults.Add(vm);
+                HasFaults = Faults.Count > 0;
+                FaultSummaryText = HasFaults ? $"舱故障({Faults.Count})" : "舱室均正常,无故障";
+                FaultSummaryBrush = HasFaults ? Red : Green;
+                FaultListVisibility = HasFaults ? Visibility.Visible : Visibility.Collapsed;
+                NoFaultVisibility = HasFaults ? Visibility.Collapsed : Visibility.Visible;
             }
             catch
             {
@@ -196,6 +220,32 @@ namespace ivf_tl_Operate.ViewModel
 
         private static string FmtOk(DateTime? t) => t == null ? "—" : t.Value.ToString("HH:mm:ss");
 
+        /// <summary>
+        /// 舱故障行映射(H-08):snap.Faults(control 透出的 HouseFaultRow) → 展示行。
+        /// 文案走纯静态 <see cref="ServiceMonitorFaultMapper"/>(可独立 harness/单测),颜色 Brush 在此加。
+        /// public static 便于不起 WPF 外壳即可验证有故障场景的中文/时间/隔离映射。
+        /// </summary>
+        public static System.Collections.Generic.List<HouseFaultRowVm> BuildFaultRows(
+            System.Collections.Generic.IEnumerable<ivf_tl_Control.HouseFaultRow> faults)
+        {
+            var list = new System.Collections.Generic.List<HouseFaultRowVm>();
+            if (faults == null) return list;
+            foreach (var f in faults)
+            {
+                list.Add(new HouseFaultRowVm
+                {
+                    HouseText = ServiceMonitorFaultMapper.HouseText(f.HouseSn),
+                    FaultTypeText = ServiceMonitorFaultMapper.FaultTypeZh(f.FaultType),
+                    Reason = string.IsNullOrEmpty(f.Reason) ? "—" : f.Reason,
+                    Stage = string.IsNullOrEmpty(f.Stage) ? "—" : f.Stage,
+                    AtText = ServiceMonitorFaultMapper.AtText(f.At),
+                    IsolatedText = ServiceMonitorFaultMapper.IsolatedText(f.Isolated),
+                    IsolatedBrush = f.Isolated ? Red : Amber,
+                });
+            }
+            return list;
+        }
+
         public void Dispose()
         {
             try { _timer?.Stop(); } catch { }
@@ -222,4 +272,16 @@ namespace ivf_tl_Operate.ViewModel
         public string CapturePausedText { get; set; }
         public Brush CapturePausedBrush { get; set; }
     }
+
+    /// <summary>单条舱故障只读展示行(H-08)。</summary>
+    public class HouseFaultRowVm
+    {
+        public string HouseText { get; set; }
+        public string FaultTypeText { get; set; }
+        public string Reason { get; set; }
+        public string Stage { get; set; }
+        public string AtText { get; set; }
+        public string IsolatedText { get; set; }
+        public Brush IsolatedBrush { get; set; }
+    }
 }

+ 18 - 0
项目文档/进度/交接卡.md

@@ -502,3 +502,21 @@
 - **清理**:测试假告警(PHOTO×2)、`alarm_send_info` 测试记录、测试联系人**全部删除核零**。新 control pid10360 **保留运行**(健康、无回归;本特性分支构建,未并 main)。
 - **H-07 残留**:启动期**物理拔相机USB**制造半坏舱→看 SerialBin 实产 CcdSnMissing+排除——软件拔不了 USB,需物理动作;但剔除决策已单测(StartupFaultPolicy 6 绿)、登记点镜像现有 errorlist 错误位、基线+闭环已真机验,残留仅"硬件真故障→Faults 填充"这一物理环节。
 - **下一步**:第一阶段收尾(本回写)→ 可并 main。后续阶段:operate 监控页"舱故障"区(读 /status Faults)/ 运行期按需补去抖 / H-07 物理注入(人配合或软件模拟)。
+
+---
+
+## 2026-06-24 · 舱室故障隔离后续阶段:operate 监控页"舱故障"区(H-08 operate 半)代码完成 + 逻辑/契约验证
+
+- **目标**:第一阶段已把启动坏舱透出到 control `/status` 的 `Faults` 字段(`HouseFaultRow`);本步做 H-08 的 operate 那半——监控页加红色"舱故障"区展示它。front 报警列表那半 H-05/06 已通。
+- **现状摸底(codegraph)**:control 端 `MonitorSnapshot.Faults`(HouseSn/FaultType/Reason/Stage/At/Isolated)已透出、`ControlClient.GetStatusSnapshot()` 已能整体带回,所以本步**纯 operate 侧加展示,不碰 control**。`FaultType` 透出的是**英文枚举名**(`f.Type.ToString()`,如 CcdSnMissing),需 operate 翻中文;`Reason`/`Stage` 已中文;`At` 是 **UtcNow**(展示要转本地);`HouseSn=-1`=相机/串口级未定位。
+- **改动(3 文件,纯新增展示)**:
+  - `ViewModel/ServiceMonitorFaultMapper.cs`(**新增**):纯静态映射,不依赖 WPF/control,签名只收基础类型 → `FaultTypeZh`(8 枚举名→中文 + 未知码原样 + 空→"未知故障")/`HouseText`/`AtText`(UTC→本地 MM-dd HH:mm:ss)/`IsolatedText`。解耦便于独立验证。
+  - `ViewModel/ServiceMonitorViewModel.cs`:加 `ObservableCollection<HouseFaultRowVm> Faults` + 汇总属性 `HasFaults/FaultSummaryText/FaultSummaryBrush` + 显隐 `FaultListVisibility/NoFaultVisibility`(直接给 Visibility,**不依赖未注册的布尔转换器**——项目里 BoolToVisibilityConverter 根本没注册,首版误用已纠正);`Refresh()` 调 `BuildFaultRows(snap.Faults)` 填充;新增 `HouseFaultRowVm` 展示类。
+  - `View/ServiceMonitorView.xaml`:在"舱室状态"与"受护栏停止"区之间插红色"舱故障"区——标题带数量(红/绿)、无故障显绿条"舱室均正常"、有故障逐条红底(#FBE9E7+红框)显示 舱号/类型/原因/阶段/时间/隔离态。镜像现有 Houses 区样式。
+- **验证(3 层,operate WPF 外壳受僵尸阻塞,走与 D2-01 同粒度)**:
+  - ① **编译**:`dotnet build ivf_tl_Operate.csproj -c Release` **0 错**(WPF 的 XAML 编进 BAML = 绑定路径/属性/语法全部对得上 ViewModel)。僵尸 operate 20268 未锁输出 DLL。
+  - ② **/status 契约**:curl 在跑的 control pid10360 `/status` → `Faults` 字段存在=True、当前数量 0(基线无故障)、Houses 10、ControlHosted True。证明字段透出 + operate 反序列化目标字段名对得上;无故障时 operate 显绿"舱室均正常"。
+  - ③ **映射逻辑真跑**:临时 harness `临时文件/FaultMapperTest`(Compile Include operate 真实 `ServiceMonitorFaultMapper.cs`,零外部依赖)→ 全 8 枚举名中文化正确 + 未知码原样兜底 + 舱号边界(2/11/-1/0)+ **UTC 01:30→本地 09:30(东八区+8 正确)**+ 隔离布尔,**全部通过**。"有故障"核心路径有了真实运行验证。
+- **未做(诚实边界)**:WPF 外壳像素渲染受僵尸 20268 占单实例 Mutex 阻塞(同 D2-01/D2-03,需真重启清);"有故障"端到端(真有坏舱→红区出现)无法软件注入启动故障(=H-07 同源物理门控,需物理拔相机)。二者随清僵尸重启 / H-07 物理注入时一并复测。
+- **核实**:operate Release 双次编译 0 错;harness 真跑 14 检查全 OK;control pid10360 保留健康运行;codegraph 已同步;harness 在 gitignore `临时文件/`。
+- **下一步**:本回写后可提交(代码+文档)。后续:清僵尸真重启复测监控页像素 + H-07 物理注入看红区;再推进 D2-02 二/三阶段或 H-08 余项。

+ 7 - 0
项目文档/进度/工作计划表.md

@@ -70,3 +70,10 @@
 - **真机核心验证过(2026-06-24,无活体培养)**:H-04 基线新 control(pid10360)无回归(`/status` Faults 空、好舱齐、started);H-05 reportAlarm 相机异常→`alarm` 表落 `PHOTO_STATE_ALARM`(闭环+排除舱能落库);H-06 插联系人→定时任务→阿里云短信,`alarm_send_info` 记"发送成功!"派发至 18223210384(到达由阿里云,用户自查)。测试数据已清理。
 - **残留 H-07**:物理拔相机USB 制造半坏舱(软件做不到,需物理/模拟);剔除决策已单测、登记点镜像现有错误位、基线+闭环已真机验。
 - **可并 main**。后续阶段:operate 监控页"舱故障"区(读 /status Faults)/ 运行期按需补去抖 / front 复用现有报警列表(H-05/06 已通)。
+
+### H-08 后续阶段 · operate 监控页"舱故障"区 = 🟢 代码完成 · ☑ 逻辑/契约验证(残 WPF像素 + H-07物理注入)
+- **范围**:H-08 拆两半——operate 监控页"舱故障"区(本步,读 /status `Faults`)+ front 报警列表(H-05/06 已通)。control 端 `Faults` 第一阶段已透出,本步**纯 operate 侧加展示,不碰 control**。
+- **改 3 文件**:① 新增 `ViewModel/ServiceMonitorFaultMapper.cs`(纯静态、不依赖 WPF/control:`FaultTypeZh` 8 枚举名→中文 + 未知码原样、`HouseText`、`AtText` UTC→本地、`IsolatedText`);② `ViewModel/ServiceMonitorViewModel.cs` 加 `Faults` 集合 + 汇总(`HasFaults/FaultSummaryText/FaultSummaryBrush`)+ 显隐(`FaultListVisibility/NoFaultVisibility` 直给 Visibility,不用未注册的布尔转换器)+ `BuildFaultRows` + `HouseFaultRowVm`;③ `View/ServiceMonitorView.xaml` 在"舱室状态"与"受护栏停止"间插红色故障区(无故障显绿条 / 有故障逐条红底:舱号/类型/原因/阶段/时间/隔离态)。
+- **验证 3 层**(operate WPF 外壳受僵尸 20268 占 Mutex 阻塞,同 D2-01 粒度):① operate Release 编译 **0 错**(XAML→BAML = 绑定路径/属性/语法全对);② curl 在跑 control pid10360 `/status` → `Faults` 字段透出=True、基线空数组、字段名匹配 operate 反序列化目标;③ 映射 harness `临时文件/FaultMapperTest`(Compile Include 真实 mapper 源码,零依赖)**14 检查全过**(8 枚举名中文化 + 未知码兜底 + 舱号边界 + UTC 01:30→本地 09:30 东八区+8 + 隔离布尔)。
+- **残**:WPF 像素渲染待清僵尸真重启复测;"有故障"端到端无法软件注入启动故障(=H-07 物理拔相机门控)。
+- **下一步**:代码+文档一起提交;后续清僵尸真重启 + H-07 物理注入复测红区,或推进 D2-02 二/三阶段。

+ 1 - 1
项目文档/进度/待验证清单.md

@@ -138,7 +138,7 @@
 | H-05 | **告警闭环落库**:`reportAlarm`(相机异常 photoState=1,即 ReportStartupFaults 发的)→报警链→`alarm` 表落 `PHOTO_STATE_ALARM`(舱2/舱4)。证实复用现有闭环、被排除舱(配置舱1-10)能落库 | **真机** | ☑ 2026-06-24 |
 | H-06 | **通知触发(短信)**:插联系人→1min 定时任务对在报告警→`getPersonList`→调阿里云短信→`alarm_send_info` 记 `发送成功!`(短信 alarm_model=0,号 18223210384)。短信电话通知链路打通(走 reportAlarm 闭环内置) | **真机** | ☑ 已派发(到达由阿里云,用户自查手机) |
 | H-07 | 启动期**物理半坏舱**(拔相机USB)→SerialBin 实产 CcdSnMissing→该舱排除、好舱继续 | **真机** | ☐ 残留(需物理拔相机,软件做不到;剔除决策已单测+登记点镜像现有错误位) |
-| H-08 | 双端展示:operate 监控页"舱故障"区(读 /status Faults)/ front 报警列表(读 alarm 表) | **真机** | ☐ 后续阶段(operate 区新增;front 复用现有报警列表已通 H-05/06) |
+| H-08 | 双端展示:operate 监控页"舱故障"区(读 /status Faults)/ front 报警列表(读 alarm 表) | **真机** | ◑ operate 区**代码完成+逻辑/契约验证**(编译0错/curl /status Faults契约/映射harness 14检查全过);front 半已通(H-05/06)。残:WPF像素渲染待清僵尸重启、有故障端到端待H-07物理注入 |
 
 > **真机环境(2026-06-24)**:旧 control 15840 经 `/shutdown`(tl13579)干净停;新建 Release 起 pid10360(从 `ivf_tl_ControlHost/bin/Release/net6.0-windows/`,`..\tl-shared.config` 在 bin/Release);无活体培养(空闲监测),重启安全。测试数据(假 PHOTO 告警×2 / 发送记录 / 测试联系人)**已全部清理**。新 control 保留运行(健康、无回归;本特性分支构建未并 main)。
 > **告警通道核实结论**:`reportAlarm`→报警责任链→`alarm` 表→front 报警列表 + operate"系统异常(N)" + **短信/电话通知**(报警链内置 getNotifier)+ 静音/消警 = 完整闭环;状态码 0正常/1异常/-1跳过。**弃用** `reportCloudAlarm`(只发 IM 群消息、不入闭环)及新建告警类型——无 Java 字典登记依赖。

+ 4 - 4
项目文档/进度/进度数据.js

@@ -1,10 +1,10 @@
 // 实时面板数据源(监控面板.html 读 window.PROGRESS_DATA)。每推进一步更新本文件。
 window.PROGRESS_DATA = {
   project: "operate/control 双进程拆分",
-  generatedAt: "2026-06-24 01:10",
-  phase: "三阶段主体完成;M区全闭合;D2-02 第一阶段【代码+真机全过】;舱室故障隔离 第一阶段【代码完成+真机核心验证过】(37单测绿+基线无回归+告警闭环落库+短信通知派发)",
-  currentTask: "舱室故障隔离专项 第一阶段=代码完成+真机核心验证过(8 commit, feature/house-fault-isolation)。真机(无活体培养):H-04基线新control(pid10360)无回归(/status新增Faults字段空、好舱2/4/6/7/8/9≈37℃、started);H-05 reportAlarm相机异常→alarm表落PHOTO_STATE_ALARM(证实复用现有闭环+排除舱能落库);H-06插联系人→1min定时任务→阿里云短信,alarm_send_info记'发送成功!'派发至18223210384(到达由阿里云,用户自查)。测试数据已清理。残留H-07物理拔相机(软件做不到)。下一步:可并main。",
-  note: "告警闭环=ReportAlarmController→/reportAlarm→报警责任链→alarm表→front报警列表+operate系统异常(N)+短信电话(每1min定时任务getPersonList→aivfo-service阿里云短信)+静音/消警。状态码0正常/1异常/-1跳过。阿里云凭据硬编码AliConstant(signName=艾伟孚科技)。reportCloudAlarm只发IM群消息不入闭环=弃用;短信电话保留并复用。改面:SerialBin/StartupFaultPolicy/HouseFault/StartMain/AppData/MonitorSnapshot。新control保留运行。",
+  generatedAt: "2026-06-24 14:30",
+  phase: "三阶段主体完成;M区全闭合;D2-02 第一阶段【代码+真机全过】;舱室故障隔离 第一阶段【代码+真机核心验证】已并main;后续 H-08 operate'舱故障'区【代码完成+逻辑/契约验证】",
+  currentTask: "H-08 operate 监控页'舱故障'区(读 /status Faults)代码完成+逻辑/契约验证(改在 main 工作区,未提交)。改3文件纯operate侧加展示:新增 ServiceMonitorFaultMapper(8枚举名→中文/HouseText/AtText UTC→本地/IsolatedText)+ ServiceMonitorViewModel 加 Faults集合/汇总/Visibility显隐 + XAML 红色故障区(无故障显绿条/有故障逐条红底)。验证3层:operate Release 编译0错(XAML→BAML绑定全对)/curl control pid10360 /status 证 Faults字段透出(基线空·字段名匹配)/映射harness(临时文件/FaultMapperTest 链入真源码)14检查全过(8枚举+边界+UTC东八区+8+隔离)。残:WPF像素渲染待清僵尸真重启、有故障端到端=H-07物理拔相机门控。下一步:代码+文档一起提交。",
+  note: "H-08 拆两半:operate监控页舱故障区(本步,读/status Faults)+ front报警列表(H-05/06已通)。control端Faults第一阶段已透出(MonitorSnapshot.Faults/HouseFaultRow),本步纯operate加展示不碰control。FaultType透出英文枚举名(f.Type.ToString)→operate翻中文;At是UtcNow→展示转本地;HouseSn=-1=相机/串口级未定位。BoolToVisibilityConverter项目未注册,改用ViewModel直给Visibility。新control pid10360保留健康运行。",
   milestones: [
     { name: "阶段1 · control 独立进程骨架(完成)", tasks: [
       { id: "Task1-7", name: "全过+D1-08死锁修复+operate真外壳E2E+数据入库DB铁证", status: "☑" }

+ 9 - 9
项目文档/进度/进度状态.yaml

@@ -1,13 +1,13 @@
 # 续接断点状态(机器可解析)。换会话/换电脑后首先读它定位。
 # 状态取值: 未开始 / 进行中 / 完成 / 代码完成待验证
 # 纪律:本字段只存【当前断点】,历史细节进 交接卡.md(见 CLAUDE.md 第三节)。
-更新时间: 2026-06-24 舱室故障隔离专项 第一阶段【代码完成+真机核心验证过】:Task1-6(8 commit, feature/house-fault-isolation),37单测绿;真机基线无回归(新control pid10360 /status Faults空+好舱齐)+ 告警闭环落库(reportAlarm→alarm表)+ 短信通知链路打通(已派发至18223210384,到达由阿里云)。残留 H-07 物理拔相机注入(软件做不到)。
+更新时间: 2026-06-24 舱室故障隔离【后续阶段·H-08 operate 监控页"舱故障"区】代码完成+逻辑/契约验证:新增 ServiceMonitorFaultMapper(纯静态映射,8 枚举名→中文)+ ServiceMonitorViewModel 加 Faults 集合/汇总/显隐 + XAML 红色故障区。operate Release 编译 0 错;curl control pid10360 /status 证 Faults 字段透出(基线空);映射 harness(临时文件/FaultMapperTest 链入真源码)14 检查全过(含 UTC→本地东八区+8)。
 当前任务: >
-  【舱室故障隔离专项 第一阶段 = 代码完成 + 真机核心验证过,可收尾并 main】(分支 feature/house-fault-isolation)
-  · 代码 Task1-6 全落地、37 单测绿;方案已修正为复用现有 reportAlarm 报警闭环(非群消息,短信电话保留并复用)。
-  · 真机(2026-06-24,无活体培养):H-04 基线新 control 无回归(Faults 字段空、好舱2/4/6/7/8/9≈37℃、started);H-05 reportAlarm 相机异常→alarm 表落 PHOTO_STATE_ALARM(证实闭环+排除舱能落库);H-06 插联系人→定时任务→阿里云短信 alarm_send_info 记"发送成功!"派发至 18223210384(到达由阿里云,用户自查)。测试数据已清理,新 control pid10360 保留运行
-  · 残留 H-07:物理拔相机USB制造半坏舱(软件拔不了,需物理/模拟);剔除决策已单测、登记点镜像现有错误位、基线+闭环已验
-  · 下一步:第一阶段可并 main(finishing-a-development-branch)。后续阶段:operate 监控页"舱故障"区/运行期按需补/H-07 物理注入
+  【舱室故障隔离 后续阶段:H-08 operate"舱故障"区 = 代码完成 + 逻辑/契约验证】(改在 main 工作区,未提交)
+  · 改 3 文件(纯 operate 侧加展示,不碰 control):新增 ServiceMonitorFaultMapper.cs(FaultTypeZh 8 枚举→中文/HouseText/AtText UTC→本地/IsolatedText)、ServiceMonitorViewModel.cs(Faults 集合+汇总属性+Visibility 直给不用转换器)、ServiceMonitorView.xaml(红色故障区,无故障显绿条/有故障逐条红底)。
+  · 验证 3 层:operate Release 编译 0 错(XAML→BAML 绑定全对)/ curl /status Faults 契约通(基线空数组,字段名匹配)/ 映射 harness 14 检查全过(8 枚举名+边界+UTC东八区+隔离)
+  · 残(同既有门控):WPF 外壳像素渲染受僵尸 20268 占 Mutex 阻塞(待真重启)/"有故障"端到端无法软件注入启动故障(=H-07 物理拔相机门控)
+  · 下一步:代码+文档一起提交;后续清僵尸真重启复测监控页像素 + H-07 物理注入看红区,或推进 D2-02 二/三阶段
 说明: >
   告警闭环(核实):ReportAlarmController→/reportAlarm→报警责任链→aivfo_tl_setting.alarm 表→front报警列表+operate"系统异常(N)"+短信电话(AlarmSchedule每1min扫在报→getPersonList→aivfo-service阿里云短信)+muteAlarm静音+恢复stopAlarm消警。状态码0正常/1异常/-1跳过。阿里云凭据硬编码 AliConstant(signName=艾伟孚科技)。reportCloudAlarm 只发IM群消息不入闭环=已弃用。
   代码改面:SerialBin(+Faults6处)/StartupFaultPolicy(新)/HouseFault(新,control/ivf_tl_Entity/)/StartMain(InitTL剔除+InitHouse逐舱兜底+StartRun报完整清单)/AppData(StartupFaults+快照+ReportStartupFaults走ReportAlarmController)/MonitorSnapshot(+Faults)。分支从 feature/d2-02-debug-command-proxy 切(测试工程在该分支,main落后)。
@@ -31,6 +31,6 @@
     备注: "Task0-9 全落地,27单测绿(含C-1非零起点红线回归)。真机完整冒烟过(借真实舱/读温/越界实拒/超时回收)。第二(MJPEG)/三(operate接入V-012)阶段待拆。"
   - id: 加固-舱室故障隔离
     名称: 舱室故障隔离 + 双端故障提示(新专项)
-    状态: 第一阶段代码完成·真机核心验证过(残留H-07物理注入)
-    备注: "spec+计划+方案修正(复用reportAlarm闭环)。代码 Task1-6 全完成(8 commit, feature/house-fault-isolation),37单测绿。真机:H-04基线无回归(新control pid10360 Faults空+好舱齐)/H-05 reportAlarm→alarm表落库/H-06 短信通知派发至18223210384(到达由阿里云)。残留H-07物理拔相机(软件做不到)。可并main。"
-下一步: 舱室故障隔离第一阶段 + D2-02 第一阶段【已本地合并入 main】(快进,未推 origin,main 领先 origin/main 34 commit;feature/house-fault-isolation 已删,feature/d2-02-debug-command-proxy 冗余可删)。残留 H-07 物理拔相机注入待人配合/软件模拟。后续阶段:operate 监控页"舱故障"区/运行期按需补去抖/front 复用现有报警列表。需要时 git push origin main
+    状态: 第一阶段已并main;后续 H-08 operate 区代码完成+逻辑/契约验证(残 WPF像素/H-07物理注入)
+    备注: "第一阶段(Task1-6,37单测,真机基线+闭环+短信)已本地并 main。后续 H-08 operate 监控页'舱故障'区:新增 ServiceMonitorFaultMapper+ViewModel Faults集合+XAML红区,operate Release 0错+curl /status Faults契约+映射harness14检查全过;残 WPF像素渲染(僵尸阻塞)+有故障端到端(=H-07物理注入)。front半已通H-05/06。"
+下一步: H-08 operate"舱故障"区代码完成+逻辑/契约验证【改在 main 工作区,未提交】——下一步代码+文档一起提交(本地 main 已领先 origin/main 34 commit,需要时 git push origin main)。残:清僵尸真重启复测监控页像素 + H-07 物理拔相机看红区。再后:D2-02 二/三阶段 / 运行期按需补去抖。冗余分支 feature/d2-02-debug-command-proxy 可删