409 lines
14 KiB
C#
409 lines
14 KiB
C#
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<TestLiquid> Liquids => TestLiquid.Predefined;
|
||
public List<string> PressureUnits => new() { "Pa", "cmHg", "psi" };
|
||
public List<string> 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;
|
||
}
|
||
|
||
|
||
}
|
||
} |