From 2f1cacd0971ab90397f593d7027a59d8a94fe387 Mon Sep 17 00:00:00 2001 From: xyy <544939200@qq.com> Date: Fri, 27 Mar 2026 21:35:32 +0800 Subject: [PATCH] --- ViewModels/MainViewModel.cs | 34 ++- ViewModels/PoreDistributionViewModel.cs | 282 ++++++++++++++++-- Views/BubblePointView.xaml | 76 ++--- Views/MainWindow.xaml | 2 +- Views/MainWindow.xaml.cs | 50 ++++ Views/ParameterWindow.xaml | 4 +- Views/ParameterWindow.xaml.cs | 2 +- Views/PoreDistributionView.xaml | 378 +++++++++++++++++------- Views/PoreDistributionView.xaml.cs | 17 +- appsettings.json | 2 +- 10 files changed, 665 insertions(+), 182 deletions(-) diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs index 628ae5e..325e48d 100644 --- a/ViewModels/MainViewModel.cs +++ b/ViewModels/MainViewModel.cs @@ -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); + } diff --git a/ViewModels/PoreDistributionViewModel.cs b/ViewModels/PoreDistributionViewModel.cs index ba7052a..579bf15 100644 --- a/ViewModels/PoreDistributionViewModel.cs +++ b/ViewModels/PoreDistributionViewModel.cs @@ -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 _pressureModeList; + + public List PressureModeList => _pressureModeList ??= new List + { + 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}"); + } + } + + /// + /// 清空所有数据点,重置表格和曲线 + /// + 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 } + + + + + } } \ No newline at end of file diff --git a/Views/BubblePointView.xaml b/Views/BubblePointView.xaml index 40cd46d..322dc52 100644 --- a/Views/BubblePointView.xaml +++ b/Views/BubblePointView.xaml @@ -1,12 +1,25 @@ - + + + + + - + + @@ -15,7 +28,7 @@ - + @@ -29,8 +42,7 @@ - + - + - - - - - - - - - -