This commit is contained in:
xyy
2026-03-27 21:35:32 +08:00
parent 6983e84c36
commit 2f1cacd097
10 changed files with 665 additions and 182 deletions

View File

@@ -126,9 +126,30 @@ namespace MembranePoreTester.ViewModels
_plcConfig = App.PlcConfig;
PressCommand = new RelayCommand(async () => await TogglePressAsync());
//BurstCommand = new RelayCommand(async () => await ReadBurstPressureAsync());
StartCommand = new RelayCommand(async () => await WriteCoilAsync(_plcConfig.StartCoil, true));
StopCommand = new RelayCommand(async () => await WriteCoilAsync(_plcConfig.StopCoil, true));
// 在 StationItem 构造函数中
StartCommand = new RelayCommand(async () =>
{
// 启动PLC
await WriteCoilAsync(_plcConfig.StartCoil, true);
// 启动孔分布自动采集
PoreDistributionVM.StartCollecting();
});
StopCommand = new RelayCommand(async () =>
{
// 停止自动采集
PoreDistributionVM.StopCollecting();
// 停止PLC
await WriteCoilAsync(_plcConfig.StopCoil, true);
});
// 启动定时器,每秒读取一次 M21 状态
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(1);
@@ -242,7 +263,12 @@ namespace MembranePoreTester.ViewModels
}
private bool _isPoreDistributionActive;
public bool IsPoreDistributionActive
{
get => _isPoreDistributionActive;
set => SetProperty(ref _isPoreDistributionActive, value);
}

View File

