主要修改了保护功能

This commit is contained in:
zhengsl 2026-07-02 17:43:32 +08:00
parent e4adac9f88
commit 1a8c113fbc
13 changed files with 161 additions and 113 deletions

View File

@ -783,7 +783,7 @@ PowerShell.exe -ExecutionPolicy Bypass -File .\run.ps1
| 字段 | 含义 |
| ------------- | ------------------------------------------ |
| **产品名称** | 例如 "24 路固态功率控制器" |
| **设备名称** | 例如 "24 路固态功率控制器" |
| **型号** | 产品型号 |
| **编号** | 被测件序列号 / 单号;建议唯一 |
| **生产厂家** | 被测件来源 |
@ -1370,7 +1370,7 @@ $$
| 字段 | 填入 |
| -------- | -------------------------- |
| 产品名称 | 24 路固态功率控制器 |
| 设备名称 | 24 路固态功率控制器 |
| 型号 | SSPC-24P-A |
| 编号 | SSPC-2026-001 |
| 生产厂家 | XX 公司 |

View File

@ -89,8 +89,19 @@ public partial class PowerLossRow : ObservableObject
/// <summary>保护功能单项结果。</summary>
public partial class ProtectRow : ObservableObject
{
[ObservableProperty] private int _index;
[ObservableProperty] private string _protectType = "";
[ObservableProperty] private string _trigger = "";
[ObservableProperty] private string _measuredValue = "----";
[ObservableProperty] private string _judgeMode = "人工判别";
[ObservableProperty] private double _tripTimeMs;
[ObservableProperty] private TestStatus _status = TestStatus.Untested;
public static string BuildMeasuredValue(string protectType, double? inputVoltage, double? inputCurrent) => protectType switch
{
"跳闸" when inputCurrent.HasValue => $"{inputCurrent.Value:F3} A",
"过压" or "欠压" when inputVoltage.HasValue => $"{inputVoltage.Value:F3} V",
"限流" when inputCurrent.HasValue => $"{inputCurrent.Value:F3} A",
_ => "----"
};
}

View File

