Files
VacuumPressureMembranePoreS…/ViewModels/PoreDistributionViewModel.cs
2026-03-31 20:17:17 +08:00

699 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MembranePoreTester.Communication;
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 * _plcConfig.PressureFactor, 2);
// 2. 读取当前模式对应的流量
double flow = 0;
if (TestMode.Contains("湿膜"))
{
float rawFlow = await _plcService.ReadWetFlowAsync();
flow = Math.Round(rawFlow, 2);
}
else
{
float rawFlow = await _plcService.ReadDryFlowAsync();
flow = Math.Round(rawFlow, 2);
}
// 3. 直接新增一行(不查找相同压力)
var newPoint = new Models.DataPoint
{
Pressure = pressure,
WetFlow = TestMode.Contains("湿膜") ? flow : 0,
DryFlow = TestMode.Contains("干膜") ? flow : 0
};
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;
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 Models.DataPoint _selectedDataPoint;
// 添加属性
public Models.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);
OpenFlowCalibCommand2 = new RelayCommand(OpenFlowCalibration2);
Record.DataPoints.CollectionChanged += (s, e) => UpdatePlot();
// 加到构造函数最后即可
ClearAllCommand = new RelayCommand(ClearAllData);
RemoveDataPointCommand = new RelayCommand(RemoveSelectedDataPoint, () => SelectedDataPoint != null);
// 延迟2秒后读取确保连接稳定
Task.Delay(2000).ContinueWith(async _ =>
{
await ReadPressureModeAsync();
}, TaskScheduler.Default);
}
private void ClearAllData()
{
ClearData();
}
private async Task ReadPressureModeAsync()
{
await SafeExecuteAsync($"ReadPressureModeAsync{StationId}", async () =>
{
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()
{
await SafeExecuteAsync($"ManualRead_Station{StationId}", async () =>
{
// 始终读取压力
float rawPressure = await _plcService.ReadPressureAsync(StationId);
double pressure = Math.Round(rawPressure * _plcConfig.PressureFactor, 2);
if (SelectedDataPoint != null)
{
// 更新选中行
SelectedDataPoint.Pressure = pressure;
if (TestMode == "湿膜")
{
float rawWet = await _plcService.ReadWetFlowAsync();
SelectedDataPoint.WetFlow = rawWet;
}
else
{
float rawDry = await _plcService.ReadDryFlowAsync();
SelectedDataPoint.DryFlow = rawDry;
}
}
else
{
// 新增一行
var newPoint = new Models.DataPoint { Pressure = pressure };
if (TestMode == "湿膜")
{
float rawWet = await _plcService.ReadWetFlowAsync();
newPoint.WetFlow = rawWet;
}
else
{
float rawDry = await _plcService.ReadDryFlowAsync();
newPoint.DryFlow = rawDry;
}
Record.DataPoints.Add(newPoint);
}
});
}
private void AddDataPoint()
{
Record.DataPoints.Add(new Models.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()
{
ushort address = new ushort();
switch (StationId)
{
case 1:
{
address = _plcConfig.BigFlow1;
break;
}
case 2:
{
address = _plcConfig.BigFlow2;
break;
}
case 3:
{
address = _plcConfig.BigFlow3;
break;
}
}
Task.Run(async () =>
{
await _plcService.WriteCoilAsync(address, true);
});
}
private void OpenFlowCalibration2()
{
ushort address = new ushort();
switch (StationId)
{
case 1:
{
address = _plcConfig.SmallFlow1;
break;
}
case 2:
{
address = _plcConfig.SmallFlow2;
break;
}
case 3:
{
address = _plcConfig.SmallFlow3;
break;
}
}
Task.Run(async () =>
{
await _plcService.WriteCoilAsync(address, true);
});
}
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 Models.DataPoint
{
Pressure = dp.Pressure,
WetFlow = dp.WetFlow,
DryFlow = dp.DryFlow
});
}
// 重新计算平均孔径和分布(触发计算命令)
Calculate();
}
public ICommand SaveCommand { get; }
public ICommand OpenFlowCalibCommand { get; }
public ICommand OpenFlowCalibCommand2 { 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 OxyPlot.PlotModel _plotModel;
public OxyPlot.PlotModel PlotModel
{
get => _plotModel;
set => SetProperty(ref _plotModel, value);
}
private async Task WriteFlowModeAsync(string mode)
{
ushort val = mode.ToString().Contains("大流量") ? (ushort)0 : (ushort)1;
try
{
await _plcService.WriteSingleRegisterAsync(_plcConfig.FlowModeRegister, val);
}
catch (Exception ex)
{
MessageBox.Show($"写流量模式失败: {ex.Message}");
}
}
private void UpdatePlot()
{
var sorted = Record.DataPoints.OrderBy(p => p.Pressure).ToList();
if (sorted.Count == 0)
{
PlotModel = null;
return;
}
var model = new PlotModel
{
Title = "流量-压力曲线",
TitleFontSize = 14,
TitleFontWeight = 1
};
// 添加坐标轴
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)
{
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;
}
public ICommand ClearAllCommand { get; }
// 修改命令初始化(构造函数中)
// 实现删除方法
private void RemoveSelectedDataPoint()
{
if (SelectedDataPoint != null)
{
Record.DataPoints.Remove(SelectedDataPoint);
SelectedDataPoint = null; // 清除选中状态
UpdatePlot(); // 刷新曲线
}
}
}
}