This commit is contained in:
xyy
2026-03-24 21:05:32 +08:00
parent 9437c6010a
commit 01a07f8811
26 changed files with 7 additions and 2387 deletions

View File

@@ -1,89 +0,0 @@
<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"/>
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 输入区域 -->
<StackPanel Grid.Row="0">
<GroupBox Header="样品信息" Margin="0,0,0,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<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}" Margin="5"/>
<Label Grid.Row="0" Grid.Column="2" Content="规格:"/>
<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding Record.SampleSpec}" Margin="5"/>
<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"/>
</Grid>
</GroupBox>
<GroupBox Header="测试液体" Margin="0,0,0,10">
<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"/>
<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.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"/>
</StackPanel>
</GroupBox>
</StackPanel>
<!-- 结果显示 -->
<GroupBox Grid.Row="1" Header="测试结果" VerticalAlignment="Center">
<TextBlock FontSize="36" TextAlignment="Center"
Text="{Binding MaxPoreSize, StringFormat={}{0:F3} μm}"/>
</GroupBox>
<!-- 底部按钮 -->
<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>
</UserControl>

View File

@@ -1,13 +0,0 @@
using System.Windows.Controls;
namespace MembranePoreTester.Views
{
public partial class BubblePointView : UserControl
{
public BubblePointView()
{
InitializeComponent();
//DataContext = new ViewModels.BubblePointViewModel();
}
}
}

View File

@@ -1,57 +0,0 @@
<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">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<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>
<!-- 历史记录列表 -->
<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>
<!-- 底部按钮 -->
<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"/>
</StackPanel>
</Grid>
</Window>

View File

@@ -1,156 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace MembranePoreTester.Views
{
public partial class HistoryWindow : Window
{
public int? SelectedStation { get; set; } // 传入当前工位ID
public HistoryWindow()
{
InitializeComponent();
}
private void Query_Click(object sender, RoutedEventArgs e)
{
using var db = new AppDbContext();
var query = new List<object>();
// 获取泡点法记录
var bubblePoints = db.BubblePointRecords
.Select(b => new
{
b.Id,
b.StationId,
Type = "泡点法",
b.TestDate,
b.SampleType,
b.SampleSpec,
b.Tester,
Result = $"最大孔径: {b.MaxPoreSize:F3} μm"
});
query.AddRange(bubblePoints);
// 获取孔分布记录
var poreDistributions = db.PoreDistributionRecords
.Select(p => new
{
p.Id,
p.StationId,
Type = "孔分布",
p.TestDate,
p.SampleType,
p.SampleSpec,
p.Tester,
Result = $"平均孔径: {p.AveragePoreSize:F3} μm"
});
query.AddRange(poreDistributions);
// 过滤工位
if (cmbStation.SelectedIndex > 0)
{
int station = cmbStation.SelectedIndex;
query = query.Where(x => ((dynamic)x).StationId == station).ToList();
}
// 过滤类型
if (cmbType.SelectedIndex == 1)
query = query.Where(x => ((dynamic)x).Type == "泡点法").ToList();
else if (cmbType.SelectedIndex == 2)
query = query.Where(x => ((dynamic)x).Type == "孔分布").ToList();
// 过滤日期范围
if (dpStart.SelectedDate != null)
query = query.Where(x => ((dynamic)x).TestDate >= dpStart.SelectedDate).ToList();
if (dpEnd.SelectedDate != null)
query = query.Where(x => ((dynamic)x).TestDate <= dpEnd.SelectedDate.Value.AddDays(1)).ToList();
dgHistory.ItemsSource = query.OrderByDescending(x => ((dynamic)x).TestDate).ToList();
}
private void ExportSelected_Click(object sender, RoutedEventArgs e)
{
if (dgHistory.SelectedItem == null)
{
MessageBox.Show("请先选中一条记录");
return;
}
dynamic selected = dgHistory.SelectedItem;
int id = selected.Id;
string type = selected.Type;
var saveFileDialog = new SaveFileDialog
{
Filter = "Excel文件|*.xlsx",
FileName = $"{type}_{id}.xlsx"
};
if (saveFileDialog.ShowDialog() == true)
{
if (type == "泡点法")
{
using var db = new AppDbContext();
var entity = db.BubblePointRecords.Find(id);
if (entity != null)
{
ExportHelper.ExportBubblePoint(entity, saveFileDialog.FileName);
}
}
else
{
using var db = new AppDbContext();
var entity = db.PoreDistributionRecords
.Include(p => p.DataPoints)
.FirstOrDefault(p => p.Id == id);
if (entity != null)
{
ExportHelper.ExportPoreDistribution(entity, saveFileDialog.FileName);
}
}
}
}
private void LoadToCurrentStation_Click(object sender, RoutedEventArgs e)
{
if (dgHistory.SelectedItem == null) return;
if (SelectedStation == null)
{
MessageBox.Show("未指定当前工位");
return;
}
dynamic selected = dgHistory.SelectedItem;
int id = selected.Id;
string type = selected.Type;
// 通过事件或回调将记录加载到当前工位
// 这里需要与主窗口交互,简单起见,我们使用静态事件
LoadRecordEvent?.Invoke(this, new LoadRecordEventArgs
{
RecordId = id,
Type = type,
TargetStation = SelectedStation.Value
});
DialogResult = true;
Close();
}
private void Close_Click(object sender, RoutedEventArgs e) => Close();
public static event EventHandler<LoadRecordEventArgs> LoadRecordEvent;
}
public class LoadRecordEventArgs : EventArgs
{
public int RecordId { get; set; }
public string Type { get; set; }
public int TargetStation { get; set; }
}
}