@ -95,9 +95,9 @@ public sealed class ReportGenerator
});
IdentityRow(table, "报告编号", reportNo, true);
IdentityRow(table, "项目名称", ValueOrDash(p.Name));
IdentityRow(table, "产品名称", ValueOrDash(p.Dut.ProductName));
IdentityRow(table, "产品型号", ValueOrDash(p.Dut.Model));
IdentityRow(table, "产品编号", ValueOrDash(p.Dut.SerialNumber));
IdentityRow(table, "设备编号", ValueOrDash(p.Dut.SerialNumber));
IdentityRow(table, "设备名称", ValueOrDash(p.Dut.ProductName));
IdentityRow(table, "设备型号", ValueOrDash(p.Dut.Model));
IdentityRow(table, "委托/生产单位", ValueOrDash(p.Dut.Manufacturer));
IdentityRow(table, "检测日期", p.Site.TestDate.ToString("yyyy年MM月dd日"));
});
@ -275,9 +275,9 @@ public sealed class ReportGenerator
columns.ConstantColumn(82); columns.RelativeColumn();
columns.ConstantColumn(82); columns.RelativeColumn();
});
OverviewCell(table, "设备名称", p.Dut.ProductName);
OverviewCell(table, "设备型号", p.Dut.Model);
OverviewCell(table, "设备编号", p.Dut.SerialNumber);
OverviewCell(table, "设备名称", p.Dut.ProductName);
OverviewCell(table, "设备型号", p.Dut.Model);
OverviewCell(table, "生产厂家", p.Dut.Manufacturer);
OverviewCell(table, "测试人员", p.Site.Tester);
OverviewCell(table, "测试日期", p.Site.TestDate.ToString("yyyy-MM-dd"));
@ -377,12 +377,16 @@ public sealed class ReportGenerator
})));
private static void ProtectTable(IContainer container, Project p) =>
DataTable(container, new[] { 1.2f, 1.6f, 1f, 0.72f },
new[] { "保护类型", "触发条件", "关断时间 ms", "判定" },
DataTable(container, new[] { 0.55f, 1.1f, 1.45f, 1f, 0.95f, 0.75f },
new[] { "序号", "保护类型", "触发条件", "实测值", "判别模式", "判别结果" },
p.ProtectResults.Select(r => new ReportRow(r.Status, new[]
{
ValueOrDash(r.ProtectType), ValueOrDash(r.Trigger),
Format(r.TripTimeMs, "F2"), StatusText(r.Status)
r.Index > 0 ? r.Index.ToString() : "—",
ValueOrDash(r.ProtectType),
ValueOrDash(r.Trigger),
ValueOrDash(r.MeasuredValue),
ValueOrDash(r.JudgeMode),
StatusText(r.Status)
})));
private static void DataTable(IContainer container, IReadOnlyList<float> widths,

View File

@ -145,9 +145,9 @@ internal sealed class LegacyReportGenerator
c.Table(t =>
{
t.ColumnsDefinition(cd => { cd.ConstantColumn(120); cd.RelativeColumn(); });
Row(t, "设备名称", p.Dut.ProductName);
Row(t, "设备型号", p.Dut.Model);
Row(t, "设备编号", p.Dut.SerialNumber);
Row(t, "设备名称", p.Dut.ProductName);
Row(t, "设备型号", p.Dut.Model);
Row(t, "生产厂家", p.Dut.Manufacturer);
});
}
@ -256,14 +256,21 @@ internal sealed class LegacyReportGenerator
{
t.ColumnsDefinition(cd =>
{
cd.RelativeColumn(); cd.RelativeColumn(); cd.RelativeColumn(); cd.ConstantColumn(60);
cd.ConstantColumn(40);
cd.RelativeColumn();
cd.RelativeColumn();
cd.RelativeColumn();
cd.ConstantColumn(70);
cd.ConstantColumn(60);
});
HeaderRow(t, "保护类型", "触发条件", "关断时间(ms)", "结果");
HeaderRow(t, "序号", "保护类型", "触发条件", "实测值", "判别模式", "判别结果");
foreach (var r in p.ProtectResults)
{
t.Cell().Padding(3).Text(r.Index.ToString());
t.Cell().Padding(3).Text(r.ProtectType);
t.Cell().Padding(3).Text(r.Trigger);
t.Cell().Padding(3).Text(r.TripTimeMs.ToString("F2"));
t.Cell().Padding(3).Text(r.MeasuredValue);
t.Cell().Padding(3).Text(r.JudgeMode);
t.Cell().Padding(3).Text(StatusText(r.Status)).FontColor(StatusColor(r.Status));
}
});

View File

