using CapMachine.Core; using CapMachine.Wpf.Models.PPCalc; using CapMachine.Wpf.Models.Tag; using CapMachine.Wpf.PPCalculation; using ImTools; using MaterialDesignThemes.Wpf; using NLog; using NPOI.XWPF.UserModel; using Prism.Events; using Prism.Mvvm; using Prism.Services.Dialogs; using SixLabors.ImageSharp.ColorSpaces; using System.Collections.Generic; using System.Text; namespace CapMachine.Wpf.Services { /// /// 物性计算的服务 /// public class PPCService : BindableBase { /// /// 计算扫描 Task /// private static Task CalcTask { get; set; } public ConfigService ConfigService { get; } private IEventAggregator _EventAggregator { get; set; } public DataRecordService DataRecordService { get; } public SysRunService SysRunServer { get; } public ILogService Logger { get; } public MachineRtDataService MachineRtDataService { get; } public IDialogService DialogService { get; } private readonly IDrynessCalculator _drynessCalculator; /// /// 标签中心 /// public TagManager TagManager { get; set; } /// /// 实例化 /// public PPCService(ConfigService configService, IEventAggregator eventAggregator, DataRecordService dataRecordService, SysRunService sysRunService, ILogService logService, MachineRtDataService machineRtDataService, IDialogService dialogService, IDrynessCalculator drynessCalculator) { ConfigService = configService; //事件服务 _EventAggregator = eventAggregator; DataRecordService = dataRecordService; SysRunServer = sysRunService; Logger = logService; MachineRtDataService = machineRtDataService; DialogService = dialogService; _drynessCalculator = drynessCalculator; TagManager = MachineRtDataService.TagManger; //SpeedTag = TagManager.DicTags.GetValueOrDefault("转速[rpm]"); //ExPressTag = TagManager.DicTags.GetValueOrDefault("排气压力[BarA]"); //ExTempTag = TagManager.DicTags.GetValueOrDefault("排气温度[℃]"); if (TagManager.TryGetShortTagByName("吸气压力[BarA]", out ShortValueTag? InhPressShortControlTag)) { InhPressTag = InhPressShortControlTag!; } if (TagManager.TryGetShortTagByName("吸气温度[℃]", out ShortValueTag? InhTempShortControlTag)) { InhTempTag = InhTempShortControlTag!; } //InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]")!; //ComCapBusVolTag = TagManager.DicTags.GetValueOrDefault("通讯母线电压[V]"); //ComCapBusCurTag = TagManager.DicTags.GetValueOrDefault("通讯母线电流[A]"); //ComCapPwTag = TagManager.DicTags.GetValueOrDefault("通讯功率[W]"); //OS2TempTag = TagManager.DicTags.GetValueOrDefault("吸气混合器温度[℃]"); //TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!; //TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!; if (TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out ShortValueTag? TxvFrTempShortTag)) { TxvFrTempTag = TxvFrTempShortTag!; } if (TagManager.TryGetShortTagByName("膨胀阀前压力[BarA]", out ShortValueTag? TxvFrPressShortTag)) { TxvFrPressTag = TxvFrPressShortTag!; } if (TagManager.TryGetShortTagByName("液冷媒流量[kg/h]", out ShortValueTag? LiqRefFlowShortTag)) { LiqRefFlowTag = LiqRefFlowShortTag!; } //kg/h if (TagManager.TryGetShortTagByName("冷媒流量[kg/h]", out ShortValueTag? VRVShortTag)) { VRVTag = VRVShortTag!; } // 气路阀前 P/T(用于质量流量加权混合焓的计算,LabVIEW 流程) if (TagManager.TryGetShortTagByName("气路阀前压力[BarA]", out ShortValueTag? gasPreP)) { GasPreValvePressTag = gasPreP!; } if (TagManager.TryGetShortTagByName("气路阀前温度[℃]", out ShortValueTag? gasPreT)) { GasPreValveTempTag = gasPreT!; } //Cond1TempTag = TagManager.DicTags.GetValueOrDefault("冷凝器出口水温[℃]"); //CondInTempTag = TagManager.DicTags.GetValueOrDefault("冷凝器进口温度[℃]"); //Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]"); //Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]"); if (TagManager.TryGetShortTagByName("过热度[K]", out ShortValueTag? SuperheatShortTag)) { Superheat = SuperheatShortTag!; } if (TagManager.TryGetShortTagByName("过冷度[K]", out ShortValueTag? SubcoolShortTag)) { Subcool = SubcoolShortTag!; } // 干度标签(如不存在则仅跳过写入,不抛异常)。优先使用“干度[%]”,其次“干度[-]”。 if (TagManager.TryGetShortTagByName("干度[%]", out ShortValueTag? drynessPct)) { DrynessTag = drynessPct!; } SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath"); SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen"); RtScanDeviceStart(); } /// /// 当前的配置 /// public SuperHeatCoolConfigModel SuperHeatCoolConfig { get; set; } = new SuperHeatCoolConfigModel(); /// /// 保存配置信息 /// public void SaveSuperHeatCoolConfig() { ConfigHelper.SetValue("FluidsPath", SuperHeatCoolConfig.FluidsPath); ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen); } /// /// 吸气压力 /// public ShortValueTag InhPressTag { get; set; } /// /// 吸气温度 /// public ShortValueTag InhTempTag { get; set; } /// /// 液体阀前温度 /// public ShortValueTag TxvFrTempTag { get; set; } /// /// 液体阀前压力 /// public ShortValueTag TxvFrPressTag { get; set; } /// /// 过热度 /// public ShortValueTag Superheat { get; set; } /// /// 过冷度 /// public ShortValueTag Subcool { get; set; } /// /// 干度(无量纲 [-]) /// public ShortValueTag DrynessTag { get; set; } /// /// 气路阀前压力(BarA) /// public ShortValueTag GasPreValvePressTag { get; set; } /// /// 气路阀前温度(℃) /// public ShortValueTag GasPreValveTempTag { get; set; } /// /// 冷媒流量kg/h /// public ShortValueTag VRVTag { get; set; } /// /// 液体流量(kg/h) /// 液冷媒流量kg/h=液体流量kg/h /// public ShortValueTag LiqRefFlowTag { get; set; } /// /// 风量数据-乘以系数的后的最终结果 /// private double AirVolumeData { get; set; } = 0.0; /// /// 风量数据-公式计算的原始数据 /// private double AirVolumeDataSource { get; set; } = 0; /// /// 风量喷嘴启用信息数据 /// private string RozzleEnableInfo { get; set; } = ""; /// /// 启用计算 /// private bool RtCalcEnable { get; set; } = true; /// /// 触发日志 /// private bool DebugLog { get; set; } = false; /// /// PLC扫描线程 /// private void RtScanDeviceStart() { CalcTask = Task.Run(async () => { while (RtCalcEnable) { await Task.Delay(1000); try { long iErr, kph = 1; double te = 0.0, te1 = 0.0, p = 0.0, p1 = 0.0, d = 0.0, Dl = 0.0, Dv = 0.0, q = 0.0, h = 0.0, ee = 0.0, hh = 0.0, ss = 0.0, cp = 0.0, cv = 0.0, w = 0.0; double[] x = new double[20], xliq = new double[20], xvap = new double[20]; double[] xlkg = new double[20], xvkg = new double[20]; double tk = 0.0, wm = 0.0, prevDeltaH = 0.0;//prevDeltaH 未使用 //textBox5.Text = ""; string hpath = SuperHeatCoolConfig.FluidsPath; long size = hpath.Length; hpath += new String(' ', 255 - (int)size); IRefProp64.SETPATHdll(hpath, ref size); long numComps = 1;//冷媒个数 //string hfld = "R1234YF.FLD"; R1234YF string hfld = ""; if (SuperHeatCoolConfig.Cryogen == "R134a") { hfld = "R134A.FLD"; } else { hfld = "R134A.FLD"; } //string hfld = "R134A.FLD"; //string hfld = Convert.ToString(comboBox1.Text);//从控件获取数据 size = hfld.Length; hfld += new String(' ', 10000 - (int)size); //string hfmix = "hmx.bnc" + new String(' ', 255);//原来的 string hfmix = "hmx.bnc" + new String(' ', 255);//248 string hrf = "DEF"; string herr = new String(' ', 255); iErr = 0; long hfldLen = hfld.Length, hfmixLen = hfmix.Length, hrfLen = hrf.Length, herrLen = herr.Length; //numComps = -1; IRefProp64.SETUPdll(ref numComps, ref hfld, ref hfmix, ref hrf, ref iErr, ref herr, ref hfldLen, ref hfmixLen, ref hrfLen, ref herrLen); double version = iErr / 10000.0;//错误信息 if (iErr != 0) { //MessageBox.Show(herr, "RefProp SETUPdll() Error ", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } IRefProp64.WMOLdll(x, ref wm); //p = Convert.ToDouble(textBox2.Text) * 1000.0;//textBox2 Comp.吸气压力(Mpa) p = (InhPressTag.PVModel.EngValue) * 1000.0;//textBox2 Comp.吸气压力(Mpa) kph = 1; p1 = (TxvFrPressTag.PVModel.EngValue) * 1000.0;//textBox3 Evap.膨胀阀前压力(Mpa) //p1 = Convert.ToDouble(textBox3.Text) * 1000.0;//textBox3 Evap.膨胀阀前压力(Mpa) IRefProp64.SATPdll(ref p, x, ref kph, ref te, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen); if (iErr == 0) Superheat.PVModel.EngValue = InhTempTag.PVModel.EngValue - (te - 273.15); //textBox5.Text = String.Format("{0:n4}", Convert.ToDouble(textBox1.Text) - (te - 273.15));//textBox1 Comp.吸气温度(℃) else Superheat.PVModel.EngValue = 0; IRefProp64.SATPdll(ref p1, x, ref kph, ref te1, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen); if (iErr == 0) Subcool.PVModel.EngValue = TxvFrTempTag.PVModel.EngValue - (te1 - 273.15);//textBox4 Evap.膨胀阀前温度(℃) //GuoLengDu = (te1 - 273.15) - ListKRLogCellValue.Find(a => a.Name == "膨胀阀前温度").Value;//textBox4 Evap.膨胀阀前温度(℃) //textBox6.Text = String.Format("{0:n4}", Convert.ToDouble(textBox4.Text) - (te1 - 273.15));//textBox4 Evap.膨胀阀前温度(℃) else Subcool.PVModel.EngValue = 0; //气体流量kg/h=冷媒流量kg/h-液冷媒流量kg/h var GasFlowKgPerH = VRVTag.PVModel.EngValue - LiqRefFlowTag.PVModel.EngValue;//气体流量kg/h //摩尔质量(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, GasPreValveTempTag.PVModel.EngValue, out var D_molL, out var D_molLErr1)) { 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; } } //定义液相质量焓 kJ/(kg·K) var Liquid_kJkg = 0.0; //步骤2:计算液路阀前气相焓hlig(单相液相) TxvFrTempTag 液体阀前温度 TxvFrPressTag 液体阀前压力 // 1) 先求 D_liq if (TryTPRHO_LiquidDensity_ByTP_MPa_C(TxvFrPressTag.PVModel.EngValue, 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 } } else { // 处理 err1 } //定义饱和液质量焓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 * 10, out var h_liq, out var h_liqErr1) && TryGetSaturationVaporEnthalpy_ByP_MPa(InhPressTag.PVModel.EngValue * 10, out var h_vap, out var h_vapErr2)) { // 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)) { // x 为最终干度 [0..1],hMix 为混合后比焓 DrynessTag.PVModel.EngValue = GasValue; } else { // 处理 err } } catch (Exception ex) { Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); } } }); } /// /// 获取指定组分的摩尔质量(kg/mol) /// /// 组分ID(icomp) /// 摩尔质量(kg/mol) public static double GetMolarMass() { // 初始化所有变量,因为我们将使用ref传递 double wmm = 0, Trp = 0, Tnbpt = 0, Tc = 0, Pc = 0, Dc = 0, Zc = 0, acf = 0, dip = 0, Rgas = 0; long componentId = 1; try { // 调用INFOdll函数获取物性参数,使用ref IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas); // 将wmm从g/mol转换为kg/mol(乘以0.001) double molarMassKgPerMol = wmm * 0.001; return molarMassKgPerMol; } catch (Exception ex) { throw new Exception($"获取组分{componentId}的摩尔质量时出错: {ex.Message}", ex); } } // 若类中尚未定义,请添加全局互斥锁,串行化所有 REFPROP 调用 private static readonly object _refpropLock = new object(); /// /// /// 计算获取 气相密度D_vap mol/l /// /// TPRHOdll 封装:按 T(℃)、P(MPa) 计算“气相”摩尔密度 D [mol/L] /// LabVIEW 对应:kph=2(气相),kguess=0 /// /// 气体阀前压力mpa /// 气体阀前温度 C /// “气相”摩尔密度 D [mol/L] / 气相密度D_vap mol/l /// ERR /// public 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; // K double pKPa = pressureMPa * 1000.0; // MPa -> kPa // 纯工质:x[0]=1 double[] x = new double[20]; x[0] = 1.0; // TPRHO 参数 long kph = 2; // 气相 long kguess = 0; // 让例程自行选择初值 double D = 0.0; // 输出:mol/L 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; // mol/L return true; } /// /// 计算气路阀前气相焓h_vap(单相气相) /// /// THERMdll 封装:按 T(℃) 与上一步得到的 D[mol/L] 计算气相熵 s [kJ/(kg·K)] /// LabVIEW 对应:THERM(T, D) -> s[J/(mol·K)],再 /M[kg/mol] 并 *0.001 /// /// 气体阀前温度 C /// 气相密度D_vap mol/l /// /// /// public bool TryTHERM_VaporEntropy_ByTD( double temperatureC, double densityMolPerL, out double entropy_kJ_per_kgK, out string error) { entropy_kJ_per_kgK = double.NaN; error = string.Empty; // 输入换算 double tK = temperatureC + 273.15; // K double D = densityMolPerL; // mol/L // 纯工质:x[0]=1 double[] x = new double[20]; x[0] = 1.0; // THERM 输出 double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0; // 摩尔质量(kg/mol)——使用你已封装的 GetMolarMass() 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); // THERM 不返回 ierr,如输入越界会在上游 TPRHO 就失败 } // J/(mol·K) -> kJ/(kg·K):先除以 kg/mol,再乘 0.001 entropy_kJ_per_kgK = (sJmolK / molarMassKgPerMol) * 0.001; return true; } /// /// /// 计算液路阀前液相密度D_liq(单相液相) 液相密度D_liq mol/L /// /// TPRHOdll 封装(液相):按 T(℃)、P(MPa) 计算“液相”摩尔密度 D_liq [mol/L] /// LabVIEW 对应:kph=1(液相),kguess=0 /// /// /// /// /// /// public 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; // K double pKPa = pressureMPa * 1000.0; // MPa -> kPa // 纯工质:x[0]=1 double[] x = new double[20]; x[0] = 1.0; // TPRHO 参数 long kph = 1; // 液相 long kguess = 0; // 初值由例程选择 double D = 0.0; // 输出:mol/L 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; // mol/L return true; } /// /// 计算获取 液相质量焓h lig k/kg /// THERMdll 封装(液相):按 T(℃) 与 D_liq[mol/L] 计算液相质量焓 h_liq [kJ/kg] /// LabVIEW 对应:THERM(T, D) -> h[J/mol],再 /M[kg/mol] * 0.001 /// /// 液体阀前温度'℃ /// 液相密度D_liq mol/L /// 液相质量焓h lig k/kg /// Err /// public 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; // K double D = densityMolPerL; // mol/L // 纯工质:x[0]=1 double[] x = new double[20]; x[0] = 1.0; // THERM 输出 double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0; // 摩尔质量(kg/mol)——使用已封装的 GetMolarMass() 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); // THERM 不返回 ierr,越界一般在上游 TPRHO 即失败 } // J/mol -> kJ/kg:先除以 kg/mol,再乘 0.001 h_liq_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001; return true; } /// /// SATPdll 封装:由压力 P(MPa) 求饱和温度 Tsat[K]、饱和液/气摩尔密度 Dl/Dv [mol/L] /// LabVIEW 对应:P(MPa)*1000 -> kPa;kph=1(bubble) /// /// 王缩机吸气压力mpa /// 吸气压力下的饱和温度T_sat K /// 饱和液相密度Dl molL /// 饱和气相密度Dv mol/L /// /// public 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; // MPa -> kPa double[] x = new double[20]; x[0] = 1.0; long kph = 1; // 饱和液侧(bubble),纯工质下 dew/bubble 的 Tsat 一致 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] /// LabVIEW 对应:THERM(T, D) -> h[J/mol],再 /M[kg/mol] * 0.001 /// /// /// /// /// /// public 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; 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_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001; // J/mol -> kJ/kg return true; } /// /// 便捷:由压力 P(MPa) 直接得到“饱和液”质量比焓 h_liq[kJ/kg] /// 流程:SATP(P)->Tsat/Dl,再 THERM(Tsat, Dl) /// /// /// /// /// public 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] /// 流程:SATP(P)->Tsat/Dv,再 THERM(Tsat, Dv) /// /// /// /// /// public 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 /// 返回: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; } } }