@@ -3,17 +3,171 @@ using MembranePoreTester.Helpers;
using MembranePoreTester.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace MembranePoreTester.ViewModels
{
public class PoreDistributionViewModel : ViewModelBase, IStationViewModel
{
#region
public class PressureModeItem
{
public string Text { get; set; }
public int Value { get; set; }
public override string ToString()
{
return Text; // 直接返回要显示的文本
}
}
private PressureModeItem _selectedPressureMode;
private List<PressureModeItem> _pressureModeList;
public List<PressureModeItem> PressureModeList => _pressureModeList ??= new List<PressureModeItem>
{
new PressureModeItem { Text = "小流量", Value = 1 },
new PressureModeItem { Text = "大流量", Value = 0 }
};
public PressureModeItem SelectedPressureMode
{
get => _selectedPressureMode;
set
{
if (SetProperty(ref _selectedPressureMode, value))
{
Task.Run(async () => await WriteFlowModeAsync(value?.Text ?? "小流量"));
}
}
}
#endregion
// 添加字段
private System.Windows.Threading.DispatcherTimer _autoCollectTimer;
private bool _isCollecting = false;
// 添加公共方法供工位调用
public void StartCollecting()
{
if (!IsActive)
{
// 如果当前不在孔分布界面,不启动采集
return;
}
if (_isCollecting) return;
_isCollecting = true;
_autoCollectTimer = new System.Windows.Threading.DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_autoCollectTimer.Tick += AutoCollectTimer_Tick;
_autoCollectTimer.Start();
}
public void StopCollecting()
{
_isCollecting = false;
_autoCollectTimer?.Stop();
_autoCollectTimer = null;
}
private async void AutoCollectTimer_Tick(object sender, EventArgs e)
{
if (!IsActive) return; // 不在当前标签页,跳过采集
try
{
// 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();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"自动采集失败: {ex.Message}");
}
}
/// <summary>
/// 清空所有数据点,重置表格和曲线
/// </summary>
public void ClearData()
{
// 清空数据点集合
Record.DataPoints.Clear();
// 重置曲线
UpdatePlot();
// 重置计算结果
AveragePoreSize = 0;
RangePercentage = 0;
// 可选:显示提示
System.Diagnostics.Debug.WriteLine("孔分布数据已清空");
}
private bool _isActive;
public bool IsActive
{
get => _isActive;
set => SetProperty(ref _isActive, value);
}
private PoreDistributionRecord _record = new();
private TestLiquid _selectedLiquid;
private bool _isCustomLiquid;
@@ -27,11 +181,11 @@ namespace MembranePoreTester.ViewModels
// 添加字段
private readonly IPlcService _plcService;
private readonly PlcConfiguration _plcConfig;
private DataPoint _selectedDataPoint;
private Models.DataPoint _selectedDataPoint;
// 添加属性
public DataPoint SelectedDataPoint
public Models.DataPoint SelectedDataPoint
{
get => _selectedDataPoint;
set => SetProperty(ref _selectedDataPoint, value);
@@ -127,8 +281,49 @@ namespace MembranePoreTester.ViewModels
Record.DataPoints.CollectionChanged += (s, e) => UpdatePlot();
// 延迟2秒后读取确保连接稳定
Task.Delay(2000).ContinueWith(async _ =>
{
await ReadPressureModeAsync();
}, TaskScheduler.Default);
}
private async Task ReadPressureModeAsync()
{
try
{
ushort[] values = await _plcService.ReadHoldingRegistersAsync(_plcConfig.FlowModeRegister, 1);
ushort val = values[0];
string newValue = val == 0 ? "大流量" : "小流量";
Application.Current.Dispatcher.Invoke(() =>
{
// 更新选中项
HighLowPressure = newValue;
});
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取流量模式失败: {ex.Message}");
}
}
public string HighLowPressure
{
get => _selectedPressureMode?.Text ?? "小流量";
set
{
var mode = PressureModeList.FirstOrDefault(m => m.Text == value);
if (mode != null)
{
SelectedPressureMode = mode;
}
}
}
private async Task ReadPlcAsync()
{
@@ -156,7 +351,7 @@ namespace MembranePoreTester.ViewModels
else
{
// 新增一行
var newPoint = new DataPoint { Pressure = pressure };
var newPoint = new Models.DataPoint { Pressure = pressure };
if (TestMode == "湿膜")
{
float rawWet = await _plcService.ReadWetFlowAsync();
@@ -178,7 +373,7 @@ namespace MembranePoreTester.ViewModels
private void AddDataPoint()
{
Record.DataPoints.Add(new DataPoint());
Record.DataPoints.Add(new Models.DataPoint());
}
private void RemoveDataPoint()
@@ -302,7 +497,7 @@ namespace MembranePoreTester.ViewModels
Record.DataPoints.Clear();
foreach (var dp in entity.DataPoints)
{
Record.DataPoints.Add(new DataPoint
Record.DataPoints.Add(new Models.DataPoint
{
Pressure = dp.Pressure,
WetFlow = dp.WetFlow,
@@ -347,19 +542,6 @@ namespace MembranePoreTester.ViewModels
set => SetProperty(ref _testMode, value);
}
private string _selectedFlowModeIndex; // 0=大流量1=小流量
public string SelectedFlowModeIndex
{
get => _selectedFlowModeIndex;
set
{
if (SetProperty(ref _selectedFlowModeIndex, value))
{
// 当选择变化时,写入 PLC 压力模式寄存器
Task.Run(async () => await WriteFlowModeAsync(value));
}
}
}
private OxyPlot.PlotModel _plotModel;
@@ -371,10 +553,10 @@ namespace MembranePoreTester.ViewModels
private async Task WriteFlowModeAsync(string mode)
{
float val = mode == "大流量" ? 0.0f : 1.0f;
ushort val = mode.ToString().Contains("大流量") ? (ushort)0 : (ushort)1;
try
{
await _plcService.WriteMultipleRegistersAsync(_plcConfig.PressureModeRegister, val);
await _plcService.WriteSingleRegisterAsync(_plcConfig.FlowModeRegister, val);
}
catch (Exception ex)
{
@@ -382,6 +564,8 @@ namespace MembranePoreTester.ViewModels
}
}
private void UpdatePlot()
{
var sorted = Record.DataPoints.OrderBy(p => p.Pressure).ToList();
@@ -391,12 +575,53 @@ namespace MembranePoreTester.ViewModels
return;
}
var model = new OxyPlot.PlotModel { Title = "流量-压力曲线" };
model.Axes.Add(new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Bottom, Title = "压力" });
model.Axes.Add(new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Left, Title = "流量 (L/min)" });
var model = new PlotModel
{
Title = "流量-压力曲线",
TitleFontSize = 14,
TitleFontWeight = 1
};
var wetSeries = new OxyPlot.Series.LineSeries { Title = "湿膜流量", Color = OxyPlot.OxyColors.Blue, MarkerType = OxyPlot.MarkerType.Circle };
var drySeries = new OxyPlot.Series.LineSeries { Title = "干膜流量", Color = OxyPlot.OxyColors.Red, MarkerType = OxyPlot.MarkerType.Circle };
// 添加坐标轴
model.Axes.Add(new LinearAxis
{
Position = AxisPosition.Bottom,
Title = "压力 (kPa)",
TitleFontSize = 12
});
model.Axes.Add(new LinearAxis
{
Position = AxisPosition.Left,
Title = "流量 (L/min)",
TitleFontSize = 12
});
// 湿膜流量 - 使用更清晰的标题
var wetSeries = new LineSeries
{
Title = "● 湿膜流量 (Wet Flow)",
Color = OxyColors.Blue,
MarkerType = MarkerType.Circle,
MarkerSize = 4,
MarkerStroke = OxyColors.Blue,
MarkerFill = OxyColors.Blue,
StrokeThickness = 2
};
// 干膜流量 - 使用更清晰的标题
var drySeries = new LineSeries
{
Title = "▲ 干膜流量 (Dry Flow)",
Color = OxyColors.Red,
MarkerType = MarkerType.Triangle,
MarkerSize = 5,
MarkerStroke = OxyColors.Red,
MarkerFill = OxyColors.Red,
StrokeThickness = 2
};
foreach (var dp in sorted)
{
@@ -411,5 +636,10 @@ namespace MembranePoreTester.ViewModels
}
}
}

View File

@@ -1,12 +1,25 @@
<UserControl x:Class="MembranePoreTester.Views.BubblePointView"
<!-- BubblePointView.xaml -->
<UserControl x:Class="MembranePoreTester.Views.BubblePointView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MembranePoreTester.ViewModels"
xmlns:conv="clr-namespace:MembranePoreTester.Converters">
<UserControl.Resources>
<conv:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
<Style TargetType="RadioButton">
<Setter Property="Margin" Value="5,2"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<Style TargetType="Label">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="#2C3E50"/>
</Style>
</UserControl.Resources>
<Grid Margin="10">
<Grid Margin="10" Background="#F5F7FA">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
@@ -15,7 +28,7 @@
<!-- 输入区域 -->
<StackPanel Grid.Row="0">
<GroupBox Header="样品信息" Margin="0,0,0,10">
<GroupBox Header="📄 样品信息">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@@ -29,8 +42,7 @@
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="膜类型:"/>
<ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding MembraneTypes}"
SelectedItem="{Binding Record.SampleType}" Margin="5"/>
<ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding MembraneTypes}" SelectedItem="{Binding Record.SampleType}" Margin="5"/>
<Label Grid.Row="0" Grid.Column="2" Content="规格:"/>
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Record.SampleSpec}" Margin="5"/>
@@ -43,51 +55,49 @@
</Grid>
</GroupBox>
<GroupBox Header="测试液体" Margin="0,0,0,10">
<GroupBox Header="🧪 测试液体">
<StackPanel>
<RadioButton Content="预定义" IsChecked="{Binding IsCustomLiquid, Converter={StaticResource InverseBooleanConverter}}" Margin="5"/>
<ComboBox ItemsSource="{Binding Liquids}" SelectedItem="{Binding SelectedLiquid}"
DisplayMemberPath="Name" IsEnabled="{Binding IsCustomLiquid, Converter={StaticResource InverseBooleanConverter}}"
Margin="5,0,5,5"/>
<ComboBox ItemsSource="{Binding Liquids}" SelectedItem="{Binding SelectedLiquid}" DisplayMemberPath="Name" IsEnabled="{Binding IsCustomLiquid, Converter={StaticResource InverseBooleanConverter}}" Margin="5,0,5,5"/>
<RadioButton Content="自定义" IsChecked="{Binding IsCustomLiquid}" Margin="5"/>
<StackPanel Orientation="Horizontal" Margin="20,0,0,0" IsEnabled="{Binding IsCustomLiquid}">
<Label Content="表面张力(mN/m):"/>
<TextBox Text="{Binding CustomSurfaceTension}" Width="100" Margin="5"/>
</StackPanel>
<!--<Label Content="生产厂家:" Margin="5"/>
<TextBox Text="{Binding Record.LiquidManufacturer}" Margin="5"/>-->
</StackPanel>
</GroupBox>
<GroupBox Header="当前压力" Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Record.BubbleCurrentPressure}" Width="150" Margin="5"/>
</StackPanel>
</GroupBox>
<GroupBox Header="泡点压力" Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Record.BubblePointPressure}" Width="150" Margin="5"/>
<Button Content="涨破" Command="{Binding ReadPlcCommand}" Padding="10,5" Margin="5"/>
<ComboBox ItemsSource="{Binding PressureUnits}" SelectedItem="{Binding Record.PressureUnit}" Width="80" Margin="5"/>
<Button Content="计算最大孔径" Command="{Binding CalculateCommand}" Padding="10,5" Margin="5"/>
<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"/>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Record.BubblePointPressure}" Width="150" Margin="5"/>
<Button Content="涨破" Command="{Binding ReadPlcCommand}" Padding="10,5" Margin="5" Background="#FF9800"/>
<ComboBox ItemsSource="{Binding PressureUnits}" SelectedItem="{Binding Record.PressureUnit}" Width="80" Margin="5"/>
<Button Content="计算最大孔径" Command="{Binding CalculateCommand}" Padding="10,5" Margin="5" Background="#2196F3"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<!-- 结果显示 -->
<GroupBox Grid.Row="1" Header="测试结果" VerticalAlignment="Center">
<TextBlock FontSize="36" TextAlignment="Center"
Text="{Binding MaxPoreSize, StringFormat={}{0:F3} μm}"/>
</GroupBox>
<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>
</Grid>
</Border>
<!-- 底部按钮 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="压力校准" Command="{Binding OpenPressureCalibCommand}" Padding="15,8" Margin="5"/>
<Button Content="保存到历史" Command="{Binding SaveCommand}" Padding="15,8" Margin="5"/>
<Button Content="生成报告" Command="{Binding GenerateReportCommand}" Padding="15,8" Margin="5"/>
<Button Content="导出Excel" Command="{Binding ExportCommand}" Padding="15,8" Margin="5"/>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="压力校准" Command="{Binding OpenPressureCalibCommand}" Padding="15,8" Background="#607D8B"/>
<Button Content="💾 保存到历史" Command="{Binding SaveCommand}" Padding="15,8" Background="#4CAF50"/>
<Button Content="📄 生成报告" Command="{Binding GenerateReportCommand}" Padding="15,8" Background="#2196F3"/>
<Button Content="📊 导出Excel" Command="{Binding ExportCommand}" Padding="15,8" Background="#FF9800"/>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -60,7 +60,7 @@
</StackPanel>
<!-- 测试类型选项卡 -->
<TabControl>
<TabControl x:Name="stationTabControl" ItemsSource="{Binding Stations}" SelectionChanged="TabControl_SelectionChanged">
<TabItem Header="泡点法测试最大孔径">
<local:BubblePointView DataContext="{Binding BubblePointVM}"/>
</TabItem>

