This commit is contained in:
25
Helpers/Interpolation.cs
Normal file
25
Helpers/Interpolation.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace MembranePoreTester.Helpers
|
||||
{
|
||||
public static class Interpolation
|
||||
{
|
||||
// 线性插值
|
||||
public static double Linear(double[] xs, double[] ys, double x)
|
||||
{
|
||||
if (xs.Length == 0 || ys.Length == 0 || xs.Length != ys.Length)
|
||||
throw new ArgumentException("数组长度不匹配或为空");
|
||||
|
||||
if (x <= xs[0]) return ys[0];
|
||||
if (x >= xs[^1]) return ys[^1];
|
||||
|
||||
int i = Array.BinarySearch(xs, x);
|
||||
if (i >= 0) return ys[i];
|
||||
|
||||
i = ~i; // 大于 x 的第一个索引
|
||||
double x0 = xs[i - 1], x1 = xs[i];
|
||||
double y0 = ys[i - 1], y1 = ys[i];
|
||||
return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Helpers/PoreCalculator.cs
Normal file
31
Helpers/PoreCalculator.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using MembranePoreTester.Models;
|
||||
|
||||
namespace MembranePoreTester.Helpers
|
||||
{
|
||||
public static class PoreCalculator
|
||||
{
|
||||
public static double PressureToPore(double pressure, string unit, TestLiquid liquid)
|
||||
{
|
||||
if (liquid == null) return 0;
|
||||
return unit switch
|
||||
{
|
||||
"Pa" => liquid.C_Pa / pressure,
|
||||
"cmHg" => liquid.C_cmHg / pressure,
|
||||
"psi" => liquid.C_psi / pressure,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
public static double PoreToPressure(double pore, string unit, TestLiquid liquid)
|
||||
{
|
||||
if (liquid == null) return 0;
|
||||
return unit switch
|
||||
{
|
||||
"Pa" => liquid.C_Pa / pore,
|
||||
"cmHg" => liquid.C_cmHg / pore,
|
||||
"psi" => liquid.C_psi / pore,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
69
Helpers/PoreDistributionAnalysis.cs
Normal file
69
Helpers/PoreDistributionAnalysis.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using MembranePoreTester.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MembranePoreTester.Helpers
|
||||
{
|
||||
public static class PoreDistributionAnalysis
|
||||
{
|
||||
public static double CalculateAveragePore(
|
||||
IEnumerable<DataPoint> points,
|
||||
string unit,
|
||||
TestLiquid liquid)
|
||||
{
|
||||
var sorted = points.OrderBy(p => p.Pressure).ToList();
|
||||
if (sorted.Count < 2) return 0;
|
||||
|
||||
double[] pressures = sorted.Select(p => p.Pressure).ToArray();
|
||||
double[] dryFlows = sorted.Select(p => p.DryFlow).ToArray();
|
||||
double[] wetFlows = sorted.Select(p => p.WetFlow).ToArray();
|
||||
|
||||
double halfDryFlow = dryFlows.Max() / 2.0;
|
||||
|
||||
// 找到湿膜流量等于 halfDryFlow 的压力(交点)
|
||||
for (int i = 0; i < wetFlows.Length - 1; i++)
|
||||
{
|
||||
if ((wetFlows[i] <= halfDryFlow && wetFlows[i + 1] >= halfDryFlow) ||
|
||||
(wetFlows[i] >= halfDryFlow && wetFlows[i + 1] <= halfDryFlow))
|
||||
{
|
||||
double p = Interpolation.Linear(
|
||||
new[] { wetFlows[i], wetFlows[i + 1] },
|
||||
new[] { pressures[i], pressures[i + 1] },
|
||||
halfDryFlow);
|
||||
return PoreCalculator.PressureToPore(p, unit, liquid);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static double CalculatePoreRangePercentage(
|
||||
IEnumerable<DataPoint> points,
|
||||
string unit,
|
||||
TestLiquid liquid,
|
||||
double lowerPore,
|
||||
double upperPore)
|
||||
{
|
||||
var sorted = points.OrderBy(p => p.Pressure).ToList();
|
||||
if (sorted.Count < 2) return 0;
|
||||
|
||||
double[] pressures = sorted.Select(p => p.Pressure).ToArray();
|
||||
double[] wetFlows = sorted.Select(p => p.WetFlow).ToArray();
|
||||
double[] dryFlows = sorted.Select(p => p.DryFlow).ToArray();
|
||||
|
||||
// 大孔径对应低压,小孔径对应高压
|
||||
double pLower = PoreCalculator.PoreToPressure(upperPore, unit, liquid);
|
||||
double pUpper = PoreCalculator.PoreToPressure(lowerPore, unit, liquid);
|
||||
|
||||
// 插值
|
||||
double qWetLower = Interpolation.Linear(pressures, wetFlows, pLower);
|
||||
double qDryLower = Interpolation.Linear(pressures, dryFlows, pLower);
|
||||
double qWetUpper = Interpolation.Linear(pressures, wetFlows, pUpper);
|
||||
double qDryUpper = Interpolation.Linear(pressures, dryFlows, pUpper);
|
||||
|
||||
double ratioLow = qWetLower / qDryLower;
|
||||
double ratioHigh = qWetUpper / qDryUpper;
|
||||
|
||||
return (ratioHigh - ratioLow) * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
227
Helpers/ReportGenerator.cs
Normal file
227
Helpers/ReportGenerator.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using MembranePoreTester.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Xps.Packaging;
|
||||
|
||||
namespace MembranePoreTester.Helpers
|
||||
{
|
||||
public static class ReportGenerator
|
||||
{
|
||||
public static void GenerateBubblePointReport(BubblePointRecord record)
|
||||
{
|
||||
FlowDocument doc = CreateBubblePointDocument(record);
|
||||
ShowPrintPreview(doc, "泡点法测试报告");
|
||||
}
|
||||
|
||||
public static void GeneratePoreDistributionReport(PoreDistributionRecord record)
|
||||
{
|
||||
FlowDocument doc = CreatePoreDistributionDocument(record);
|
||||
ShowPrintPreview(doc, "孔分布测试报告");
|
||||
}
|
||||
|
||||
private static FlowDocument CreateBubblePointDocument(BubblePointRecord record)
|
||||
{
|
||||
FlowDocument doc = new FlowDocument();
|
||||
doc.PageWidth = 800; // 适应打印
|
||||
|
||||
// 标题
|
||||
doc.Blocks.Add(new Paragraph(new Run("泡点法测试报告"))
|
||||
{
|
||||
FontSize = 24,
|
||||
FontWeight = FontWeights.Bold,
|
||||
TextAlignment = TextAlignment.Center
|
||||
});
|
||||
|
||||
// 日期/测试者
|
||||
doc.Blocks.Add(new Paragraph(new Run($"测试日期: {record.TestDate:yyyy-MM-dd} 测试者: {record.Tester}"))
|
||||
{
|
||||
TextAlignment = TextAlignment.Right
|
||||
});
|
||||
|
||||
// 样品信息表格
|
||||
Table table = new Table();
|
||||
table.Columns.Add(new TableColumn { Width = new GridLength(150) });
|
||||
table.Columns.Add(new TableColumn { Width = new GridLength(200) });
|
||||
|
||||
TableRowGroup group = new TableRowGroup();
|
||||
AddRow(group, "膜类型", record.SampleType);
|
||||
AddRow(group, "规格", record.SampleSpec);
|
||||
AddRow(group, "室温(°C)", record.RoomTemperature.ToString("F1"));
|
||||
AddRow(group, "浸润时间(h)", record.SoakingTime.ToString("F1"));
|
||||
AddRow(group, "测试液体", record.Liquid?.Name);
|
||||
AddRow(group, "液体生产厂家", record.LiquidManufacturer);
|
||||
AddRow(group, "泡点压力", $"{record.BubblePointPressure} {record.PressureUnit}");
|
||||
AddRow(group, "最大孔径(μm)", record.MaxPoreSize.ToString("F3"));
|
||||
|
||||
table.RowGroups.Add(group);
|
||||
doc.Blocks.Add(table);
|
||||
|
||||
doc.Blocks.Add(new Paragraph(new Run("本报告依据 GB/T 32361-2015 生成。"))
|
||||
{
|
||||
FontStyle = FontStyles.Italic,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
Margin = new Thickness(0, 20, 0, 0)
|
||||
});
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static FlowDocument CreatePoreDistributionDocument(PoreDistributionRecord record)
|
||||
{
|
||||
FlowDocument doc = new FlowDocument();
|
||||
doc.PageWidth = 800;
|
||||
|
||||
// 标题
|
||||
doc.Blocks.Add(new Paragraph(new Run("孔分布测试报告"))
|
||||
{
|
||||
FontSize = 24,
|
||||
FontWeight = FontWeights.Bold,
|
||||
TextAlignment = TextAlignment.Center
|
||||
});
|
||||
|
||||
doc.Blocks.Add(new Paragraph(new Run($"测试日期: {record.TestDate:yyyy-MM-dd} 测试者: {record.Tester}"))
|
||||
{
|
||||
TextAlignment = TextAlignment.Right
|
||||
});
|
||||
|
||||
// 样品信息表格
|
||||
Table infoTable = new Table();
|
||||
infoTable.Columns.Add(new TableColumn { Width = new GridLength(150) });
|
||||
infoTable.Columns.Add(new TableColumn { Width = new GridLength(200) });
|
||||
TableRowGroup group = new TableRowGroup();
|
||||
AddRow(group, "膜类型", record.SampleType);
|
||||
AddRow(group, "规格", record.SampleSpec);
|
||||
AddRow(group, "室温(°C)", record.RoomTemperature.ToString("F1"));
|
||||
AddRow(group, "浸润时间(h)", record.SoakingTime.ToString("F1"));
|
||||
AddRow(group, "测试液体", record.Liquid?.Name);
|
||||
AddRow(group, "液体生产厂家", record.LiquidManufacturer);
|
||||
infoTable.RowGroups.Add(group);
|
||||
doc.Blocks.Add(infoTable);
|
||||
|
||||
// 计算结果
|
||||
doc.Blocks.Add(new Paragraph(new Run("测试结果"))
|
||||
{
|
||||
FontSize = 18,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Margin = new Thickness(0, 10, 0, 5)
|
||||
});
|
||||
|
||||
Table resultTable = new Table();
|
||||
resultTable.Columns.Add(new TableColumn { Width = new GridLength(150) });
|
||||
resultTable.Columns.Add(new TableColumn { Width = new GridLength(200) });
|
||||
TableRowGroup resultGroup = new TableRowGroup();
|
||||
AddRow(resultGroup, "泡点压力", $"{record.BubblePointPressure} {record.PressureUnit}");
|
||||
AddRow(resultGroup, "平均孔径(μm)", record.AveragePoreSize.ToString("F3"));
|
||||
resultTable.RowGroups.Add(resultGroup);
|
||||
doc.Blocks.Add(resultTable);
|
||||
|
||||
// 数据点列表
|
||||
doc.Blocks.Add(new Paragraph(new Run("压力-流量数据"))
|
||||
{
|
||||
FontSize = 14,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Margin = new Thickness(0, 10, 0, 5)
|
||||
});
|
||||
|
||||
Table dataTable = new Table();
|
||||
dataTable.Columns.Add(new TableColumn { Width = new GridLength(80) });
|
||||
dataTable.Columns.Add(new TableColumn { Width = new GridLength(80) });
|
||||
dataTable.Columns.Add(new TableColumn { Width = new GridLength(80) });
|
||||
dataTable.CellSpacing = 2;
|
||||
|
||||
TableRowGroup dataGroup = new TableRowGroup();
|
||||
|
||||
// 表头
|
||||
TableRow headerRow = new TableRow();
|
||||
headerRow.Cells.Add(new TableCell(new Paragraph(new Run("压力"))));
|
||||
headerRow.Cells.Add(new TableCell(new Paragraph(new Run("湿膜流量(L/min)"))));
|
||||
headerRow.Cells.Add(new TableCell(new Paragraph(new Run("干膜流量(L/min)"))));
|
||||
headerRow.FontWeight = FontWeights.Bold;
|
||||
dataGroup.Rows.Add(headerRow);
|
||||
|
||||
foreach (var dp in record.DataPoints)
|
||||
{
|
||||
TableRow row = new TableRow();
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(dp.Pressure.ToString("F2")))));
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(dp.WetFlow.ToString("F3")))));
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(dp.DryFlow.ToString("F3")))));
|
||||
dataGroup.Rows.Add(row);
|
||||
}
|
||||
|
||||
dataTable.RowGroups.Add(dataGroup);
|
||||
doc.Blocks.Add(dataTable);
|
||||
|
||||
// 孔分布结果
|
||||
if (record.PoreDistributions != null && record.PoreDistributions.Any())
|
||||
{
|
||||
doc.Blocks.Add(new Paragraph(new Run("孔分布"))
|
||||
{
|
||||
FontSize = 14,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Margin = new Thickness(0, 10, 0, 5)
|
||||
});
|
||||
|
||||
Table distTable = new Table();
|
||||
distTable.Columns.Add(new TableColumn { Width = new GridLength(80) });
|
||||
distTable.Columns.Add(new TableColumn { Width = new GridLength(80) });
|
||||
distTable.Columns.Add(new TableColumn { Width = new GridLength(80) });
|
||||
|
||||
TableRowGroup distGroup = new TableRowGroup();
|
||||
|
||||
TableRow distHeader = new TableRow();
|
||||
distHeader.Cells.Add(new TableCell(new Paragraph(new Run("孔径下限(μm)"))));
|
||||
distHeader.Cells.Add(new TableCell(new Paragraph(new Run("孔径上限(μm)"))));
|
||||
distHeader.Cells.Add(new TableCell(new Paragraph(new Run("百分比(%)"))));
|
||||
distHeader.FontWeight = FontWeights.Bold;
|
||||
distGroup.Rows.Add(distHeader);
|
||||
|
||||
foreach (var pd in record.PoreDistributions)
|
||||
{
|
||||
TableRow row = new TableRow();
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(pd.LowerPore.ToString("F3")))));
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(pd.UpperPore.ToString("F3")))));
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(pd.Percentage.ToString("F1")))));
|
||||
distGroup.Rows.Add(row);
|
||||
}
|
||||
|
||||
distTable.RowGroups.Add(distGroup);
|
||||
doc.Blocks.Add(distTable);
|
||||
}
|
||||
|
||||
doc.Blocks.Add(new Paragraph(new Run("本报告依据 GB/T 32361-2015 生成。"))
|
||||
{
|
||||
FontStyle = FontStyles.Italic,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
Margin = new Thickness(0, 20, 0, 0)
|
||||
});
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static void AddRow(TableRowGroup group, string label, string value)
|
||||
{
|
||||
TableRow row = new TableRow();
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(label))));
|
||||
row.Cells.Add(new TableCell(new Paragraph(new Run(value ?? ""))));
|
||||
group.Rows.Add(row);
|
||||
}
|
||||
|
||||
private static void ShowPrintPreview(FlowDocument document, string title)
|
||||
{
|
||||
PrintDialog printDialog = new PrintDialog();
|
||||
if (printDialog.ShowDialog() == true)
|
||||
{
|
||||
// 调整文档页面大小以匹配打印机
|
||||
document.PageHeight = printDialog.PrintableAreaHeight;
|
||||
document.PageWidth = printDialog.PrintableAreaWidth;
|
||||
document.PagePadding = new Thickness(50);
|
||||
|
||||
IDocumentPaginatorSource dps = document;
|
||||
printDialog.PrintDocument(dps.DocumentPaginator, title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user