diff --git a/CapMachine.Wpf/PPCalculation/EnthalpyDrynessCalculator.cs b/CapMachine.Wpf/PPCalculation/EnthalpyDrynessCalculator.cs
new file mode 100644
index 0000000..6733cf5
--- /dev/null
+++ b/CapMachine.Wpf/PPCalculation/EnthalpyDrynessCalculator.cs
@@ -0,0 +1,599 @@
+using System;
+
+namespace CapMachine.Wpf.PPCalculation
+{
+ ///
+ /// 干度计算(基于焓法)的独立封装:
+ /// - 先分别计算气路/液路阀前的单相质量比焓(TPRHO + THERM)
+ /// - 再计算吸气压力对应的饱和液/饱和气质量比焓(SATP + THERM)
+ /// - 最终按既定流程计算混合焓与干度,并限幅到 [0,1]
+ ///
+ /// 注意:该类仅做封装以便维护,不应改变既有干度计算过程与逻辑。
+ /// 调用方需确保 REFPROP 已完成 SETPATH/SETUP 初始化(与现有 PPCService 保持一致)。
+ ///
+ public sealed class EnthalpyDrynessCalculator
+ {
+ private readonly object _refpropLock;
+
+ ///
+ /// 构造函数。
+ ///
+ /// REFPROP 全局互斥锁对象(必须与系统其它 REFPROP 调用共用,以避免并发竞态)。
+ public EnthalpyDrynessCalculator(object refpropLock)
+ {
+ _refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock));
+ }
+
+ ///
+ /// 干度计算输入模型(以 Tag 读数为准)。
+ ///
+ public readonly struct Input
+ {
+ public Input(
+ double gasPreValvePressBarA,
+ double gasPreValveTempC,
+ double txvFrPressBarA,
+ double txvFrTempC,
+ double inhPressBarA,
+ double vrvFlowKgPerH,
+ double liqRefFlowKgPerH,
+ double lubeFlowKgPerH)
+ {
+ GasPreValvePressBarA = gasPreValvePressBarA;
+ GasPreValveTempC = gasPreValveTempC;
+ TxvFrPressBarA = txvFrPressBarA;
+ TxvFrTempC = txvFrTempC;
+ InhPressBarA = inhPressBarA;
+ VRVFlowKgPerH = vrvFlowKgPerH;
+ LiqRefFlowKgPerH = liqRefFlowKgPerH;
+ LubeFlowKgPerH = lubeFlowKgPerH;
+ }
+
+ public double GasPreValvePressBarA { get; }
+ public double GasPreValveTempC { get; }
+ public double TxvFrPressBarA { get; }
+ public double TxvFrTempC { get; }
+ public double InhPressBarA { get; }
+
+ public double VRVFlowKgPerH { get; }
+ public double LiqRefFlowKgPerH { get; }
+ public double LubeFlowKgPerH { get; }
+ }
+
+ ///
+ /// 干度计算输出模型。
+ ///
+ public readonly struct Result
+ {
+ public Result(
+ double gasFlowKgPerH,
+ double gasEnthalpy_kJkg,
+ double liquidEnthalpy_kJkg,
+ double satLiquidEnthalpy_kJkg,
+ double satVaporEnthalpy_kJkg,
+ bool isDryness1Success,
+ double dryness1_01,
+ double hMix1_kJkg,
+ string error1,
+ bool isDryness2Success,
+ double dryness2_01,
+ double hMix2_kJkg,
+ string error2)
+ {
+ GasFlowKgPerH = gasFlowKgPerH;
+ GasEnthalpy_kJkg = gasEnthalpy_kJkg;
+ LiquidEnthalpy_kJkg = liquidEnthalpy_kJkg;
+ SatLiquidEnthalpy_kJkg = satLiquidEnthalpy_kJkg;
+ SatVaporEnthalpy_kJkg = satVaporEnthalpy_kJkg;
+
+ IsDryness1Success = isDryness1Success;
+ Dryness1_01 = dryness1_01;
+ HMix1_kJkg = hMix1_kJkg;
+ Error1 = error1 ?? string.Empty;
+
+ IsDryness2Success = isDryness2Success;
+ Dryness2_01 = dryness2_01;
+ HMix2_kJkg = hMix2_kJkg;
+ Error2 = error2 ?? string.Empty;
+ }
+
+ public double GasFlowKgPerH { get; }
+
+ public double GasEnthalpy_kJkg { get; }
+ public double LiquidEnthalpy_kJkg { get; }
+ public double SatLiquidEnthalpy_kJkg { get; }
+ public double SatVaporEnthalpy_kJkg { get; }
+
+ public bool IsDryness1Success { get; }
+ public double Dryness1_01 { get; }
+ public double HMix1_kJkg { get; }
+ public string Error1 { get; }
+
+ public bool IsDryness2Success { get; }
+ public double Dryness2_01 { get; }
+ public double HMix2_kJkg { get; }
+ public string Error2 { get; }
+ }
+
+ ///
+ /// 按既有流程计算干度(两套:干度1/干度2)。
+ ///
+ /// 输入数据。
+ /// 计算结果。
+ public Result Calculate(Input input)
+ {
+ // 气体流量 kg/h = 冷媒流量 kg/h - 液冷媒流量 kg/h
+ double gasFlowKgPerH = input.VRVFlowKgPerH - input.LiqRefFlowKgPerH;
+
+ // 定义气相质量焓 kJ/kg(注意:保持既有逻辑,默认 0,仅在成功计算时赋值)
+ double gas_hVap_kJkg = 0.0;
+
+ // 步骤1: 计算气路阀前气相焓 h_vap (单相气相)
+ if (TryTPRHO_VaporDensity_ByTP_MPa_C(input.GasPreValvePressBarA * 0.1, input.GasPreValveTempC, out var dVap_molL, out _))
+ {
+ if (TryTHERM_VaporEnthalpy_ByTD(input.GasPreValveTempC, dVap_molL, out var hVap_kJkg, out _))
+ {
+ gas_hVap_kJkg = hVap_kJkg;
+ }
+ }
+
+ // 定义液相质量焓 kJ/kg(保持既有逻辑,默认 0,仅在成功计算时赋值)
+ double liquid_hLiq_kJkg = 0.0;
+
+ // 步骤2: 计算液路阀前液相焓 h_liq (单相液相)
+ if (TryTPRHO_LiquidDensity_ByTP_MPa_C(input.TxvFrPressBarA * 0.1, input.TxvFrTempC, out var dLiq_molL, out _))
+ {
+ if (TryTHERM_LiquidEnthalpy_ByTD(input.TxvFrTempC, dLiq_molL, out var hLiq_kJkg, out _))
+ {
+ liquid_hLiq_kJkg = hLiq_kJkg;
+ }
+ }
+
+ // 定义饱和液/饱和气质量焓 kJ/kg(保持既有逻辑,默认 0,仅在同时成功时赋值)
+ double hSatL_kJkg = 0.0;
+ double hSatV_kJkg = 0.0;
+
+ if (TryGetSaturationLiquidEnthalpy_ByP_MPa(input.InhPressBarA * 0.1, out var satL, out _) &&
+ TryGetSaturationVaporEnthalpy_ByP_MPa(input.InhPressBarA * 0.1, out var satV, out _))
+ {
+ hSatL_kJkg = satL;
+ hSatV_kJkg = satV;
+ }
+
+ // 干度1:mg=气体流量;ml=液体流量
+ bool ok1 = TryComputeDrynessByEnthalpy(
+ gas_hVap_kJkg,
+ liquid_hLiq_kJkg,
+ gasFlowKgPerH,
+ input.LiqRefFlowKgPerH,
+ hSatL_kJkg,
+ hSatV_kJkg,
+ out var dryness1,
+ out var hMix1,
+ out var err1);
+
+ // 干度2:mg=气体流量+润滑油流量;ml=液体流量(保持既有策略)
+ bool ok2 = TryComputeDrynessByEnthalpy2(
+ gas_hVap_kJkg,
+ liquid_hLiq_kJkg,
+ gasFlowKgPerH,
+ input.LubeFlowKgPerH,
+ input.LiqRefFlowKgPerH,
+ hSatL_kJkg,
+ hSatV_kJkg,
+ out var dryness2,
+ out var hMix2,
+ out var err2);
+
+ return new Result(
+ gasFlowKgPerH,
+ gas_hVap_kJkg,
+ liquid_hLiq_kJkg,
+ hSatL_kJkg,
+ hSatV_kJkg,
+ ok1,
+ dryness1,
+ hMix1,
+ err1,
+ ok2,
+ dryness2,
+ hMix2,
+ err2);
+ }
+
+ ///
+ /// 获取指定组分的摩尔质量(kg/mol)。
+ ///
+ /// 组分ID(icomp)。
+ /// 摩尔质量(kg/mol)。
+ /// 错误信息。
+ /// 是否成功。
+ private bool TryGetMolarMassKgPerMol(long componentId, out double molarMassKgPerMol, out string error)
+ {
+ molarMassKgPerMol = double.NaN;
+ error = string.Empty;
+
+ try
+ {
+ double wmm = 0, Trp = 0, Tnbpt = 0, Tc = 0, Pc = 0, Dc = 0, Zc = 0, acf = 0, dip = 0, Rgas = 0;
+ lock (_refpropLock)
+ {
+ IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas);
+ }
+
+ molarMassKgPerMol = wmm * 0.001;
+ if (double.IsNaN(molarMassKgPerMol) || double.IsInfinity(molarMassKgPerMol) || molarMassKgPerMol <= 0)
+ {
+ error = "无效的摩尔质量";
+ return false;
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ error = $"获取组分{componentId}的摩尔质量时出错: {ex.Message}";
+ return false;
+ }
+ }
+
+ ///
+ /// TPRHOdll 封装:按 T(℃)、P(MPa) 计算“气相”摩尔密度 D [mol/L](kph=2)。
+ ///
+ private bool TryTPRHO_VaporDensity_ByTP_MPa_C(double pressureMPa, double temperatureC, out double densityMolPerL, out string error)
+ {
+ densityMolPerL = double.NaN;
+ error = string.Empty;
+
+ double tK = temperatureC + 273.15;
+ double pKPa = pressureMPa * 1000.0;
+
+ double[] x = new double[20];
+ x[0] = 1.0;
+
+ long kph = 2;
+ long kguess = 0;
+ double D = 0.0;
+ long ierr = 0;
+ long herrLen = 255;
+ string herr = new string(' ', 255);
+
+ lock (_refpropLock)
+ {
+ IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kph, ref kguess, ref D, ref ierr, ref herr, ref herrLen);
+ }
+
+ if (ierr != 0)
+ {
+ error = $"TPRHO 错误: {herr.Trim()} (ierr={ierr})";
+ return false;
+ }
+
+ densityMolPerL = D;
+ return true;
+ }
+
+ ///
+ /// THERMdll 封装:按 T(℃) 与 D[mol/L] 计算气相质量焓 h_vap [kJ/kg]。
+ ///
+ private bool TryTHERM_VaporEnthalpy_ByTD(double temperatureC, double densityMolPerL, out double h_vap_kJ_per_kg, out string error)
+ {
+ h_vap_kJ_per_kg = double.NaN;
+ error = string.Empty;
+
+ double tK = temperatureC + 273.15;
+ double D = densityMolPerL;
+
+ double[] x = new double[20];
+ x[0] = 1.0;
+
+ double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0;
+
+ if (!TryGetMolarMassKgPerMol(1, out var molarMassKgPerMol, out error))
+ {
+ return false;
+ }
+
+ lock (_refpropLock)
+ {
+ IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt);
+ }
+
+ h_vap_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001;
+ return true;
+ }
+
+ ///
+ /// TPRHOdll 封装:按 T(℃)、P(MPa) 计算“液相”摩尔密度 D [mol/L](kph=1)。
+ ///
+ private bool TryTPRHO_LiquidDensity_ByTP_MPa_C(double pressureMPa, double temperatureC, out double densityMolPerL, out string error)
+ {
+ densityMolPerL = double.NaN;
+ error = string.Empty;
+
+ double tK = temperatureC + 273.15;
+ double pKPa = pressureMPa * 1000.0;
+
+ double[] x = new double[20];
+ x[0] = 1.0;
+
+ long kph = 1;
+ long kguess = 0;
+ double D = 0.0;
+ long ierr = 0;
+ long herrLen = 255;
+ string herr = new string(' ', 255);
+
+ lock (_refpropLock)
+ {
+ IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kph, ref kguess, ref D, ref ierr, ref herr, ref herrLen);
+ }
+
+ if (ierr != 0)
+ {
+ error = $"TPRHO(液相) 错误: {herr.Trim()} (ierr={ierr})";
+ return false;
+ }
+
+ densityMolPerL = D;
+ return true;
+ }
+
+ ///
+ /// THERMdll 封装:按 T(℃) 与 D[mol/L] 计算液相质量焓 h_liq [kJ/kg]。
+ ///
+ private bool TryTHERM_LiquidEnthalpy_ByTD(double temperatureC, double densityMolPerL, out double h_liq_kJ_per_kg, out string error)
+ {
+ h_liq_kJ_per_kg = double.NaN;
+ error = string.Empty;
+
+ double tK = temperatureC + 273.15;
+ double D = densityMolPerL;
+
+ double[] x = new double[20];
+ x[0] = 1.0;
+
+ double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0;
+
+ if (!TryGetMolarMassKgPerMol(1, out var molarMassKgPerMol, out error))
+ {
+ return false;
+ }
+
+ lock (_refpropLock)
+ {
+ IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt);
+ }
+
+ h_liq_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001;
+ return true;
+ }
+
+ ///
+ /// SATPdll:由压力 P(MPa) 求饱和温度 Tsat[K]、饱和液/气摩尔密度 Dl/Dv [mol/L]。
+ ///
+ private bool TrySATP_SaturationByP_MPa(double pressureMPa, out double tSatK, out double Dl_molL, out double Dv_molL, out string error)
+ {
+ tSatK = double.NaN;
+ Dl_molL = double.NaN;
+ Dv_molL = double.NaN;
+ error = string.Empty;
+
+ double pKPa = pressureMPa * 1000.0;
+ double[] x = new double[20];
+ x[0] = 1.0;
+
+ long kph = 1;
+ double Dl = 0, Dv = 0;
+ double[] xliq = new double[20];
+ double[] xvap = new double[20];
+ long ierr = 0, herrLen = 255;
+ string herr = new string(' ', 255);
+
+ lock (_refpropLock)
+ {
+ IRefProp64.SATPdll(ref pKPa, x, ref kph, ref tSatK, ref Dl, ref Dv, xliq, xvap, ref ierr, ref herr, ref herrLen);
+ }
+
+ if (ierr != 0)
+ {
+ error = $"SATP 错误: {herr.Trim()} (ierr={ierr})";
+ return false;
+ }
+
+ Dl_molL = Dl;
+ Dv_molL = Dv;
+ return true;
+ }
+
+ ///
+ /// THERMdll:由 T[K] 与 D[mol/L] 计算质量比焓 h[kJ/kg]。
+ ///
+ private bool TryTHERM_Enthalpy_kJkg_ByT_K_D(double temperatureK, double densityMolPerL, out double h_kJ_per_kg, out string error)
+ {
+ h_kJ_per_kg = double.NaN;
+ error = string.Empty;
+
+ double tK = temperatureK;
+ double D = densityMolPerL;
+
+ double[] x = new double[20];
+ x[0] = 1.0;
+
+ double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0;
+
+ if (!TryGetMolarMassKgPerMol(1, out var molarMassKgPerMol, out error))
+ {
+ return false;
+ }
+
+ lock (_refpropLock)
+ {
+ IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt);
+ }
+
+ h_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001;
+ return true;
+ }
+
+ ///
+ /// 便捷:由压力 P(MPa) 直接得到“饱和液”质量比焓 h_liq[kJ/kg]。
+ ///
+ private bool TryGetSaturationLiquidEnthalpy_ByP_MPa(double pressureMPa, out double h_liq_kJkg, out string error)
+ {
+ h_liq_kJkg = double.NaN;
+ error = string.Empty;
+
+ if (!TrySATP_SaturationByP_MPa(pressureMPa, out double tSatK, out double Dl, out _, out error))
+ {
+ return false;
+ }
+
+ return TryTHERM_Enthalpy_kJkg_ByT_K_D(tSatK, Dl, out h_liq_kJkg, out error);
+ }
+
+ ///
+ /// 便捷:由压力 P(MPa) 直接得到“饱和气”质量比焓 h_vap[kJ/kg]。
+ ///
+ private bool TryGetSaturationVaporEnthalpy_ByP_MPa(double pressureMPa, out double h_vap_kJkg, out string error)
+ {
+ h_vap_kJkg = double.NaN;
+ error = string.Empty;
+
+ if (!TrySATP_SaturationByP_MPa(pressureMPa, out double tSatK, out _, out double Dv, out error))
+ {
+ return false;
+ }
+
+ return TryTHERM_Enthalpy_kJkg_ByT_K_D(tSatK, Dv, out h_vap_kJkg, out error);
+ }
+
+ ///
+ /// 按图片的最终流程计算干度:
+ /// 1) 质量流量加权混合焓 h_mix = (h_vap*mg + h_liq*ml) / (mg + ml)
+ /// 2) 干度 x = (h_mix - h_l) / (h_v - h_l),并限幅到 [0,1]
+ ///
+ /// 入参单位:
+ /// - hVap_kJkg, hLiq_kJkg, hSatL_kJkg, hSatV_kJkg 均为 kJ/kg
+ /// - mGas_kg_h, mLiq_kg_h 均为 kg/h
+ ///
+ private bool TryComputeDrynessByEnthalpy(
+ double hVap_kJkg,
+ double hLiq_kJkg,
+ double mGas_kg_h,
+ double mLiq_kg_h,
+ double hSatL_kJkg,
+ double hSatV_kJkg,
+ out double dryness,
+ out double hMix_kJkg,
+ out string error)
+ {
+ dryness = double.NaN;
+ hMix_kJkg = double.NaN;
+ error = string.Empty;
+
+ if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg))
+ {
+ error = "输入焓值存在 NaN";
+ return false;
+ }
+ if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h))
+ {
+ error = "输入质量流量存在 NaN";
+ return false;
+ }
+
+ double mg = Math.Max(0.0, mGas_kg_h);
+ double ml = Math.Max(0.0, mLiq_kg_h);
+ double mSum = mg + ml;
+ if (mSum <= 0)
+ {
+ error = "气液质量流量之和为 0,无法进行加权混合焓计算";
+ return false;
+ }
+
+ hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum;
+
+ double denom = (hSatV_kJkg - hSatL_kJkg);
+ const double eps = 1e-9;
+ if (Math.Abs(denom) < eps)
+ {
+ error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)";
+ return false;
+ }
+
+ double x = (hMix_kJkg - hSatL_kJkg) / denom;
+
+ if (double.IsNaN(x) || double.IsInfinity(x))
+ {
+ error = "干度计算结果异常(NaN/Inf)";
+ return false;
+ }
+
+ dryness = Math.Min(1.0, Math.Max(0.0, x));
+ return true;
+ }
+
+ ///
+ /// 按图片的最终流程计算干度2:
+ /// 计算逻辑同干度1,但气体流量 mg = 气体流量 + 润滑油流量。
+ ///
+ private bool TryComputeDrynessByEnthalpy2(
+ double hVap_kJkg,
+ double hLiq_kJkg,
+ double mGas_kg_h,
+ double lubeFlow_kg_h,
+ double mLiq_kg_h,
+ double hSatL_kJkg,
+ double hSatV_kJkg,
+ out double dryness,
+ out double hMix_kJkg,
+ out string error)
+ {
+ dryness = double.NaN;
+ hMix_kJkg = double.NaN;
+ error = string.Empty;
+
+ if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg))
+ {
+ error = "输入焓值存在 NaN";
+ return false;
+ }
+ if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h))
+ {
+ error = "输入质量流量存在 NaN";
+ return false;
+ }
+
+ double mg = Math.Max(0.0, mGas_kg_h) + Math.Max(0.0, lubeFlow_kg_h);
+ double ml = Math.Max(0.0, mLiq_kg_h);
+ double mSum = mg + ml;
+ if (mSum <= 0)
+ {
+ error = "气液质量流量之和为 0,无法进行加权混合焓计算";
+ return false;
+ }
+
+ hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum;
+
+ double denom = (hSatV_kJkg - hSatL_kJkg);
+ const double eps = 1e-9;
+ if (Math.Abs(denom) < eps)
+ {
+ error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)";
+ return false;
+ }
+
+ double x = (hMix_kJkg - hSatL_kJkg) / denom;
+
+ if (double.IsNaN(x) || double.IsInfinity(x))
+ {
+ error = "干度计算结果异常(NaN/Inf)";
+ return false;
+ }
+
+ dryness = Math.Min(1.0, Math.Max(0.0, x));
+ return true;
+ }
+ }
+}
diff --git a/CapMachine.Wpf/Services/PPCService.cs b/CapMachine.Wpf/Services/PPCService.cs
index 4ac1cde..82eca19 100644
--- a/CapMachine.Wpf/Services/PPCService.cs
+++ b/CapMachine.Wpf/Services/PPCService.cs
@@ -10,6 +10,7 @@ using Prism.Events;
using Prism.Mvvm;
using Prism.Services.Dialogs;
using SixLabors.ImageSharp.ColorSpaces;
+using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.Text;
@@ -35,6 +36,7 @@ namespace CapMachine.Wpf.Services
public MachineRtDataService MachineRtDataService { get; }
public IDialogService DialogService { get; }
private readonly IDrynessCalculator _drynessCalculator;
+ private readonly EnthalpyDrynessCalculator _enthalpyDrynessCalculator;
///
/// 标签中心
@@ -58,6 +60,7 @@ namespace CapMachine.Wpf.Services
MachineRtDataService = machineRtDataService;
DialogService = dialogService;
_drynessCalculator = drynessCalculator;
+ _enthalpyDrynessCalculator = new EnthalpyDrynessCalculator(_refpropLock);
TagManager = MachineRtDataService.TagManger;
if (TagManager.TryGetShortTagByName("转速[rpm]", out ShortValueTag? speedShortTag))
@@ -95,8 +98,7 @@ namespace CapMachine.Wpf.Services
//TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!;
//TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!;
- if (TagManager.TryGetShortTagByName("膨胀阀前温度[℃]", out ShortValueTag? TxvFrTempShortTag) ||
- TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out TxvFrTempShortTag))
+ if (TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out ShortValueTag? TxvFrTempShortTag))
{
TxvFrTempTag = TxvFrTempShortTag!;
}
@@ -326,6 +328,70 @@ namespace CapMachine.Wpf.Services
///
private bool DebugLog { get; set; } = false;
+ private DateTime _lastDrynessSnapshotUtc = DateTime.MinValue;
+ private static readonly TimeSpan _drynessSnapshotInterval = TimeSpan.FromSeconds(5);
+
+ ///
+ /// 干度计算过程值输出(限频),用于现场快速定位干度为何为0/为何被限幅。
+ ///
+ private void LogDrynessSnapshotIfNeeded(
+ string stage,
+ string error,
+ double dryness01,
+ double hMix_kJkg,
+ double gasInput_kJkg,
+ double liquidInput_kJkg,
+ double hSatL_kJkg,
+ double hSatV_kJkg,
+ double mGas_kg_h,
+ double mLube_kg_h,
+ double mLiq_kg_h)
+ {
+ var now = DateTime.UtcNow;
+ if (now - _lastDrynessSnapshotUtc < _drynessSnapshotInterval)
+ {
+ return;
+ }
+
+ _lastDrynessSnapshotUtc = now;
+
+ double denom = hSatV_kJkg - hSatL_kJkg;
+ double xRaw = double.NaN;
+ if (!double.IsNaN(hMix_kJkg) && !double.IsNaN(hSatL_kJkg) && !double.IsNaN(denom) && Math.Abs(denom) > 1e-9)
+ {
+ xRaw = (hMix_kJkg - hSatL_kJkg) / denom;
+ }
+
+ double vrv = VRVTag?.PVModel?.EngValue ?? double.NaN;
+ double liqRef = LiqRefFlowTag?.PVModel?.EngValue ?? double.NaN;
+ double lube = LubeFlowTag?.PVModel?.EngValue ?? double.NaN;
+ double gasFlow = vrv - liqRef;
+
+ double mg = Math.Max(0.0, mGas_kg_h) + Math.Max(0.0, mLube_kg_h);
+ double ml = Math.Max(0.0, mLiq_kg_h);
+ double mSum = mg + ml;
+
+ var msg =
+ $"[DrynessSnap] Stage={stage}; Err={error}; x={dryness01:F6} ({dryness01 * 100.0:F3}%); xRaw={xRaw}; " +
+ $"hMix={hMix_kJkg}; hSatL={hSatL_kJkg}; hSatV={hSatV_kJkg}; denom={denom}; " +
+ $"GasInput={gasInput_kJkg}; LiquidInput={liquidInput_kJkg}; " +
+ $"mGas={mGas_kg_h}kg/h, mLube={mLube_kg_h}kg/h, mLiq={mLiq_kg_h}kg/h => mg={mg}kg/h, ml={ml}kg/h, mSum={mSum}kg/h; " +
+ $"VRV={vrv}kg/h(Addr={VRVTag?.PVModel?.Address}), LiqRef={liqRef}kg/h(Addr={LiqRefFlowTag?.PVModel?.Address}), Lube={lube}kg/h(Addr={LubeFlowTag?.PVModel?.Address}), GasFlow(VRV-LiqRef)={gasFlow}kg/h; " +
+ $"InhP={InhPressTag?.PVModel?.EngValue}BarA(Addr={InhPressTag?.PVModel?.Address}), InhT={InhTempTag?.PVModel?.EngValue}C(Addr={InhTempTag?.PVModel?.Address}), " +
+ $"TxvP={TxvFrPressTag?.PVModel?.EngValue}BarA(Addr={TxvFrPressTag?.PVModel?.Address}), TxvT={TxvFrTempTag?.PVModel?.EngValue}C(Addr={TxvFrTempTag?.PVModel?.Address}), " +
+ $"GasPreValveP={GasPreValvePressTag?.PVModel?.EngValue}BarA(Addr={GasPreValvePressTag?.PVModel?.Address}), GasPreValveT={GasPreValveTempTag?.PVModel?.EngValue}C(Addr={GasPreValveTempTag?.PVModel?.Address})";
+
+ Debug.WriteLine(msg);
+ try
+ {
+ Console.WriteLine(msg);
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
///
/// PLC扫描线程
///
@@ -393,99 +459,87 @@ namespace CapMachine.Wpf.Services
//干度技术
- //气体流量kg/h=冷媒流量kg/h-液冷媒流量kg/h
- var GasFlowKgPerH = VRVTag.PVModel.EngValue - LiqRefFlowTag.PVModel.EngValue;//气体流量kg/h
+ var drynessResult = _enthalpyDrynessCalculator.Calculate(
+ new EnthalpyDrynessCalculator.Input(
+ GasPreValvePressTag.PVModel.EngValue,
+ GasPreValveTempTag.PVModel.EngValue,
+ TxvFrPressTag.PVModel.EngValue,
+ TxvFrTempTag.PVModel.EngValue,
+ InhPressTag.PVModel.EngValue,
+ VRVTag.PVModel.EngValue,
+ LiqRefFlowTag.PVModel.EngValue,
+ LubeFlowTag.PVModel.EngValue));
- //摩尔质量(kg/mol)
- var molarMassKgPerMol = GetMolarMass();
-
- //定义气相质量焓 kJ/(kg·K)
- var Gas_kJkgK = 0.0;
-
- //步骤1:计算气路阀前气相焓h vap(单相气相)
- if (TryTPRHO_VaporDensity_ByTP_MPa_C(GasPreValvePressTag.PVModel.EngValue * 0.1, GasPreValveTempTag.PVModel.EngValue, out var D_molL, out var D_molLErr1))
+ if (drynessResult.IsDryness1Success)
{
- if (TryTHERM_VaporEntropy_ByTD(GasPreValveTempTag.PVModel.EngValue, D_molL, out var s_kJkgK, out var D_molLErr2))
- {
- // s_kJkgK 即为图片中的“气相质量熵 kJ/(kg·K)”
- Gas_kJkgK = s_kJkgK;
- }
- }
+ DrynessTag2Value = drynessResult.Dryness1_01 * 100.0;
- //定义液相质量焓 kJ/(kg·K)
- var Liquid_kJkg = 0.0;
-
- //步骤2:计算液路阀前气相焓hlig(单相液相) TxvFrTempTag 液体阀前温度 TxvFrPressTag 液体阀前压力
- // 1) 先求 D_liq
- if (TryTPRHO_LiquidDensity_ByTP_MPa_C(TxvFrPressTag.PVModel.EngValue * 0.1, TxvFrTempTag.PVModel.EngValue, out var D_liq_molL, out var D_liqErr1))
- {
- // 2) 再用 THERM 求 h_liq
- if (TryTHERM_LiquidEnthalpy_ByTD(TxvFrTempTag.PVModel.EngValue, D_liq_molL, out var h_liq_kJkg, out var D_liqErr2))
- {
- // h_liq_kJkg 即为图片中的“液相质量焓 h_liq kJ/kg”
- Liquid_kJkg = h_liq_kJkg;
- }
- else
- {
- // 处理 err2
- }
+ //if (drynessResult.Dryness1_01 <= 0)
+ //{
+ // LogDrynessSnapshotIfNeeded(
+ // "TryComputeDrynessByEnthalpy(SuccessButZero)",
+ // string.Empty,
+ // drynessResult.Dryness1_01,
+ // drynessResult.HMix1_kJkg,
+ // drynessResult.GasEnthalpy_kJkg,
+ // drynessResult.LiquidEnthalpy_kJkg,
+ // drynessResult.SatLiquidEnthalpy_kJkg,
+ // drynessResult.SatVaporEnthalpy_kJkg,
+ // drynessResult.GasFlowKgPerH,
+ // 0,
+ // LiqRefFlowTag.PVModel.EngValue);
+ //}
}
else
{
- // 处理 err1
+ //LogDrynessSnapshotIfNeeded(
+ // "TryComputeDrynessByEnthalpy(Fail)",
+ // drynessResult.Error1,
+ // double.NaN,
+ // double.NaN,
+ // drynessResult.GasEnthalpy_kJkg,
+ // drynessResult.LiquidEnthalpy_kJkg,
+ // drynessResult.SatLiquidEnthalpy_kJkg,
+ // drynessResult.SatVaporEnthalpy_kJkg,
+ // drynessResult.GasFlowKgPerH,
+ // 0,
+ // LiqRefFlowTag.PVModel.EngValue);
}
- //定义饱和液质量焓hl kJ/kg
- var Liquid_h_liq = 0.0;
- //定义饱和气质量焓hl k)/kg
- var Gas_h_vap = 0.0;
-
- if (TryGetSaturationLiquidEnthalpy_ByP_MPa(InhPressTag.PVModel.EngValue * 0.1, out var h_liq, out var h_liqErr1) &&
- TryGetSaturationVaporEnthalpy_ByP_MPa(InhPressTag.PVModel.EngValue * 0.1, out var h_vap, out var h_vapErr2))
+ if (drynessResult.IsDryness2Success)
{
- // h_liq / h_vap 即为图片右侧的两个“饱和液/饱和气 质量焓 kJ/kg”
- Liquid_h_liq = h_liq;
- Gas_h_vap = h_vap;
- }
-
- // 气相焓 h_vap_kJkg(由 TPRHO 气相 + THERM)
- // 液相焓 h_liq_kJkg(由 TPRHO 液相 + THERM)
- // 饱和液 / 气焓 h_l / h_v(由 SATP +THERM)
- //气/液质量流量 mg/ml
- if (TryComputeDrynessByEnthalpy(
- Gas_kJkgK, Liquid_kJkg,//气相质量焓 h vap [k/kg] 液相质量焓 h liq [kJ/kg]
- GasFlowKgPerH, LiqRefFlowTag.PVModel.EngValue,//气体质量流量 mg [kg/h] 液体质量流量 ml [kg/h]
- Liquid_h_liq, Gas_h_vap, //饱和液质量焓 h liq [kJ/kg] 饱和气质量焓 h vap [kJ/kg]
- out var GasValue, out var hMix, out var err))
- {
- DrynessTag2Value = GasValue * 100.0;
- // x 为最终干度 [0..1],hMix 为混合后比焓
+ DrynessTag.PVModel.EngValue = drynessResult.Dryness2_01 * 100.0;
+ //if (drynessResult.Dryness2_01 <= 0)
+ //{
+ // LogDrynessSnapshotIfNeeded(
+ // "TryComputeDrynessByEnthalpy2(SuccessButZero)",
+ // string.Empty,
+ // drynessResult.Dryness2_01,
+ // drynessResult.HMix2_kJkg,
+ // drynessResult.GasEnthalpy_kJkg,
+ // drynessResult.LiquidEnthalpy_kJkg,
+ // drynessResult.SatLiquidEnthalpy_kJkg,
+ // drynessResult.SatVaporEnthalpy_kJkg,
+ // drynessResult.GasFlowKgPerH,
+ // LubeFlowTag.PVModel.EngValue,
+ // LiqRefFlowTag.PVModel.EngValue);
+ //}
}
else
{
- // 处理 err
- }
-
- //计算干度2
- // 气相焓 h_vap_kJkg(由 TPRHO 气相 + THERM)
- // 液相焓 h_liq_kJkg(由 TPRHO 液相 + THERM)
- // 饱和液 / 气焓 h_l / h_v(由 SATP +THERM)
- //气/液质量流量 mg/ml
- //***************** 经过验证,当前方法是正确 *****************
- if (TryComputeDrynessByEnthalpy2(
- Gas_kJkgK, Liquid_kJkg,//气相质量焓 h vap [k/kg] 液相质量焓 h liq [kJ/kg]
- GasFlowKgPerH, LubeFlowTag.PVModel.EngValue, //气体质量流量 mg [kg/h] 润滑油流量
- LiqRefFlowTag.PVModel.EngValue,// 液体质量流量 ml [kg/h]
- Liquid_h_liq, Gas_h_vap, //饱和液质量焓 h liq [kJ/kg] 饱和气质量焓 h vap [kJ/kg]
- out var GasValue2, out var hMix2, out var err2))
- {
- // x 为最终干度 [0..1],hMix 为混合后比焓
- DrynessTag.PVModel.EngValue = GasValue2 * 100.0;
- }
- else
- {
- // 处理 err2
+ //LogDrynessSnapshotIfNeeded(
+ // "TryComputeDrynessByEnthalpy2(Fail)",
+ // drynessResult.Error2,
+ // double.NaN,
+ // double.NaN,
+ // drynessResult.GasEnthalpy_kJkg,
+ // drynessResult.LiquidEnthalpy_kJkg,
+ // drynessResult.SatLiquidEnthalpy_kJkg,
+ // drynessResult.SatVaporEnthalpy_kJkg,
+ // drynessResult.GasFlowKgPerH,
+ // LubeFlowTag.PVModel.EngValue,
+ // LiqRefFlowTag.PVModel.EngValue);
}
if (TryUpdateThermodynamicSixResults(out var thermoErr))
@@ -714,6 +768,40 @@ namespace CapMachine.Wpf.Services
}
+ public bool TryTHERM_VaporEnthalpy_ByTD(
+ double temperatureC,
+ double densityMolPerL,
+ out double h_vap_kJ_per_kg,
+ out string error)
+ {
+ h_vap_kJ_per_kg = double.NaN;
+ error = string.Empty;
+
+ double tK = temperatureC + 273.15; // K
+ double D = densityMolPerL; // mol/L
+
+ double[] x = new double[20];
+ x[0] = 1.0;
+
+ double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0;
+
+ double molarMassKgPerMol = GetMolarMass();
+ if (molarMassKgPerMol <= 0)
+ {
+ error = "无效的摩尔质量";
+ return false;
+ }
+
+ lock (_refpropLock)
+ {
+ IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt);
+ }
+
+ h_vap_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001;
+ return true;
+ }
+
+
///
///
/// 计算液路阀前液相密度D_liq(单相液相) 液相密度D_liq mol/L
@@ -952,170 +1040,6 @@ namespace CapMachine.Wpf.Services
return TryTHERM_Enthalpy_kJkg_ByT_K_D(tSatK, Dv, out h_vap_kJkg, out error);
}
- ///
- /// 按图片的最终流程计算干度:
- /// 1) 质量流量加权混合焓 h_mix = (h_vap*mg + h_liq*ml) / (mg + ml)
- /// 2) 干度 x = (h_mix - h_l) / (h_v - h_l),并限幅到 [0,1]
- ///
- /// 入参单位:
- /// - hVap_kJkg, hLiq_kJkg, hSatL_kJkg, hSatV_kJkg 均为 kJ/kg
- /// - mGas_kg_h, mLiq_kg_h 均为 kg/h
- /// 返回:true 表示成功,输出 x∈[0,1] 与 h_mix;false 返回 error
- ///
- /// 气相质量焓 h_vap [kJ/kg]
- /// 液相质量焓 h_liq [kJ/kg]
- /// 气体质量流量 mg [kg/h]
- /// 液体质量流量 ml [kg/h]
- /// 饱和液质量焓 h_l [kJ/kg]
- /// 饱和气质量焓 h_v [kJ/kg]
- /// 输出干度 x ∈ [0,1]
- /// 输出混合后总比焓 h_mix [kJ/kg]
- /// Err
- ///
- public bool TryComputeDrynessByEnthalpy(
- double hVap_kJkg, // 气相质量焓 h_vap [kJ/kg]
- double hLiq_kJkg, // 液相质量焓 h_liq [kJ/kg]
- double mGas_kg_h, // 气体质量流量 mg [kg/h]
- double mLiq_kg_h, // 液体质量流量 ml [kg/h]
- double hSatL_kJkg, // 饱和液质量焓 h_l [kJ/kg]
- double hSatV_kJkg, // 饱和气质量焓 h_v [kJ/kg]
- out double dryness, // 输出干度 x ∈ [0,1]
- out double hMix_kJkg, // 输出混合后总比焓 h_mix [kJ/kg]
- out string error)
- {
- dryness = double.NaN;
- hMix_kJkg = double.NaN;
- error = string.Empty;
-
- // 1) 合法性校验
- if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg))
- {
- error = "输入焓值存在 NaN";
- return false;
- }
- if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h))
- {
- error = "输入质量流量存在 NaN";
- return false;
- }
- // 负值处理:小于 0 视为 0(避免传感器噪声或符号错误影响)
- double mg = Math.Max(0.0, mGas_kg_h);
- double ml = Math.Max(0.0, mLiq_kg_h);
- double mSum = mg + ml;
- if (mSum <= 0)
- {
- error = "气液质量流量之和为 0,无法进行加权混合焓计算";
- return false;
- }
-
- // 2) 质量流量加权混合焓(严格按图片:上、下两路相加后除以总流量)
- hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum;
-
- // 3) 干度计算:x = (h_mix - h_l) / (h_v - h_l)
- double denom = (hSatV_kJkg - hSatL_kJkg);
- const double eps = 1e-9;
- if (Math.Abs(denom) < eps)
- {
- error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)";
- return false;
- }
-
- double x = (hMix_kJkg - hSatL_kJkg) / denom;
-
- // 4) 限幅到 [0,1]
- if (double.IsNaN(x) || double.IsInfinity(x))
- {
- error = "干度计算结果异常(NaN/Inf)";
- return false;
- }
- dryness = Math.Min(1.0, Math.Max(0.0, x));
- return true;
- }
-
-
- ///
- /// 按图片的最终流程计算干度2:
- /// 干度2的计算:临时用作为对比使用
- /// 1) 质量流量加权混合焓 h_mix = (h_vap*mg + h_liq*ml) / (mg + ml)
- /// 2) 干度 x = (h_mix - h_l) / (h_v - h_l),并限幅到 [0,1]
- ///
- /// 入参单位:
- /// - hVap_kJkg, hLiq_kJkg, hSatL_kJkg, hSatV_kJkg 均为 kJ/kg
- /// - mGas_kg_h, mLiq_kg_h 均为 kg/h
- /// 返回:true 表示成功,输出 x∈[0,1] 与 h_mix;false 返回 error
- ///
- /// 气相质量焓 h_vap [kJ/kg]
- /// 液相质量焓 h_liq [kJ/kg]
- /// 气体质量流量 mg [kg/h]
- /// 液体质量流量 ml [kg/h]
- /// 饱和液质量焓 h_l [kJ/kg]
- /// 饱和气质量焓 h_v [kJ/kg]
- /// 输出干度 x ∈ [0,1]
- /// 输出混合后总比焓 h_mix [kJ/kg]
- /// Err
- ///
- public bool TryComputeDrynessByEnthalpy2(
- double hVap_kJkg, // 气相质量焓 h_vap [kJ/kg]
- double hLiq_kJkg, // 液相质量焓 h_liq [kJ/kg]
- double mGas_kg_h, // 气体质量流量 mg [kg/h]
- double lubeFlow_kg_h, // 润滑油流量 mg [kg/h]
- double mLiq_kg_h, // 液体质量流量 ml [kg/h]
- double hSatL_kJkg, // 饱和液质量焓 h_l [kJ/kg]
- double hSatV_kJkg, // 饱和气质量焓 h_v [kJ/kg]
- out double dryness, // 输出干度 x ∈ [0,1]
- out double hMix_kJkg, // 输出混合后总比焓 h_mix [kJ/kg]
- out string error)
- {
- dryness = double.NaN;
- hMix_kJkg = double.NaN;
- error = string.Empty;
-
- // 1) 合法性校验
- if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg))
- {
- error = "输入焓值存在 NaN";
- return false;
- }
- if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h))
- {
- error = "输入质量流量存在 NaN";
- return false;
- }
- // 负值处理:小于 0 视为 0(避免传感器噪声或符号错误影响)
- //double mg1 = Math.Max(0.0, mGas_kg_h);
- double mg = Math.Max(0.0, mGas_kg_h) + Math.Max(0.0, lubeFlow_kg_h); // 这个是改动 气体流量再加上润滑油流量
- double ml = Math.Max(0.0, mLiq_kg_h);
- double mSum = mg + ml;
- if (mSum <= 0)
- {
- error = "气液质量流量之和为 0,无法进行加权混合焓计算";
- return false;
- }
-
- // 2) 质量流量加权混合焓(严格按图片:上、下两路相加后除以总流量)
- hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum;
-
- // 3) 干度计算:x = (h_mix - h_l) / (h_v - h_l)
- double denom = (hSatV_kJkg - hSatL_kJkg);
- const double eps = 1e-9;
- if (Math.Abs(denom) < eps)
- {
- error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)";
- return false;
- }
-
- double x = (hMix_kJkg - hSatL_kJkg) / denom;
-
- // 4) 限幅到 [0,1]
- if (double.IsNaN(x) || double.IsInfinity(x))
- {
- error = "干度计算结果异常(NaN/Inf)";
- return false;
- }
- dryness = Math.Min(1.0, Math.Max(0.0, x));
- return true;
- }
-