View File

@@ -1,6 +1,8 @@
using MembranePoreTester.ViewModels;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace MembranePoreTester.Views
{
@@ -53,5 +55,53 @@ namespace MembranePoreTester.Views
win.ShowDialog();
}
}
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedStation = stationTabControl.SelectedItem as MainViewModel.StationItem;
if (selectedStation != null)
{
var content = stationTabControl.SelectedContent as ContentPresenter;
if (content != null)
{
var innerTabControl = FindVisualChild<TabControl>(content);
if (innerTabControl != null)
{
var selectedTabItem = innerTabControl.SelectedItem as TabItem;
bool isPoreDistributionActive = selectedTabItem?.Header?.ToString() == "孔分布测试";
// 关键:设置 StationItem 的状态
selectedStation.IsPoreDistributionActive = isPoreDistributionActive;
selectedStation.PoreDistributionVM.IsActive = isPoreDistributionActive;
if (isPoreDistributionActive)
{
// 切换到孔分布时,清空之前的数据
selectedStation.PoreDistributionVM.ClearData();
}
else
{
// 离开孔分布界面时,停止自动采集
selectedStation.PoreDistributionVM.StopCollecting();
}
}
}
}
}
// 辅助方法:查找 Visual Tree 中的指定类型元素
private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T t) return t;
var result = FindVisualChild<T>(child);
if (result != null) return result;
}
return null;
}
}
}

