diff --git a/CapMachine.Wpf/App.xaml.cs b/CapMachine.Wpf/App.xaml.cs index d44c3dc..4e0a516 100644 --- a/CapMachine.Wpf/App.xaml.cs +++ b/CapMachine.Wpf/App.xaml.cs @@ -308,8 +308,14 @@ namespace CapMachine.Wpf //给当前的全局异常捕捉服务使用 LogService = ContainerLocator.Container.Resolve(); LogService.Info("程序启动"); + base.OnInitialized(); + //#if DEBUG + //var ppc = Prism.Ioc.ContainerLocator.Container.Resolve(); + //ppc.DebugRunThermoValidationCase_FromScreenshot_20260423(); + //#endif + //#endregion //*******起始弹窗 * ******* diff --git a/CapMachine.Wpf/Models/CsvRecordModelMap.cs b/CapMachine.Wpf/Models/CsvRecordModelMap.cs index 047f9e2..0d2bdc8 100644 --- a/CapMachine.Wpf/Models/CsvRecordModelMap.cs +++ b/CapMachine.Wpf/Models/CsvRecordModelMap.cs @@ -36,13 +36,11 @@ namespace CapMachine.Wpf.Models Map(m => m.OS1Temp).Name("OS1温度[℃]"); Map(m => m.OS2Temp).Name("OS2温度[℃]"); Map(m => m.Cond2Temp).Name("COND2温度[℃]"); - Map(m => m.EVAPExpTemp).Name("EVAP出口温度[℃]"); Map(m => m.VRV).Name("冷媒流量[kg/h]"); Map(m => m.LubeFlow).Name("润滑油流量[kg/h]"); Map(m => m.ExTemp).Name("排气温度[℃]"); Map(m => m.TxvFrPress).Name("膨胀阀前压力[BarA]"); Map(m => m.TxvFrTemp).Name("膨胀阀前温度[℃]"); - Map(m => m.EVAPExpPress).Name("EVAP出口压力[BarA]"); Map(m => m.IntrplPress).Name("腔内压力[BarA]"); Map(m => m.CapSurfTemp).Name("压缩机表面温度[℃]"); Map(m => m.PTCFlow).Name("PTC流量[L/min]"); @@ -63,14 +61,12 @@ namespace CapMachine.Wpf.Models Map(m => m.ComPTCFlmTemp).Name("通讯PTC膜温[℃]"); Map(m => m.ComPTCMdTemp).Name("通讯PTC模块温度[℃]"); Map(m => m.LiqRefFlow).Name("液冷媒流量[kg/h]"); - Map(m => m.DryFlow).Name("干度流量[kg/h]"); Map(m => m.H5InhMixTemp).Name("H5吸气混合器温度[℃]"); Map(m => m.CONDCirWaterTemp).Name("COND循环水温[℃]"); Map(m => m.SubcoolOutTemp).Name("SUBCOOL出口温度[℃]"); Map(m => m.InhMixOutTemp).Name("吸气混合器出口温度[℃]"); Map(m => m.H1OutTemp).Name("H1出口温度[℃]"); Map(m => m.SCCirWaterTemp).Name("S.C循环水温[℃]"); - Map(m => m.EvapCirWaterTemp).Name("EVAP循环水温[℃]"); Map(m => m.InhMixOutPress).Name("吸气混合器出口压力[BarA]"); Map(m => m.GasFrPress).Name("气路阀前压力[BarA]"); Map(m => m.GasFrTemp).Name("气路阀前温度[℃]"); diff --git a/CapMachine.Wpf/PPCalculation/ThermodynamicSixResultsCalculator.cs b/CapMachine.Wpf/PPCalculation/ThermodynamicSixResultsCalculator.cs new file mode 100644 index 0000000..19b9b4d --- /dev/null +++ b/CapMachine.Wpf/PPCalculation/ThermodynamicSixResultsCalculator.cs @@ -0,0 +1,983 @@ +using CapMachine.Core; +using System; +using System.Diagnostics; +using System.Text; + +namespace CapMachine.Wpf.PPCalculation +{ + /// + /// 六个物性结果(制热量/COP(制热)/等熵效率/制冷量/COP(制冷)/容积效率)计算封装类。 + /// + /// 说明: + /// - 本类用于将原先位于 PPCService 的计算过程完整搬迁为独立类,方便后续维护与对比验证。 + /// - 计算公式、单位换算、REFPROP 调用链路应与现有实现保持一致,仅做结构性封装。 + /// + public sealed class ThermodynamicSixResultsCalculator + { + private readonly object _refpropLock; + + private static volatile bool _rpInitialized; + private static string _rpFluidFile = string.Empty; + + /// + /// 构造函数。 + /// + /// REFPROP 全局互斥锁对象(建议传入与 PPCService 相同的锁对象,避免并发竞态)。 + public ThermodynamicSixResultsCalculator(object refpropLock) + { + _refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock)); + } + + /// + /// 输入模型(从 Tag 或外部来源读取后传入)。 + /// + public readonly struct Input + { + public Input( + double suctionPress_BarA, + double suctionTemp_C, + double dischargePress_BarA, + double dischargeTemp_C, + double txvFrPress_BarA, + double txvFrTemp_C, + double hvPower_W, + double totalFlow_kg_h, + double speed_rpm, + double displacement_cc) + { + SuctionPress_BarA = suctionPress_BarA; + SuctionTemp_C = suctionTemp_C; + DischargePress_BarA = dischargePress_BarA; + DischargeTemp_C = dischargeTemp_C; + TxvFrPress_BarA = txvFrPress_BarA; + TxvFrTemp_C = txvFrTemp_C; + HvPower_W = hvPower_W; + TotalFlow_kg_h = totalFlow_kg_h; + Speed_rpm = speed_rpm; + Displacement_cc = displacement_cc; + } + + public double SuctionPress_BarA { get; } + public double SuctionTemp_C { get; } + + public double DischargePress_BarA { get; } + public double DischargeTemp_C { get; } + + public double TxvFrPress_BarA { get; } + public double TxvFrTemp_C { get; } + + public double HvPower_W { get; } + public double TotalFlow_kg_h { get; } + + public double Speed_rpm { get; } + public double Displacement_cc { get; } + } + + /// + /// 计算输出模型。 + /// + public readonly struct Result + { + public Result( + double heatingCapacityQh_kW, + double coolingCapacityQc_kW, + double copHeating, + double copCooling, + double isentropicEfficiencyPct, + double volumetricEfficiencyPct, + double mRef_kg_s, + double w_kW, + double h1_kJkg, + double s1_JmolK, + double v1_m3kg, + double h2_kJkg, + double h3_kJkg, + double h2s_kJkg) + { + HeatingCapacityQh_kW = heatingCapacityQh_kW; + CoolingCapacityQc_kW = coolingCapacityQc_kW; + COPHeating = copHeating; + COPCooling = copCooling; + IsentropicEfficiencyPct = isentropicEfficiencyPct; + VolumetricEfficiencyPct = volumetricEfficiencyPct; + + MRef_kg_s = mRef_kg_s; + W_kW = w_kW; + + H1_kJkg = h1_kJkg; + S1_JmolK = s1_JmolK; + V1_m3kg = v1_m3kg; + H2_kJkg = h2_kJkg; + H3_kJkg = h3_kJkg; + H2s_kJkg = h2s_kJkg; + } + + public double HeatingCapacityQh_kW { get; } + public double CoolingCapacityQc_kW { get; } + public double COPHeating { get; } + public double COPCooling { get; } + public double IsentropicEfficiencyPct { get; } + public double VolumetricEfficiencyPct { get; } + + public double MRef_kg_s { get; } + public double W_kW { get; } + + public double H1_kJkg { get; } + public double S1_JmolK { get; } + public double V1_m3kg { get; } + public double H2_kJkg { get; } + public double H3_kJkg { get; } + public double H2s_kJkg { get; } + } + + /// + /// 计算六个物性结果。 + /// + /// 返回值: + /// - true:至少已成功计算 Qh/Qc/COP/ηs;容积效率若失败将返回 NaN,但本方法仍返回 true,并通过 error 输出警告信息。 + /// - false:关键输入/REFPROP 计算失败。 + /// + /// 输入参数。 + /// 输出结果。 + /// 错误/警告信息。 + /// 是否成功。 + public bool TryCalculate(Input input, out Result result, out string error) + { + result = default; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + var w_W = input.HvPower_W; + if (!double.IsNaN(w_W) && !double.IsInfinity(w_W) && w_W == 0) + { + result = new Result( + heatingCapacityQh_kW: 0, + coolingCapacityQc_kW: 0, + copHeating: 0, + copCooling: 0, + isentropicEfficiencyPct: 0, + volumetricEfficiencyPct: 0, + mRef_kg_s: 0, + w_kW: 0, + h1_kJkg: 0, + s1_JmolK: 0, + v1_m3kg: 0, + h2_kJkg: 0, + h3_kJkg: 0, + h2s_kJkg: 0); + return true; + } + + if (!TryGetCompressorPower_kW(input, out var w_kW, out var wErr)) + { + error = wErr; + return false; + } + + if (!TryGetRefrigerantMassFlow_kg_s(input, out var mRef_kg_s, out var mRefErr)) + { + error = mRefErr; + return false; + } + + if (!TryGetVaporPointState_ByTP_BarA_C(input.SuctionPress_BarA, input.SuctionTemp_C, out var h1_kJkg, out var s1_JmolK, out var v1_m3kg, out var p1Err)) + { + error = $"h1/s1/吸气比容计算失败: {p1Err}"; + return false; + } + + if (!TryGetVaporPointEnthalpy_ByTP_BarA_C(input.DischargePress_BarA, input.DischargeTemp_C, out var h2_kJkg, out var p2Err)) + { + error = $"h2 计算失败: {p2Err}"; + return false; + } + + double txvFrTempForH3_C = input.TxvFrTemp_C; + if (TryGetTxvFrTempForH3_ByDischargePress_BarA(input.DischargePress_BarA, out var derivedTxvFrTempForH3_C, out var satWarn)) + { + txvFrTempForH3_C = derivedTxvFrTempForH3_C; + } + else if (!string.IsNullOrWhiteSpace(satWarn)) + { + error = string.IsNullOrWhiteSpace(error) + ? $"h3温度改用SATP(排气压力)计算Tsat失败,已回退使用TxvFrTemp_C。原因: {satWarn}" + : $"{error}; h3温度改用SATP(排气压力)计算Tsat失败,已回退使用TxvFrTemp_C。原因: {satWarn}"; + } + + if (!TryGetLiquidPointEnthalpy_ByTP_BarA_C(input.TxvFrPress_BarA, txvFrTempForH3_C, out var h3_kJkg, out var p3Err)) + { + error = $"h3 计算失败: {p3Err}"; + return false; + } + + if (!TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(input.DischargePress_BarA, s1_JmolK, 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(mRef_kg_s, h1_kJkg, h2s_kJkg, w_kW, out var etaS_pct, out var etaSErr)) + { + error = etaSErr; + return false; + } + + if (!TryComputeVolumetricEfficiencyPct(mRef_kg_s, v1_m3kg, input.Speed_rpm, input.Displacement_cc, out var etaV_pct, out var etaVErr)) + { + result = new Result( + heatingCapacityQh_kW: qh_kW, + coolingCapacityQc_kW: qc_kW, + copHeating: copH, + copCooling: copC, + isentropicEfficiencyPct: etaS_pct, + volumetricEfficiencyPct: double.NaN, + mRef_kg_s: mRef_kg_s, + w_kW: w_kW, + h1_kJkg: h1_kJkg, + s1_JmolK: s1_JmolK, + v1_m3kg: v1_m3kg, + h2_kJkg: h2_kJkg, + h3_kJkg: h3_kJkg, + h2s_kJkg: h2s_kJkg); + + error = etaVErr; + return true; + } + + result = new Result( + heatingCapacityQh_kW: qh_kW, + coolingCapacityQc_kW: qc_kW, + copHeating: copH, + copCooling: copC, + isentropicEfficiencyPct: etaS_pct, + volumetricEfficiencyPct: etaV_pct, + mRef_kg_s: mRef_kg_s, + w_kW: w_kW, + h1_kJkg: h1_kJkg, + s1_JmolK: s1_JmolK, + v1_m3kg: v1_m3kg, + h2_kJkg: h2_kJkg, + h3_kJkg: h3_kJkg, + h2s_kJkg: h2s_kJkg); + + return true; + } + + /// + /// 输出调试快照到 Console/Debug(用于人工对比验证)。 + /// + /// 标题/阶段标识。 + /// 输入。 + /// 输出结果(可为 null,表示计算失败时仅输出输入)。 + /// 错误/警告。 + public static void PrintDebugSnapshotToConsole(string title, Input input, Result? result, string error) + { + void Print(string line) + { + try { Debug.WriteLine(line); } catch { } + try { Console.WriteLine(line); } catch { } + } + + Print($"================ {title} ================"); + Print("--- Inputs ---"); + Print($"Suction : T1={input.SuctionTemp_C} °C, P1={input.SuctionPress_BarA} BarA"); + Print($"Discharge : T2={input.DischargeTemp_C} °C, P2={input.DischargePress_BarA} BarA"); + Print($"TXV-before : T3={input.TxvFrTemp_C} °C, P3={input.TxvFrPress_BarA} BarA"); + Print($"Flow : mTotal={input.TotalFlow_kg_h} kg/h"); + Print($"Power(HV) : W={input.HvPower_W} W"); + Print($"Speed : n={input.Speed_rpm} rpm"); + Print($"Disp : disp={input.Displacement_cc} cc"); + + if (result.HasValue) + { + var r = result.Value; + Print("--- Intermediate (computed) ---"); + Print($"mRef={r.MRef_kg_s:F8} kg/s, W={r.W_kW:F6} kW"); + Print($"h1={r.H1_kJkg:F6} kJ/kg"); + Print($"s1={r.S1_JmolK:F6} J/(mol·K)"); + Print($"v1={r.V1_m3kg:F8} m3/kg"); + Print($"h2={r.H2_kJkg:F6} kJ/kg"); + Print($"h3={r.H3_kJkg:F6} kJ/kg"); + Print($"h2s={r.H2s_kJkg:F6} kJ/kg"); + + Print("--- Final (computed) ---"); + Print($"Qc={r.CoolingCapacityQc_kW:F5} kW"); + Print($"COP(cool)={r.COPCooling:F5}"); + Print($"Qh={r.HeatingCapacityQh_kW:F5} kW"); + Print($"COP(heat)={r.COPHeating:F5}"); + Print($"etaS={r.IsentropicEfficiencyPct:F4} %"); + Print($"etaV={r.VolumetricEfficiencyPct:F3} %"); + } + + if (!string.IsNullOrWhiteSpace(error)) + { + Print($"--- Error/Warning ---\r\n{error}"); + } + + Print("===================================================="); + } + + 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"; + + 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}"; + _rpInitialized = false; + return false; + } + } + + private static double GetMolarMass() + { + double wmm = 0, Trp = 0, Tnbpt = 0, Tc = 0, Pc = 0, Dc = 0, Zc = 0, acf = 0, dip = 0, Rgas = 0; + long componentId = 1; + IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas); + return wmm * 0.001; + } + + private bool TryGetCompressorPower_kW(Input input, out double w_kW, out string error) + { + w_kW = double.NaN; + error = string.Empty; + + double w_W = input.HvPower_W; + 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; + } + + private bool TryGetRefrigerantMassFlow_kg_s(Input input, out double mRef_kg_s, out string error) + { + mRef_kg_s = double.NaN; + error = string.Empty; + + double mTotal_kg_h = input.TotalFlow_kg_h; + if (double.IsNaN(mTotal_kg_h) || double.IsInfinity(mTotal_kg_h)) + { + error = "总流量(冷媒流量)为 NaN/Inf"; + return false; + } + + double mRef_kg_h = mTotal_kg_h; + if (mRef_kg_h <= 0) + { + error = $"冷媒质量流量<=0,总流量={mTotal_kg_h}kg/h"; + return false; + } + + mRef_kg_s = mRef_kg_h / 3600.0; + return true; + } + + private bool TryGetVaporPointEnthalpy_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out string error) + { + h_kJkg = double.NaN; + error = string.Empty; + + if (!TryTPFLSH_ByTP_BarA_C(pressureBarA, temperatureC, out _, out var h_Jmol, out _, out _, out var flashErr)) + { + error = flashErr; + return false; + } + + if (!TryConvertH_Jmol_To_kJkg(h_Jmol, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + + return true; + } + + 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; + } + + private bool TryGetVaporPointState_ByTP_BarA_C( + double pressureBarA, + double temperatureC, + out double h_kJkg, + out double s_JmolK, + out double v_m3kg, + out string error) + { + h_kJkg = double.NaN; + s_JmolK = double.NaN; + v_m3kg = double.NaN; + error = string.Empty; + + if (!TryTPFLSH_ByTP_BarA_C(pressureBarA, temperatureC, out var d_molL, out var h_Jmol, out var sOut_JmolK, out _, out var flashErr)) + { + error = flashErr; + return false; + } + + if (!TryConvertH_Jmol_To_kJkg(h_Jmol, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + + s_JmolK = sOut_JmolK; + if (double.IsNaN(s_JmolK) || double.IsInfinity(s_JmolK)) + { + error = "无效吸气熵"; + return false; + } + + if (!TryConvertMolarDensityToSpecificVolume(d_molL, out v_m3kg, out var vErr)) + { + error = vErr; + return false; + } + + return true; + } + + private bool TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(double dischargePressureBarA, double suctionEntropy_JmolK, 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_JmolK) || double.IsInfinity(suctionEntropy_JmolK)) + { + error = "无效吸气熵 s1"; + return false; + } + + double s_JmolK = suctionEntropy_JmolK; + + 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; + } + + 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; + } + + private bool TryComputeIsentropicEfficiencyPct(double mRef_kg_s, double h1_kJkg, double h2s_kJkg, double w_kW, out double etaS_pct, out string error) + { + etaS_pct = double.NaN; + error = string.Empty; + + if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) + { + error = "无效冷媒质量流量"; + return false; + } + if (double.IsNaN(w_kW) || double.IsInfinity(w_kW) || w_kW <= 0) + { + error = "无效压缩机功率"; + return false; + } + + double dh_isentropic = h2s_kJkg - h1_kJkg; + double eta = (mRef_kg_s * dh_isentropic) / w_kW; + if (double.IsNaN(eta) || double.IsInfinity(eta)) + { + error = "等熵效率结果异常"; + return false; + } + + etaS_pct = eta * 100.0; + return true; + } + + private bool TryComputeVolumetricEfficiencyPct( + double mRef_kg_s, + double v1_m3kg, + double speed_rpm, + double displacement_cc, + out double etaV_pct, + out string error) + { + etaV_pct = double.NaN; + error = string.Empty; + + if (double.IsNaN(speed_rpm) || double.IsInfinity(speed_rpm) || speed_rpm <= 0) + { + error = $"无效转速: {speed_rpm} rpm"; + return false; + } + + if (double.IsNaN(displacement_cc) || double.IsInfinity(displacement_cc) || displacement_cc <= 0) + { + error = $"无效排量: {displacement_cc} cc"; + 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) * displacement_cc * 0.0036; + if (theoVolFlow_m3_h <= 0) + { + error = "理论吸气体积流量<=0"; + return false; + } + + etaV_pct = (suctionVolFlow_m3_h / theoVolFlow_m3_h) * 100.0; + return true; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + + 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_liq_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001; + return true; + } + + private bool TrySATP_SaturationTemperatureK_ByP_BarA(double pressureBarA, out double tSatK, out string error) + { + tSatK = double.NaN; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + double pKPa = pressureBarA * 100.0; + if (pKPa <= 0) + { + error = $"无效压力: {pressureBarA} BarA"; + return false; + } + + double[] x = new double[20]; + x[0] = 1.0; + + long kph = 1; + double t = 0.0; + double Dl = 0.0; + double Dv = 0.0; + double[] xliq = new double[20]; + double[] xvap = new double[20]; + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); + + lock (_refpropLock) + { + IRefProp64.SATPdll(ref pKPa, x, ref kph, ref t, ref Dl, ref Dv, xliq, xvap, ref ierr, ref herr, ref herrLen); + } + + if (ierr != 0) + { + error = $"SATP 错误: {herr.Trim()} (ierr={ierr})"; + return false; + } + + tSatK = t; + return true; + } + + private bool TryGetTxvFrTempForH3_ByDischargePress_BarA(double dischargePressBarA, out double txvFrTempForH3_C, out string warning) + { + txvFrTempForH3_C = double.NaN; + warning = string.Empty; + + if (!TrySATP_SaturationTemperatureK_ByP_BarA(dischargePressBarA, out var tSatK, out var satErr)) + { + warning = satErr; + return false; + } + + double tSatC = tSatK - 273.15; + txvFrTempForH3_C = tSatC - 10.0; + return true; + } + + /// + /// 生成一段便于保存/对比的文本报告。 + /// + /// 标题。 + /// 输入。 + /// 输出。 + /// 错误/警告。 + /// 报告文本。 + public static string BuildDebugReport(string title, Input input, Result? result, string error) + { + var sb = new StringBuilder(512); + sb.AppendLine($"================ {title} ================"); + sb.AppendLine("--- Inputs ---"); + sb.AppendLine($"Suction : T1={input.SuctionTemp_C} °C, P1={input.SuctionPress_BarA} BarA"); + sb.AppendLine($"Discharge : T2={input.DischargeTemp_C} °C, P2={input.DischargePress_BarA} BarA"); + sb.AppendLine($"TXV-before : T3={input.TxvFrTemp_C} °C, P3={input.TxvFrPress_BarA} BarA"); + sb.AppendLine($"Flow : mTotal={input.TotalFlow_kg_h} kg/h"); + sb.AppendLine($"Power(HV) : W={input.HvPower_W} W"); + sb.AppendLine($"Speed : n={input.Speed_rpm} rpm"); + sb.AppendLine($"Disp : disp={input.Displacement_cc} cc"); + + if (result.HasValue) + { + var r = result.Value; + sb.AppendLine("--- Intermediate (computed) ---"); + sb.AppendLine($"mRef={r.MRef_kg_s:F8} kg/s, W={r.W_kW:F6} kW"); + sb.AppendLine($"h1={r.H1_kJkg:F6} kJ/kg"); + sb.AppendLine($"s1={r.S1_JmolK:F6} J/(mol·K)"); + sb.AppendLine($"v1={r.V1_m3kg:F8} m3/kg"); + sb.AppendLine($"h2={r.H2_kJkg:F6} kJ/kg"); + sb.AppendLine($"h3={r.H3_kJkg:F6} kJ/kg"); + sb.AppendLine($"h2s={r.H2s_kJkg:F6} kJ/kg"); + + sb.AppendLine("--- Final (computed) ---"); + sb.AppendLine($"Qc={r.CoolingCapacityQc_kW:F5} kW"); + sb.AppendLine($"COP(cool)={r.COPCooling:F5}"); + sb.AppendLine($"Qh={r.HeatingCapacityQh_kW:F5} kW"); + sb.AppendLine($"COP(heat)={r.COPHeating:F5}"); + sb.AppendLine($"etaS={r.IsentropicEfficiencyPct:F4} %"); + sb.AppendLine($"etaV={r.VolumetricEfficiencyPct:F3} %"); + } + + if (!string.IsNullOrWhiteSpace(error)) + { + sb.AppendLine("--- Error/Warning ---"); + sb.AppendLine(error); + } + + sb.AppendLine("===================================================="); + return sb.ToString(); + } + } +} diff --git a/CapMachine.Wpf/Services/DataRecordService.cs b/CapMachine.Wpf/Services/DataRecordService.cs index 8ac136e..5ae1519 100644 --- a/CapMachine.Wpf/Services/DataRecordService.cs +++ b/CapMachine.Wpf/Services/DataRecordService.cs @@ -701,6 +701,114 @@ namespace CapMachine.Wpf.Services HasHeaderRecord = false, }; + private static readonly HashSet ExcludedCsvHeaderNames = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "EVAP出口温度[℃]", + "EVAP出口温度[°C]", + "EVAP出口压力[BarA]", + "干度流量[kg/h]", + "干度流量[kq/h]", + "EVAP循环水温[℃]", + "EVAP循坏水温[℃]", + }; + + private static readonly object CsvSchemaRewriteLock = new object(); + private static readonly HashSet CsvSchemaCheckedFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + + private bool TryRewriteCsvWithoutExcludedColumnsIfNeeded(string filePath) + { + try + { + if (!File.Exists(filePath)) + { + return true; + } + + lock (CsvSchemaRewriteLock) + { + if (CsvSchemaCheckedFiles.Contains(filePath)) + { + return true; + } + } + + using (var reader = new StreamReader(filePath, Encoding.UTF8, true)) + using (var csv = new CsvReader(reader, CultureInfo.CurrentCulture)) + { + if (!csv.Read()) + { + return true; + } + + csv.ReadHeader(); + var header = csv.HeaderRecord; + if (header == null || header.Length == 0) + { + return true; + } + + bool containsExcluded = header.Any(h => !string.IsNullOrWhiteSpace(h) && ExcludedCsvHeaderNames.Contains(h.Trim())); + if (!containsExcluded) + { + lock (CsvSchemaRewriteLock) + { + CsvSchemaCheckedFiles.Add(filePath); + } + return true; + } + } + + string tmpPath = filePath + ".tmp"; + using (var srcReader = new StreamReader(filePath, Encoding.UTF8, true)) + using (var srcCsv = new CsvReader(srcReader, CultureInfo.CurrentCulture)) + using (var dstWriter = new StreamWriter(tmpPath, false, Encoding.UTF8)) + using (var dstCsv = new CsvWriter(dstWriter, CultureInfo.CurrentCulture)) + { + srcCsv.Context.RegisterClassMap(); + dstCsv.Context.RegisterClassMap(); + dstCsv.WriteRecords(srcCsv.GetRecords()); + } + + string bakPath = filePath + ".bak"; + if (File.Exists(bakPath)) + { + File.Delete(bakPath); + } + + try + { + File.Replace(tmpPath, filePath, bakPath, true); + if (File.Exists(bakPath)) + { + File.Delete(bakPath); + } + } + catch + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + File.Move(tmpPath, filePath); + if (File.Exists(bakPath)) + { + File.Delete(bakPath); + } + } + + lock (CsvSchemaRewriteLock) + { + CsvSchemaCheckedFiles.Add(filePath); + } + return true; + } + catch (Exception ex) + { + LogService?.Error($"重写CSV列失败: {ex.Message}"); + return false; + } + } + /// /// 保存到CSV文件 /// @@ -737,6 +845,11 @@ namespace CapMachine.Wpf.Services { lock (ConfigService.CsvFileLock) { + if (!TryRewriteCsvWithoutExcludedColumnsIfNeeded(FileFullInfo)) + { + return; + } + //往已有的文件增加数据 using (var stream = File.Open(FileFullInfo, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) //using (var stream = new FileStream(FileFullInfo, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) diff --git a/CapMachine.Wpf/Services/PPCService.cs b/CapMachine.Wpf/Services/PPCService.cs index 82eca19..18b0fb8 100644 --- a/CapMachine.Wpf/Services/PPCService.cs +++ b/CapMachine.Wpf/Services/PPCService.cs @@ -2,19 +2,11 @@ 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.Diagnostics; +using System; using System.Globalization; -using System.Collections.Generic; -using System.Text; using System.Text.RegularExpressions; +using System.Threading.Tasks; namespace CapMachine.Wpf.Services { @@ -28,15 +20,10 @@ namespace CapMachine.Wpf.Services /// private static Task CalcTask { get; set; } = Task.CompletedTask; 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; private readonly EnthalpyDrynessCalculator _enthalpyDrynessCalculator; + private readonly ThermodynamicSixResultsCalculator _thermodynamicSixResultsCalculator; /// /// 标签中心 @@ -46,21 +33,14 @@ namespace CapMachine.Wpf.Services /// /// 实例化 /// - public PPCService(ConfigService configService, IEventAggregator eventAggregator, - DataRecordService dataRecordService, SysRunService sysRunService, ILogService logService, - MachineRtDataService machineRtDataService, IDialogService dialogService, - IDrynessCalculator drynessCalculator) + public PPCService(ConfigService configService, ILogService logService, + MachineRtDataService machineRtDataService) { ConfigService = configService; - //事件服务 - _EventAggregator = eventAggregator; - DataRecordService = dataRecordService; - SysRunServer = sysRunService; Logger = logService; MachineRtDataService = machineRtDataService; - DialogService = dialogService; - _drynessCalculator = drynessCalculator; _enthalpyDrynessCalculator = new EnthalpyDrynessCalculator(_refpropLock); + _thermodynamicSixResultsCalculator = new ThermodynamicSixResultsCalculator(_refpropLock); TagManager = MachineRtDataService.TagManger; if (TagManager.TryGetShortTagByName("转速[rpm]", out ShortValueTag? speedShortTag)) @@ -289,6 +269,7 @@ namespace CapMachine.Wpf.Services /// /// 冷媒流量kg/h + /// 气体流量+液体流量=冷媒流量 /// public ShortValueTag VRVTag { get; set; } = null!; @@ -303,95 +284,11 @@ namespace CapMachine.Wpf.Services /// public ShortValueTag LubeFlowTag { get; set; } = null!; - /// - /// 风量数据-乘以系数的后的最终结果 - /// - 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; - - 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扫描线程 /// @@ -401,7 +298,7 @@ namespace CapMachine.Wpf.Services { while (RtCalcEnable) { - await Task.Delay(1000); + await Task.Delay(100); try { @@ -456,23 +353,39 @@ namespace CapMachine.Wpf.Services Subcool.PVModel.EngValue = 0; + //这里写死干度的输入数据 + + double gasPreValvePressBarA = GasPreValvePressTag.PVModel.EngValue; + double gasPreValveTempC = GasPreValveTempTag.PVModel.EngValue; + double txvFrPressBarA = TxvFrPressTag.PVModel.EngValue; + double txvFrTempC = TxvFrTempTag.PVModel.EngValue; + double inhPressBarA = InhPressTag.PVModel.EngValue; + double vrvFlowKgPerH = VRVTag.PVModel.EngValue; + double liqRefFlowKgPerH = LiqRefFlowTag.PVModel.EngValue; + double lubeFlowKgPerH = 0.0; + //干度技术 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)); + gasPreValvePressBarA, + gasPreValveTempC, + txvFrPressBarA, + txvFrTempC, + inhPressBarA, + vrvFlowKgPerH, + liqRefFlowKgPerH, + lubeFlowKgPerH)); + //以前会受 LubeFlowTag 影响,现在由于固定为 0,会与 Dryness1 更接近/一致(只要两者公式差异仅在油这一项) if (drynessResult.IsDryness1Success) { - DrynessTag2Value = drynessResult.Dryness1_01 * 100.0; + if (DrynessTag != null) + { + DrynessTag.PVModel.EngValue = drynessResult.Dryness1_01 * 100.0; + } + //DrynessTag2Value = drynessResult.Dryness1_01 * 100.0; //if (drynessResult.Dryness1_01 <= 0) //{ @@ -508,8 +421,8 @@ namespace CapMachine.Wpf.Services if (drynessResult.IsDryness2Success) { - DrynessTag.PVModel.EngValue = drynessResult.Dryness2_01 * 100.0; - + //DrynessTag.PVModel.EngValue = drynessResult.Dryness2_01 * 100.0; + DrynessTag2Value = drynessResult.Dryness2_01 * 100.0; //if (drynessResult.Dryness2_01 <= 0) //{ // LogDrynessSnapshotIfNeeded( @@ -542,6 +455,13 @@ namespace CapMachine.Wpf.Services // LiqRefFlowTag.PVModel.EngValue); } + +#if DEBUG + //DebugRunThermoValidationCase_UsingCalculator_FromScreenshot_20260423(); + //DebugRunDrynessValidationCase_FromScreenshot_20260425(); + //DebugRunThermoValidationCase_UsingCalculator_FromScreenshot_20260428(); +#endif + if (TryUpdateThermodynamicSixResults(out var thermoErr)) { if (!string.IsNullOrWhiteSpace(thermoErr)) @@ -566,39 +486,11 @@ namespace CapMachine.Wpf.Services }); } - /// - /// 获取指定组分的摩尔质量(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(); // REFPROP 初始化状态(全局、幂等) private static volatile bool _rpInitialized = false; - private static string _rpFluidFile = string.Empty; /// /// 幂等初始化:设置流体路径/工质/参考态;确保全局只初始化一次。 @@ -651,7 +543,6 @@ namespace CapMachine.Wpf.Services } _rpInitialized = true; - _rpFluidFile = hfldCore; return true; } } @@ -664,382 +555,6 @@ namespace CapMachine.Wpf.Services } } - /// - /// - /// 计算获取 气相密度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; - } - - - /// - /// 计算气相熵(单相气相)。 - /// - /// 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; - } - - - 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 - /// - /// 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); - } - @@ -1133,14 +648,55 @@ namespace CapMachine.Wpf.Services error = "缺少膨胀阀前压力/膨胀阀前温度标签"; return false; } - + if (ExPressTag == null || ExTempTag == null) + { + error = "缺少排气压力/排气温度标签"; + return false; + } if (HVPwTag == null) { error = "缺少 HV[W] 功率标签"; return false; } + if (VRVTag == null) + { + error = "缺少总流量(冷媒流量)标签"; + return false; + } + + + + double suctionPress_BarA = InhPressTag.PVModel.EngValue; + double suctionTemp_C = InhTempTag.PVModel.EngValue; + double dischargePress_BarA = ExPressTag.PVModel.EngValue; + double dischargeTemp_C = ExTempTag.PVModel.EngValue; + double txvFrPress_BarA = TxvFrPressTag.PVModel.EngValue; + double txvFrTemp_C = TxvFrTempTag.PVModel.EngValue; + double totalFlow_kg_h = VRVTag.PVModel.EngValue; + double w_W = HVPwTag.PVModel.EngValue; + + double speed_rpm = SpeedTag?.PVModel.EngValue ?? double.NaN; + if (!TryGetCompressorDisplacement_cc(out var disp_cc, out var dispErr)) + { + error = dispErr; + return false; + } + + //这里把输入数据写死计算出结果。 + //#if DEBUG + // // 附件截图输入(单位换算:MPa -> BarA,kW -> W) + // suctionPress_BarA = 0.3 * 10.0; + // suctionTemp_C = 10.8; + // dischargePress_BarA = 1.502 * 10.0; + // dischargeTemp_C = 88.4; + // txvFrPress_BarA = 1.494 * 10.0; + // txvFrTemp_C = 45.0; + // totalFlow_kg_h = 100.1; + // w_W = 1.289 * 1000.0; + // speed_rpm = 3004; + // disp_cc = 35; + //#endif - var w_W = HVPwTag.PVModel.EngValue; if (!double.IsNaN(w_W) && !double.IsInfinity(w_W) && w_W == 0) { HeatingCapacityQh_kW = 0; @@ -1178,63 +734,29 @@ namespace CapMachine.Wpf.Services return true; } - if (!TryGetCompressorPower_kW(out var w_kW, out var wErr)) + var calcInput = new ThermodynamicSixResultsCalculator.Input( + suctionPress_BarA: suctionPress_BarA, + suctionTemp_C: suctionTemp_C, + dischargePress_BarA: dischargePress_BarA, + dischargeTemp_C: dischargeTemp_C, + txvFrPress_BarA: txvFrPress_BarA, + txvFrTemp_C: txvFrTemp_C, + hvPower_W: w_W, + totalFlow_kg_h: totalFlow_kg_h, + speed_rpm: speed_rpm, + displacement_cc: disp_cc); + + if (!_thermodynamicSixResultsCalculator.TryCalculate(calcInput, out var r, out var calcErr)) { - error = wErr; - return false; - } - if (!TryGetRefrigerantMassFlow_kg_s(out var mRef_kg_s, out var mRefErr)) - { - error = mRefErr; + error = calcErr; return false; } - if (!TryGetVaporPointState_ByTP_BarA_C(InhPressTag.PVModel.EngValue, InhTempTag.PVModel.EngValue, 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.PVModel.EngValue, ExTempTag.PVModel.EngValue, out var h2_kJkg, out var p2Err)) - { - error = $"h2 计算失败: {p2Err}"; - return false; - } - - if (!TryGetLiquidPointEnthalpy_ByTP_BarA_C(TxvFrPressTag.PVModel.EngValue, TxvFrTempTag.PVModel.EngValue, out var h3_kJkg, out var p3Err)) - { - error = $"h3 计算失败: {p3Err}"; - return false; - } - - if (!TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(ExPressTag.PVModel.EngValue, 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; + HeatingCapacityQh_kW = r.HeatingCapacityQh_kW; + CoolingCapacityQc_kW = r.CoolingCapacityQc_kW; + COPHeating = r.COPHeating; + COPCooling = r.COPCooling; + IsentropicEfficiencyPct = r.IsentropicEfficiencyPct; if (HeatingCapacityTag != null) { @@ -1257,425 +779,20 @@ namespace CapMachine.Wpf.Services COPCoolTag.PVModel.EngValue = COPCooling; } - if (!TryComputeVolumetricEfficiencyPct(mRef_kg_s, v1_m3kg, out var etaV_pct, out var etaVErr)) + if (double.IsNaN(r.VolumetricEfficiencyPct) || double.IsInfinity(r.VolumetricEfficiencyPct)) { VolumetricEfficiencyPct = double.NaN; - error = etaVErr; + error = calcErr; return true; } - VolumetricEfficiencyPct = etaV_pct; - + VolumetricEfficiencyPct = r.VolumetricEfficiencyPct; if (VoltricEffTag != null) { VoltricEffTag.PVModel.EngValue = 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.PVModel.EngValue; - 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; - - if (VRVTag == null) - { - error = "缺少总流量(冷媒流量)标签"; - return false; - } - if (LubeFlowTag == null) - { - error = "缺少油流量标签"; - return false; - } - - double mTotal_kg_h = VRVTag.PVModel.EngValue; - double mOil_kg_h = LubeFlowTag.PVModel.EngValue; - - 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.PVModel.EngValue; - 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; + error = calcErr; return true; } @@ -1736,190 +853,6 @@ namespace CapMachine.Wpf.Services 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 } } diff --git a/CapMachine.Wpf/ViewModels/HistoryDataViewModel.cs b/CapMachine.Wpf/ViewModels/HistoryDataViewModel.cs index 36132d6..6263341 100644 --- a/CapMachine.Wpf/ViewModels/HistoryDataViewModel.cs +++ b/CapMachine.Wpf/ViewModels/HistoryDataViewModel.cs @@ -605,8 +605,7 @@ namespace CapMachine.Wpf.ViewModels { if (File.Exists(item.FilePath))//是否存在文件 { - //Copy文件 - CopyToFile(item.FilePath, TargetFilePath); + ExportFilteredCsvToFolder(item.FilePath, TargetFilePath); } else { @@ -675,6 +674,34 @@ namespace CapMachine.Wpf.ViewModels File.Copy(SourceFilePath, destinationFilePath, true); // true 表示如果目标文件已存在,则覆盖 } + public void ExportFilteredCsvToFolder(string sourceFilePath, string targetFolderPath) + { + if (string.IsNullOrWhiteSpace(sourceFilePath) || string.IsNullOrWhiteSpace(targetFolderPath)) + { + return; + } + + string destinationFilePath = Path.Combine(targetFolderPath, Path.GetFileName(sourceFilePath)); + + try + { + using (var reader = new StreamReader(sourceFilePath, Encoding.UTF8, true)) + using (var csvReader = new CsvReader(reader, CultureInfo.CurrentCulture)) + using (var writer = new StreamWriter(destinationFilePath, false, Encoding.UTF8)) + using (var csvWriter = new CsvWriter(writer, CultureInfo.CurrentCulture)) + { + csvReader.Context.RegisterClassMap(); + csvWriter.Context.RegisterClassMap(); + csvWriter.WriteRecords(csvReader.GetRecords()); + } + } + catch (Exception ex) + { + Logger?.Error($"导出CSV失败: {ex.Message}"); + System.Windows.MessageBox.Show($"导出CSV失败: {ex.Message}", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); + } + } + /// /// 复制文件夹及文件 ///