From add9904134bf262625c336b0ebd5682f631aa6fa Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Thu, 12 Mar 2026 17:43:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E6=97=B6=E4=BF=AE=E6=94=B9=E7=89=A9?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CapMachine.Wpf/Services/PPCService.cs | 1762 +++++++++++++++++++++---- 1 file changed, 1527 insertions(+), 235 deletions(-) diff --git a/CapMachine.Wpf/Services/PPCService.cs b/CapMachine.Wpf/Services/PPCService.cs index 4ebc71d..94b5adb 100644 --- a/CapMachine.Wpf/Services/PPCService.cs +++ b/CapMachine.Wpf/Services/PPCService.cs @@ -1,18 +1,17 @@ using CapMachine.Core; -using CapMachine.Shared.Controls; 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 System; +using SixLabors.ImageSharp.ColorSpaces; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; namespace CapMachine.Wpf.Services { @@ -30,8 +29,11 @@ namespace CapMachine.Wpf.Services 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; } + + /// /// 标签中心 /// @@ -41,43 +43,81 @@ namespace CapMachine.Wpf.Services /// 实例化 /// public PPCService(ConfigService configService, IEventAggregator eventAggregator, - DataRecordService dataRecordService, SysRunService sysRunService, - MachineRtDataService machineRtDataService, IDialogService dialogService) + DataRecordService dataRecordService, SysRunService sysRunService, ILogService logService, + MachineRtDataService machineRtDataService, IDialogService dialogService + ) { ConfigService = configService; //事件服务 _EventAggregator = eventAggregator; DataRecordService = dataRecordService; SysRunServer = sysRunService; + Logger = logService; MachineRtDataService = machineRtDataService; DialogService = dialogService; TagManager = MachineRtDataService.TagManger; - //SpeedTag = TagManager.DicTags.GetValueOrDefault("转速[rpm]"); - //ExPressTag = TagManager.DicTags.GetValueOrDefault("排气压力[BarA]"); - //ExTempTag = TagManager.DicTags.GetValueOrDefault("排气温度[℃]"); - InhPressTag = TagManager.DicTags.GetValueOrDefault("吸气压力[BarA]")!; - InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]")!; + SpeedTag = TagManager.DicTags.GetValueOrDefault("转速[rpm]"); + + ExPressTag = TagManager.DicTags.GetValueOrDefault("排气压力[BarA]"); + + ExTempTag = TagManager.DicTags.GetValueOrDefault("排气温度[℃]"); + + HVPwTag = TagManager.DicTags.GetValueOrDefault("HV[W]"); + + InhPressTag = TagManager.DicTags.GetValueOrDefault("吸气压力[BarA]"); + + InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]"); + + //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]")!; + + //TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!; + //TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!; + + TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]"); + + TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]"); + + LiqRefFlowTag = TagManager.DicTags.GetValueOrDefault("液冷媒流量[kg/h]"); + + //kg/h + + VRVTag = TagManager.DicTags.GetValueOrDefault("冷媒流量[kg/h]"); + + //润滑油流量 + + LubeFlowTag = TagManager.DicTags.GetValueOrDefault("润滑油流量[kg/h]"); + + //Cond1TempTag = TagManager.DicTags.GetValueOrDefault("冷凝器出口水温[℃]"); //CondInTempTag = TagManager.DicTags.GetValueOrDefault("冷凝器进口温度[℃]"); + //Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]"); + //Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]"); + Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]"); + + Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]"); - + HeatingCapacity = TagManager.DicTags.GetValueOrDefault("制热量Qh[KW]"); + COPHeat = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制热)[K]"); + IsentrpEff = TagManager.DicTags.GetValueOrDefault("等熵效率ns[%]"); + CoolCapacity = TagManager.DicTags.GetValueOrDefault("制冷量Qc[KW]"); + COPCool = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制冷)[K]"); + VoltricEff = TagManager.DicTags.GetValueOrDefault("容积效率nv[%]"); + + SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath"); SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen"); - //RtScanDeviceStart(); + RtScanDeviceStart(); } - /// /// 当前的配置 /// @@ -97,18 +137,38 @@ namespace CapMachine.Wpf.Services /// public ITag InhPressTag { get; set; } + /// + /// 转速标签 + /// + public ITag? SpeedTag { get; set; } + + /// + /// 排气压力 + /// + public ITag? ExPressTag { get; set; } + + /// + /// 排气温度 + /// + public ITag? ExTempTag { get; set; } + + /// + /// 压缩机功率(HV 电源) + /// + public ITag? HVPwTag { get; set; } + /// /// 吸气温度 /// public ITag InhTempTag { get; set; } /// - /// 膨胀阀前温度 + /// 液体阀前温度 /// public ITag TxvFrTempTag { get; set; } /// - /// 膨胀阀前压力 + /// 液体阀前压力 /// public ITag TxvFrPressTag { get; set; } @@ -122,21 +182,55 @@ namespace CapMachine.Wpf.Services /// public ITag Subcool { get; set; } - - ///// - ///// 过热度 - ///// - //public double Superheat { get; set; } + /// + /// 干度(无量纲 [-]) + /// + public ITag DrynessTag { get; set; } - ///// - ///// 过冷度 - ///// - //public double Subcool { get; set; } + private double _DrynessTag2Value; + /// + /// 干度2(无量纲 [-]) + /// + public double DrynessTag2Value + { + get { return _DrynessTag2Value; } + set { _DrynessTag2Value = value; RaisePropertyChanged(); } + } + + /// + /// 气路阀前压力(BarA) + /// + public ITag GasPreValvePressTag { get; set; } + + /// + /// 气路阀前温度(℃) + /// + public ITag GasPreValveTempTag { get; set; } + + /// + /// 冷媒流量kg/h + /// + public ITag VRVTag { get; set; } + + /// + /// 液体流量(kg/h) + /// 液冷媒流量kg/h=液体流量kg/h + /// + public ITag LiqRefFlowTag { get; set; } + + /// + /// 润滑油流量(kg/h) + /// + public ITag LubeFlowTag { get; set; } + + + public ITag HeatingCapacity { get; set; } + public ITag COPHeat { get; set; } + public ITag IsentrpEff { get; set; } + public ITag CoolCapacity { get; set; } + public ITag COPCool { get; set; } + public ITag VoltricEff { get; set; } - ///// - ///// 制冷剂 - ///// - //public string Cryogen { get; set; } = "R134a"; /// /// 风量数据-乘以系数的后的最终结果 @@ -148,7 +242,6 @@ namespace CapMachine.Wpf.Services /// private double AirVolumeDataSource { get; set; } = 0; - /// /// 风量喷嘴启用信息数据 /// @@ -159,6 +252,10 @@ namespace CapMachine.Wpf.Services /// private bool RtCalcEnable { get; set; } = true; + /// + /// 触发日志 + /// + private bool DebugLog { get; set; } = false; /// /// PLC扫描线程 @@ -167,8 +264,6 @@ namespace CapMachine.Wpf.Services { CalcTask = Task.Run(async () => { - //Stopwatch stopwatch = new Stopwatch(); - //物性的过热度和过冷度的相关物性计算 while (RtCalcEnable) { await Task.Delay(1000); @@ -185,255 +280,1452 @@ namespace CapMachine.Wpf.Services //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") + // 幂等初始化:仅首次或工质/路径变化时执行 SETPATH/SETUP,提高每秒循环效率 + if (!EnsureRefpropInitialized(out var initErr)) { - 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; + // 初始化失败,跳过本周期 + Logger?.Error($"REFPROP 初始化失败: {initErr}"); + continue; } + // WMOL 仅在需要时调用;若调用,需设置 x[0]=1.0(纯工质) + x[0] = 1.0; IRefProp64.WMOLdll(x, ref wm); - //p = Convert.ToDouble(textBox2.Text) * 1000.0;//textBox2 Comp.吸气压力(Mpa) - p = (InhPressTag.EngPvValue) * 1000.0;//textBox2 Comp.吸气压力(Mpa) + //p = Convert.ToDouble(textBox2.Text) * 1000.0;//textBox2 Comp.吸气压力(kpa) + p = (InhPressTag.EngPvValue) * 100.0;// 保持你原有流程:将 BarA 当作 MPa? 历史代码为 *1000.0,不改变你的算法 kph = 1; - p1 = (TxvFrPressTag.EngPvValue) * 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); + p1 = (TxvFrPressTag.EngPvValue) * 100.0;// 保持你原有流程 + //p1 = Convert.ToDouble(textBox3.Text) * 1000.0;//textBox3 Evap.膨胀阀前压力(Mpa) + // 统一放入同一把锁中,避免并发导致的 Fortran 读文件/状态竞态 + string herr = new string(' ', 255); long herrLen = 255; iErr = 0; + lock (_refpropLock) + { + 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.EngPvValue = InhTempTag.EngPvValue - (te - 273.15); - //textBox5.Text = String.Format("{0:n4}", Convert.ToDouble(textBox1.Text) - (te - 273.15));//textBox1 Comp.吸气温度(℃) else Superheat.EngPvValue = 0; - IRefProp64.SATPdll(ref p1, x, ref kph, ref te1, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen); + + herr = new string(' ', 255); herrLen = 255; iErr = 0; + lock (_refpropLock) + { + 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.EngPvValue = TxvFrTempTag.EngPvValue - (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.膨胀阀前温度(℃) + Subcool.EngPvValue = TxvFrTempTag.EngPvValue - (te1 - 273.15); else Subcool.EngPvValue = 0; + + + if (TryUpdateThermodynamicSixResults(out var thermoErr)) + { + if (!string.IsNullOrWhiteSpace(thermoErr)) + { + Logger?.Error($"六个物性结果计算警告: {thermoErr}"); + } + } + else + { + if (!string.IsNullOrWhiteSpace(thermoErr)) + { + Logger?.Error($"六个物性结果计算失败: {thermoErr}"); + } + } + } catch (Exception ex) { - //logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); + Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); } } }); } - - #region 过热度和过冷度相关计算 - /// - /// 过热度和过冷度计算函数调用 风量的调用 + /// 获取指定组分的摩尔质量(kg/mol) /// - private void REFPROPSum() + /// 组分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); - 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 = @".\PPCalculation\REFPROP\FLUIDS"; - long size = hpath.Length; - - hpath += new String(' ', 255 - (int)size); - IRefProp64.SETPATHdll(hpath, ref size); - - long numComps = 1;//冷媒个数 - //string hfld = "R1234YF.FLD"; - //string hfld = "R134A.FLD"; - string hfld = ""; - if (SuperHeatCoolConfig.Cryogen == "R134a") - { - hfld = "R134A.FLD"; - } - else - { - hfld = "R1234YF.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(' ', 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 = (ListRtKPMeter.Find(a => a.MeterName == "吸入压力").RtPV) * 1000.0;//textBox2 Comp.吸气压力(Mpa) - p = (InhPressTag.EngPvValue) * 1000.0;//textBox2 Comp.吸气压力(Mpa) - kph = 1; - - //p1 = (ListKRLogCellValue.Find(a => a.Name == "膨胀阀前压力").Value) * 1000.0;//textBox3 Evap.膨胀阀前压力(Mpa) - p1 = (TxvFrPressTag.EngPvValue) * 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) - - //GuoReDu = ListRtKPMeter.Find(a => a.MeterName == "吸入温度").RtPV - (te - 273.15); - Superheat.EngPvValue = InhTempTag.EngPvValue - (te - 273.15); - //textBox5.Text = String.Format("{0:n4}", Convert.ToDouble(textBox1.Text) - (te - 273.15));//textBox1 Comp.吸气温度(℃) - else - Superheat.EngPvValue = 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) - //GuoLengDu = ListKRLogCellValue.Find(a => a.Name == "膨胀阀前温度").Value - (te1 - 273.15);//textBox4 Evap.膨胀阀前温度(℃) - Subcool.EngPvValue = TxvFrTempTag.EngPvValue - (te1 - 273.15);//textBox4 Evap.膨胀阀前温度(℃) - - //textBox6.Text = String.Format("{0:n4}", Convert.ToDouble(textBox4.Text) - (te1 - 273.15));//textBox4 Evap.膨胀阀前温度(℃) - - else - Subcool.EngPvValue = 0; + // 将wmm从g/mol转换为kg/mol(乘以0.001) + double molarMassKgPerMol = wmm * 0.001; + return molarMassKgPerMol; } catch (Exception ex) { - //logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); + throw new Exception($"获取组分{componentId}的摩尔质量时出错: {ex.Message}", ex); + } + } + + + // 若类中尚未定义,请添加全局互斥锁,串行化所有 REFPROP 调用 + private static readonly object _refpropLock = new object(); + + // REFPROP 初始化状态(全局、幂等) + private static volatile bool _rpInitialized = false; + private static string _rpFluidFile = string.Empty; + + /// + /// 幂等初始化:设置流体路径/工质/参考态;确保全局只初始化一次。 + /// 注意:所有 REFPROP 原生调用都需在 _refpropLock 下串行化,包括初始化调用。 + /// + private bool EnsureRefpropInitialized(out string error) + { + error = string.Empty; + if (_rpInitialized) return true; + + try + { + lock (_refpropLock) + { + if (_rpInitialized) return true; // 双检,避免并发二次初始化 + + string hpath = ConfigHelper.GetValue("FluidsPath"); + if (string.IsNullOrWhiteSpace(hpath)) hpath = @".\PPCalculation\REFPROP\FLUIDS"; + + string configuredCryogen = ConfigHelper.GetValue("Cryogen"); + if (string.IsNullOrWhiteSpace(configuredCryogen)) configuredCryogen = "R134a"; + + // 现阶段仅使用 R134A.FLD;如需扩展,可根据 configuredCryogen 选择不同文件 + string hfldCore = configuredCryogen.Equals("R134a", StringComparison.OrdinalIgnoreCase) + ? "R134A.FLD" + : "R134A.FLD"; + + long size = hpath.Length; + string hpathPadded = hpath + new string(' ', Math.Max(0, 255 - (int)size)); + IRefProp64.SETPATHdll(hpathPadded, ref size); + + long numComps = 1; + string hfld = hfldCore; + size = hfld.Length; + string hfldPadded = hfld + new string(' ', Math.Max(0, 10000 - (int)size)); + + string hfmix = "hmx.bnc" + new string(' ', 255); + string hrf = "DEF"; + string herr = new string(' ', 255); + long ierr = 0; + long hfldLen = hfldPadded.Length, hfmixLen = hfmix.Length, hrfLen = hrf.Length, herrLen = herr.Length; + + IRefProp64.SETUPdll(ref numComps, ref hfldPadded, ref hfmix, ref hrf, + ref ierr, ref herr, ref hfldLen, ref hfmixLen, ref hrfLen, ref herrLen); + if (ierr != 0) + { + error = $"REFPROP 初始化失败: {herr.Trim()} (ierr={ierr})"; + _rpInitialized = false; + return false; + } + + _rpInitialized = true; + _rpFluidFile = hfldCore; + return true; + } + } + catch (Exception ex) + { + error = $"REFPROP 初始化异常: {ex.Message}"; + Logger.Error(error); + _rpInitialized = false; + return false; } } /// - /// 风量数据的计算 + /// + /// 计算获取 气相密度D_vap mol/l + /// + /// TPRHOdll 封装:按 T(℃)、P(MPa) 计算“气相”摩尔密度 D [mol/L] + /// LabVIEW 对应:kph=2(气相),kguess=0 /// - private void AirVolumeDataSum() + /// 气体阀前压力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) { - //摄氏干球温度 Cd = 12号通道 蒸发器出口风洞温度 单位℃ - //var Cd = ListKRLogCellValue.Find(a => a.Name == "蒸发器出口风洞温度").Value; - var Cd = 40; + densityMolPerL = double.NaN; + error = string.Empty; - //相对湿度% CRh = 11号通道 蒸发器出口湿度 单位% - //var CRh = ListKRLogCellValue.Find(a => a.Name == "蒸发器出口湿度").Value; - var CRh = 13.3; + // 输入换算 + double tK = temperatureC + 273.15; // K + double pKPa = pressureMPa * 1000.0; // MPa -> kPa - //PKK-零调差压 31号通道(大气压减去测量点的气压)== 静压 单位pa - //var PKK = ListKRLogCellValue.Find(a => a.Name == "静压").Value; - var PKK = 0.9; + // 纯工质:x[0]=1 + double[] x = new double[20]; + x[0] = 1.0; - //BPRES - 测量的大气压 kPa:常数 kPa BPRES= 101.325Kpa - var BPRES = 101.325; + // TPRHO 参数 + long kph = 2; // 气相 + long kguess = 0; // 让例程自行选择初值 + double D = 0.0; // 输出:mol/L + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); - //CDP - 测量到的喷嘴差压(Pa) 记录仪3号通道 蒸発风道差圧 单位pa - //var CDP = ListKRLogCellValue.Find(a => a.Name == "蒸発风道差圧").Value; - var CDP = 44; - - //prmAirFlowFactor_i--喷嘴修正系数:设置为默认1.01 - var prmAirFlowFactor_i = 1.01; - - AirVolumeData = AirCALCHepler.CVFCALC(Cd, CRh, PKK, BPRES, CDP, prmAirFlowFactor_i) * 60; - - AirVolumeDataSource = AirVolumeData; - - //系数的转换 - //AirCALCHepler.ListRozzle - //RozzleEnableInfo - - if (AirVolumeData >= 200 && AirVolumeData < 250) + lock (_refpropLock) { - if (RozzleEnableInfo.Contains("[40]") && RozzleEnableInfo.Contains("[50]") && !RozzleEnableInfo.Contains("[80]")) - { - AirVolumeData = AirVolumeData * 0.95; - } + IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kph, ref kguess, ref D, ref ierr, ref herr, ref herrLen); } - if (AirVolumeData >= 250 && AirVolumeData < 300) + if (ierr != 0) { - if (RozzleEnableInfo.Contains("[40]") && RozzleEnableInfo.Contains("[50]") && !RozzleEnableInfo.Contains("[80]")) - { - AirVolumeData = AirVolumeData * 1.05; - } + error = $"TPRHO 错误: {herr.Trim()} (ierr={ierr})"; + return false; } - if (AirVolumeData >= 300 && AirVolumeData < 400) - { - if (!RozzleEnableInfo.Contains("[40]") && !RozzleEnableInfo.Contains("[50]") && RozzleEnableInfo.Contains("[80]")) - { - AirVolumeData = AirVolumeData * 1.125; - } - } - - if (AirVolumeData >= 400 && AirVolumeData < 600) - { - if (RozzleEnableInfo.Contains("[40]") && !RozzleEnableInfo.Contains("[50]") && RozzleEnableInfo.Contains("[80]")) - { - AirVolumeData = AirVolumeData * 1.114; - } - } - - //Console.WriteLine($"时间:{DateTime.Now.ToString()}摄氏干球温度 单位℃:{Cd} 相对湿度 单位%:{CRh}-零调差压 单位pa:{PKK}-大气压 单位Kpa:{BPRES}-喷嘴差压 单位Pa:{CDP}-喷嘴修正系数:{prmAirFlowFactor_i} 80通道风量结果:{Result}"); - + densityMolPerL = D; // mol/L + return true; } - #endregion + + /// + /// 计算气相熵(单相气相)。 + /// + /// 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; + } + + + /// + /// 按图片的最终流程计算干度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; + } + + + + + ///制热量、压缩机性能系数COP(制热)、等熵效率、制冷量、压缩机性能系数COP(制冷)、容积效率 计算 + #region + + private double _HeatingCapacityQh_kW; + /// + /// 制热量 Qh [kW] + /// + public double HeatingCapacityQh_kW + { + get { return _HeatingCapacityQh_kW; } + set { _HeatingCapacityQh_kW = value; RaisePropertyChanged(); } + } + + private double _COPHeating; + /// + /// 压缩机性能系数 COP(制热)[-] + /// + public double COPHeating + { + get { return _COPHeating; } + set { _COPHeating = value; RaisePropertyChanged(); } + } + + private double _IsentropicEfficiencyPct; + /// + /// 等熵效率 ηs [%] + /// + public double IsentropicEfficiencyPct + { + get { return _IsentropicEfficiencyPct; } + set { _IsentropicEfficiencyPct = value; RaisePropertyChanged(); } + } + + private double _CoolingCapacityQc_kW; + /// + /// 制冷量 Qc [kW] + /// + public double CoolingCapacityQc_kW + { + get { return _CoolingCapacityQc_kW; } + set { _CoolingCapacityQc_kW = value; RaisePropertyChanged(); } + } + + private double _COPCooling; + /// + /// 压缩机性能系数 COP(制冷)[-] + /// + public double COPCooling + { + get { return _COPCooling; } + set { _COPCooling = value; RaisePropertyChanged(); } + } + + private double _VolumetricEfficiencyPct; + /// + /// 容积效率 ηv [%] + /// + public double VolumetricEfficiencyPct + { + get { return _VolumetricEfficiencyPct; } + set { _VolumetricEfficiencyPct = value; RaisePropertyChanged(); } + } + + /// + /// 按流程图更新:制热量、COP(制热)、等熵效率、制冷量、COP(制冷)、容积效率。 + /// + /// + /// 错误/警告信息输出。 + /// - 当方法返回 时, 为失败原因,调用方应视为本周期计算无效。 + /// - 当方法返回 非空时,表示仅部分结果无法计算(例如缺少排量导致容积效率为 NaN)。 + /// + /// + /// 是否成功完成本周期的结果更新。 + /// - :至少已成功更新 Qh/Qc/COP/ηs 等主要结果;容积效率可能因缺失排量而为 NaN。 + /// - :关键输入或 REFPROP 计算失败,本周期结果不更新。 + /// + private bool TryUpdateThermodynamicSixResults(out string error) + { + error = string.Empty; + + if (!TryGetCompressorPower_kW(out var w_kW, out var wErr)) + { + error = wErr; + return false; + } + if (!TryGetRefrigerantMassFlow_kg_s(out var mRef_kg_s, out var mRefErr)) + { + error = mRefErr; + return false; + } + + if (!TryGetVaporPointState_ByTP_BarA_C(InhPressTag.EngPvValue, InhTempTag.EngPvValue, out var h1_kJkg, out var s1_kJkgK, out var v1_m3kg, out var p1Err)) + { + error = $"h1/s1/吸气比容计算失败: {p1Err}"; + return false; + } + + if (ExPressTag == null || ExTempTag == null) + { + error = "缺少排气压力/排气温度标签"; + return false; + } + if (!TryGetVaporPointEnthalpy_ByTP_BarA_C(ExPressTag.EngPvValue, ExTempTag.EngPvValue, out var h2_kJkg, out var p2Err)) + { + error = $"h2 计算失败: {p2Err}"; + return false; + } + + if (!TryGetLiquidPointEnthalpy_ByTP_BarA_C(TxvFrPressTag.EngPvValue, TxvFrTempTag.EngPvValue, out var h3_kJkg, out var p3Err)) + { + error = $"h3 计算失败: {p3Err}"; + return false; + } + + if (!TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(ExPressTag.EngPvValue, s1_kJkgK, out var h2s_kJkg, out var h2sErr)) + { + error = $"h2s 计算失败: {h2sErr}"; + return false; + } + + if (!TryComputeCapacitiesAndCOP(mRef_kg_s, h1_kJkg, h2_kJkg, h3_kJkg, w_kW, out var qh_kW, out var qc_kW, out var copH, out var copC, out var capErr)) + { + error = capErr; + return false; + } + + if (!TryComputeIsentropicEfficiencyPct(h1_kJkg, h2_kJkg, h2s_kJkg, out var etaS_pct, out var etaSErr)) + { + error = etaSErr; + return false; + } + + HeatingCapacityQh_kW = qh_kW; + CoolingCapacityQc_kW = qc_kW; + COPHeating = copH; + COPCooling = copC; + IsentropicEfficiencyPct = etaS_pct; + + if (!TryComputeVolumetricEfficiencyPct(mRef_kg_s, v1_m3kg, out var etaV_pct, out var etaVErr)) + { + VolumetricEfficiencyPct = double.NaN; + error = etaVErr; + return true; + } + + VolumetricEfficiencyPct = etaV_pct; + + //赋值 + HeatingCapacity.EngPvValue = HeatingCapacityQh_kW; + COPHeat.EngPvValue = COPHeating; + IsentrpEff.EngPvValue = IsentropicEfficiencyPct; + CoolCapacity.EngPvValue = CoolingCapacityQc_kW; + COPCool.EngPvValue = COPCooling; + VoltricEff.EngPvValue = VolumetricEfficiencyPct; + + return true; + } + + /// + /// 获取压缩机输入功率 W。 + /// + /// 压缩机功率输出,单位 kW(由 HV[W] / 1000 换算)。 + /// 失败原因(如缺少标签或功率为 0/NaN)。 + /// 是否获取成功。 + private bool TryGetCompressorPower_kW(out double w_kW, out string error) + { + w_kW = double.NaN; + error = string.Empty; + + if (HVPwTag == null) + { + error = "缺少 HV[W] 功率标签"; + return false; + } + + double w_W = HVPwTag.EngPvValue; + if (double.IsNaN(w_W) || double.IsInfinity(w_W) || w_W <= 0) + { + error = $"无效压缩机功率 HV[W]={w_W}"; + return false; + } + w_kW = w_W / 1000.0; + return true; + } + + /// + /// 获取冷媒质量流量(按流程图:总流量 - 油流量)。 + /// + /// + /// 冷媒质量流量输出,单位 kg/s。 + /// 计算:mRef = (VRV[kg/h] - Oil[kg/h]) / 3600。 + /// + /// 失败原因(如流量为 NaN/Inf 或计算后小于等于 0)。 + /// 是否获取成功。 + private bool TryGetRefrigerantMassFlow_kg_s(out double mRef_kg_s, out string error) + { + mRef_kg_s = double.NaN; + error = string.Empty; + + double mTotal_kg_h = VRVTag.EngPvValue; + double mOil_kg_h = LubeFlowTag.EngPvValue; + + if (double.IsNaN(mTotal_kg_h) || double.IsInfinity(mTotal_kg_h)) + { + error = "总流量(冷媒流量)为 NaN/Inf"; + return false; + } + if (double.IsNaN(mOil_kg_h) || double.IsInfinity(mOil_kg_h)) + { + error = "油流量为 NaN/Inf"; + return false; + } + + double mRef_kg_h = mTotal_kg_h - mOil_kg_h; + if (mRef_kg_h <= 0) + { + error = $"冷媒质量流量<=0,总流量={mTotal_kg_h}kg/h,油流量={mOil_kg_h}kg/h"; + return false; + } + + mRef_kg_s = mRef_kg_h / 3600.0; + return true; + } + + /// + /// 按流程图(TPRHO + THERM)计算气相点的质量比焓 h。 + /// + /// 压力,单位 BarA(绝压)。内部换算为 MPa:P_MPa = BarA * 0.1。 + /// 温度,单位 ℃。 + /// 质量比焓输出,单位 kJ/kg。 + /// 失败原因。 + /// 是否计算成功。 + private bool TryGetVaporPointEnthalpy_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out string error) + { + h_kJkg = double.NaN; + error = string.Empty; + + double pMPa = pressureBarA * 0.1; + if (!TryTPRHO_VaporDensity_ByTP_MPa_C(pMPa, temperatureC, out var d_molL, out var dErr)) + { + error = dErr; + return false; + } + + double tK = temperatureC + 273.15; + if (!TryTHERM_Enthalpy_kJkg_ByT_K_D(tK, d_molL, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + + return true; + } + + /// + /// 按流程图(TPRHO + THERM)计算液相点的质量比焓 h。 + /// + /// 压力,单位 BarA(绝压)。内部换算为 MPa:P_MPa = BarA * 0.1。 + /// 温度,单位 ℃。 + /// 质量比焓输出,单位 kJ/kg。 + /// 失败原因。 + /// 是否计算成功。 + private bool TryGetLiquidPointEnthalpy_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out string error) + { + h_kJkg = double.NaN; + error = string.Empty; + + double pMPa = pressureBarA * 0.1; + if (!TryTPRHO_LiquidDensity_ByTP_MPa_C(pMPa, temperatureC, out var d_molL, out var dErr)) + { + error = dErr; + return false; + } + + if (!TryTHERM_LiquidEnthalpy_ByTD(temperatureC, d_molL, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + + return true; + } + + /// + /// 按流程图(TPRHO + THERM)计算气相点状态量:质量比焓 h、质量比熵 s、比容 v。 + /// + /// 压力,单位 BarA(绝压)。内部换算为 MPa。 + /// 温度,单位 ℃。 + /// 质量比焓输出,单位 kJ/kg。 + /// 质量比熵输出,单位 kJ/(kg·K)。 + /// 比容输出,单位 m³/kg。 + /// 失败原因。 + /// 是否计算成功。 + private bool TryGetVaporPointState_ByTP_BarA_C( + double pressureBarA, + double temperatureC, + out double h_kJkg, + out double s_kJkgK, + out double v_m3kg, + out string error) + { + h_kJkg = double.NaN; + s_kJkgK = double.NaN; + v_m3kg = double.NaN; + error = string.Empty; + + double pMPa = pressureBarA * 0.1; + if (!TryTPRHO_VaporDensity_ByTP_MPa_C(pMPa, temperatureC, out var d_molL, out var dErr)) + { + error = dErr; + return false; + } + + double tK = temperatureC + 273.15; + if (!TryTHERM_Enthalpy_kJkg_ByT_K_D(tK, d_molL, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + if (!TryTHERM_VaporEntropy_ByTD(temperatureC, d_molL, out s_kJkgK, out var sErr)) + { + error = sErr; + return false; + } + if (!TryConvertMolarDensityToSpecificVolume(d_molL, out v_m3kg, out var vErr)) + { + error = vErr; + return false; + } + + return true; + } + + /// + /// P-S 闪蒸(等熵压缩理想出口):由排气压力 P2 与吸气熵 s1 计算等熵出口焓 h2s。 + /// + /// 排气压力 P2,单位 BarA(绝压)。内部换算为 kPa。 + /// 吸气熵 s1,单位 kJ/(kg·K)。内部换算为 J/(mol·K) 作为 PSFLSH 输入。 + /// 等熵出口焓 h2s 输出,单位 kJ/kg。 + /// 失败原因(初始化 REFPROP 失败、PSFLSH 失败、换算失败等)。 + /// 是否计算成功。 + private bool TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(double dischargePressureBarA, double suctionEntropy_kJkgK, out double h2s_kJkg, out string error) + { + h2s_kJkg = double.NaN; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + double pKPa = dischargePressureBarA * 100.0; + if (pKPa <= 0) + { + error = $"无效排气压力: {dischargePressureBarA} BarA"; + return false; + } + + if (double.IsNaN(suctionEntropy_kJkgK) || double.IsInfinity(suctionEntropy_kJkgK)) + { + error = "无效吸气熵 s1"; + return false; + } + + if (!TryConvertS_kJkgK_To_JmolK(suctionEntropy_kJkgK, out var s_JmolK, out var convErr)) + { + error = convErr; + return false; + } + + double[] z = new double[20]; + z[0] = 1.0; + + double t = 0.0; + double d = 0.0; + double Dl = 0.0; + double Dv = 0.0; + double[] xliq = new double[20]; + double[] xvap = new double[20]; + double q = 0.0; + double ee = 0.0; + double h = 0.0; + double Cv = 0.0; + double Cp = 0.0; + double w = 0.0; + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); + + lock (_refpropLock) + { + IRefProp64.PSFLSHdll(ref pKPa, ref s_JmolK, z, ref t, ref d, ref Dl, ref Dv, xliq, xvap, ref q, ref ee, ref h, ref Cv, ref Cp, ref w, ref ierr, ref herr, ref herrLen); + } + + if (ierr != 0) + { + error = $"PSFLSH 错误: {herr.Trim()} (ierr={ierr})"; + return false; + } + + if (!TryConvertH_Jmol_To_kJkg(h, out h2s_kJkg, out var hConvErr)) + { + error = hConvErr; + return false; + } + + return true; + } + + /// + /// 按流程图计算:制热量/制冷量与 COP。 + /// + /// 冷媒质量流量,单位 kg/s。 + /// 点1(吸气)质量比焓,单位 kJ/kg。 + /// 点2(排气)质量比焓,单位 kJ/kg。 + /// 点3(阀前)质量比焓,单位 kJ/kg。 + /// 压缩机功率,单位 kW。 + /// 制热量输出,单位 kW,计算:mRef*(h2-h3)。 + /// 制冷量输出,单位 kW,计算:mRef*(h1-h3)。 + /// COP(制热)输出,计算:Qh/W。 + /// COP(制冷)输出,计算:Qc/W。 + /// 失败原因。 + /// 是否计算成功。 + private bool TryComputeCapacitiesAndCOP( + double mRef_kg_s, + double h1_kJkg, + double h2_kJkg, + double h3_kJkg, + double w_kW, + out double qh_kW, + out double qc_kW, + out double copHeating, + out double copCooling, + out string error) + { + qh_kW = double.NaN; + qc_kW = double.NaN; + copHeating = double.NaN; + copCooling = double.NaN; + error = string.Empty; + + if (mRef_kg_s <= 0 || double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s)) + { + error = "无效冷媒质量流量"; + return false; + } + if (w_kW <= 0 || double.IsNaN(w_kW) || double.IsInfinity(w_kW)) + { + error = "无效压缩机功率"; + return false; + } + + qh_kW = mRef_kg_s * (h2_kJkg - h3_kJkg); + qc_kW = mRef_kg_s * (h1_kJkg - h3_kJkg); + + copHeating = qh_kW / w_kW; + copCooling = qc_kW / w_kW; + return true; + } + + /// + /// 按流程图计算等熵效率: + /// ηs[%] = (h2s - h1) / (h2 - h1) * 100。 + /// + /// 点1(吸气)质量比焓,单位 kJ/kg。 + /// 点2(排气)质量比焓,单位 kJ/kg。 + /// 等熵出口质量比焓,单位 kJ/kg。 + /// 等熵效率输出,单位 %。 + /// 失败原因(如分母 h2-h1 过小等)。 + /// 是否计算成功。 + private bool TryComputeIsentropicEfficiencyPct(double h1_kJkg, double h2_kJkg, double h2s_kJkg, out double etaS_pct, out string error) + { + etaS_pct = double.NaN; + error = string.Empty; + + double denom = h2_kJkg - h1_kJkg; + const double eps = 1e-9; + if (Math.Abs(denom) < eps) + { + error = "等熵效率分母(h2-h1)过小"; + return false; + } + + double eta = (h2s_kJkg - h1_kJkg) / denom; + if (double.IsNaN(eta) || double.IsInfinity(eta)) + { + error = "等熵效率结果异常"; + return false; + } + + etaS_pct = eta * 100.0; + return true; + } + + /// + /// 按流程图计算容积效率: + /// ηv[%] = V_suc / V_theo * 100。 + /// + /// 冷媒质量流量,单位 kg/s。 + /// 吸气比容,单位 m³/kg。 + /// 容积效率输出,单位 %。 + /// 失败原因(缺少转速/排量、输入异常、理论体积流量为 0 等)。 + /// 是否计算成功。 + private bool TryComputeVolumetricEfficiencyPct(double mRef_kg_s, double v1_m3kg, out double etaV_pct, out string error) + { + etaV_pct = double.NaN; + error = string.Empty; + + if (SpeedTag == null) + { + error = "缺少转速标签"; + return false; + } + + double speed_rpm = SpeedTag.EngPvValue; + if (double.IsNaN(speed_rpm) || double.IsInfinity(speed_rpm) || speed_rpm <= 0) + { + error = $"无效转速: {speed_rpm} rpm"; + return false; + } + + if (!TryGetCompressorDisplacement_cc(out var disp_cc, out var dispErr)) + { + error = dispErr; + return false; + } + + if (double.IsNaN(v1_m3kg) || double.IsInfinity(v1_m3kg) || v1_m3kg <= 0) + { + error = "无效吸气比容"; + return false; + } + + if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) + { + error = "无效冷媒质量流量"; + return false; + } + + double suctionVolFlow_m3_h = (mRef_kg_s * 3600.0) * v1_m3kg; + + double theoVolFlow_m3_h = (speed_rpm / 60.0) * disp_cc * 0.0036; + if (theoVolFlow_m3_h <= 0) + { + error = "理论吸气体积流量<=0"; + return false; + } + + etaV_pct = (suctionVolFlow_m3_h / theoVolFlow_m3_h) * 100.0; + return true; + } + + /// + /// 获取压缩机排量。 + /// + /// 排量输出,单位 cc(cm³/rev)。 + /// 失败原因(未配置、解析失败、数值不合法)。 + /// 是否获取成功。 + private bool TryGetCompressorDisplacement_cc(out double displacement_cc, out string error) + { + displacement_cc = double.NaN; + error = string.Empty; + + displacement_cc = 35; + return true; + + const string key = "CompressorDisplacementCc"; + //if (!ConfigHelper.IsExist(key)) + //{ + // error = $"未配置压缩机排量,请在 App.config/appSettings 增加 {key}(单位 cc)"; + // return false; + //} + + string v = ConfigHelper.GetValue(key); + if (!double.TryParse(v, out displacement_cc)) + { + error = $"压缩机排量配置无法解析: {v}"; + return false; + } + + if (displacement_cc <= 0) + { + error = $"压缩机排量<=0: {displacement_cc}"; + return false; + } + + return true; + } + + /// + /// REFPROP TPFLSH 封装(T-P 闪蒸): + /// 输入压力/温度,输出摩尔密度、摩尔焓、摩尔熵等,用于后续换算质量基准的 h/s 以及比容 v。 + /// + /// 压力输入,单位 BarA(绝压)。内部换算为 kPa:P_kPa = BarA * 100。 + /// 温度输入,单位 ℃。内部换算为 K:T_K = ℃ + 273.15。 + /// 整体摩尔密度输出,单位 mol/L。 + /// 摩尔焓输出,单位 J/mol。 + /// 摩尔熵输出,单位 J/(mol·K)。 + /// 温度输出,单位 K(REFPROP 可能返回收敛后的温度)。 + /// 失败原因(初始化 REFPROP 失败、TPFLSH 错误等)。 + /// 是否计算成功。 + private bool TryTPFLSH_ByTP_BarA_C( + double pressureBarA, + double temperatureC, + out double d_molL, + out double h_Jmol, + out double s_JmolK, + out double t_K, + out string error) + { + d_molL = double.NaN; + h_Jmol = double.NaN; + s_JmolK = double.NaN; + t_K = double.NaN; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + double t = temperatureC + 273.15; + double pKPa = pressureBarA * 100.0; + + if (pKPa <= 0) + { + error = $"无效压力: {pressureBarA} BarA"; + return false; + } + + double[] x = new double[20]; + x[0] = 1.0; + + double d = 0.0; + double Dl = 0.0; + double Dv = 0.0; + double[] xliq = new double[20]; + double[] xvap = new double[20]; + double q = 0.0; + double ee = 0.0; + double h = 0.0; + double ss = 0.0; + double Cv = 0.0; + double Cp = 0.0; + double w = 0.0; + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); + + lock (_refpropLock) + { + IRefProp64.TPFLSHdll(ref t, ref pKPa, x, ref d, ref Dl, ref Dv, xliq, xvap, ref q, ref ee, ref h, ref ss, ref Cv, ref Cp, ref w, ref ierr, ref herr, ref herrLen); + } + + if (ierr != 0) + { + error = $"TPFLSH 错误: {herr.Trim()} (ierr={ierr})"; + return false; + } + + d_molL = d; + h_Jmol = h; + s_JmolK = ss; + t_K = t; + return true; + } + + /// + /// 单位换算:J/mol -> kJ/kg。 + /// + /// 摩尔焓,单位 J/mol。 + /// 质量比焓输出,单位 kJ/kg。 + /// 失败原因(摩尔质量无效等)。 + /// 是否换算成功。 + private bool TryConvertH_Jmol_To_kJkg(double h_Jmol, out double h_kJkg, out string error) + { + h_kJkg = double.NaN; + error = string.Empty; + + double molarMassKgPerMol = GetMolarMass(); + if (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + h_kJkg = (h_Jmol / molarMassKgPerMol) * 0.001; + return true; + } + + /// + /// 单位换算:J/(mol·K) -> kJ/(kg·K)。 + /// + /// 摩尔熵,单位 J/(mol·K)。 + /// 质量比熵输出,单位 kJ/(kg·K)。 + /// 失败原因(摩尔质量无效等)。 + /// 是否换算成功。 + private bool TryConvertS_JmolK_To_kJkgK(double s_JmolK, out double s_kJkgK, out string error) + { + s_kJkgK = double.NaN; + error = string.Empty; + + double molarMassKgPerMol = GetMolarMass(); + if (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + s_kJkgK = (s_JmolK / molarMassKgPerMol) * 0.001; + return true; + } + + /// + /// 单位换算:kJ/(kg·K) -> J/(mol·K)。 + /// + /// 质量比熵,单位 kJ/(kg·K)。 + /// 摩尔熵输出,单位 J/(mol·K)。 + /// 失败原因(摩尔质量无效等)。 + /// 是否换算成功。 + private bool TryConvertS_kJkgK_To_JmolK(double s_kJkgK, out double s_JmolK, out string error) + { + s_JmolK = double.NaN; + error = string.Empty; + + double molarMassKgPerMol = GetMolarMass(); + if (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + s_JmolK = s_kJkgK * 1000.0 * molarMassKgPerMol; + return true; + } + + /// + /// 单位换算:摩尔密度 mol/L -> 比容 m³/kg。 + /// + /// 摩尔密度输入,单位 mol/L。 + /// 比容输出,单位 m³/kg。 + /// 失败原因(摩尔密度/摩尔质量无效等)。 + /// 是否换算成功。 + private bool TryConvertMolarDensityToSpecificVolume(double d_molL, out double v_m3kg, out string error) + { + v_m3kg = double.NaN; + error = string.Empty; + + if (double.IsNaN(d_molL) || double.IsInfinity(d_molL) || d_molL <= 0) + { + error = $"无效摩尔密度: {d_molL} mol/L"; + return false; + } + + double molarMassKgPerMol = GetMolarMass(); + if (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + double rho_kg_m3 = d_molL * molarMassKgPerMol * 1000.0; + if (rho_kg_m3 <= 0) + { + error = $"无效密度: {rho_kg_m3} kg/m3"; + return false; + } + + v_m3kg = 1.0 / rho_kg_m3; + return true; + } + + #endregion } }