This commit is contained in:
2
App.xaml
2
App.xaml
@@ -3,5 +3,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="Views/MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
19
Converters/BoolToStringConverter.cs
Normal file
19
Converters/BoolToStringConverter.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace MembranePoreTester.Converters
|
||||
{
|
||||
public class BoolToStringConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return (value is bool b && b) ? "开" : "关";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value?.ToString() == "开";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,11 @@ using System.IO;
|
||||
|
||||
public static class ExportHelper
|
||||
{
|
||||
static ExportHelper()
|
||||
{
|
||||
// 设置许可上下文为非商业用途(开源项目/个人学习)
|
||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||
}
|
||||
public static void ExportBubblePoint(BubblePointEntity entity, string filePath)
|
||||
{
|
||||
using var package = new ExcelPackage();
|
||||
|
||||
@@ -1,57 +1,141 @@
|
||||
public interface ITestRecord
|
||||
{
|
||||
/// <summary>主键ID,自增</summary>
|
||||
int Id { get; set; }
|
||||
|
||||
/// <summary>工位编号(1~3)</summary>
|
||||
int StationId { get; set; }
|
||||
|
||||
/// <summary>测试日期时间</summary>
|
||||
DateTime TestDate { get; set; }
|
||||
|
||||
/// <summary>测试人员姓名</summary>
|
||||
string Tester { get; set; }
|
||||
// 公共字段
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 泡点法测试记录实体,用于数据库存储。
|
||||
/// 对应 GB/T 32361-2015 标准中的最大孔径测试记录。
|
||||
/// </summary>
|
||||
public class BubblePointEntity : ITestRecord
|
||||
{
|
||||
/// <summary>主键ID,自增</summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>工位编号(1~3)</summary>
|
||||
public int StationId { get; set; }
|
||||
|
||||
/// <summary>测试日期时间</summary>
|
||||
public DateTime TestDate { get; set; }
|
||||
public string Tester { get; set; }
|
||||
public string SampleType { get; set; }
|
||||
public string SampleSpec { get; set; }
|
||||
public double RoomTemperature { get; set; }
|
||||
public double SoakingTime { get; set; }
|
||||
public string LiquidName { get; set; }
|
||||
public double LiquidSurfaceTension { get; set; }
|
||||
public string LiquidManufacturer { get; set; }
|
||||
public double BubblePointPressure { get; set; }
|
||||
public string PressureUnit { get; set; }
|
||||
public double MaxPoreSize { get; set; }
|
||||
|
||||
/// <summary>测试人员姓名</summary>
|
||||
public string? Tester { get; set; }
|
||||
|
||||
/// <summary>膜类型(平板膜/中空纤维膜)</summary>
|
||||
public string? SampleType { get; set; }
|
||||
|
||||
/// <summary>样品规格(如直径、厚度等)</summary>
|
||||
public string? SampleSpec { get; set; }
|
||||
|
||||
/// <summary>测试时室温(°C)</summary>
|
||||
public double? RoomTemperature { get; set; }
|
||||
|
||||
/// <summary>膜在测试液体中的浸润时间(小时)</summary>
|
||||
public double? SoakingTime { get; set; }
|
||||
|
||||
/// <summary>测试液体名称(如“水”、“乙醇”等)</summary>
|
||||
public string? LiquidName { get; set; }
|
||||
|
||||
/// <summary>测试液体的表面张力(mN/m,25°C)</summary>
|
||||
public double? LiquidSurfaceTension { get; set; }
|
||||
|
||||
/// <summary>测试液体生产厂家(可选)</summary>
|
||||
public string? LiquidManufacturer { get; set; }
|
||||
|
||||
/// <summary>泡点压力数值</summary>
|
||||
public double? BubblePointPressure { get; set; }
|
||||
|
||||
/// <summary>泡点压力单位(Pa / cmHg / psi)</summary>
|
||||
public string? PressureUnit { get; set; }
|
||||
|
||||
/// <summary>计算得出的最大孔径(μm)</summary>
|
||||
public double? MaxPoreSize { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 孔分布测试记录实体,用于数据库存储。
|
||||
/// 对应 GB/T 32361-2015 标准中的平均流量法测试记录。
|
||||
/// </summary>
|
||||
public class PoreDistributionEntity : ITestRecord
|
||||
{
|
||||
/// <summary>主键ID,自增</summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>工位编号(1~3)</summary>
|
||||
public int StationId { get; set; }
|
||||
|
||||
/// <summary>测试日期时间</summary>
|
||||
public DateTime TestDate { get; set; }
|
||||
|
||||
/// <summary>测试人员姓名</summary>
|
||||
public string Tester { get; set; }
|
||||
|
||||
/// <summary>膜类型(平板膜/中空纤维膜)</summary>
|
||||
public string SampleType { get; set; }
|
||||
|
||||
/// <summary>样品规格(如直径、厚度等)</summary>
|
||||
public string SampleSpec { get; set; }
|
||||
|
||||
/// <summary>测试时室温(°C)</summary>
|
||||
public double RoomTemperature { get; set; }
|
||||
|
||||
/// <summary>膜在测试液体中的浸润时间(小时)</summary>
|
||||
public double SoakingTime { get; set; }
|
||||
|
||||
/// <summary>测试液体名称(如“水”、“乙醇”等)</summary>
|
||||
public string LiquidName { get; set; }
|
||||
|
||||
/// <summary>测试液体的表面张力(mN/m,25°C)</summary>
|
||||
public double LiquidSurfaceTension { get; set; }
|
||||
|
||||
/// <summary>测试液体生产厂家(可选)</summary>
|
||||
public string LiquidManufacturer { get; set; }
|
||||
|
||||
/// <summary>压力单位(Pa / cmHg / psi)</summary>
|
||||
public string PressureUnit { get; set; }
|
||||
|
||||
/// <summary>泡点压力(可选,用于记录)</summary>
|
||||
public double BubblePointPressure { get; set; }
|
||||
|
||||
/// <summary>计算得出的平均孔径(μm)</summary>
|
||||
public double AveragePoreSize { get; set; }
|
||||
// 导航属性:数据点
|
||||
|
||||
/// <summary>导航属性:该测试记录对应的所有压力-流量数据点</summary>
|
||||
public List<DataPointEntity> DataPoints { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 孔分布测试的数据点实体,存储每个压力点对应的湿膜和干膜流量。
|
||||
/// 与 PoreDistributionEntity 构成一对多的关系。
|
||||
/// </summary>
|
||||
public class DataPointEntity
|
||||
{
|
||||
/// <summary>主键ID,自增</summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>关联的孔分布测试记录ID</summary>
|
||||
public int PoreDistributionId { get; set; }
|
||||
|
||||
/// <summary>测试压力值,单位由所属记录的 PressureUnit 决定</summary>
|
||||
public double Pressure { get; set; }
|
||||
|
||||
/// <summary>湿膜流量(L/min)</summary>
|
||||
public double WetFlow { get; set; }
|
||||
|
||||
/// <summary>干膜流量(L/min)</summary>
|
||||
public double DryFlow { get; set; }
|
||||
|
||||
/// <summary>导航属性:所属的孔分布测试记录</summary>
|
||||
public PoreDistributionEntity PoreDistribution { get; set; }
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace MembranePoreTester.Models
|
||||
}
|
||||
}
|
||||
}
|
||||
public string PressureUnit { get; set; } // Pa/cmHg/psi
|
||||
public string PressureUnit { get; set; } = "Pa"; // Pa/cmHg/psi
|
||||
public DateTime TestDate { get; set; } = DateTime.Now;
|
||||
public string Tester { get; set; }
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace MembranePoreTester.Models
|
||||
/// <summary>
|
||||
/// 测试人员姓名。
|
||||
/// </summary>
|
||||
public string Tester { get; set; }
|
||||
public string Tester { get; set; } = "系统测试员";
|
||||
|
||||
// ---------- 计算结果(由计算逻辑填充) ----------
|
||||
|
||||
|
||||
@@ -158,16 +158,26 @@ namespace MembranePoreTester.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task ReadPlcAsync()
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
|
||||
Record.BubblePointPressure = Record.BubbleCurrentPressure;
|
||||
|
||||
OnPropertyChanged(nameof(Record.BubblePointPressure));
|
||||
try
|
||||
{
|
||||
double rawPressure = await _plcService.ReadPressureAsync(StationId);
|
||||
rawPressure = Math.Round((double)rawPressure, 2);
|
||||
double pressure = rawPressure * _plcConfig.PressureFactor;
|
||||
|
||||
// 更新当前压力显示
|
||||
Record.BubbleCurrentPressure = pressure;
|
||||
// 同时将泡点压力设为该值(符合涨破测试逻辑)
|
||||
Record.BubblePointPressure = pressure;
|
||||
|
||||
OnPropertyChanged(nameof(Record.BubblePointPressure));
|
||||
OnPropertyChanged(nameof(Record.BubbleCurrentPressure));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -207,12 +217,12 @@ namespace MembranePoreTester.ViewModels
|
||||
{
|
||||
StationId = StationId,
|
||||
TestDate = Record.TestDate,
|
||||
Tester = Record.Tester,
|
||||
SampleType = Record.SampleType,
|
||||
SampleSpec = Record.SampleSpec,
|
||||
Tester = Record.Tester ?? "系统操作员",
|
||||
SampleType = Record.SampleType ?? "缺省值",
|
||||
SampleSpec = Record.SampleSpec ?? "缺省值",
|
||||
RoomTemperature = Record.RoomTemperature,
|
||||
SoakingTime = Record.SoakingTime,
|
||||
LiquidName = Record.Liquid?.Name,
|
||||
LiquidName = Record.Liquid?.Name ?? "缺省值",
|
||||
LiquidSurfaceTension = Record.Liquid?.SurfaceTension ?? 0,
|
||||
LiquidManufacturer = Record.LiquidManufacturer,
|
||||
BubblePointPressure = Record.BubblePointPressure,
|
||||
@@ -232,12 +242,12 @@ namespace MembranePoreTester.ViewModels
|
||||
|
||||
Record.SampleType = entity.SampleType;
|
||||
Record.SampleSpec = entity.SampleSpec;
|
||||
Record.RoomTemperature = entity.RoomTemperature;
|
||||
Record.SoakingTime = entity.SoakingTime;
|
||||
Record.RoomTemperature = entity.RoomTemperature ?? 0;
|
||||
Record.SoakingTime = entity.SoakingTime ?? 0;
|
||||
Record.Liquid = TestLiquid.Predefined.FirstOrDefault(l => l.Name == entity.LiquidName)
|
||||
?? new TestLiquid { Name = entity.LiquidName, SurfaceTension = entity.LiquidSurfaceTension };
|
||||
?? new TestLiquid { Name = entity.LiquidName, SurfaceTension = entity.LiquidSurfaceTension ?? 0 };
|
||||
Record.LiquidManufacturer = entity.LiquidManufacturer;
|
||||
Record.BubblePointPressure = entity.BubblePointPressure;
|
||||
Record.BubblePointPressure = entity.BubblePointPressure ?? 0;
|
||||
Record.PressureUnit = entity.PressureUnit;
|
||||
Record.TestDate = entity.TestDate;
|
||||
Record.Tester = entity.Tester;
|
||||
@@ -316,8 +326,23 @@ namespace MembranePoreTester.ViewModels
|
||||
};
|
||||
if (saveFileDialog.ShowDialog() == true)
|
||||
{
|
||||
// 转换为Entity后导出
|
||||
var entity = new BubblePointEntity { /* 从Record复制 */ };
|
||||
// 将 Record 转换为 Entity
|
||||
var entity = new BubblePointEntity
|
||||
{
|
||||
StationId = this.StationId,
|
||||
TestDate = Record.TestDate,
|
||||
Tester = Record.Tester,
|
||||
SampleType = Record.SampleType,
|
||||
SampleSpec = Record.SampleSpec,
|
||||
RoomTemperature = Record.RoomTemperature,
|
||||
SoakingTime = Record.SoakingTime,
|
||||
LiquidName = Record.Liquid?.Name,
|
||||
LiquidSurfaceTension = Record.Liquid?.SurfaceTension,
|
||||
LiquidManufacturer = Record.LiquidManufacturer,
|
||||
BubblePointPressure = Record.BubblePointPressure,
|
||||
PressureUnit = Record.PressureUnit,
|
||||
MaxPoreSize = Record.MaxPoreSize
|
||||
};
|
||||
ExportHelper.ExportBubblePoint(entity, saveFileDialog.FileName);
|
||||
MessageBox.Show("导出成功");
|
||||
}
|
||||
|
||||
@@ -91,59 +91,44 @@ namespace MembranePoreTester.ViewModels
|
||||
|
||||
private async void AutoCollectTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsActive) return; // 不在当前标签页,跳过采集
|
||||
await SafeExecuteAsync($"AutoCollect_Station{StationId}", async () =>
|
||||
if (!IsActive) return;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
// 1. 读取当前压力
|
||||
float rawPressure = await _plcService.ReadPressureAsync(StationId);
|
||||
double pressure = Math.Round(rawPressure * _plcConfig.PressureFactor, 2);
|
||||
|
||||
// 2. 读取当前模式对应的流量
|
||||
double flow = 0;
|
||||
if (TestMode.Contains("湿膜"))
|
||||
{
|
||||
// 1. 读取当前压力
|
||||
float rawPressure = await _plcService.ReadPressureAsync(StationId);
|
||||
double pressure = Math.Round(rawPressure, 2);
|
||||
|
||||
double flow = 0;
|
||||
if (TestMode == "湿膜")
|
||||
{
|
||||
float rawFlow = await _plcService.ReadWetFlowAsync();
|
||||
flow = rawFlow;
|
||||
}
|
||||
else
|
||||
{
|
||||
float rawFlow = await _plcService.ReadDryFlowAsync();
|
||||
flow = rawFlow;
|
||||
}
|
||||
|
||||
flow = Math.Round(flow, 2);
|
||||
// 3. 在 DataPoints 中查找是否存在相同压力的行(允许微小误差)
|
||||
var existing = Record.DataPoints.FirstOrDefault(p => Math.Abs(p.Pressure - pressure) < 0.001);
|
||||
if (existing != null)
|
||||
{
|
||||
// 更新对应列
|
||||
if (TestMode == "湿膜")
|
||||
existing.WetFlow = flow;
|
||||
else
|
||||
existing.DryFlow = flow;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 新增一行
|
||||
var newPoint = new Models.DataPoint { Pressure = pressure };
|
||||
if (TestMode == "湿膜")
|
||||
newPoint.WetFlow = flow;
|
||||
else
|
||||
newPoint.DryFlow = flow;
|
||||
Record.DataPoints.Add(newPoint);
|
||||
}
|
||||
|
||||
// 4. 刷新曲线
|
||||
UpdatePlot();
|
||||
float rawFlow = await _plcService.ReadWetFlowAsync();
|
||||
flow = Math.Round(rawFlow, 2);
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"自动采集失败: {ex.Message}");
|
||||
float rawFlow = await _plcService.ReadDryFlowAsync();
|
||||
flow = Math.Round(rawFlow, 2);
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 直接新增一行(不查找相同压力)
|
||||
var newPoint = new Models.DataPoint
|
||||
{
|
||||
Pressure = pressure,
|
||||
WetFlow = TestMode.Contains("湿膜") ? flow : 0,
|
||||
DryFlow = TestMode.Contains("干膜") ? flow : 0
|
||||
};
|
||||
Record.DataPoints.Add(newPoint);
|
||||
|
||||
// 4. 刷新曲线
|
||||
UpdatePlot();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"自动采集失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有数据点,重置表格和曲线
|
||||
/// </summary>
|
||||
@@ -285,7 +270,10 @@ namespace MembranePoreTester.ViewModels
|
||||
|
||||
Record.DataPoints.CollectionChanged += (s, e) => UpdatePlot();
|
||||
|
||||
// 加到构造函数最后即可
|
||||
ClearAllCommand = new RelayCommand(ClearAllData);
|
||||
|
||||
RemoveDataPointCommand = new RelayCommand(RemoveSelectedDataPoint, () => SelectedDataPoint != null);
|
||||
|
||||
// 延迟2秒后读取,确保连接稳定
|
||||
Task.Delay(2000).ContinueWith(async _ =>
|
||||
@@ -295,6 +283,11 @@ namespace MembranePoreTester.ViewModels
|
||||
|
||||
}
|
||||
|
||||
private void ClearAllData()
|
||||
{
|
||||
ClearData();
|
||||
}
|
||||
|
||||
private async Task ReadPressureModeAsync()
|
||||
{
|
||||
await SafeExecuteAsync($"ReadPressureModeAsync{StationId}", async () =>
|
||||
@@ -338,7 +331,7 @@ namespace MembranePoreTester.ViewModels
|
||||
|
||||
// 始终读取压力
|
||||
float rawPressure = await _plcService.ReadPressureAsync(StationId);
|
||||
double pressure = rawPressure;
|
||||
double pressure = Math.Round(rawPressure * _plcConfig.PressureFactor, 2);
|
||||
|
||||
if (SelectedDataPoint != null)
|
||||
{
|
||||
@@ -495,14 +488,14 @@ namespace MembranePoreTester.ViewModels
|
||||
{
|
||||
StationId = this.StationId,
|
||||
TestDate = Record.TestDate,
|
||||
Tester = Record.Tester,
|
||||
Tester = Record.Tester ?? "系统操作员",
|
||||
SampleType = Record.SampleType,
|
||||
SampleSpec = Record.SampleSpec,
|
||||
SampleSpec = Record.SampleSpec ?? "缺省值",
|
||||
RoomTemperature = Record.RoomTemperature,
|
||||
SoakingTime = Record.SoakingTime,
|
||||
LiquidName = Record.Liquid?.Name,
|
||||
LiquidSurfaceTension = Record.Liquid?.SurfaceTension ?? 0,
|
||||
LiquidManufacturer = Record.LiquidManufacturer,
|
||||
LiquidManufacturer = Record.LiquidManufacturer ?? "缺省值",
|
||||
PressureUnit = Record.PressureUnit,
|
||||
BubblePointPressure = Record.BubblePointPressure,
|
||||
AveragePoreSize = AveragePoreSize, // 使用计算后的属性
|
||||
@@ -685,10 +678,22 @@ namespace MembranePoreTester.ViewModels
|
||||
}
|
||||
|
||||
|
||||
public ICommand ClearAllCommand { get; }
|
||||
|
||||
|
||||
// 修改命令初始化(构造函数中)
|
||||
|
||||
|
||||
// 实现删除方法
|
||||
private void RemoveSelectedDataPoint()
|
||||
{
|
||||
if (SelectedDataPoint != null)
|
||||
{
|
||||
Record.DataPoints.Remove(SelectedDataPoint);
|
||||
SelectedDataPoint = null; // 清除选中状态
|
||||
UpdatePlot(); // 刷新曲线
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -70,8 +70,8 @@
|
||||
<GroupBox Header="📊 压力数据">
|
||||
<StackPanel>
|
||||
<Label Content="当前压力(kPa)" FontWeight="Normal" Margin="5,0,0,0"/>
|
||||
<TextBox Text="{Binding Record.BubbleCurrentPressure}" Width="150" Margin="5,0,5,5"/>
|
||||
<Label Content="泡点压力(kPa)" FontWeight="Normal" Margin="5,0,0,0"/>
|
||||
<TextBox HorizontalAlignment="Left" Text="{Binding Record.BubbleCurrentPressure}" Width="150" Margin="5,0,5,5"/>
|
||||
<Label Content="泡点压力(Pa)" FontWeight="Normal" Margin="5,0,0,0"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding Record.BubblePointPressure}" Width="150" Margin="5"/>
|
||||
<Button Content="涨破" Command="{Binding ReadPlcCommand}" Padding="10,5" Margin="5" Background="#FF9800"/>
|
||||
@@ -83,11 +83,16 @@
|
||||
</StackPanel>
|
||||
|
||||
<!-- 结果显示 -->
|
||||
<Border Grid.Row="1" Margin="0,10" Background="White" CornerRadius="8" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<Border Grid.Row="1" Margin="0,10" Background="White" CornerRadius="8" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<Grid>
|
||||
<GroupBox Header="✨ 测试结果" BorderThickness="0" Margin="0">
|
||||
<TextBlock FontSize="48" FontWeight="Bold" TextAlignment="Center"
|
||||
Text="{Binding MaxPoreSize, StringFormat={}{0:F3} μm}" Foreground="#2196F3"/>
|
||||
<GroupBox Header="✨ 测试结果" BorderThickness="0"
|
||||
Margin="0" Padding="5,0">
|
||||
<!-- 减少内边距 -->
|
||||
<TextBlock FontSize="40" FontWeight="Bold" TextAlignment="Center"
|
||||
Text="{Binding MaxPoreSize, StringFormat={}{0:F3} μm}"
|
||||
Foreground="#2196F3"
|
||||
Margin="0,-30,0,-10"/>
|
||||
<!-- 负边距压缩垂直空间 -->
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -1,8 +1,92 @@
|
||||
<Window x:Class="MembranePoreTester.Views.HistoryWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="历史记录" Height="600" Width="900"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
Title="历史记录" Height="600" Width="1000"
|
||||
WindowStartupLocation="CenterOwner" Loaded="Window_Loaded"
|
||||
Background="#F5F7FA" FontFamily="Segoe UI">
|
||||
<Window.Resources>
|
||||
<!-- 统一按钮样式 -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#2196F3"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#1976D2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#0D47A1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- 统一文本框样式 -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="5,2"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一组合框样式 -->
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一标签样式 -->
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="#2C3E50"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一 DatePicker 样式(保持一致性) -->
|
||||
<Style TargetType="DatePicker">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</Style>
|
||||
|
||||
<!-- DataGrid 样式 -->
|
||||
<Style TargetType="DataGrid">
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#E9ECF0"/>
|
||||
<Setter Property="RowHeight" Value="28"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="GridLinesVisibility" Value="Horizontal"/>
|
||||
<Setter Property="HeadersVisibility" Value="Column"/>
|
||||
<Setter Property="RowBackground" Value="White"/>
|
||||
<Setter Property="AlternatingRowBackground" Value="#F9F9F9"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@@ -10,48 +94,53 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 过滤条件 -->
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="0" Margin="0,0,0,10">
|
||||
<Label Content="工位:"/>
|
||||
<ComboBox x:Name="cmbStation" SelectedIndex="0" Width="80" Margin="5,0">
|
||||
<ComboBoxItem Content="全部"/>
|
||||
<ComboBoxItem Content="1"/>
|
||||
<ComboBoxItem Content="2"/>
|
||||
<ComboBoxItem Content="3"/>
|
||||
</ComboBox>
|
||||
<Label Content="测试类型:"/>
|
||||
<ComboBox x:Name="cmbType" SelectedIndex="0" Width="120" Margin="5,0">
|
||||
<ComboBoxItem Content="全部"/>
|
||||
<ComboBoxItem Content="泡点法"/>
|
||||
<ComboBoxItem Content="孔分布"/>
|
||||
</ComboBox>
|
||||
<Label Content="日期范围:"/>
|
||||
<DatePicker x:Name="dpStart" Width="120"/>
|
||||
<Label Content="-" Margin="2"/>
|
||||
<DatePicker x:Name="dpEnd" Width="120"/>
|
||||
<Button Content="查询" Click="Query_Click" Padding="15,5" Margin="10,0"/>
|
||||
<Button Content="导出选中" Click="ExportSelected_Click" Padding="15,5"/>
|
||||
</StackPanel>
|
||||
<!-- 过滤条件区域(卡片式) -->
|
||||
<Border Grid.Row="0" Background="White" CornerRadius="6" Padding="10" Margin="0,0,0,10" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="工位:"/>
|
||||
<ComboBox x:Name="cmbStation" Width="80">
|
||||
<ComboBoxItem Content="全部"/>
|
||||
<ComboBoxItem Content="1"/>
|
||||
<ComboBoxItem Content="2"/>
|
||||
<ComboBoxItem Content="3"/>
|
||||
</ComboBox>
|
||||
<Label Content="测试类型:"/>
|
||||
<ComboBox x:Name="cmbType" Width="120">
|
||||
<ComboBoxItem Content="全部"/>
|
||||
<ComboBoxItem Content="泡点法"/>
|
||||
<ComboBoxItem Content="孔分布"/>
|
||||
</ComboBox>
|
||||
<Label Content="日期范围:"/>
|
||||
<DatePicker x:Name="dpStart" Width="120"/>
|
||||
<Label Content="-" Margin="2,0,2,0" VerticalAlignment="Center"/>
|
||||
<DatePicker x:Name="dpEnd" Width="120"/>
|
||||
<Button Content="🔍 查询" Click="Query_Click" Padding="15,5" Background="#2196F3"/>
|
||||
<Button Content="📊 导出选中" Click="ExportSelected_Click" Padding="15,5" Background="#4CAF50"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 历史记录列表 -->
|
||||
<DataGrid x:Name="dgHistory" Grid.Row="1" AutoGenerateColumns="False"
|
||||
IsReadOnly="True" SelectionMode="Single" SelectionUnit="FullRow">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="50"/>
|
||||
<DataGridTextColumn Header="工位" Binding="{Binding StationId}" Width="50"/>
|
||||
<DataGridTextColumn Header="类型" Binding="{Binding Type}" Width="80"/>
|
||||
<DataGridTextColumn Header="测试日期" Binding="{Binding TestDate}" Width="150"/>
|
||||
<DataGridTextColumn Header="样品类型" Binding="{Binding SampleType}" Width="100"/>
|
||||
<DataGridTextColumn Header="规格" Binding="{Binding SampleSpec}" Width="100"/>
|
||||
<DataGridTextColumn Header="测试者" Binding="{Binding Tester}" Width="100"/>
|
||||
<DataGridTextColumn Header="结果" Binding="{Binding Result}" Width="150"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<!-- 历史记录列表(带边框) -->
|
||||
<Border Grid.Row="1" Background="White" CornerRadius="6" BorderBrush="#E9ECF0" BorderThickness="1" Padding="5">
|
||||
<DataGrid x:Name="dgHistory" AutoGenerateColumns="False"
|
||||
IsReadOnly="True" SelectionMode="Single" SelectionUnit="FullRow"
|
||||
BorderThickness="0" HorizontalGridLinesBrush="#E9ECF0">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="50"/>
|
||||
<DataGridTextColumn Header="工位" Binding="{Binding StationId}" Width="50"/>
|
||||
<DataGridTextColumn Header="类型" Binding="{Binding Type}" Width="80"/>
|
||||
<DataGridTextColumn Header="测试日期" Binding="{Binding TestDate}" Width="150"/>
|
||||
<DataGridTextColumn Header="样品类型" Binding="{Binding SampleType}" Width="100"/>
|
||||
<DataGridTextColumn Header="规格" Binding="{Binding SampleSpec}" Width="100"/>
|
||||
<DataGridTextColumn Header="测试者" Binding="{Binding Tester}" Width="100"/>
|
||||
<DataGridTextColumn Header="结果" Binding="{Binding Result}" Width="150"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<!-- 底部按钮区域 -->
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
|
||||
<Button Content="加载到当前工位" Click="LoadToCurrentStation_Click" Padding="15,5" Margin="5"/>
|
||||
<Button Content="关闭" Click="Close_Click" Padding="15,5"/>
|
||||
<Button Content="📂 加载到当前工位" Click="LoadToCurrentStation_Click" Padding="15,5" Background="#FF9800"/>
|
||||
<Button Content="❌ 关闭" Click="Close_Click" Padding="15,5" Background="#607D8B"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -145,6 +145,11 @@ namespace MembranePoreTester.Views
|
||||
private void Close_Click(object sender, RoutedEventArgs e) => Close();
|
||||
|
||||
public static event EventHandler<LoadRecordEventArgs> LoadRecordEvent;
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Query_Click(sender, e);
|
||||
}
|
||||
}
|
||||
|
||||
public class LoadRecordEventArgs : EventArgs
|
||||
|
||||
@@ -3,64 +3,146 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:MembranePoreTester.Views"
|
||||
xmlns:viewModels="clr-namespace:MembranePoreTester.ViewModels"
|
||||
xmlns:conv="clr-namespace:MembranePoreTester.Converters"
|
||||
Title="膜孔径测试系统 (GB/T 32361-2015)"
|
||||
Width="1024" Height="768"
|
||||
WindowStartupLocation="CenterScreen" KeyDown="Window_KeyDown">
|
||||
WindowStartupLocation="CenterScreen" KeyDown="Window_KeyDown"
|
||||
Background="#F5F7FA" FontFamily="Segoe UI">
|
||||
|
||||
<Window.DataContext>
|
||||
<viewModels:MainViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
<Window.Resources>
|
||||
<conv:BoolToStringConverter x:Key="BoolToStringConverter"/>
|
||||
<!-- 统一按钮样式(与子视图保持一致) -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#2196F3"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#1976D2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#0D47A1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- 统一 ComboBox 样式 -->
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一 TabControl 样式 -->
|
||||
<Style TargetType="TabControl">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TabItem">
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="Padding" Value="15,8"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="#5F6A7A"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TabItem">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderThickness="0,0,0,2"
|
||||
BorderBrush="Transparent"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter ContentSource="Header" HorizontalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter Property="Foreground" Value="Blue"/>
|
||||
<Setter Property="BorderBrush" Value="#2196F3"/>
|
||||
<Setter Property="Background" Value="#E3F2FD"/>
|
||||
<!-- 添加浅蓝背景 -->
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Foreground" Value="Blue"/>
|
||||
<Setter Property="Background" Value="#F5F5F5"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<DockPanel>
|
||||
<!-- 全局历史记录按钮 -->
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5">
|
||||
<Button Content="历史记录" Click="OpenHistory_Click" Padding="10,5" Margin="5"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 顶部历史记录栏 -->
|
||||
<Border DockPanel.Dock="Top" Background="White" BorderBrush="#E9ECF0" BorderThickness="0,0,0,1" Padding="10,5">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="📋 历史记录" Click="OpenHistory_Click" Width="100" HorizontalAlignment="Left" Background="#607D8B"/>
|
||||
<Button Content="阀门控制" Click="OpenValveControl_Click" Padding="10,5" Margin="5"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 工位选项卡 -->
|
||||
<TabControl x:Name="stationTabControl" ItemsSource="{Binding Stations}">
|
||||
<TabControl x:Name="stationTabControl" ItemsSource="{Binding Stations}" Background="White" Margin="10,10,10,10">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}"/>
|
||||
<TextBlock Text="{Binding Name}" FontSize="14" FontWeight="Medium"/>
|
||||
</DataTemplate>
|
||||
</TabControl.ItemTemplate>
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel>
|
||||
<DockPanel Margin="0,10,0,0">
|
||||
<!-- 工位控制栏 -->
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5">
|
||||
<ComboBox ItemsSource="{Binding PressureModeList}"
|
||||
SelectedItem="{Binding SelectedPressureMode}" Width="80" Margin="5"/>
|
||||
<Border DockPanel.Dock="Top" Background="White" CornerRadius="6" Padding="10" Margin="0,0,0,10" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ComboBox ItemsSource="{Binding PressureModeList}" SelectedItem="{Binding SelectedPressureMode}" Width="80"/>
|
||||
<Button Content="▶ 启动" Command="{Binding StartCommand}" Width="80" Background="#4CAF50"/>
|
||||
<Button Content="⏹ 停止" Command="{Binding StopCommand}" Width="80" Background="#F44336"/>
|
||||
<Button Content="{Binding PressButtonText}" Command="{Binding PressCommand}" Width="80" Background="#FF9800"/>
|
||||
|
||||
|
||||
<Button Content="启动" Command="{Binding StartCommand}" Width="80" Margin="5"/>
|
||||
<Button Content="停止" Command="{Binding StopCommand}" Width="80" Margin="5"/>
|
||||
|
||||
|
||||
<Button Content="{Binding PressButtonText}" Command="{Binding PressCommand}" Width="80" Margin="5"/>
|
||||
|
||||
<!-- 替换为状态指示灯 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="5">
|
||||
<!-- 带阴影和光泽的圆形指示灯 -->
|
||||
<Grid Width="18" Height="18" Margin="2">
|
||||
<Ellipse Fill="{Binding EnableStatusColor}">
|
||||
<Ellipse.Effect>
|
||||
<DropShadowEffect BlurRadius="3" ShadowDepth="0" Opacity="0.5"/>
|
||||
</Ellipse.Effect>
|
||||
</Ellipse>
|
||||
<Ellipse Margin="2,2,2,2" Opacity="0.6">
|
||||
<Ellipse.Fill>
|
||||
<RadialGradientBrush>
|
||||
<GradientStop Color="White" Offset="0.2"/>
|
||||
<GradientStop Color="Transparent" Offset="1"/>
|
||||
</RadialGradientBrush>
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding EnableStatusText}" VerticalAlignment="Center" Margin="5,0,0,0"/>
|
||||
<!-- 状态指示灯 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="15,0,0,0">
|
||||
<Grid Width="18" Height="18" Margin="2">
|
||||
<Ellipse Fill="{Binding EnableStatusColor}">
|
||||
<Ellipse.Effect>
|
||||
<DropShadowEffect BlurRadius="3" ShadowDepth="0" Opacity="0.3"/>
|
||||
</Ellipse.Effect>
|
||||
</Ellipse>
|
||||
<Ellipse Margin="2" Opacity="0.6">
|
||||
<Ellipse.Fill>
|
||||
<RadialGradientBrush>
|
||||
<GradientStop Color="White" Offset="0.2"/>
|
||||
<GradientStop Color="Transparent" Offset="1"/>
|
||||
</RadialGradientBrush>
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding EnableStatusText}" VerticalAlignment="Center" Margin="5,0,0,0" FontSize="12"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 测试类型选项卡 -->
|
||||
<TabControl x:Name="stationTabControl" ItemsSource="{Binding Stations}" SelectionChanged="TabControl_SelectionChanged">
|
||||
<!-- 测试类型选项卡:修复嵌套绑定,直接定义两个TabItem -->
|
||||
<TabControl SelectionChanged="TabControl_SelectionChanged">
|
||||
<TabItem Header="泡点法测试最大孔径">
|
||||
<local:BubblePointView DataContext="{Binding BubblePointVM}"/>
|
||||
</TabItem>
|
||||
|
||||
@@ -103,5 +103,12 @@ namespace MembranePoreTester.Views
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OpenValveControl_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var win = new ValveControlWindow();
|
||||
win.Owner = this;
|
||||
win.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,29 +87,6 @@
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!--<GroupBox Header="校准参数">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label Grid.Row="0" Grid.Column="0">压力零点校准:</Label>
|
||||
<TextBox x:Name="txtPressureZero" Grid.Row="0" Grid.Column="1"/>
|
||||
<Label Grid.Row="1" Grid.Column="0">压力量程校准:</Label>
|
||||
<TextBox x:Name="txtPressureSpan" Grid.Row="1" Grid.Column="1"/>
|
||||
<Label Grid.Row="2" Grid.Column="0">流量零点校准:</Label>
|
||||
<TextBox x:Name="txtFlowZero" Grid.Row="2" Grid.Column="1"/>
|
||||
<Label Grid.Row="3" Grid.Column="0">流量量程校准:</Label>
|
||||
<TextBox x:Name="txtFlowSpan" Grid.Row="3" Grid.Column="1"/>
|
||||
</Grid>
|
||||
</GroupBox>-->
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20,0,0">
|
||||
<!--<Button Content="读取参数" Click="ReadParameters_Click" Width="100" Margin="5"/>-->
|
||||
|
||||
|
||||
@@ -155,6 +155,13 @@
|
||||
</ComboBox>
|
||||
<ComboBox Grid.Row="3" HorizontalAlignment="Right" Grid.Column="3" ItemsSource="{Binding PressureModeList}"
|
||||
SelectedItem="{Binding SelectedPressureMode}" Width="100"/>
|
||||
<Button Grid.Row="3" Grid.Column="3"
|
||||
Content="🗑 清除数据"
|
||||
Command="{Binding ClearAllCommand}"
|
||||
Background="#F44336"
|
||||
Width="90"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,5,5,5"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
@@ -181,11 +188,12 @@
|
||||
<!-- 湿膜表格 -->
|
||||
<GroupBox Grid.Column="0" Header="💧 湿膜数据" Margin="0,0,5,0">
|
||||
<DataGrid ItemsSource="{Binding Record.DataPoints}"
|
||||
SelectedItem="{Binding SelectedDataPoint}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
ColumnWidth="*">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="压力"
|
||||
<DataGridTextColumn Header="压力(kPa)"
|
||||
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
|
||||
MinWidth="100"/>
|
||||
<DataGridTextColumn Header="湿膜流量(L/min)"
|
||||
@@ -198,11 +206,12 @@
|
||||
<!-- 干膜表格 -->
|
||||
<GroupBox Grid.Column="1" Header="🔥 干膜数据" Margin="5,0,0,0">
|
||||
<DataGrid ItemsSource="{Binding Record.DataPoints}"
|
||||
SelectedItem="{Binding SelectedDataPoint}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
ColumnWidth="*">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="压力"
|
||||
<DataGridTextColumn Header="压力(kPa)"
|
||||
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
|
||||
MinWidth="100"/>
|
||||
<DataGridTextColumn Header="干膜流量(L/min)"
|
||||
@@ -235,27 +244,30 @@
|
||||
<!-- 计算结果及操作按钮区域 -->
|
||||
<Border Grid.Row="2" Margin="0,10,0,0" Background="White" CornerRadius="6" Padding="10" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<GroupBox Header="📏 平均孔径" Margin="5" BorderThickness="0">
|
||||
<TextBlock Text="{Binding AveragePoreSize, StringFormat={}{0:F3} μm}" FontSize="20" FontWeight="Bold" Foreground="#2196F3"/>
|
||||
|
||||
<GroupBox Header="📏 平均孔径" Margin="0" BorderThickness="0">
|
||||
<TextBlock Text="{Binding AveragePoreSize, StringFormat={}{0:F2} μm}" FontSize="14" FontWeight="Bold" Foreground="#2196F3"/>
|
||||
</GroupBox>
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="10,0"/>
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="2,0"/>
|
||||
|
||||
<GroupBox Header="🎯 孔分布区间计算" Margin="5" BorderThickness="0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding LowerPore}" Width="60" Margin="2"/>
|
||||
<TextBlock Text="~" VerticalAlignment="Center" Margin="2"/>
|
||||
<TextBox Text="{Binding UpperPore}" Width="60" Margin="2"/>
|
||||
<TextBlock Text="μm" VerticalAlignment="Center" Margin="5,0,10,0"/>
|
||||
<Button Content="计算" Command="{Binding CalculateCommand}" Margin="5,0" Padding="12,5" Background="#2196F3"/>
|
||||
<TextBlock Text="{Binding RangePercentage, StringFormat={}{0:F1}%}" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0" Foreground="#4CAF50"/>
|
||||
<TextBox Text="{Binding LowerPore}" Width="48" Margin="1"/>
|
||||
<TextBlock Text="~" VerticalAlignment="Center" Margin="1"/>
|
||||
<TextBox Text="{Binding UpperPore}" Width="48" Margin="1"/>
|
||||
<TextBlock Text="μm" VerticalAlignment="Center" Margin="2,0,5,0"/>
|
||||
<Button Content="计算" Command="{Binding CalculateCommand}" Margin="2,0" Padding="8,5" Background="#2196F3"/>
|
||||
<TextBlock Text="{Binding RangePercentage, StringFormat={}{0:F1}%}" FontSize="14" FontWeight="Bold" VerticalAlignment="Center" Margin="5,0" Foreground="#4CAF50"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="10,0"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="🔧 大流量校准" Command="{Binding OpenFlowCalibCommand}" Background="#607D8B"/>
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="3,0"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Button Content="🔧 大流量校准" Command="{Binding OpenFlowCalibCommand}" Background="#607D8B"/>
|
||||
<Button Content="🔧 小流量校准" Command="{Binding OpenFlowCalibCommand2}" Background="#607D8B"/>
|
||||
<Button Content="📄 生成报告" Command="{Binding GenerateReportCommand}" Background="#2196F3"/>
|
||||
<Button Content="💾 保存到历史" Command="{Binding SaveCommand}" Background="#4CAF50"/>
|
||||
<Button Content="📊 导出Excel" Command="{Binding ExportCommand}" Background="#FF9800"/>
|
||||
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
161
Views/ValveControlWindow.xaml
Normal file
161
Views/ValveControlWindow.xaml
Normal file
@@ -0,0 +1,161 @@
|
||||
<Window x:Class="MembranePoreTester.Views.ValveControlWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:MembranePoreTester.Converters"
|
||||
Title="阀门控制" Height="768" Width="1024"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Background="#F5F7FA" FontFamily="Segoe UI">
|
||||
<Window.Resources>
|
||||
<local:BoolToStringConverter x:Key="BoolToStringConverter"/>
|
||||
|
||||
<!-- 统一按钮样式 -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#2196F3"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#1976D2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#0D47A1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- 统一组合框样式 -->
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一文本框样式 -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#D0D3D9"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="5,2"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- 统一标签样式 -->
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="#2C3E50"/>
|
||||
</Style>
|
||||
|
||||
<!-- ToggleButton 样式(开关按钮) -->
|
||||
<Style TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="#9E9E9E"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="8,4"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="Background" Value="#4CAF50"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#757575"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="Background" Value="#45a049"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- ItemsControl 项容器样式(圆角边框,交替背景) -->
|
||||
<Style TargetType="ContentPresenter">
|
||||
<Setter Property="Margin" Value="0,2"/>
|
||||
</Style>
|
||||
<Style TargetType="Border" x:Key="ValveItemBorder">
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="CornerRadius" Value="4"/>
|
||||
<Setter Property="BorderBrush" Value="#E9ECF0"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="10,8"/>
|
||||
<Setter Property="Margin" Value="0,2"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 工位选择卡片 -->
|
||||
<Border Grid.Row="0" Background="White" CornerRadius="6" Padding="10" Margin="0,0,0,10" BorderBrush="#E9ECF0" BorderThickness="1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="工位:" VerticalAlignment="Center" Margin="5"/>
|
||||
<ComboBox x:Name="cmbStation" Width="80">
|
||||
<ComboBoxItem IsSelected="True" Content="工位1"/>
|
||||
<ComboBoxItem Content="工位2"/>
|
||||
<ComboBoxItem Content="工位3"/>
|
||||
</ComboBox>
|
||||
<Button Content="🔄 刷新状态" Click="Refresh_Click" Margin="10,0,0,0" Background="#607D8B"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 阀门列表(带滚动条,整体卡片) -->
|
||||
<Border Grid.Row="1" Background="White" CornerRadius="6" BorderBrush="#E9ECF0" BorderThickness="1" Padding="5">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl x:Name="valveList">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Style="{StaticResource ValveItemBorder}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding CoilAddress}" Foreground="#7F8C8D" VerticalAlignment="Center" FontSize="11"/>
|
||||
<ToggleButton Grid.Column="2" IsChecked="{Binding IsOn, Mode=TwoWay}"
|
||||
Checked="ToggleButton_Checked"
|
||||
Unchecked="ToggleButton_Unchecked"
|
||||
Content="{Binding IsOn, Converter={StaticResource BoolToStringConverter}}"
|
||||
HorizontalAlignment="Right" Width="70"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
181
Views/ValveControlWindow.xaml.cs
Normal file
181
Views/ValveControlWindow.xaml.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using MembranePoreTester.Communication;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace MembranePoreTester.Views
|
||||
{
|
||||
public partial class ValveControlWindow : Window
|
||||
{
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly PlcConfiguration _config;
|
||||
private readonly DispatcherTimer _refreshTimer;
|
||||
|
||||
private readonly Dictionary<int, List<ValveInfo>> _valvesByStation = new()
|
||||
{
|
||||
[1] = new()
|
||||
{
|
||||
new ValveInfo { Name = "高压进气阀", CoilAddress = 100 },
|
||||
new ValveInfo { Name = "低压进气阀", CoilAddress = 101 },
|
||||
new ValveInfo { Name = "排气阀", CoilAddress = 102 },
|
||||
new ValveInfo { Name = "低压保护阀", CoilAddress = 103 },
|
||||
new ValveInfo { Name = "大流量阀", CoilAddress = 104 },
|
||||
new ValveInfo { Name = "小流量阀", CoilAddress = 105 },
|
||||
new ValveInfo { Name = "底部出气阀", CoilAddress = 106 },
|
||||
new ValveInfo { Name = "顶部出气阀", CoilAddress = 107 }
|
||||
},
|
||||
[2] = new()
|
||||
{
|
||||
new ValveInfo { Name = "高压进气阀", CoilAddress = 108 },
|
||||
new ValveInfo { Name = "低压进气阀", CoilAddress = 109 },
|
||||
new ValveInfo { Name = "排气阀", CoilAddress = 110 },
|
||||
new ValveInfo { Name = "低压保护阀", CoilAddress = 111 },
|
||||
new ValveInfo { Name = "大流量阀", CoilAddress = 112 },
|
||||
new ValveInfo { Name = "小流量阀", CoilAddress = 113 },
|
||||
new ValveInfo { Name = "底部出气阀", CoilAddress = 114 },
|
||||
new ValveInfo { Name = "顶部出气阀", CoilAddress = 115 }
|
||||
},
|
||||
[3] = new()
|
||||
{
|
||||
new ValveInfo { Name = "高压进气阀", CoilAddress = 116 },
|
||||
new ValveInfo { Name = "低压进气阀", CoilAddress = 117 },
|
||||
new ValveInfo { Name = "排气阀", CoilAddress = 118 },
|
||||
new ValveInfo { Name = "低压保护阀", CoilAddress = 119 },
|
||||
new ValveInfo { Name = "大流量阀", CoilAddress = 120 },
|
||||
new ValveInfo { Name = "小流量阀", CoilAddress = 121 },
|
||||
new ValveInfo { Name = "底部出气阀", CoilAddress = 122 },
|
||||
new ValveInfo { Name = "顶部出气阀", CoilAddress = 123 }
|
||||
}
|
||||
};
|
||||
|
||||
public ValveControlWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_plcService = App.PlcService;
|
||||
_config = App.PlcConfig;
|
||||
|
||||
_refreshTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
_refreshTimer.Tick += async (s, e) => await RefreshValveStates();
|
||||
_refreshTimer.Start();
|
||||
|
||||
Loaded += (s, e) => ShowStation(1);
|
||||
}
|
||||
|
||||
private void ShowStation(int station)
|
||||
{
|
||||
if (valveList == null) return; // 防御性检查
|
||||
if (_valvesByStation.TryGetValue(station, out var valves))
|
||||
{
|
||||
valveList.ItemsSource = valves;
|
||||
}
|
||||
else
|
||||
{
|
||||
valveList.ItemsSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void CmbStation_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (valveList == null) return; // 初始化未完成
|
||||
if (cmbStation.SelectedItem is ComboBoxItem item)
|
||||
{
|
||||
int station = int.Parse(item.Content.ToString().Replace("工位", ""));
|
||||
ShowStation(station);
|
||||
await RefreshValveStates();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshValveStates()
|
||||
{
|
||||
if (valveList.ItemsSource is not List<ValveInfo> valves) return;
|
||||
|
||||
foreach (var valve in valves)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool state = await _plcService.ReadCoilAsync((ushort)valve.CoilAddress);
|
||||
valve.IsOn = state;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"读取线圈{valve.CoilAddress}失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void ToggleButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleButton btn && btn.DataContext is ValveInfo valve)
|
||||
{
|
||||
await WriteCoil(valve.CoilAddress, true);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ToggleButton_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleButton btn && btn.DataContext is ValveInfo valve)
|
||||
{
|
||||
await WriteCoil(valve.CoilAddress, false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WriteCoil(int coilAddress, bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _plcService.WriteCoilAsync((ushort)coilAddress, value);
|
||||
await RefreshValveStates();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"控制阀门失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void Refresh_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await RefreshValveStates();
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_refreshTimer?.Stop();
|
||||
base.OnClosed(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ValveInfo : INotifyPropertyChanged
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int CoilAddress { get; set; }
|
||||
|
||||
private bool _isOn;
|
||||
public bool IsOn
|
||||
{
|
||||
get => _isOn;
|
||||
set
|
||||
{
|
||||
if (_isOn != value)
|
||||
{
|
||||
_isOn = value;
|
||||
OnPropertyChanged(nameof(IsOn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
"StopCoil": 7, // 停止线圈(M7),ON 停止测试
|
||||
|
||||
// 压力值系数(用于单位转换,例如 kPa 转 Pa 时设为 1000)
|
||||
"PressureFactor": 1.0,
|
||||
"PressureFactor": 1000.0,
|
||||
|
||||
// 流量寄存器地址(每个工位共用,但需配合流量模式切换)
|
||||
"WetFlowRegister": 12, // 湿膜流量寄存器起始地址(D2~D3)
|
||||
|
||||
Reference in New Issue
Block a user