View File

@@ -1,66 +0,0 @@
<Window x:Class="MembranePoreTester.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MembranePoreTester.Views"
xmlns:viewModels="clr-namespace:MembranePoreTester.ViewModels"
Title="膜孔径测试系统 (GB/T 32361-2015)"
Width="1024" Height="768"
WindowStartupLocation="CenterScreen" KeyDown="Window_KeyDown">
<Window.DataContext>
<viewModels:MainViewModel />
</Window.DataContext>
<DockPanel>
<!-- 全局历史记录按钮 -->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5">
<Button Content="历史记录" Click="OpenHistory_Click" Padding="10,5" Margin="5"/>
</StackPanel>
<!-- 工位选项卡 -->
<TabControl x:Name="stationTabControl" ItemsSource="{Binding Stations}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DockPanel>
<!-- 工位控制栏 -->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5">
<ComboBox SelectedItem="{Binding HighLowPressure}" Width="80" Margin="5">
<ComboBoxItem IsSelected="True">低压</ComboBoxItem>
<ComboBoxItem>高压</ComboBoxItem>
</ComboBox>
<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"/>
<!--<Button Content="涨破" Command="{Binding BurstCommand}" Width="80" Margin="5"/>-->
<!--<CheckBox Content="使能" IsChecked="{Binding EnableChecked}" Margin="5"/>-->
<!-- 原来的复选框 -->
<!-- <CheckBox Content="使能" IsChecked="{Binding EnableChecked}" Margin="5"/> -->
<!-- 替换为状态指示灯 -->
<StackPanel Orientation="Horizontal" Margin="5">
<Ellipse Width="12" Height="12" Fill="{Binding EnableStatusColor}" Margin="2"/>
<TextBlock Text="{Binding EnableStatusText}" VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
<!-- 测试类型选项卡 -->
<TabControl>
<TabItem Header="泡点法测试最大孔径">
<local:BubblePointView DataContext="{Binding BubblePointVM}"/>
</TabItem>
<TabItem Header="孔分布测试">
<local:PoreDistributionView DataContext="{Binding PoreDistributionVM}"/>
</TabItem>
</TabControl>
</DockPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</DockPanel>
</Window>

View File

