添加项目文件。
This commit is contained in:
232
ViewModels/BubblePointViewModel.cs
Normal file
232
ViewModels/BubblePointViewModel.cs
Normal file
@@ -0,0 +1,232 @@
|
||||
using MembranePoreTester.Communication;
|
||||
using MembranePoreTester.Helpers;
|
||||
using MembranePoreTester.Models;
|
||||
using Microsoft.Win32;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MembranePoreTester.ViewModels
|
||||
{
|
||||
public class BubblePointViewModel : ViewModelBase
|
||||
{
|
||||
private BubblePointRecord _record = new();
|
||||
private TestLiquid _selectedLiquid;
|
||||
private bool _isCustomLiquid;
|
||||
private double _customSurfaceTension = 30.0;
|
||||
|
||||
|
||||
// 添加字段
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly PlcConfiguration _plcConfig;
|
||||
|
||||
public ICommand ReadPlcCommand { get; }
|
||||
|
||||
public BubblePointRecord 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;
|
||||
OnPropertyChanged(nameof(Record.MaxPoreSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCustomLiquid
|
||||
{
|
||||
get => _isCustomLiquid;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isCustomLiquid, value))
|
||||
{
|
||||
UpdateCustomLiquid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double CustomSurfaceTension
|
||||
{
|
||||
get => _customSurfaceTension;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _customSurfaceTension, value))
|
||||
{
|
||||
UpdateCustomLiquid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCustomLiquid()
|
||||
{
|
||||
if (IsCustomLiquid)
|
||||
{
|
||||
Record.Liquid = new TestLiquid
|
||||
{
|
||||
Name = "自定义",
|
||||
SurfaceTension = CustomSurfaceTension
|
||||
};
|
||||
OnPropertyChanged(nameof(Record.MaxPoreSize));
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand CalculateCommand { get; }
|
||||
public ICommand GenerateReportCommand { get; }
|
||||
|
||||
public ICommand OpenPressureCalibCommand { get; }
|
||||
|
||||
public BubblePointViewModel()
|
||||
{
|
||||
|
||||
_plcService = App.PlcService;
|
||||
_plcConfig = App.PlcConfig;
|
||||
CalculateCommand = new RelayCommand(Calculate);
|
||||
GenerateReportCommand = new RelayCommand(GenerateReport);
|
||||
SelectedLiquid = Liquids[0];
|
||||
ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync());
|
||||
SaveCommand = new RelayCommand(SaveToDatabase);
|
||||
ExportCommand = new RelayCommand(ExportToExcel);
|
||||
OpenPressureCalibCommand = new RelayCommand(OpenPressureCalibration);
|
||||
}
|
||||
|
||||
|
||||
private async Task ReadPlcAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
float rawPressure = await _plcService.ReadPressureAsync(StationId);
|
||||
Record.BubblePointPressure = rawPressure * _plcConfig.PressureFactor;
|
||||
OnPropertyChanged(nameof(Record.BubblePointPressure));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"读取PLC失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public double MaxPoreSize => Record.MaxPoreSize;
|
||||
|
||||
private void Calculate()
|
||||
{
|
||||
// 触发最大孔径重新计算(通过绑定已自动刷新)
|
||||
OnPropertyChanged(nameof(Record.MaxPoreSize));
|
||||
}
|
||||
|
||||
private void GenerateReport()
|
||||
{
|
||||
ReportGenerator.GenerateBubblePointReport(Record);
|
||||
}
|
||||
|
||||
public void UpdateBubblePointPressure(double pressure)
|
||||
{
|
||||
Record.BubblePointPressure = pressure;
|
||||
OnPropertyChanged(nameof(Record.BubblePointPressure));
|
||||
OnPropertyChanged(nameof(MaxPoreSize)); // 最大孔径依赖于压力
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int _stationId;
|
||||
public int StationId
|
||||
{
|
||||
get => _stationId;
|
||||
set => SetProperty(ref _stationId, value);
|
||||
}
|
||||
|
||||
// 保存当前记录到数据库
|
||||
public void SaveToDatabase()
|
||||
{
|
||||
var entity = new BubblePointEntity
|
||||
{
|
||||
StationId = 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,
|
||||
BubblePointPressure = Record.BubblePointPressure,
|
||||
PressureUnit = Record.PressureUnit,
|
||||
MaxPoreSize = Record.MaxPoreSize
|
||||
};
|
||||
using var db = new AppDbContext();
|
||||
db.BubblePointRecords.Add(entity);
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
public void LoadFromDatabase(int recordId)
|
||||
{
|
||||
using var db = new AppDbContext();
|
||||
var entity = db.BubblePointRecords.Find(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.BubblePointPressure = entity.BubblePointPressure;
|
||||
Record.PressureUnit = entity.PressureUnit;
|
||||
Record.TestDate = entity.TestDate;
|
||||
Record.Tester = entity.Tester;
|
||||
|
||||
OnPropertyChanged(nameof(Record));
|
||||
OnPropertyChanged(nameof(MaxPoreSize)); // 触发界面更新
|
||||
}
|
||||
|
||||
|
||||
public ICommand SaveCommand { get; }
|
||||
|
||||
public ICommand ExportCommand { get; }
|
||||
|
||||
private void OpenPressureCalibration()
|
||||
{
|
||||
// 使用简单的输入框获取新零点系数和量程系数
|
||||
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.PressureCalibZero, zero);
|
||||
await WriteFloatAsync(_plcConfig.PressureCalibSpan, span);
|
||||
MessageBox.Show("压力校准系数已写入", "完成");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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("导出成功");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
ViewModels/IStationViewModel.cs
Normal file
6
ViewModels/IStationViewModel.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
public interface IStationViewModel
|
||||
{
|
||||
int StationId { get; set; }
|
||||
void SaveToDatabase();
|
||||
void LoadFromDatabase(int recordId); // 加载指定记录
|
||||
}
|
||||
160
ViewModels/MainViewModel.cs
Normal file
160
ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using MembranePoreTester.Communication;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel; // 用于 PropertyChanged
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MembranePoreTester.ViewModels
|
||||
{
|
||||
public class MainViewModel : ViewModelBase
|
||||
{
|
||||
public ObservableCollection<StationItem> Stations { get; } = new();
|
||||
|
||||
public class StationItem : ViewModelBase
|
||||
{
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly PlcConfiguration _plcConfig;
|
||||
private bool _isPressing;
|
||||
private string _pressButtonText = "加压";
|
||||
private string _highLowPressure = "低压";
|
||||
private bool _enableStatus; // M21 状态
|
||||
|
||||
public string Name { get; set; }
|
||||
public BubblePointViewModel BubblePointVM { get; set; }
|
||||
public PoreDistributionViewModel PoreDistributionVM { get; set; }
|
||||
public int StationId { get; set; }
|
||||
|
||||
public string HighLowPressure
|
||||
{
|
||||
get => _highLowPressure;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _highLowPressure, value))
|
||||
{
|
||||
// 当选择变化时,写入 PLC 压力模式寄存器
|
||||
Task.Run(async () => await WritePressureModeAsync(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string PressButtonText
|
||||
{
|
||||
get => _pressButtonText;
|
||||
set => SetProperty(ref _pressButtonText, value);
|
||||
}
|
||||
|
||||
// 使能状态(只读)
|
||||
public bool EnableStatus
|
||||
{
|
||||
get => _enableStatus;
|
||||
private set => SetProperty(ref _enableStatus, value);
|
||||
}
|
||||
// 使能状态显示文本
|
||||
public string EnableStatusText => EnableStatus ? "运行中" : "未启动";
|
||||
|
||||
// 使能状态显示颜色(绿色表示运行中,灰色表示未启动)
|
||||
public string EnableStatusColor => EnableStatus ? "Green" : "Gray";
|
||||
|
||||
// 定时器,用于轮询 M21 状态
|
||||
private System.Windows.Threading.DispatcherTimer _timer;
|
||||
|
||||
public ICommand PressCommand { get; }
|
||||
//public ICommand BurstCommand { get; }
|
||||
public ICommand StartCommand { get; }
|
||||
public ICommand StopCommand { get; }
|
||||
public ICommand EnableCommand { get; } // 备用,但可以使用复选框直接绑定
|
||||
|
||||
public StationItem()
|
||||
{
|
||||
_plcService = App.PlcService;
|
||||
_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));
|
||||
// 启动定时器,每秒读取一次 M21 状态
|
||||
_timer = new System.Windows.Threading.DispatcherTimer();
|
||||
_timer.Interval = TimeSpan.FromSeconds(1);
|
||||
_timer.Tick += async (s, e) => await ReadEnableStatusAsync();
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
private async Task TogglePressAsync()
|
||||
{
|
||||
_isPressing = !_isPressing;
|
||||
await WriteCoilAsync(_plcConfig.PressCoil, _isPressing);
|
||||
PressButtonText = _isPressing ? "停止加压" : "加压";
|
||||
}
|
||||
|
||||
|
||||
private async Task ReadEnableStatusAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool status = await _plcService.ReadCoilAsync(_plcConfig.EnableCoil); // 读取 M21
|
||||
EnableStatus = status;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 读取出错时保持原状态或显示错误
|
||||
System.Diagnostics.Debug.WriteLine($"读取使能状态失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WriteCoilAsync(ushort coil, bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _plcService.WriteCoilAsync(coil, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"写线圈失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WritePressureModeAsync(string mode)
|
||||
{
|
||||
ushort val = mode == "高压" ? (ushort)1 : (ushort)0;
|
||||
try
|
||||
{
|
||||
await _plcService.WriteRegisterAsync(_plcConfig.PressureModeRegister, val);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"写压力模式失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
for (int i = 1; i <= 3; i++)
|
||||
{
|
||||
var station = new StationItem
|
||||
{
|
||||
Name = $"工位 {i}",
|
||||
BubblePointVM = new BubblePointViewModel { StationId = i },
|
||||
PoreDistributionVM = new PoreDistributionViewModel { StationId = i },
|
||||
StationId = i
|
||||
};
|
||||
Stations.Add(station);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
409
ViewModels/PoreDistributionViewModel.cs
Normal file
409
ViewModels/PoreDistributionViewModel.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
47
ViewModels/RelayCommand.cs
Normal file
47
ViewModels/RelayCommand.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MembranePoreTester.ViewModels
|
||||
{
|
||||
public class RelayCommand : ICommand
|
||||
{
|
||||
private readonly Action _execute;
|
||||
private readonly Func<bool> _canExecute;
|
||||
|
||||
public RelayCommand(Action execute, Func<bool> canExecute = null)
|
||||
{
|
||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add { CommandManager.RequerySuggested += value; }
|
||||
remove { CommandManager.RequerySuggested -= value; }
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
|
||||
public void Execute(object parameter) => _execute();
|
||||
}
|
||||
|
||||
public class RelayCommand<T> : ICommand
|
||||
{
|
||||
private readonly Action<T> _execute;
|
||||
private readonly Func<T, bool> _canExecute;
|
||||
|
||||
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
|
||||
{
|
||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add { CommandManager.RequerySuggested += value; }
|
||||
remove { CommandManager.RequerySuggested -= value; }
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter) => _canExecute == null || _canExecute((T)parameter);
|
||||
public void Execute(object parameter) => _execute((T)parameter);
|
||||
}
|
||||
}
|
||||
97
ViewModels/StationViewModel.cs
Normal file
97
ViewModels/StationViewModel.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System.Windows.Input;
|
||||
using MembranePoreTester.Communication;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace MembranePoreTester.ViewModels
|
||||
{
|
||||
public class StationViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly PlcConfiguration _plcConfig;
|
||||
private bool _isPressing;
|
||||
private string _pressButtonText = "加压";
|
||||
private string _highLowPressure = "低压";
|
||||
private bool _enableChecked;
|
||||
|
||||
public int StationId { get; set; }
|
||||
|
||||
public BubblePointViewModel BubblePointVM { get; } = new();
|
||||
public PoreDistributionViewModel PoreDistributionVM { get; } = new();
|
||||
|
||||
public string HighLowPressure
|
||||
{
|
||||
get => _highLowPressure;
|
||||
set => SetProperty(ref _highLowPressure, value);
|
||||
}
|
||||
|
||||
public string PressButtonText
|
||||
{
|
||||
get => _pressButtonText;
|
||||
set => SetProperty(ref _pressButtonText, value);
|
||||
}
|
||||
|
||||
public bool EnableChecked
|
||||
{
|
||||
get => _enableChecked;
|
||||
set => SetProperty(ref _enableChecked, value);
|
||||
}
|
||||
|
||||
public ICommand PressCommand { get; }
|
||||
public ICommand BurstCommand { get; }
|
||||
public ICommand StartCommand { get; }
|
||||
public ICommand StopCommand { get; }
|
||||
public ICommand EnableCommand { get; } // 用于使能复选框
|
||||
|
||||
public StationViewModel()
|
||||
{
|
||||
_plcService = App.PlcService;
|
||||
_plcConfig = App.PlcConfig;
|
||||
BubblePointVM.StationId = StationId;
|
||||
PoreDistributionVM.StationId = StationId;
|
||||
// 初始化按钮文字
|
||||
_pressButtonText = "加压"; // 直接设置字段,避免触发属性通知循环
|
||||
PressButtonText = "加压"; // 或者通过属性设置
|
||||
System.Diagnostics.Debug.WriteLine($"工位{StationId} PressButtonText初始值: {PressButtonText}");
|
||||
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));
|
||||
// 使能复选框点击时写入对应线圈
|
||||
EnableCommand = new RelayCommand(async () => await WriteCoilAsync(_plcConfig.EnableCoil, EnableChecked));
|
||||
}
|
||||
|
||||
private async Task TogglePressAsync()
|
||||
{
|
||||
_isPressing = !_isPressing;
|
||||
await WriteCoilAsync(_plcConfig.PressCoil, _isPressing);
|
||||
PressButtonText = _isPressing ? "停止加压" : "加压";
|
||||
}
|
||||
|
||||
private async Task ReadBurstPressureAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
float pressure = await ((ModbusTcpPlcService)_plcService).ReadPressureAsync(StationId);
|
||||
BubblePointVM.UpdateBubblePointPressure(pressure * _plcConfig.PressureFactor);
|
||||
MessageBox.Show($"涨破压力: {pressure} {BubblePointVM.Record.PressureUnit}", "提示");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"读取压力失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WriteCoilAsync(ushort coil, bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ((ModbusTcpPlcService)_plcService).WriteCoilAsync(coil, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"写线圈失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
ViewModels/ViewModelBase.cs
Normal file
34
ViewModels/ViewModelBase.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using MembranePoreTester.Communication;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MembranePoreTester.ViewModels
|
||||
{
|
||||
public class ViewModelBase : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async Task WriteFloatAsync(ushort address, float value)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
||||
ushort high = (ushort)((bytes[0] << 8) | bytes[1]);
|
||||
ushort low = (ushort)((bytes[2] << 8) | bytes[3]);
|
||||
await App.PlcService.WriteSingleRegisterAsync(address, high);
|
||||
await App.PlcService.WriteSingleRegisterAsync((ushort)(address + 1), low);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user