@ -87,9 +87,9 @@ public sealed class WordReportGenerator
{
("报告编号", reportNo),
("项目名称", ValueOrDash(p.Name)),
("设备名称", ValueOrDash(p.Dut.ProductName)),
("设备型号", ValueOrDash(p.Dut.Model)),
("设备编号", ValueOrDash(p.Dut.SerialNumber)),
("设备名称", ValueOrDash(p.Dut.ProductName)),
("设备型号", ValueOrDash(p.Dut.Model)),
("委托/生产单位", ValueOrDash(p.Dut.Manufacturer)),
("检测日期", p.Site.TestDate.ToString("yyyy-MM-dd"))
}),
@ -166,11 +166,15 @@ public sealed class WordReportGenerator
Section(body, section++, "保护功能", "PROTECTION FUNCTION");
if (p.ProtectResults.Count > 0)
body.Append(ResultTable(
new[] { "保护类型", "触发条件", "关断时间 ms", "判定" },
new[] { "序号", "保护类型", "触发条件", "实测值", "判别模式", "判别结果" },
p.ProtectResults.Select(r => new[]
{
ValueOrDash(r.ProtectType), ValueOrDash(r.Trigger),
Format(r.TripTimeMs, "F2"), StatusText(r.Status)
r.Index > 0 ? r.Index.ToString() : "—",
ValueOrDash(r.ProtectType),
ValueOrDash(r.Trigger),
ValueOrDash(r.MeasuredValue),
ValueOrDash(r.JudgeMode),
StatusText(r.Status)
})));
ManualNote(body, p.TestFunction?.Protect);
}
@ -189,8 +193,7 @@ public sealed class WordReportGenerator
private static Table OverviewTable(Project p) => KeyValueGrid(new[]
{
("产品名称", p.Dut.ProductName), ("产品型号", p.Dut.Model),
("产品编号", p.Dut.SerialNumber), ("生产厂家", p.Dut.Manufacturer),
("设备编号", p.Dut.SerialNumber), ("设备名称", p.Dut.ProductName), ("设备型号", p.Dut.Model),
("测试人员", p.Site.Tester), ("测试日期", p.Site.TestDate.ToString("yyyy-MM-dd")),
("环境温度", $"{p.Site.TemperatureC:F1} ℃"), ("相对湿度", $"{p.Site.HumidityPct:F1} %RH"),
("环境气压", $"{p.Site.PressureKpa:F1} kPa"), ("项目创建", p.CreatedAt.ToString("yyyy-MM-dd HH:mm")),

View File

@ -22,13 +22,17 @@ public sealed class ProtectTest : ITestModule
public async Task RunAsync(TestContext ctx, IProgress<TestProgress> progress, CancellationToken ct)
{
ctx.Project.ProtectResults.Clear();
var latestPower = ctx.Project.PowerResults.LastOrDefault();
for (int i = 0; i < _items.Length; i++)
{
ct.ThrowIfCancellationRequested();
var row = new ProtectRow
{
Index = i + 1,
ProtectType = _items[i].Type,
Trigger = _items[i].Trigger,
MeasuredValue = ProtectRow.BuildMeasuredValue(_items[i].Type, latestPower?.Vin, latestPower?.Iin),
JudgeMode = "人工判别",
Status = TestStatus.Running
};
ctx.Project.ProtectResults.Add(row);

View File

@ -163,8 +163,11 @@ public static class Program
{
p.ProtectResults.Add(new ProtectRow
{
Index = i + 1,
ProtectType = protects[i].Type,
Trigger = protects[i].Trigger,
MeasuredValue = ProtectRow.BuildMeasuredValue(protects[i].Type, 28.0, 1.0),
JudgeMode = "人工判别",
TripTimeMs = Math.Round(2.0 + i * 0.3 + rng.NextDouble() * 0.2, 2),
Status = TestStatus.Pass
});

View File

@ -354,7 +354,7 @@ public partial class MainViewModel : ObservableObject
Name = dlg.ProjectName,
Dut = new DutInfo
{
ProductName = "固态功率控制器",
ProductName = dlg.ProductName,
SerialNumber = dlg.SerialNumber,
Model = dlg.Model,
},

View File

@ -30,8 +30,25 @@ public partial class ProtectViewModel : ObservableObject
public void SetProject(Project p)
{
Project = p;
SyncRowsFromProject();
}
private void SyncRowsFromProject()
{
if (Project == null) return;
var latestPower = Project.PowerResults.LastOrDefault();
for (int i = 0; i < Project.ProtectResults.Count; i++)
{
var row = Project.ProtectResults[i];
row.Index = i + 1;
row.MeasuredValue = ProtectRow.BuildMeasuredValue(row.ProtectType, latestPower?.Vin, latestPower?.Iin);
if (string.IsNullOrWhiteSpace(row.JudgeMode))
row.JudgeMode = "人工判别";
}
Rows.Clear();
foreach (var r in p.ProtectResults) Rows.Add(r);
foreach (var row in Project.ProtectResults) Rows.Add(row);
}
[RelayCommand]
@ -40,6 +57,7 @@ public partial class ProtectViewModel : ObservableObject
if (Project == null) return;
Project.ProtectResults.Clear();
Rows.Clear();
var latestPower = Project.PowerResults.LastOrDefault();
var items = new (string, string)[]
{
("跳闸", "1.5x额定电流"),
@ -51,7 +69,16 @@ public partial class ProtectViewModel : ObservableObject
};
for (int i = 0; i < items.Length; i++)
{
var r = new ProtectRow { ProtectType = items[i].Item1, Trigger = items[i].Item2, TripTimeMs = 2 + i * 0.3, Status = TestStatus.Pass };
var r = new ProtectRow
{
Index = i + 1,
ProtectType = items[i].Item1,
Trigger = items[i].Item2,
MeasuredValue = ProtectRow.BuildMeasuredValue(items[i].Item1, latestPower?.Vin, latestPower?.Iin),
JudgeMode = "人工判别",
TripTimeMs = 2 + i * 0.3,
Status = TestStatus.Pass
};
Project.ProtectResults.Add(r);
Rows.Add(r);
}

View File

@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="新建项目"
Width="480" Height="380"
Width="480" Height="420"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Background="{StaticResource BgAppBrush}"
@ -31,6 +31,8 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource FieldLabel}" Text="项目名称 *" />
@ -39,11 +41,14 @@
<TextBlock Grid.Row="2" Style="{StaticResource FieldLabel}" Text="设备编号" />
<TextBox x:Name="SerialBox" Grid.Row="2" Grid.Column="1" FontFamily="{StaticResource FontMono}" />
<TextBlock Grid.Row="4" Style="{StaticResource FieldLabel}" Text="设备型号" />
<TextBox x:Name="ModelBox" Grid.Row="4" Grid.Column="1" FontFamily="{StaticResource FontMono}" />
<TextBlock Grid.Row="4" Style="{StaticResource FieldLabel}" Text="设备名称" />
<TextBox x:Name="ProductNameBox" Grid.Row="4" Grid.Column="1" FontFamily="{StaticResource FontMono}" />
<TextBlock Grid.Row="6" Style="{StaticResource FieldLabel}" Text="测试人员" />
<TextBox x:Name="TesterBox" Grid.Row="6" Grid.Column="1" />
<TextBlock Grid.Row="6" Style="{StaticResource FieldLabel}" Text="设备型号" />
<TextBox x:Name="ModelBox" Grid.Row="6" Grid.Column="1" FontFamily="{StaticResource FontMono}" />
<TextBlock Grid.Row="8" Style="{StaticResource FieldLabel}" Text="测试人员" />
<TextBox x:Name="TesterBox" Grid.Row="8" Grid.Column="1" />
</Grid>
</Border>

View File

@ -6,6 +6,7 @@ public partial class NewProjectDialog : Window
{
public string ProjectName { get; private set; } = "";
public string SerialNumber { get; private set; } = "";
public string ProductName { get; private set; } = "";
public string Model { get; private set; } = "";
public string Tester { get; private set; } = "";
@ -31,6 +32,7 @@ public partial class NewProjectDialog : Window
}
ProjectName = name;
SerialNumber = SerialBox.Text?.Trim() ?? "";
ProductName = ProductNameBox.Text?.Trim() ?? "";
Model = ModelBox.Text?.Trim() ?? "";
Tester = TesterBox.Text?.Trim() ?? "";
DialogResult = true;

View File

@ -35,12 +35,12 @@
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource FieldLabel}" Text="设备名称" Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Project.Dut.ProductName, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="设备型号" Grid.Row="0" Grid.Column="2" Margin="16,0,12,0" />
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Project.Dut.Model, UpdateSourceTrigger=PropertyChanged}" FontFamily="{StaticResource FontMono}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="设备编号" Grid.Row="2" Grid.Column="0" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Project.Dut.SerialNumber, UpdateSourceTrigger=PropertyChanged}" FontFamily="{StaticResource FontMono}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="设备编号" Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Project.Dut.SerialNumber, UpdateSourceTrigger=PropertyChanged}" FontFamily="{StaticResource FontMono}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="设备名称" Grid.Row="0" Grid.Column="2" Margin="16,0,12,0"/>
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Project.Dut.ProductName, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="设备型号" Grid.Row="2" Grid.Column="0" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Project.Dut.Model, UpdateSourceTrigger=PropertyChanged}" FontFamily="{StaticResource FontMono}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="生产厂家" Grid.Row="2" Grid.Column="2" Margin="16,0,12,0" />
<TextBox Grid.Row="2" Grid.Column="3" Text="{Binding Project.Dut.Manufacturer, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
@ -62,27 +62,35 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource FieldLabel}" Text="测试人员" Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Project.Site.Tester, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="测试日期" Grid.Row="2" Grid.Column="0" />
<DatePicker Grid.Row="2" Grid.Column="1" SelectedDate="{Binding Project.Site.TestDate}" Height="30" />
<TextBlock Style="{StaticResource FieldLabel}" Text="测试环境" Grid.Row="4" Grid.Column="0" />
<StackPanel Orientation="Horizontal" Grid.Row="4" Grid.Column="1">
<Grid Grid.Row="0" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="110" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource FieldLabel}" Text="测试人员" Grid.Column="0" />
<TextBox Grid.Column="1" Text="{Binding Project.Site.Tester, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource FieldLabel}" Text="测试日期" Grid.Column="2" Margin="20,0,0,0"/>
<DatePicker Grid.Column="3" SelectedDate="{Binding Project.Site.TestDate}" Height="30" />
</Grid>
<TextBlock Style="{StaticResource FieldLabel}" Text="测试环境" Grid.Row="2" Grid.Column="0" />
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1">
<TextBlock Text="温度" VerticalAlignment="Center" Foreground="{StaticResource TextSecondaryBrush}" />
<TextBox Width="70" Margin="6,0,4,0" Text="{Binding Project.Site.TemperatureC}" FontFamily="{StaticResource FontMono}" />
<TextBlock Text="℃" VerticalAlignment="Center" Margin="0,0,16,0" />
<TextBox Width="100" Margin="6,0,4,0" Text="{Binding Project.Site.TemperatureC}" FontFamily="{StaticResource FontMono}" />
<TextBlock Text="℃" VerticalAlignment="Center" Margin="0,0,40,0" />
<TextBlock Text="湿度" VerticalAlignment="Center" Foreground="{StaticResource TextSecondaryBrush}" />
<TextBox Width="70" Margin="6,0,4,0" Text="{Binding Project.Site.HumidityPct}" FontFamily="{StaticResource FontMono}" />
<TextBlock Text="%" VerticalAlignment="Center" Margin="0,0,16,0" />
<TextBox Width="100" Margin="6,0,4,0" Text="{Binding Project.Site.HumidityPct}" FontFamily="{StaticResource FontMono}" />
<TextBlock Text="%" VerticalAlignment="Center" Margin="0,0,40,0" />
<TextBlock Text="气压" VerticalAlignment="Center" Foreground="{StaticResource TextSecondaryBrush}" />
<TextBox Width="80" Margin="6,0,4,0" Text="{Binding Project.Site.PressureKpa}" FontFamily="{StaticResource FontMono}" />
<TextBox Width="100" Margin="6,0,4,0" Text="{Binding Project.Site.PressureKpa}" FontFamily="{StaticResource FontMono}" />
<TextBlock Text="kPa" VerticalAlignment="Center" />
</StackPanel>
<TextBlock Style="{StaticResource FieldLabel}" Text="备注" Grid.Row="6" Grid.Column="0" />
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding Project.Site.Remark, UpdateSourceTrigger=PropertyChanged}" Height="60"
<TextBlock Style="{StaticResource FieldLabel}" Text="备注" Grid.Row="4" Grid.Column="0" />
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Project.Site.Remark, UpdateSourceTrigger=PropertyChanged}" Height="60"
TextWrapping="Wrap" AcceptsReturn="True" VerticalContentAlignment="Top" />
</Grid>
</StackPanel>