@@ -1,57 +0,0 @@
using MembranePoreTester.ViewModels;
using System.Windows;
using System.Windows.Input;
namespace MembranePoreTester.Views
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
HistoryWindow.LoadRecordEvent += OnLoadRecord;
}
private void OnLoadRecord(object sender, LoadRecordEventArgs e)
{
var mainVM = DataContext as MainViewModel;
if (mainVM == null) return;
// 根据工位和类型找到对应的 ViewModel
if (e.Type == "泡点法")
{
var targetVM = mainVM.Stations[e.TargetStation - 1].BubblePointVM;
targetVM.LoadFromDatabase(e.RecordId);
}
else
{
var targetVM = mainVM.Stations[e.TargetStation - 1].PoreDistributionVM;
targetVM.LoadFromDatabase(e.RecordId);
}
}
private void OpenHistory_Click(object sender, RoutedEventArgs e)
{
var mainVM = DataContext as MainViewModel;
if (mainVM == null) return;
// 获取当前选中的工位索引(假设选项卡控件是 TabControl名称为 stationTabControl
// 需要在 XAML 中为 TabControl 设置 x:Name="stationTabControl"
int currentStation = stationTabControl.SelectedIndex + 1; // 假设索引从0开始
var historyWin = new HistoryWindow { SelectedStation = currentStation };
historyWin.ShowDialog();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.P)
{
var win = new ParameterWindow();
win.Owner = this;
win.ShowDialog();
}
}
}
}

View File

@@ -1,120 +0,0 @@
<Window x:Class="MembranePoreTester.Views.ParameterWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="运维参数设置" Height="600" Width="800"
WindowStartupLocation="CenterOwner">
<ScrollViewer>
<StackPanel Margin="10">
<GroupBox Header="加压控制">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">加压上限(D300):</Label>
<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"/>
</Grid>
</GroupBox>
<GroupBox Header="高压/低压系数">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<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" FontWeight="Bold">工位</Label>
<Label Grid.Row="0" Grid.Column="1" FontWeight="Bold">高压系数</Label>
<Label Grid.Row="0" Grid.Column="2" FontWeight="Bold">低压系数</Label>
<Label Grid.Row="1" Grid.Column="0">工位1</Label>
<TextBox x:Name="txtHPCoeff1" Grid.Row="1" Grid.Column="1" Width="80" HorizontalAlignment="Left"/>
<TextBox x:Name="txtLPCoeff1" Grid.Row="1" Grid.Column="2" Width="80" HorizontalAlignment="Left"/>
<Label Grid.Row="2" Grid.Column="0">工位2</Label>
<TextBox x:Name="txtHPCoeff2" Grid.Row="2" Grid.Column="1" Width="80" HorizontalAlignment="Left"/>
<TextBox x:Name="txtLPCoeff2" Grid.Row="2" Grid.Column="2" Width="80" HorizontalAlignment="Left"/>
<Label Grid.Row="3" Grid.Column="0">工位3</Label>
<TextBox x:Name="txtHPCoeff3" Grid.Row="3" Grid.Column="1" Width="80" HorizontalAlignment="Left"/>
<TextBox x:Name="txtLPCoeff3" Grid.Row="3" Grid.Column="2" Width="80" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Header="流量系数">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</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" FontWeight="Bold">工位</Label>
<Label Grid.Row="0" Grid.Column="1" FontWeight="Bold">大流量系数</Label>
<Label Grid.Row="0" Grid.Column="2" FontWeight="Bold">小流量系数</Label>
<Label Grid.Row="1" Grid.Column="0">工位1</Label>
<TextBox x:Name="txtLargeFlow1" Grid.Row="1" Grid.Column="1"/>
<TextBox x:Name="txtSmallFlow1" Grid.Row="1" Grid.Column="2"/>
<Label Grid.Row="2" Grid.Column="0">工位2</Label>
<TextBox x:Name="txtLargeFlow2" Grid.Row="2" Grid.Column="1"/>
<TextBox x:Name="txtSmallFlow2" Grid.Row="2" Grid.Column="2"/>
<Label Grid.Row="3" Grid.Column="0">工位3</Label>
<TextBox x:Name="txtLargeFlow3" Grid.Row="3" Grid.Column="1"/>
<TextBox x:Name="txtSmallFlow3" Grid.Row="3" Grid.Column="2"/>
</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"/>-->
<Button Content="写入参数" Click="WriteParameters_Click" Width="100" Margin="5"/>
<Button Content="关闭" Click="Close_Click" Width="100" Margin="5"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>

View File

