699 lines
21 KiB
C#
699 lines
21 KiB
C#
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(); // 刷新曲线
|
||
}
|
||
}
|
||
|
||
}
|
||
} |