diff --git a/CSI-Z173 吸入制剂药物测试仪(4).gxw b/CSI-Z173 吸入制剂药物测试仪.gxw similarity index 95% rename from CSI-Z173 吸入制剂药物测试仪(4).gxw rename to CSI-Z173 吸入制剂药物测试仪.gxw index 9d12467..f2c0d08 100644 Binary files a/CSI-Z173 吸入制剂药物测试仪(4).gxw and b/CSI-Z173 吸入制剂药物测试仪.gxw differ diff --git a/Models/CalibrationConfig.cs b/Models/CalibrationConfig.cs index e176541..62c9697 100644 --- a/Models/CalibrationConfig.cs +++ b/Models/CalibrationConfig.cs @@ -24,5 +24,17 @@ namespace AciTester.Models [ObservableProperty] private float temperatureProtect = 40.0f; // D1084 + + + + // 新增校准开关状态 + [ObservableProperty] + private bool flowCalibrationEnabled; // M1300 + + [ObservableProperty] + private bool pumpPressureCalibrationEnabled; // M1302 + + [ObservableProperty] + private bool impactorPressureCalibrationEnabled; // M1303 } } \ No newline at end of file diff --git a/Models/PlcConfiguration.cs b/Models/PlcConfiguration.cs index 9bd2159..9c4482d 100644 --- a/Models/PlcConfiguration.cs +++ b/Models/PlcConfiguration.cs @@ -69,6 +69,11 @@ namespace AciTester.Models public ushort ConstantTempStartCoil { get; set; } = 4; // M4 public ushort DefrostStartCoil { get; set; } = 19; // M19 + + + public ushort FlowCalibrationCoil { get; set; } = 1300; // M1300 + public ushort PumpPressureCalibCoil { get; set; } = 1302; // M1302 + } /// diff --git a/ViewModels/ConfigViewModel.cs b/ViewModels/ConfigViewModel.cs index 082d20d..03da971 100644 --- a/ViewModels/ConfigViewModel.cs +++ b/ViewModels/ConfigViewModel.cs @@ -43,6 +43,17 @@ namespace AciTester.ViewModels var protect = await _plcService.ReadFloatAsync(_config.FlowProtectReg); _calib.FlowLowLimit = protect - 5; // 示例,实际根据协议解析 _calib.FlowHighLimit = protect + 5; + + + // 读取线圈状态(新增) + _calib.FlowCalibrationEnabled = await _plcService.ReadCoilAsync(_config.FlowCalibrationCoil); + _calib.PumpPressureCalibrationEnabled = await _plcService.ReadCoilAsync(_config.PumpPressureCalibCoil); + _calib.ImpactorPressureCalibrationEnabled = await _plcService.ReadCoilAsync(_config.ImpactorPressureCalibCoil); + + + + + } catch (System.Exception ex) { @@ -64,6 +75,19 @@ namespace AciTester.ViewModels await _plcService.WriteMultipleRegistersAsync(_config.PumpPressureCalibReg, _calib.PumpPressureCalibration); await _plcService.WriteMultipleRegistersAsync(_config.ImpactorPressureCalibReg, _calib.ImpactorPressureCalibration); // 写入流量保护值(需根据实际协议拆分高低限) + + + + + await _plcService.WriteCoilAsync(_config.FlowCalibrationCoil, _calib.FlowCalibrationEnabled); + await _plcService.WriteCoilAsync(_config.PumpPressureCalibCoil, _calib.PumpPressureCalibrationEnabled); + await _plcService.WriteCoilAsync(_config.ImpactorPressureCalibCoil, _calib.ImpactorPressureCalibrationEnabled); + + + + + + MessageBox.Show("配置保存成功", "提示"); // 可选:触发重新校准 await _plcService.WriteCoilAsync(_config.ImpactorPressureCalibCoil, true); diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs index a6f3966..e2b1575 100644 --- a/ViewModels/MainViewModel.cs +++ b/ViewModels/MainViewModel.cs @@ -75,18 +75,20 @@ namespace AciTester.ViewModels _plcService = new ModbusTcpPlcService(_config); _reportService = new ExcelReportService(); + // NGI 装置三 - 依据《中国药典》2025年版通则0951 表3 + // 包含预分离器 + Stage 1 ~ 7 + MOC,共9个收集部件 Stages = new ObservableCollection - { - new StageData { StageName = "Stage 0", CutoffDiameter = 9.0 }, - new StageData { StageName = "Stage 1", CutoffDiameter = 5.8 }, - new StageData { StageName = "Stage 2", CutoffDiameter = 4.7 }, - new StageData { StageName = "Stage 3", CutoffDiameter = 3.3 }, - new StageData { StageName = "Stage 4", CutoffDiameter = 2.1 }, - new StageData { StageName = "Stage 5", CutoffDiameter = 1.1 }, - new StageData { StageName = "Stage 6", CutoffDiameter = 0.7 }, - new StageData { StageName = "Stage 7", CutoffDiameter = 0.4 }, - new StageData { StageName = "Filter", CutoffDiameter = 0.0 } - }; +{ + new StageData { StageName = "预分离器", CutoffDiameter = 12.80 }, // 不计入粒径分布 + new StageData { StageName = "Stage 1", CutoffDiameter = 14.30 }, + new StageData { StageName = "Stage 2", CutoffDiameter = 4.88 }, + new StageData { StageName = "Stage 3", CutoffDiameter = 2.185 }, + new StageData { StageName = "Stage 4", CutoffDiameter = 1.207 }, + new StageData { StageName = "Stage 5", CutoffDiameter = 0.608 }, + new StageData { StageName = "Stage 6", CutoffDiameter = 0.323 }, + new StageData { StageName = "Stage 7", CutoffDiameter = 0.206 }, + new StageData { StageName = "MOC", CutoffDiameter = 0.070 } +}; ConnectCommand = new AsyncRelayCommand(ConnectAsync); DisconnectCommand = new RelayCommand(Disconnect); @@ -438,11 +440,100 @@ namespace AciTester.ViewModels } } + //private void CalculateResult() + //{ + // double totalMass = 0; + // foreach (var stage in Stages) + // totalMass += stage.NetWeight; + + // if (totalMass <= 0) + // { + // MessageBox.Show("总质量为零,无法计算", "警告", MessageBoxButton.OK, MessageBoxImage.Warning); + // return; + // } + + // // 1. 计算各级占比和累积分布(8个层级) + // double[] diamArray = new double[8]; + // for (int i = 0; i < 8; i++) + // diamArray[i] = Stages[i].CutoffDiameter; + + // double[] percentages = new double[8]; + // double[] cumulatives = new double[8]; + // double sum = 0; + // for (int i = 0; i < 8; i++) + // { + // percentages[i] = Stages[i].NetWeight / totalMass * 100; + // sum += percentages[i]; + // cumulatives[i] = sum; + // } + + // // 2. 插值函数(与之前相同,但只使用8个点) + // double Interpolate(double targetCum) + // { + // if (targetCum <= cumulatives[0]) return diamArray[0]; + // if (targetCum >= cumulatives[7]) return diamArray[7]; + // for (int i = 0; i < 7; i++) + // { + // if (cumulatives[i] <= targetCum && cumulatives[i + 1] >= targetCum) + // { + // double d1 = diamArray[i]; + // double d2 = diamArray[i + 1]; + // double c1 = cumulatives[i]; + // double c2 = cumulatives[i + 1]; + // double logD1 = Math.Log(d1); + // double logD2 = Math.Log(d2); + // double logD = logD1 + (targetCum - c1) * (logD2 - logD1) / (c2 - c1); + // return Math.Exp(logD); + // } + // } + // return diamArray[7]; + // } + + // double d10 = Interpolate(10); + // double d50 = Interpolate(50); + // double d90 = Interpolate(90); + + // // 3. GSD (D84/D16) + // double d16 = Interpolate(16); + // double d84 = Interpolate(84); + // double gsd = (d16 > 0 && d84 > 0) ? d84 / d16 : 0; + + // // 4. 微细粒子剂量(FPD)和分数(FPF) + // // NGI中,截止直径 ≤ 5μm 的层级为 Stage 3~7 以及 MOC(Stage 3的D50=2.82μm,Stage 7=0.34μm,全部≤5μm) + // // 因此统计 Stage 3,4,5,6,7 和 MOC 的质量 + // double fineMass = 0; + // for (int i = 2; i < 8; i++) // i=2 对应Stage 3(数组索引从0开始) + // fineMass += Stages[i].NetWeight; + + // double fpd = fineMass * 1000; // mg + // double fpf = (fineMass / totalMass) * 100; + + // // 5. 赋值给 CurrentResult + // CurrentResult = new TestResult + // { + // TestTime = DateTime.Now, + // TotalMass = totalMass, + // FineParticleDose = fpd, + // FineParticleFraction = fpf, + // Stages = Stages.ToList(), + // FlowRate = CurrentFlow, + // Temperature = RealTime.Temperature, + // DifferentialPressure = RealTime.DifferentialPressure, + // D10 = d10, + // D50 = d50, + // D90 = d90, + // GSD = gsd + // }; + + // MessageBox.Show($"计算完成\n总质量: {totalMass:F4} g\n微细粒子剂量: {fpd:F2} mg\n微细粒子分数: {fpf:F2}%\nD50: {d50:F2} μm", + // "计算结果", MessageBoxButton.OK, MessageBoxImage.Information); + //} private void CalculateResult() { + // 排除预分离器(索引0),只计算 Stage 1 ~ MOC(索引1~8) double totalMass = 0; - foreach (var stage in Stages) - totalMass += stage.NetWeight; + for (int i = 1; i < 9; i++) + totalMass += Stages[i].NetWeight; if (totalMass <= 0) { @@ -450,30 +541,32 @@ namespace AciTester.ViewModels return; } - // 1. 计算各级占比和累积分布 - // 注意:Stages[0] ~ Stages[7] 对应 Stage0~Stage7,Stages[8] 是 Filter - // 截止直径数组(与 Stages 的顺序一致) - double[] diamArray = new double[9]; - for (int i = 0; i < 9; i++) - diamArray[i] = Stages[i].CutoffDiameter; - - // 占比和累积分布 - double[] percentages = new double[9]; - double[] cumulatives = new double[9]; - double sum = 0; - for (int i = 0; i < 9; i++) + // 提取 Stage 1 ~ MOC 的直径和质量(索引1~8) + double[] diamArray = new double[8]; + double[] netWeights = new double[8]; + for (int i = 0; i < 8; i++) { - percentages[i] = Stages[i].NetWeight / totalMass * 100; + diamArray[i] = Stages[i + 1].CutoffDiameter; + netWeights[i] = Stages[i + 1].NetWeight; + } + + // 计算占比和累积分布 + double[] percentages = new double[8]; + double[] cumulatives = new double[8]; + double sum = 0; + for (int i = 0; i < 8; i++) + { + percentages[i] = netWeights[i] / totalMass * 100; sum += percentages[i]; cumulatives[i] = sum; } - // 2. 插值函数:给定累积百分比,返回对应的粒径(对数线性插值) + // 插值函数(只针对8个数据点) double Interpolate(double targetCum) { if (targetCum <= cumulatives[0]) return diamArray[0]; - if (targetCum >= cumulatives[8]) return diamArray[8]; - for (int i = 0; i < 8; i++) + if (targetCum >= cumulatives[7]) return diamArray[7]; + for (int i = 0; i < 7; i++) { if (cumulatives[i] <= targetCum && cumulatives[i + 1] >= targetCum) { @@ -487,31 +580,27 @@ namespace AciTester.ViewModels return Math.Exp(logD); } } - return diamArray[8]; + return diamArray[7]; } double d10 = Interpolate(10); double d50 = Interpolate(50); double d90 = Interpolate(90); - // 3. 计算 GSD(几何标准偏差):GSD = D84 / D16(如果可用) + // GSD double d16 = Interpolate(16); double d84 = Interpolate(84); double gsd = (d16 > 0 && d84 > 0) ? d84 / d16 : 0; - // 4. 计算微细粒子剂量和分数(原有逻辑) + // FPD: Stage 3 ~ MOC(索引3~8) double fineMass = 0; - foreach (var stage in Stages) - { - if (stage.CutoffDiameter <= 5.0 && stage.CutoffDiameter > 0) - fineMass += stage.NetWeight; - } - fineMass += Stages[8].NetWeight; // Filter + for (int i = 3; i < 9; i++) + fineMass += Stages[i].NetWeight; - double fpd = fineMass * 1000; // mg + double fpd = fineMass * 1000; double fpf = (fineMass / totalMass) * 100; - // 5. 赋值给 CurrentResult + // 保存结果 CurrentResult = new TestResult { TestTime = DateTime.Now, @@ -522,7 +611,6 @@ namespace AciTester.ViewModels FlowRate = CurrentFlow, Temperature = RealTime.Temperature, DifferentialPressure = RealTime.DifferentialPressure, - // 新增粒径参数 D10 = d10, D50 = d50, D90 = d90, diff --git a/Views/ConfigWindow.xaml b/Views/ConfigWindow.xaml index d02631e..15be49e 100644 --- a/Views/ConfigWindow.xaml +++ b/Views/ConfigWindow.xaml @@ -1,7 +1,7 @@  @@ -11,31 +11,38 @@ - + + - + + + + + + + @@ -43,16 +50,28 @@ + - + + + + + + + + + + + +