diff --git a/App.xaml.cs b/App.xaml.cs index 61f77a3..374cc83 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -15,6 +15,9 @@ namespace MembranePoreTester { base.OnStartup(e); + using var db = new AppDbContext(); + db.Database.EnsureCreated(); // 自动建表 + var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); diff --git a/Helpers/AppDbContext.cs b/Helpers/AppDbContext.cs new file mode 100644 index 0000000..e19fa18 --- /dev/null +++ b/Helpers/AppDbContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; + +public class AppDbContext : DbContext +{ + public DbSet BubblePointRecords { get; set; } + public DbSet PoreDistributionRecords { get; set; } + public DbSet DataPoints { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder options) + => options.UseSqlite("Data Source=membrane_test.db"); + + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasMany(p => p.DataPoints) + .WithOne(d => d.PoreDistribution) + .HasForeignKey(d => d.PoreDistributionId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/Helpers/ExportHelper.cs b/Helpers/ExportHelper.cs new file mode 100644 index 0000000..510ee05 --- /dev/null +++ b/Helpers/ExportHelper.cs @@ -0,0 +1,54 @@ +using OfficeOpenXml; +using System.IO; + +public static class ExportHelper +{ + public static void ExportBubblePoint(BubblePointEntity entity, string filePath) + { + using var package = new ExcelPackage(); + var sheet = package.Workbook.Worksheets.Add("泡点法测试"); + sheet.Cells["A1"].Value = "工位"; sheet.Cells["B1"].Value = entity.StationId; + sheet.Cells["A2"].Value = "测试日期"; sheet.Cells["B2"].Value = entity.TestDate.ToString("yyyy-MM-dd HH:mm:ss"); + sheet.Cells["A3"].Value = "测试者"; sheet.Cells["B3"].Value = entity.Tester; + sheet.Cells["A4"].Value = "样品类型"; sheet.Cells["B4"].Value = entity.SampleType; + sheet.Cells["A5"].Value = "规格"; sheet.Cells["B5"].Value = entity.SampleSpec; + sheet.Cells["A6"].Value = "室温(°C)"; sheet.Cells["B6"].Value = entity.RoomTemperature; + sheet.Cells["A7"].Value = "浸润时间(h)"; sheet.Cells["B7"].Value = entity.SoakingTime; + sheet.Cells["A8"].Value = "测试液体"; sheet.Cells["B8"].Value = entity.LiquidName; + sheet.Cells["A9"].Value = "液体生产厂家"; sheet.Cells["B9"].Value = entity.LiquidManufacturer; + sheet.Cells["A10"].Value = "泡点压力"; sheet.Cells["B10"].Value = $"{entity.BubblePointPressure} {entity.PressureUnit}"; + sheet.Cells["A11"].Value = "最大孔径(μm)"; sheet.Cells["B11"].Value = entity.MaxPoreSize; + + package.SaveAs(new FileInfo(filePath)); + } + + public static void ExportPoreDistribution(PoreDistributionEntity entity, string filePath) + { + using var package = new ExcelPackage(); + var sheet = package.Workbook.Worksheets.Add("孔分布测试"); + sheet.Cells["A1"].Value = "工位"; sheet.Cells["B1"].Value = entity.StationId; + sheet.Cells["A2"].Value = "测试日期"; sheet.Cells["B2"].Value = entity.TestDate.ToString("yyyy-MM-dd HH:mm:ss"); + sheet.Cells["A3"].Value = "测试者"; sheet.Cells["B3"].Value = entity.Tester; + sheet.Cells["A4"].Value = "样品类型"; sheet.Cells["B4"].Value = entity.SampleType; + sheet.Cells["A5"].Value = "规格"; sheet.Cells["B5"].Value = entity.SampleSpec; + sheet.Cells["A6"].Value = "室温(°C)"; sheet.Cells["B6"].Value = entity.RoomTemperature; + sheet.Cells["A7"].Value = "浸润时间(h)"; sheet.Cells["B7"].Value = entity.SoakingTime; + sheet.Cells["A8"].Value = "测试液体"; sheet.Cells["B8"].Value = entity.LiquidName; + sheet.Cells["A9"].Value = "液体生产厂家"; sheet.Cells["B9"].Value = entity.LiquidManufacturer; + sheet.Cells["A10"].Value = "泡点压力"; sheet.Cells["B10"].Value = $"{entity.BubblePointPressure} {entity.PressureUnit}"; + sheet.Cells["A11"].Value = "平均孔径(μm)"; sheet.Cells["B11"].Value = entity.AveragePoreSize; + + // 数据点表格 + sheet.Cells["A13"].Value = "压力"; sheet.Cells["B13"].Value = "湿膜流量(L/min)"; sheet.Cells["C13"].Value = "干膜流量(L/min)"; + int row = 14; + foreach (var dp in entity.DataPoints) + { + sheet.Cells[$"A{row}"].Value = dp.Pressure; + sheet.Cells[$"B{row}"].Value = dp.WetFlow; + sheet.Cells[$"C{row}"].Value = dp.DryFlow; + row++; + } + + package.SaveAs(new FileInfo(filePath)); + } +} \ No newline at end of file diff --git a/Helpers/ITestRecord.cs b/Helpers/ITestRecord.cs new file mode 100644 index 0000000..5bd3f6e --- /dev/null +++ b/Helpers/ITestRecord.cs @@ -0,0 +1,57 @@ +public interface ITestRecord +{ + int Id { get; set; } + int StationId { get; set; } + DateTime TestDate { get; set; } + string Tester { get; set; } + // 公共字段 +} + +public class BubblePointEntity : ITestRecord +{ + public int Id { get; set; } + public int StationId { get; set; } + public DateTime TestDate { get; set; } + public string Tester { get; set; } + public string SampleType { get; set; } + public string SampleSpec { get; set; } + public double RoomTemperature { get; set; } + public double SoakingTime { get; set; } + public string LiquidName { get; set; } + public double LiquidSurfaceTension { get; set; } + public string LiquidManufacturer { get; set; } + public double BubblePointPressure { get; set; } + public string PressureUnit { get; set; } + public double MaxPoreSize { get; set; } +} + +public class PoreDistributionEntity : ITestRecord +{ + public int Id { get; set; } + public int StationId { get; set; } + public DateTime TestDate { get; set; } + public string Tester { get; set; } + public string SampleType { get; set; } + public string SampleSpec { get; set; } + public double RoomTemperature { get; set; } + public double SoakingTime { get; set; } + public string LiquidName { get; set; } + public double LiquidSurfaceTension { get; set; } + public string LiquidManufacturer { get; set; } + public string PressureUnit { get; set; } + public double BubblePointPressure { get; set; } + public double AveragePoreSize { get; set; } + // 导航属性:数据点 + public List DataPoints { get; set; } +} + +public class DataPointEntity +{ + public int Id { get; set; } + public int PoreDistributionId { get; set; } + public double Pressure { get; set; } + public double WetFlow { get; set; } + public double DryFlow { get; set; } + + public PoreDistributionEntity PoreDistribution { get; set; } +} \ No newline at end of file diff --git a/ViewModels/BubblePointViewModel.cs b/ViewModels/BubblePointViewModel.cs index e96575d..e9e9db3 100644 --- a/ViewModels/BubblePointViewModel.cs +++ b/ViewModels/BubblePointViewModel.cs @@ -1,6 +1,7 @@ using MembranePoreTester.Communication; using MembranePoreTester.Helpers; using MembranePoreTester.Models; +using Microsoft.Win32; using System.Collections.Generic; using System.Windows; using System.Windows.Input; @@ -93,6 +94,8 @@ namespace MembranePoreTester.ViewModels GenerateReportCommand = new RelayCommand(GenerateReport); SelectedLiquid = Liquids[0]; ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync()); + SaveCommand = new RelayCommand(SaveToDatabase); + ExportCommand = new RelayCommand(ExportToExcel); } @@ -123,5 +126,83 @@ namespace MembranePoreTester.ViewModels { ReportGenerator.GenerateBubblePointReport(Record); } + + + + 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 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("导出成功"); + } + } } } \ No newline at end of file diff --git a/ViewModels/IStationViewModel.cs b/ViewModels/IStationViewModel.cs new file mode 100644 index 0000000..e3d177f --- /dev/null +++ b/ViewModels/IStationViewModel.cs @@ -0,0 +1,6 @@ +public interface IStationViewModel +{ + int StationId { get; set; } + void SaveToDatabase(); + void LoadFromDatabase(int recordId); // 加载指定记录 +} \ No newline at end of file diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..364bfd2 --- /dev/null +++ b/ViewModels/MainViewModel.cs @@ -0,0 +1,29 @@ +using System.Collections.ObjectModel; + +namespace MembranePoreTester.ViewModels +{ + public class MainViewModel : ViewModelBase + { + public ObservableCollection Stations { get; } = new(); + + public MainViewModel() + { + for (int i = 1; i <= 3; i++) + { + Stations.Add(new StationItem + { + Name = $"工位 {i}", + BubblePointVM = new BubblePointViewModel { StationId = i }, + PoreDistributionVM = new PoreDistributionViewModel { StationId = i } + }); + } + } + } + + public class StationItem + { + public string Name { get; set; } + public BubblePointViewModel BubblePointVM { get; set; } + public PoreDistributionViewModel PoreDistributionVM { get; set; } + } +} \ No newline at end of file diff --git a/ViewModels/PoreDistributionViewModel.cs b/ViewModels/PoreDistributionViewModel.cs index 7308646..eda238b 100644 --- a/ViewModels/PoreDistributionViewModel.cs +++ b/ViewModels/PoreDistributionViewModel.cs @@ -1,6 +1,8 @@ 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; @@ -9,7 +11,7 @@ using System.Windows.Input; namespace MembranePoreTester.ViewModels { - public class PoreDistributionViewModel : ViewModelBase + public class PoreDistributionViewModel : ViewModelBase, IStationViewModel { private PoreDistributionRecord _record = new(); private TestLiquid _selectedLiquid; @@ -118,6 +120,8 @@ namespace MembranePoreTester.ViewModels Record.PressureUnit = PressureUnits[0]; // 默认 "Pa" ReadPlcCommand = new RelayCommand(async () => await ReadPlcAsync()); + SaveCommand = new RelayCommand(SaveToDatabase); + ExportCommand = new RelayCommand(ExportToExcel); } @@ -203,5 +207,107 @@ namespace MembranePoreTester.ViewModels { ReportGenerator.GeneratePoreDistributionReport(Record); } + + + + + 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 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("导出成功"); + } + } } } \ No newline at end of file diff --git a/Views/BubblePointView.xaml b/Views/BubblePointView.xaml index 2fe1c6e..6b642e3 100644 --- a/Views/BubblePointView.xaml +++ b/Views/BubblePointView.xaml @@ -78,7 +78,11 @@ +