using MembranePoreTester.Communication; using MembranePoreTester.Helpers; using MembranePoreTester.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Win32; 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 { private PoreDistributionRecord _record = new(); private TestLiquid _selectedLiquid; private bool _isCustomLiquid; private double _customSurfaceTension = 30.0; private double _lowerPore = 0.2; private double _upperPore = 0.8; private double _rangePercentage; // 添加字段 private readonly IPlcService _plcService; private readonly PlcConfiguration _plcConfig; private DataPoint _selectedDataPoint; // 添加属性 public DataPoint SelectedDataPoint { get => _selectedDataPoint; set => SetProperty(ref _selectedDataPoint, value); } public ICommand ReadPlcCommand { get; } public PoreDistributionRecord Record { get => _record; set => SetProperty(ref _record, value); } public List Liquids => TestLiquid.Predefined; public List PressureUnits => new() { "Pa", "cmHg", "psi" }; public List MembraneTypes => new() { "平板膜", "中空纤维膜" }; public TestLiquid SelectedLiquid { get => _selectedLiquid; set { if (SetProperty(ref _selectedLiquid, value)) { Record.Liquid = value; } } } public bool IsCustomLiquid { get => _isCustomLiquid; set { if (SetProperty(ref _isCustomLiquid, value)) { UpdateCustomLiquid(); } } } public double CustomSurfaceTension { get => _customSurfaceTension; set { if (SetProperty(ref _customSurfaceTension, value)) { UpdateCustomLiquid(); } } } public double LowerPore { get => _lowerPore; set => SetProperty(ref _lowerPore, value); } public double UpperPore { get => _upperPore; set => SetProperty(ref _upperPore, value); } public double RangePercentage { get => _rangePercentage; set => SetProperty(ref _rangePercentage, value); } public ICommand AddDataPointCommand { get; } public ICommand RemoveDataPointCommand { get; } public ICommand CalculateCommand { get; } public ICommand GenerateReportCommand { get; } public PoreDistributionViewModel() { _plcService = App.PlcService; _plcConfig = App.PlcConfig; AddDataPointCommand = new RelayCommand(AddDataPoint); RemoveDataPointCommand = new RelayCommand(RemoveDataPoint, () => Record.DataPoints.Any()); CalculateCommand = new RelayCommand(Calculate); GenerateReportCommand = new RelayCommand(GenerateReport); SelectedLiquid = Liquids[0]; Record.PressureUnit = PressureUnits[0]; // 默认 "Pa" ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync()); SaveCommand = new RelayCommand(SaveToDatabase); ExportCommand = new RelayCommand(ExportToExcel); OpenFlowCalibCommand = new RelayCommand(OpenFlowCalibration); Record.DataPoints.CollectionChanged += (s, e) => UpdatePlot(); } private async Task ReadPlcAsync() { try { // 始终读取压力 float rawPressure = await _plcService.ReadPressureAsync(StationId); double pressure = rawPressure * _plcConfig.PressureFactor; if (SelectedDataPoint != null) { // 更新选中行 SelectedDataPoint.Pressure = pressure; if (TestMode == "湿膜") { float rawWet = await _plcService.ReadWetFlowAsync(); SelectedDataPoint.WetFlow = rawWet * _plcConfig.WetFlowFactor; } else { float rawDry = await _plcService.ReadDryFlowAsync(); SelectedDataPoint.DryFlow = rawDry * _plcConfig.DryFlowFactor; } } else { // 新增一行 var newPoint = new DataPoint { Pressure = pressure }; if (TestMode == "湿膜") { float rawWet = await _plcService.ReadWetFlowAsync(); newPoint.WetFlow = rawWet * _plcConfig.WetFlowFactor; } else { float rawDry = await _plcService.ReadDryFlowAsync(); newPoint.DryFlow = rawDry * _plcConfig.DryFlowFactor; } Record.DataPoints.Add(newPoint); } } catch (Exception ex) { MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private void AddDataPoint() { Record.DataPoints.Add(new DataPoint()); } private void RemoveDataPoint() { if (Record.DataPoints.Any()) Record.DataPoints.RemoveAt(Record.DataPoints.Count - 1); } private void UpdateCustomLiquid() { if (IsCustomLiquid) { Record.Liquid = new TestLiquid { Name = "自定义", SurfaceTension = CustomSurfaceTension }; } } // 添加私有字段和公共属性 private double _averagePoreSize; public double AveragePoreSize { get => _averagePoreSize; set => SetProperty(ref _averagePoreSize, value); } // 修改 Calculate 方法: private void Calculate() { if (Record.DataPoints.Count < 2) return; AveragePoreSize = PoreDistributionAnalysis.CalculateAveragePore( Record.DataPoints, Record.PressureUnit, Record.Liquid); RangePercentage = PoreDistributionAnalysis.CalculatePoreRangePercentage( Record.DataPoints, Record.PressureUnit, Record.Liquid, LowerPore, UpperPore); } private void GenerateReport() { ReportGenerator.GeneratePoreDistributionReport(Record); } private void OpenFlowCalibration() { string zeroStr = Microsoft.VisualBasic.Interaction.InputBox("请输入流量零点系数", "流量校准", "0"); string spanStr = Microsoft.VisualBasic.Interaction.InputBox("请输入流量量程系数", "流量校准", "1"); if (float.TryParse(zeroStr, out float zero) && float.TryParse(spanStr, out float span)) { Task.Run(async () => { await WriteFloatAsync(_plcConfig.FlowCalibZero, zero); await WriteFloatAsync(_plcConfig.FlowCalibSpan, span); MessageBox.Show("流量校准系数已写入", "完成"); }); } } private int _stationId; public int StationId { get => _stationId; set => SetProperty(ref _stationId, value); } public void SaveToDatabase() { var entity = new PoreDistributionEntity { 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 ?? 0, LiquidManufacturer = Record.LiquidManufacturer, PressureUnit = Record.PressureUnit, BubblePointPressure = Record.BubblePointPressure, AveragePoreSize = AveragePoreSize, // 使用计算后的属性 DataPoints = Record.DataPoints.Select(dp => new DataPointEntity { Pressure = dp.Pressure, WetFlow = dp.WetFlow, DryFlow = dp.DryFlow }).ToList() }; using var db = new AppDbContext(); db.PoreDistributionRecords.Add(entity); db.SaveChanges(); MessageBox.Show("保存成功!", "提示"); } public void LoadFromDatabase(int recordId) { using var db = new AppDbContext(); var entity = db.PoreDistributionRecords .Include(p => p.DataPoints) .FirstOrDefault(p => p.Id == recordId); if (entity == null) return; Record.SampleType = entity.SampleType; Record.SampleSpec = entity.SampleSpec; Record.RoomTemperature = entity.RoomTemperature; Record.SoakingTime = entity.SoakingTime; Record.Liquid = TestLiquid.Predefined.FirstOrDefault(l => l.Name == entity.LiquidName) ?? new TestLiquid { Name = entity.LiquidName, SurfaceTension = entity.LiquidSurfaceTension }; Record.LiquidManufacturer = entity.LiquidManufacturer; Record.PressureUnit = entity.PressureUnit; Record.BubblePointPressure = entity.BubblePointPressure; Record.TestDate = entity.TestDate; Record.Tester = entity.Tester; Record.DataPoints.Clear(); foreach (var dp in entity.DataPoints) { Record.DataPoints.Add(new DataPoint { Pressure = dp.Pressure, WetFlow = dp.WetFlow, DryFlow = dp.DryFlow }); } // 重新计算平均孔径和分布(触发计算命令) Calculate(); } public ICommand SaveCommand { get; } public ICommand OpenFlowCalibCommand { get; } public ICommand ExportCommand { get; } private void ExportToExcel() { var saveFileDialog = new SaveFileDialog { Filter = "Excel文件|*.xlsx", FileName = $"泡点法_工位{StationId}_{DateTime.Now:yyyyMMddHHmmss}.xlsx" }; if (saveFileDialog.ShowDialog() == true) { // 转换为Entity后导出 var entity = new BubblePointEntity { /* 从Record复制 */ }; ExportHelper.ExportBubblePoint(entity, saveFileDialog.FileName); MessageBox.Show("导出成功"); } } private string _testMode = "湿膜"; public string TestMode { get => _testMode; set => SetProperty(ref _testMode, value); } private int _selectedFlowModeIndex; // 0=大流量,1=小流量 public int SelectedFlowModeIndex { get => _selectedFlowModeIndex; set { if (SetProperty(ref _selectedFlowModeIndex, value)) { ushort reg = StationId switch { 1 => _plcConfig.FlowModeRegister1, 2 => _plcConfig.FlowModeRegister2, 3 => _plcConfig.FlowModeRegister3, _ => 0 }; Task.Run(async () => await _plcService.WriteSingleRegisterAsync(reg, (ushort)value)); } } } private OxyPlot.PlotModel _plotModel; public OxyPlot.PlotModel PlotModel { get => _plotModel; set => SetProperty(ref _plotModel, value); } private void UpdatePlot() { var sorted = Record.DataPoints.OrderBy(p => p.Pressure).ToList(); if (sorted.Count == 0) { PlotModel = null; 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 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 }; foreach (var dp in sorted) { wetSeries.Points.Add(new OxyPlot.DataPoint(dp.Pressure, dp.WetFlow)); drySeries.Points.Add(new OxyPlot.DataPoint(dp.Pressure, dp.DryFlow)); } model.Series.Add(wetSeries); model.Series.Add(drySeries); PlotModel = model; } } }