@@ -1,186 +0,0 @@
using MembranePoreTester.Communication;
using MembranePoreTester.ViewModels;
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace MembranePoreTester.Views
{
public partial class ParameterWindow : Window
{
private readonly IPlcService _plcService;
private readonly PlcConfiguration _config;
private DispatcherTimer _autoRefreshTimer;
private bool _isEditing = false; // 用户是否正在编辑任何文本框
public ParameterWindow()
{
InitializeComponent();
_plcService = App.PlcService;
_config = App.PlcConfig;
// 为所有文本框注册焦点事件在XAML中设置事件或在此处统一查找
RegisterTextBoxEvents();
Loaded += async (s, e) =>
{
// 启动自动刷新定时器每秒1次
_autoRefreshTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_autoRefreshTimer.Tick += AutoRefreshTimer_Tick;
_autoRefreshTimer.Start();
// 首次加载时读取一次
await ReadParametersAsync();
};
Closed += (s, e) => _autoRefreshTimer?.Stop();
}
/// <summary>
/// 为所有文本框注册焦点事件,用于检测编辑状态
/// </summary>
private void RegisterTextBoxEvents()
{
// 查找当前窗口中所有文本框(可根据实际布局调整)
var textBoxes = FindVisualChildren<System.Windows.Controls.TextBox>(this);
foreach (var tb in textBoxes)
{
tb.GotFocus += (s, e) => _isEditing = true;
tb.LostFocus += (s, e) => _isEditing = false;
}
}
/// <summary>
/// 定时器事件:如果用户未在编辑,则刷新参数
/// </summary>
private async void AutoRefreshTimer_Tick(object sender, EventArgs e)
{
if (!_isEditing)
{
await ReadParametersAsync();
}
}
private async Task ReadParametersAsync()
{
try
{
// 加压控制
txtPressureUpper.Text = (await ReadFloatAsync(_config.PressureUpperLimit)).ToString("F3");
txtPressureRate.Text = (await ReadFloatAsync(_config.PressureRate)).ToString("F3");
txtPressureCoeff.Text = (await ReadFloatAsync(_config.PressureCoeff)).ToString("F3");
// 高压/低压系数
txtHPCoeff1.Text = (await ReadFloatAsync(_config.HPCoeff1)).ToString("F3");
txtLPCoeff1.Text = (await ReadFloatAsync(_config.LPCoeff1)).ToString("F3");
txtHPCoeff2.Text = (await ReadFloatAsync(_config.HPCoeff2)).ToString("F3");
txtLPCoeff2.Text = (await ReadFloatAsync(_config.LPCoeff2)).ToString("F3");
txtHPCoeff3.Text = (await ReadFloatAsync(_config.HPCoeff3)).ToString("F3");
txtLPCoeff3.Text = (await ReadFloatAsync(_config.LPCoeff3)).ToString("F3");
// 流量系数
txtLargeFlow1.Text = (await ReadFloatAsync(_config.LargeFlowCoeff1)).ToString("F3");
txtSmallFlow1.Text = (await ReadFloatAsync(_config.SmallFlowCoeff1)).ToString("F3");
txtLargeFlow2.Text = (await ReadFloatAsync(_config.LargeFlowCoeff2)).ToString("F3");
txtSmallFlow2.Text = (await ReadFloatAsync(_config.SmallFlowCoeff2)).ToString("F3");
txtLargeFlow3.Text = (await ReadFloatAsync(_config.LargeFlowCoeff3)).ToString("F3");
txtSmallFlow3.Text = (await ReadFloatAsync(_config.SmallFlowCoeff3)).ToString("F3");
// 校准参数
txtPressureZero.Text = (await ReadFloatAsync(_config.PressureCalibZero)).ToString("F3");
txtPressureSpan.Text = (await ReadFloatAsync(_config.PressureCalibSpan)).ToString("F3");
txtFlowZero.Text = (await ReadFloatAsync(_config.FlowCalibZero)).ToString("F3");
txtFlowSpan.Text = (await ReadFloatAsync(_config.FlowCalibSpan)).ToString("F3");
}
catch (Exception ex)
{
// 静默处理,避免频繁弹窗干扰(可记录日志)
System.Diagnostics.Debug.WriteLine($"自动读取参数失败: {ex.Message}");
}
}
private async void WriteParameters_Click(object sender, RoutedEventArgs e)
{
try
{
// 加压控制
await WriteFloatAsync(_config.PressureUpperLimit, ParseFloat(txtPressureUpper.Text));
await WriteFloatAsync(_config.PressureRate, ParseFloat(txtPressureRate.Text));
await WriteFloatAsync(_config.PressureCoeff, ParseFloat(txtPressureCoeff.Text));
// 高压/低压系数
await WriteFloatAsync(_config.HPCoeff1, ParseFloat(txtHPCoeff1.Text));
await WriteFloatAsync(_config.LPCoeff1, ParseFloat(txtLPCoeff1.Text));
await WriteFloatAsync(_config.HPCoeff2, ParseFloat(txtHPCoeff2.Text));
await WriteFloatAsync(_config.LPCoeff2, ParseFloat(txtLPCoeff2.Text));
await WriteFloatAsync(_config.HPCoeff3, ParseFloat(txtHPCoeff3.Text));
await WriteFloatAsync(_config.LPCoeff3, ParseFloat(txtLPCoeff3.Text));
// 流量系数
await WriteFloatAsync(_config.LargeFlowCoeff1, ParseFloat(txtLargeFlow1.Text));
await WriteFloatAsync(_config.SmallFlowCoeff1, ParseFloat(txtSmallFlow1.Text));
await WriteFloatAsync(_config.LargeFlowCoeff2, ParseFloat(txtLargeFlow2.Text));
await WriteFloatAsync(_config.SmallFlowCoeff2, ParseFloat(txtSmallFlow2.Text));
await WriteFloatAsync(_config.LargeFlowCoeff3, ParseFloat(txtLargeFlow3.Text));
await WriteFloatAsync(_config.SmallFlowCoeff3, ParseFloat(txtSmallFlow3.Text));
// 校准参数
await WriteFloatAsync(_config.PressureCalibZero, ParseFloat(txtPressureZero.Text));
await WriteFloatAsync(_config.PressureCalibSpan, ParseFloat(txtPressureSpan.Text));
await WriteFloatAsync(_config.FlowCalibZero, ParseFloat(txtFlowZero.Text));
await WriteFloatAsync(_config.FlowCalibSpan, ParseFloat(txtFlowSpan.Text));
MessageBox.Show("参数写入成功");
}
catch (Exception ex)
{
MessageBox.Show($"写入参数失败: {ex.Message}");
}
}
private float ParseFloat(string text) => float.TryParse(text, out var val) ? val : 0;
private async Task<float> ReadFloatAsync(ushort address)
{
var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 2);
byte[] bytes = new byte[4];
bytes[0] = (byte)(registers[0] >> 8);
bytes[1] = (byte)(registers[0] & 0xFF);
bytes[2] = (byte)(registers[1] >> 8);
bytes[3] = (byte)(registers[1] & 0xFF);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
private async Task WriteFloatAsync(ushort address, float value)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
ushort high = (ushort)((bytes[0] << 8) | bytes[1]);
ushort low = (ushort)((bytes[2] << 8) | bytes[3]);
await ((ModbusTcpPlcService)_plcService).WriteRegisterAsync(address, high);
await ((ModbusTcpPlcService)_plcService).WriteRegisterAsync((ushort)(address + 1), low);
}
private void Close_Click(object sender, RoutedEventArgs e) => Close();
/// <summary>
/// 辅助方法:查找视觉树中的所有指定类型子元素
/// </summary>
private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) yield break;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
if (child is T t) yield return t;
foreach (var childOfChild in FindVisualChildren<T>(child)) yield return childOfChild;
}
}
}
}

View File

@@ -1,140 +0,0 @@
<UserControl x:Class="MembranePoreTester.Views.PoreDistributionView"
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">
<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>
<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"/>
<!-- 第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>
<!-- 数据表格和简单图表此处用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"/>
<!-- 新增:模式选择 -->
<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>
<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>
<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}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>-->
<GroupBox Grid.Column="2" Header="流量-压力曲线" Margin="5,0,0,0">
<oxy:PlotView Model="{Binding PlotModel}" Height="300" Margin="5"/>
</GroupBox>
</GroupBox>
</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"/>
</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>
</UserControl>

View File

@@ -1,13 +0,0 @@
using System.Windows.Controls;
namespace MembranePoreTester.Views
{
public partial class PoreDistributionView : UserControl
{
public PoreDistributionView()
{
InitializeComponent();
//DataContext = new ViewModels.PoreDistributionViewModel();
}
}
}