View File

@@ -20,8 +20,8 @@
<TextBox x:Name="txtPressureUpper" Grid.Row="0" Grid.Column="1"/>
<Label Grid.Row="1" Grid.Column="0">加压速率(D280):</Label>
<TextBox x:Name="txtPressureRate" Grid.Row="1" Grid.Column="1"/>
<Label Grid.Row="2" Grid.Column="0">加压系数(D282):</Label>
<TextBox x:Name="txtPressureCoeff" Grid.Row="2" Grid.Column="1"/>
<!--<Label Grid.Row="2" Grid.Column="0">加压系数(D282):</Label>
<TextBox x:Name="txtPressureCoeff" Grid.Row="2" Grid.Column="1"/>-->
</Grid>
</GroupBox>

View File

@@ -30,7 +30,7 @@ namespace MembranePoreTester.Views
{
[txtPressureUpper] = (_config.PressureUpperLimit, "加压上限"),
[txtPressureRate] = (_config.PressureRate, "加压速率"),
[txtPressureCoeff] = (_config.PressureCoeff, "加压系数"),
//[txtPressureCoeff] = (_config.PressureCoeff, "加压系数"),
[txtHPCoeff1] = (_config.HPCoeff1, "工位1高压系数"),
[txtLPCoeff1] = (_config.LPCoeff1, "工位1低压系数"),

View File

@@ -2,139 +2,291 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.org/wpf"
xmlns:conv="clr-namespace:MembranePoreTester.Converters">
xmlns:conv="clr-namespace:MembranePoreTester.Converters"
Background="#F5F7FA" >
<UserControl.Resources>
<conv:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="样品信息" Margin="0,0,0,10">
<Grid>
<!-- 统一按钮样式 -->
<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>
<!-- 统一GroupBox样式自定义圆角白色背景 -->
<Style TargetType="GroupBox">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#E9ECF0"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Margin" Value="0,0,0,10"/>
<Setter Property="Padding" Value="10"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{TemplateBinding Header}" FontWeight="SemiBold" FontSize="14" Margin="0,0,0,10"/>
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</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"/>
</Style>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 样品信息区域 -->
<GroupBox Grid.Row="0" Header="📄 样品信息">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<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" Content="膜类型:"/>
<ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding MembraneTypes}"
SelectedItem="{Binding Record.SampleType}"/>
<!--<Label Grid.Row="0" Grid.Column="2" Content="规格:"/>
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Record.SampleSpec}"/>-->
<Label Grid.Row="0" Grid.Column="2" Content="室温(°C):"/>
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Record.RoomTemperature}"/>
<Label Grid.Row="1" Grid.Column="2" Content="浸润时间(h):"/>
<TextBox Grid.Row="1" Grid.Column="3" Text="{Binding Record.SoakingTime}"/>
<Label Grid.Row="1" Grid.Column="0" Content="测试液体:"/>
<ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Liquids}"
SelectedItem="{Binding SelectedLiquid}" DisplayMemberPath="Name"/>
<!--<Label Grid.Row="2" Grid.Column="2" Content="生产厂家:"/>
<TextBox Grid.Row="2" Grid.Column="3" Text="{Binding Record.LiquidManufacturer}"/>-->
<Label Grid.Row="3" Grid.Column="0" Content="压力单位:"/>
<ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding PressureUnits}"
SelectedItem="{Binding Record.PressureUnit}"/>
</Grid>
</GroupBox>
<!-- 数据表格与图表区域 -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<!-- 新增第4行 -->
</Grid.RowDefinitions>
<!-- 第0行 -->
<Label Grid.Row="0" Grid.Column="0" Content="膜类型:"/>
<ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding MembraneTypes}"
SelectedItem="{Binding Record.SampleType}" Margin="5"/>
<Label Grid.Row="0" Grid.Column="2" Content="规格:"/>
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Record.SampleSpec}" Margin="5"/>
<!-- 左侧数据表格 --><!--
<GroupBox Grid.Column="0" Header="📊 压力-流量数据" Margin="0,0,5,0">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,10">
<Button Content=" 添加行" Command="{Binding AddDataPointCommand}" Background="#4CAF50"/>
<Button Content=" 删除行" Command="{Binding RemoveDataPointCommand}" Background="#F44336"/>
<!-- 第1行 -->
<Label Grid.Row="1" Grid.Column="0" Content="室温(°C):"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Record.RoomTemperature}" Margin="5"/>
<Label Grid.Row="1" Grid.Column="2" Content="浸润时间(h):"/>
<TextBox Grid.Row="1" Grid.Column="3" Text="{Binding Record.SoakingTime}" Margin="5"/>
<!-- 第2行 -->
<Label Grid.Row="2" Grid.Column="0" Content="测试液体:"/>
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Liquids}"
SelectedItem="{Binding SelectedLiquid}" DisplayMemberPath="Name" Margin="5"/>
<Label Grid.Row="2" Grid.Column="2" Content="生产厂家:"/>
<TextBox Grid.Row="2" Grid.Column="3" Text="{Binding Record.LiquidManufacturer}" Margin="5"/>
<!-- 第3行压力单位 -->
<Label Grid.Row="3" Grid.Column="0" Content="压力单位:"/>
<ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding PressureUnits}"
SelectedItem="{Binding Record.PressureUnit}" Margin="5"/>
</Grid>
</GroupBox>
<ComboBox SelectedItem="{Binding TestMode}" Width="80">
<ComboBoxItem Content="湿膜" IsSelected="True"/>
<ComboBoxItem Content="干膜"/>
</ComboBox>
<ComboBox ItemsSource="{Binding PressureModeList}"
SelectedItem="{Binding SelectedPressureMode}" Width="100"/>
<!-- 数据表格和简单图表此处用ListView代替图表 -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 左侧数据输入表格 -->
<GroupBox Grid.Column="0" Header="压力-流量数据" Margin="0,0,5,0">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,5">
<Button Content="添加行" Command="{Binding AddDataPointCommand}" Padding="10,5" Margin="5"/>
<Button Content="删除行" Command="{Binding RemoveDataPointCommand}" Padding="10,5" Margin="5"/>
--><!--<Button Content="📡 读取PLC" Command="{Binding ReadPlcCommand}" Background="#FF9800"/>--><!--
</StackPanel>
<!-- 新增:模式选择 -->
<ComboBox SelectedItem="{Binding TestMode}" Width="80" Margin="5">
<ComboBoxItem Content="湿膜" IsSelected="True"/>
<ComboBoxItem Content="干膜"/>
</ComboBox>
<ComboBox SelectedIndex="{Binding SelectedFlowModeIndex}" Width="100" Margin="5">
<ComboBoxItem IsSelected="True" Content="大流量"/>
<ComboBoxItem Content="小流量"/>
</ComboBox>
<Button Content="读取PLC" Command="{Binding ReadPlcCommand}" Padding="10,5" Margin="5"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Record.DataPoints}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="压力" Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
<DataGridTextColumn Header="湿膜流量(L/min)" Binding="{Binding WetFlow, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
<DataGridTextColumn Header="干膜流量(L/min)" Binding="{Binding DryFlow, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</GroupBox>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="400">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
AutoGenerateColumns="False"
CanUserAddRows="False"
ColumnWidth="*">
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
MinWidth="120"/>
<DataGridTextColumn Header="湿膜流量(L/min)"
Binding="{Binding WetFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="120"/>
<DataGridTextColumn Header="干膜流量(L/min)"
Binding="{Binding DryFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="120"/>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</DockPanel>
</GroupBox>-->
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center"/>
<!-- 右侧简易数据展示可用图表库此处仅用ListBox预览 -->
<GroupBox Grid.Column="2" Header="数据预览" Margin="5,0,0,0">
<!--<ListBox ItemsSource="{Binding Record.DataPoints}">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- 左侧数据表格区域,改为两列 Grid -->
<Grid Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 湿膜表格 -->
<GroupBox Grid.Column="0" Header="💧 湿膜数据" Margin="0,0,5,0">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
AutoGenerateColumns="False"
CanUserAddRows="False"
ColumnWidth="*">
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
<DataGridTextColumn Header="湿膜流量(L/min)"
Binding="{Binding WetFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
<!-- 干膜表格 -->
<GroupBox Grid.Column="1" Header="🔥 干膜数据" Margin="5,0,0,0">
<DataGrid ItemsSource="{Binding Record.DataPoints}"
AutoGenerateColumns="False"
CanUserAddRows="False"
ColumnWidth="*">
<DataGrid.Columns>
<DataGridTextColumn Header="压力"
Binding="{Binding Pressure, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
<DataGridTextColumn Header="干膜流量(L/min)"
Binding="{Binding DryFlow, UpdateSourceTrigger=PropertyChanged}"
MinWidth="100"/>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</Grid>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" Background="#E9ECF0"/>
<!-- 右侧曲线图 -->
<GroupBox Grid.Column="2" Header="📈 流量-压力曲线" Margin="5,0,0,0">
<StackPanel>
<Border Background="#F5F5F5" CornerRadius="4" Padding="8,4" Margin="0,0,0,5" HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Pressure, StringFormat=P:{0:F2}}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding WetFlow, StringFormat=W:{0:F3}}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding DryFlow, StringFormat=D:{0:F3}}"/>
<Rectangle Width="20" Height="2" Fill="Blue" Margin="0,0,5,0"/>
<TextBlock Text="湿膜流量 (Wet Flow)" FontSize="11" Margin="0,0,15,0"/>
<Rectangle Width="20" Height="2" Fill="Red" Margin="0,0,5,0"/>
<TextBlock Text="干膜流量 (Dry Flow)" FontSize="11"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>-->
<GroupBox Grid.Column="2" Header="流量-压力曲线" Margin="5,0,0,0">
<oxy:PlotView Model="{Binding PlotModel}" Height="300" Margin="5"/>
</Border>
<oxy:PlotView Model="{Binding PlotModel}" Height="300" Margin="5"/>
</StackPanel>
</GroupBox>
</GroupBox>
</Grid>
</Grid>
<!-- 计算结果区域 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="0,10,0,0">
<GroupBox Header="平均孔径" Margin="5">
<TextBlock Text="{Binding AveragePoreSize, StringFormat={}{0:F3} μm}" FontSize="16"/>
</GroupBox>
<GroupBox Header="孔分布区间计算" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding LowerPore}" Width="60" Margin="5"/>
<TextBlock Text="~" VerticalAlignment="Center"/>
<TextBox Text="{Binding UpperPore}" Width="60" Margin="5"/>
<TextBlock Text="μm" VerticalAlignment="Center"/>
<Button Content="计算" Command="{Binding CalculateCommand}" Margin="10,0" Padding="10,5"/>
<TextBlock Text="{Binding RangePercentage, StringFormat={}{0:F1}%}" FontSize="16" VerticalAlignment="Center" Margin="10,0"/>
<!-- 计算结果及操作按钮区域 -->
<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>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="10,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"/>
</StackPanel>
</GroupBox>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="10,0"/>
<StackPanel Orientation="Horizontal">
<Button Content="🔧 流量校准" Command="{Binding OpenFlowCalibCommand}" 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>
</GroupBox>
<Button Content="流量校准" Command="{Binding OpenFlowCalibCommand}" Padding="15,8" Margin="5"/>
<Button Content="生成报告" Command="{Binding GenerateReportCommand}" Padding="15,8" Margin="5"/>
<Button Content="保存到历史" Command="{Binding SaveCommand}" Padding="15,8" Margin="5"/>
<Button Content="导出Excel" Command="{Binding ExportCommand}" Padding="15,8" Margin="5"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</ScrollViewer>
</UserControl>

View File

@@ -1,4 +1,5 @@
using System.Windows.Controls;
using MembranePoreTester.ViewModels;
using System.Windows.Controls;
namespace MembranePoreTester.Views
{
@@ -8,6 +9,20 @@ namespace MembranePoreTester.Views
{
InitializeComponent();
//DataContext = new ViewModels.PoreDistributionViewModel();
// 监听 IsVisible 变化
this.IsVisibleChanged += (s, e) =>
{
if (this.DataContext is ViewModels.PoreDistributionViewModel vm)
{
vm.IsActive = this.IsVisible;
if (!this.IsVisible)
{
// 离开孔分布界面时,停止自动采集
vm.StopCollecting();
}
}
};
}
}
}

View File

@@ -23,7 +23,7 @@
"PressureFactor": 1.0,
// 流量寄存器地址(每个工位共用,但需配合流量模式切换)
"WetFlowRegister": 2, // 湿膜流量寄存器起始地址D2D3
"WetFlowRegister": 12, // 湿膜流量寄存器起始地址D2D3
"DryFlowRegister": 4, // 干膜流量寄存器起始地址D4D5
"WetFlowFactor": 1.0, // 湿膜流量系数
"DryFlowFactor": 1.0, // 干膜流量系数