View File

@ -1,6 +1,7 @@
<UserControl x:Class="SSPCTester.UI.Views.ProtectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:SSPCTester.Logic.Models;assembly=SSPCTester.Logic"
xmlns:vm="clr-namespace:SSPCTester.UI.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -16,87 +17,60 @@
<TextBlock Text="保护功能(覆盖指标 11/14/15/17/19/21/22/23" FontSize="{StaticResource FsH1}" FontWeight="Bold" Margin="0,0,0,8" />
<Border Grid.Row="1" Padding="12,8" Background="{StaticResource WarningSoftBrush}" CornerRadius="{StaticResource RadiusMd}" Margin="0,0,0,12">
<TextBlock Text="此模块当前仅验证框架,暂不执行实际故障触发测试。" Foreground="{StaticResource WarningBrush}" FontWeight="SemiBold" />
</Border>
<Grid Grid.Row="2">
<Grid Grid.Row="2" Margin="0,12,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="320" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Style="{StaticResource Card}" Margin="0,0,12,0">
<StackPanel>
<TextBlock Style="{StaticResource CardTitle}" Text="保护项目" />
<CheckBox Margin="0,4" Content="跳闸" IsChecked="{Binding EnableTrip}" />
<CheckBox Margin="0,4" Content="短路" IsChecked="{Binding EnableShort}" />
<CheckBox Margin="0,4" Content="过温" IsChecked="{Binding EnableOverTemp}" />
<CheckBox Margin="0,4" Content="过压" IsChecked="{Binding EnableOverVolt}" />
<CheckBox Margin="0,4" Content="欠压" IsChecked="{Binding EnableUnderVolt}" />
<CheckBox Margin="0,4" Content="限流" IsChecked="{Binding EnableLimit}" />
<TextBlock Style="{StaticResource CardTitle}" Text="触发阈值" Margin="0,16,0,12" />
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="6" />
<RowDefinition Height="Auto" />
<RowDefinition Height="6" />
<RowDefinition Height="Auto" />
<RowDefinition Height="6" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="过压 (V)" /><TextBox Grid.Column="1" Text="{Binding OverVoltThreshold}" FontFamily="{StaticResource FontMono}" />
<TextBlock Grid.Row="2" Text="欠压 (V)" /><TextBox Grid.Row="2" Grid.Column="1" Text="{Binding UnderVoltThreshold}" FontFamily="{StaticResource FontMono}" />
<TextBlock Grid.Row="4" Text="过流 (A)" /><TextBox Grid.Row="4" Grid.Column="1" Text="{Binding OverCurrentThreshold}" FontFamily="{StaticResource FontMono}" />
<TextBlock Grid.Row="6" Text="过温 (℃)" /><TextBox Grid.Row="6" Grid.Column="1" Text="{Binding OverTempThreshold}" FontFamily="{StaticResource FontMono}" />
</Grid>
<Button Style="{StaticResource PrimaryButton}" Content="生成演示结果" Margin="0,16,0,0" Command="{Binding RunDemoCommand}" />
</StackPanel>
</Border>
<Border Style="{StaticResource Card}" Grid.Column="1">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Style="{StaticResource CardTitle}" Text="故障关断曲线(占位)" />
<Border Background="{StaticResource WaveBgBrush}" BorderBrush="{StaticResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="{StaticResource RadiusMd}">
<TextBlock Text="—— 框架占位 ——" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{StaticResource TextMutedBrush}" />
</Border>
</DockPanel>
</Border>
</Grid>
<Grid Grid.Row="3" Margin="0,12,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="576" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Style="{StaticResource Card}" Margin="0,0,12,0">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Style="{StaticResource CardTitle}" Text="结果" />
<TextBlock DockPanel.Dock="Top" Style="{StaticResource CardTitle}" Text="测试记录" />
<DataGrid ItemsSource="{Binding Rows}">
<DataGrid.Columns>
<DataGridTextColumn Header="序号" Binding="{Binding Index}" Width="60" />
<DataGridTextColumn Header="保护类型" Binding="{Binding ProtectType}" Width="*" />
<DataGridTextColumn Header="触发条件" Binding="{Binding Trigger}" Width="2*" />
<DataGridTextColumn Header="关断时间(ms)" Width="*">
<DataGridTextColumn.Binding><Binding Path="TripTimeMs" Converter="{StaticResource NumberOrDash}" ConverterParameter="F2" /></DataGridTextColumn.Binding>
<DataGridTextColumn Header="实测值" Width="*">
<DataGridTextColumn.Binding><Binding Path="MeasuredValue" /></DataGridTextColumn.Binding>
<DataGridTextColumn.ElementStyle><Style TargetType="TextBlock" BasedOn="{StaticResource MonoText}" /></DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Header="结果" Width="100">
<DataGridTemplateColumn Header="判别模式" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border Style="{StaticResource StatusBadge}" Background="{Binding Status, Converter={StaticResource StatusToSoftBrush}}">
<TextBlock Text="{Binding Status, Converter={StaticResource StatusToText}}" Foreground="{Binding Status, Converter={StaticResource StatusToBrush}}" FontSize="{StaticResource FsSmall}" />
</Border>
<ComboBox SelectedValue="{Binding JudgeMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Content"
MinWidth="100">
<ComboBoxItem Content="人工判别" />
<ComboBoxItem Content="自动判别" />
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="判别结果" Width="110">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding Status, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Tag"
MinWidth="90">
<ComboBoxItem Content="合格" Tag="{x:Static model:TestStatus.Pass}" />
<ComboBoxItem Content="不合格" Tag="{x:Static model:TestStatus.Fail}" />
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Border>
</Border>
</Grid>
<Grid Grid.Row="3" Margin="0,12,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="1" Style="{StaticResource Card}" Padding="14">
<Grid>