diff --git a/CapMachine.Wpf/CanDrive/ToomossCan.cs b/CapMachine.Wpf/CanDrive/ToomossCan.cs index 9de3eee..3df7b1a 100644 --- a/CapMachine.Wpf/CanDrive/ToomossCan.cs +++ b/CapMachine.Wpf/CanDrive/ToomossCan.cs @@ -1,29 +1,14 @@ -using CapMachine.Core; -using CapMachine.Model.CANLIN; using CapMachine.Wpf.Dtos; -using CapMachine.Wpf.Models.Tag; +using CapMachine.Wpf.Models; using CapMachine.Wpf.Services; -using HslCommunication; using ImTools; -using NLog; -using NPOI.OpenXmlFormats.Wordprocessing; using Prism.Ioc; using Prism.Mvvm; -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Collections.Concurrent; using System.Diagnostics; using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Windows.Interop; -using static CapMachine.Wpf.CanDrive.USB2CAN; -using CapMachine.Wpf.Models; namespace CapMachine.Wpf.CanDrive { diff --git a/CapMachine.Wpf/PPCalculation/2025-10-18.xlsx b/CapMachine.Wpf/PPCalculation/2025-10-18.xlsx new file mode 100644 index 0000000..7a5f696 Binary files /dev/null and b/CapMachine.Wpf/PPCalculation/2025-10-18.xlsx differ diff --git a/CapMachine.Wpf/Services/PPCService.cs b/CapMachine.Wpf/Services/PPCService.cs index 9a76ea2..5f73855 100644 --- a/CapMachine.Wpf/Services/PPCService.cs +++ b/CapMachine.Wpf/Services/PPCService.cs @@ -84,6 +84,7 @@ namespace CapMachine.Wpf.Services { LiqRefFlowTag = LiqRefFlowShortTag!; } + //kg/h L/min if (TagManager.TryGetShortTagByName("冷媒流量[L/min]", out ShortValueTag? VRVShortTag)) { VRVTag = VRVShortTag!; @@ -133,11 +134,16 @@ namespace CapMachine.Wpf.Services DrynessTag = drynessPct!; DrynessTagIsPercent = true; } + else if (TagManager.TryGetShortTagByName("干度[-]", out ShortValueTag? drynessAbs)) + { + DrynessTag = drynessAbs!; + DrynessTagIsPercent = false; + } SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath"); SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen"); - //RtScanDeviceStart(); + RtScanDeviceStart(); } @@ -265,6 +271,10 @@ namespace CapMachine.Wpf.Services /// private bool RtCalcEnable { get; set; } = true; + /// + /// 触发日志 + /// + private bool DebugLog { get; set; }=false; /// /// PLC扫描线程 @@ -273,145 +283,117 @@ namespace CapMachine.Wpf.Services { CalcTask = Task.Run(async () => { - //Stopwatch stopwatch = new Stopwatch(); - //物性的过热度和过冷度的相关物性计算 while (RtCalcEnable) { await Task.Delay(1000); try { - long iErr, kph = 1; - double te = 0.0, te1 = 0.0, p = 0.0, p1 = 0.0, d = 0.0, Dl = 0.0, Dv = 0.0, q = 0.0, h = 0.0, ee = 0.0, hh = 0.0, ss = 0.0, cp = 0.0, cv = 0.0, w = 0.0; double[] x = new double[20], xliq = new double[20], xvap = new double[20]; double[] xlkg = new double[20], xvkg = new double[20]; + double tk = 0.0, wm = 0.0, prevDeltaH = 0.0; - double tk = 0.0, wm = 0.0, prevDeltaH = 0.0;//prevDeltaH 未使用 - - // 统一初始化(仅必要时),避免循环内重复 SETUP/SETPATH if (!EnsureRefpropInitialized(out string initErrLoop)) { - // 初始化失败:保护性置零并进入下一轮 Superheat.PVModel.EngValue = 0; Subcool.PVModel.EngValue = 0; - // 可选:记录 initErrLoop + continue; } - else + + lock (_refpropLock) { - // 在统一的锁内进行 REFPROP 计算,保证并发安全与状态一致性 - lock (_refpropLock) + // 过热度 + if (TryComputeSuperheatK_ByBarA(InhPressTag.PVModel.EngValue, InhTempTag.PVModel.EngValue, out double superheatK, out string shErr)) + Superheat.PVModel.EngValue = superheatK; + else { - // 建立纯工质组分(摩尔分数) - double[] xLoc = new double[20]; xLoc[0] = 1.0; - double[] xliqLoc = new double[20]; - double[] xvapLoc = new double[20]; - double DlLoc = 0, DvLoc = 0; - string herr2 = new string(' ', 255); - long herrLen2 = 255; + Superheat.PVModel.EngValue = 0; + if (DebugLog) Logger.Error($"过热度计算失败:{shErr}"); + } - // 计算过热度/过冷度(封装方法,BarA→kPa) - if (TryComputeSuperheatK_ByBarA(InhPressTag.PVModel.EngValue, InhTempTag.PVModel.EngValue, out double superheatK, out string shErr)) - Superheat.PVModel.EngValue = superheatK; + // 过冷度 + if (TryComputeSubcoolK_ByBarA(TxvFrPressTag.PVModel.EngValue, TxvFrTempTag.PVModel.EngValue, out double subcoolK, out string scErr)) + Subcool.PVModel.EngValue = subcoolK; + else + { + Subcool.PVModel.EngValue = 0; + if (DebugLog) Logger.Error($"过冷度计算失败:{scErr}"); + } + + // 干度计算 + var missing = new List(); + if (InhPressTag == null) missing.Add("吸气压力[BarA]"); + if (GasPreValvePressTag == null) missing.Add("气路阀前压力[BarA]"); + if (GasPreValveTempTag == null) missing.Add("气路阀前温度[℃]"); + if (TxvFrPressTag == null) missing.Add("膨胀阀前压力[BarA]"); + if (TxvFrTempTag == null) missing.Add("SUBCOOL出口温度[℃]"); + if (VRVTag == null) missing.Add("气体质量流量[kg/h]"); + if (LiqRefFlowTag == null) missing.Add("液冷媒流量[kg/h]"); + + if (missing.Count > 0) + { + if (DrynessTag != null) DrynessTag.PVModel.EngValue = 0; + if (DebugLog) Logger.Error($"质量流量加权干度:必要测点缺失,已将干度置0。缺失: {string.Join(", ", missing)}"); + } + else + { + string dryErr; + //double ps = InhPressTag.PVModel.EngValue; + //double pg = GasPreValvePressTag.PVModel.EngValue; + //double tg = GasPreValveTempTag.PVModel.EngValue; + //double plv = TxvFrPressTag.PVModel.EngValue; + //double tlv = TxvFrTempTag.PVModel.EngValue; + + double ps = InhPressTag.PVModel.EngValue;//吸气压力 + //ps = 2.374421;//Mpa ->BarA + double pg = GasPreValvePressTag.PVModel.EngValue;//气路阀前压力 + //pg = 19.77032;//Mpa ->BarA + double tg = GasPreValveTempTag.PVModel.EngValue;//气路阀前温度 + //tg = 61.76869; + double plv = TxvFrPressTag.PVModel.EngValue;//膨胀阀前压力=液路阀前压力 + //plv = 19.26757;//Mpa ->BarA + double tlv = TxvFrTempTag.PVModel.EngValue;//膨胀阀前温度 = SUBCOOL出口温度 =液路阀前温度 + //tlv = 64.96428; + + double LiquidMassFlowKgPerH = Math.Max(0.0, LiqRefFlowTag?.PVModel?.EngValue ?? 0.0); + //double LiquidMassFlowKgPerH = 214.3051; + //double GasMassFlowKgPerH = Math.Max(0.0, VRVTag?.PVModel?.EngValue ?? 0.0); + double GasMassFlowKgPerH = (double)(VRVTag?.PVModel?.EngValue - LiquidMassFlowKgPerH); + + // 校验总质量流量 + if ((GasMassFlowKgPerH + LiquidMassFlowKgPerH) <= 0) + { + if (DrynessTag != null) DrynessTag.PVModel.EngValue = 0; + if (DebugLog) Logger.Error("质量流量加权干度:气/液质量流量和<=0,已将干度置0。"); + } else { - Superheat.PVModel.EngValue = 0; - Logger.Error($"过热度计算失败:{shErr}"); - } + double dryness = Step_ComputeDryness_LV_FlowWeighted( + GasMassFlowKgPerH, + LiquidMassFlowKgPerH, + ps, pg, tg, plv, tlv, + out dryErr + ); - if (TryComputeSubcoolK_ByBarA(TxvFrPressTag.PVModel.EngValue, TxvFrTempTag.PVModel.EngValue, out double subcoolK, out string scErr)) - Subcool.PVModel.EngValue = subcoolK; - else - { - Subcool.PVModel.EngValue = 0; - Logger.Error($"过冷度计算失败:{scErr}"); - } - - // 干度计算(LabVIEW 严格流程,质量流量加权): - // A) 阀前两路相焓(TPRHO→THERM) - // B) 质量流量加权混合焓 - // C) 吸气压力同压饱和焓(SATP→THERM) - // D) x = (h_mix - h_l)/(h_v - h_l) - try - { - // 输入校验:必要测点是否齐全 - var missing = new List(); - if (InhPressTag == null) missing.Add("吸气压力[BarA]"); - if (GasPreValvePressTag == null) missing.Add("气路阀前压力[BarA]"); - if (GasPreValveTempTag == null) missing.Add("气路阀前温度[℃]"); - if (TxvFrPressTag == null) missing.Add("膨胀阀前压力[BarA]"); - if (TxvFrTempTag == null) missing.Add("SUBCOOL出口温度[℃]"); - - if (missing.Count > 0) + if (!double.IsNaN(dryness)) { + // 物理意义上干度应在 [0,1],写入 Tag 时进行夹紧保护 + double drynessClamped = Math.Min(1.0, Math.Max(0.0, dryness)); if (DrynessTag != null) - { - DrynessTag.PVModel.EngValue = 0; - } - Logger.Error($"质量流量加权干度:必要测点缺失,已将干度置0。缺失: {string.Join(", ", missing)}"); + DrynessTag.PVModel.EngValue = DrynessTagIsPercent ? drynessClamped * 100.0 : drynessClamped; } else { - string dryErr; - double ps = InhPressTag.PVModel.EngValue;//吸气压力 - //ps = 2.374421;//Mpa ->BarA - double pg = GasPreValvePressTag.PVModel.EngValue;//气路阀前压力 - //pg = 19.77032;//Mpa ->BarA - double tg = GasPreValveTempTag.PVModel.EngValue;//气路阀前温度 - //tg = 61.76869; - double plv = TxvFrPressTag.PVModel.EngValue;//膨胀阀前压力=液路阀前压力 - //plv = 19.26757;//Mpa ->BarA - double tlv = TxvFrTempTag.PVModel.EngValue;//膨胀阀前温度 = SUBCOOL出口温度 =液路阀前温度 - //tlv = 64.96428; - - // 单位说明: - // - 液体质量流量 ml[kg/h] = LiqRefFlowTag.PVModel.EngValue - // - 气体质量流量 mg[kg/h] = VRVTag.PVModel.EngValue(VRV 位于气管,单位已为 kg/h) - - double GasMassFlowKgPerH, LiquidMassFlowKgPerH; - - LiquidMassFlowKgPerH = LiqRefFlowTag.PVModel.EngValue; // [kg/h] - //GasMassFlowKgPerH = Math.Max(0.0, VRVTag?.PVModel?.EngValue ?? 0.0); // [kg/h] - GasMassFlowKgPerH = (double)(VRVTag?.PVModel?.EngValue - LiquidMassFlowKgPerH); - //压力参数要求为BarA - double dryness = Step_ComputeDryness_LV_FlowWeighted( - GasMassFlowKgPerH, - LiquidMassFlowKgPerH, - ps, pg, tg, plv, tlv, - out dryErr - ); - - double val = (!double.IsNaN(dryness)) ? dryness : 0.0; - if (DrynessTag != null) - { - DrynessTag.PVModel.EngValue = DrynessTagIsPercent ? val * 100.0 : val; - } - - if (double.IsNaN(dryness)) - { - Logger.Error($"质量流量加权干度计算失败:{dryErr};已置0。输入: mg={GasMassFlowKgPerH:F3} kg/h, ml={LiquidMassFlowKgPerH:F3} kg/h, Ps={ps:F3} BarA, Pg={pg:F3} BarA, Tg={tg:F3} ℃, Pl={plv:F3} BarA, Tl={tlv:F3} ℃"); - } + if (DebugLog) Logger.Warn($"质量流量加权干度计算失败:{dryErr};已跳过本次干度写入。输入: mg={GasMassFlowKgPerH:F3} kg/h, ml={LiquidMassFlowKgPerH:F3} kg/h, Ps={ps:F3} BarA, Pg={pg:F3} BarA, Tg={tg:F3} ℃, Pl={plv:F3} BarA, Tl={tlv:F3} ℃"); } } - catch (Exception ex) - { - if (DrynessTag != null) DrynessTag.PVModel.EngValue = 0; - Logger.Error("质量流量加权干度计算异常,已置0"); - } } } - - //p = Convert.ToDouble(textBox2.Text) * 100.0;//textBox2 Comp.吸气压力(BarA→kPa) - p = (InhPressTag.PVModel.EngValue) * 100.0;//textBox2 Comp.吸气压力(BarA→kPa) - kph = 1; - - p1 = (TxvFrPressTag.PVModel.EngValue) * 100.0;//textBox3 Evap.膨胀阀前压力(BarA→kPa) - //p1 = Convert.ToDouble(textBox3.Text) * 100.0;//textBox3 Evap.膨胀阀前压力(BarA→kPa) - // 重复的 SATP 与干度计算已在上方 lock(_refpropLock) 中统一完成,此处移除重复调用 } 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())); } } }); @@ -460,7 +442,7 @@ namespace CapMachine.Wpf.Services } if (ierr != 0) { error = $"SATP 失败: {herr.Trim()} (ierr={ierr})"; return false; } double tSatC = tSatK - 273.15; - subcoolK = preValveTempC - tSatC; + subcoolK = tSatC - preValveTempC; return true; } @@ -514,7 +496,7 @@ namespace CapMachine.Wpf.Services herr = new string(' ', 255); herrLen = 255; iErr = 0; IRefProp64.SATPdll(ref p1, x, ref kph, ref te1, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen); if (iErr == 0) - Subcool.PVModel.EngValue = TxvFrTempTag.PVModel.EngValue - (te1 - 273.15); + Subcool.PVModel.EngValue = (te1 - 273.15) - TxvFrTempTag.PVModel.EngValue; else Subcool.PVModel.EngValue = 0; @@ -567,16 +549,60 @@ namespace CapMachine.Wpf.Services { h_kJkg = double.NaN; error = string.Empty; if (!EnsureRefpropInitialized(out error)) return false; + + // 统一输入与数组 double[] x = new double[20]; x[0] = 1.0; double wmm = 0; // g/mol double tK = temperatureC + 273.15; double pKPa = pressureBarA * 100.0; double D = 0; long kguess = 0; long ierr = 0; string herr = new string(' ', 255); long ln = 255; double pOut = 0, el = 0, hl = 0, sl = 0, cvl = 0, cpl = 0, wl = 0, hjt = 0; + + // 相态自适应:当期望液相(kph=1)但温度高于同压饱和或接近相边界时,改用 TPFLSH 或饱和液焓,避免跳变 + const double EpsBandK = 0.4; // 饱和带宽(K) lock (_refpropLock) { IRefProp64.WMOLdll(x, ref wmm); if (!(wmm > 0)) { error = "WMOL 返回无效摩尔质量"; return false; } + + // 计算当前压力下的饱和温度 + double tSat = 0, DlSat = 0, DvSat = 0; long kphSat = 1; string herrSat = new string(' ', 255); long lnSat = 255; long ierrSat = 0; + double pForSat = pKPa; + IRefProp64.SATPdll(ref pForSat, x, ref kphSat, ref tSat, ref DlSat, ref DvSat, new double[20], new double[20], ref ierrSat, ref herrSat, ref lnSat); + double tSatC = (ierrSat == 0) ? (tSat - 273.15) : double.NaN; + + // 当调用者要求液相,但温度在饱和带内或高于饱和温度时,优先使用 TPFLSH 获取真实相态焓 + if (kphPhase == 1 && !double.IsNaN(tSatC)) + { + double dT = temperatureC - tSatC; + if (dT >= EpsBandK) + { + // 已明显高于饱和:按真实相态计算焓(TPFLSH) + string e2; + if (TryComputeHmassKJkgByTP_BarAC(pressureBarA, temperatureC, out double hMassTPF, out e2)) + { h_kJkg = hMassTPF; return true; } + // 若 TPFLSH 也失败,则退化为气相路径 TPRHO + long kphGas = 2; double tK2 = tK; double pKPa2 = pKPa; double DG = 0; long ierr2 = 0; string herr2 = new string(' ', 255); long ln2 = 255; + IRefProp64.TPRHOdll(ref tK2, ref pKPa2, x, ref kphGas, ref kguess, ref DG, ref ierr2, ref herr2, ref ln2); + if (ierr2 == 0) + { + double tLocal2 = tK2; double DLocal2 = DG; + IRefProp64.THERMdll(ref tLocal2, ref DLocal2, x, ref pOut, ref el, ref hl, ref sl, ref cvl, ref cpl, ref wl, ref hjt); + h_kJkg = hl / wmm; return true; + } + // 否则继续走默认液相路径(尽力返回) + } + else if (Math.Abs(dT) <= EpsBandK) + { + // 接近饱和:改用 TPFLSH(T,P) 真实相态焓,避免“直接切换为饱和液焓”导致阶跃 + string e2b; + if (TryComputeHmassKJkgByTP_BarAC(pressureBarA, temperatureC, out double hMassTPF2, out e2b)) + { h_kJkg = hMassTPF2; return true; } + // 若失败,继续默认液相路径 + } + } + + // 默认:按给定 kphPhase 走 TPRHO→THERM(与 LV 路径一致) IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kphPhase, ref kguess, ref D, ref ierr, ref herr, ref ln); if (ierr != 0) { error = $"TPRHO 失败: {herr.Trim()} (ierr={ierr})"; return false; } double tLocal = tK; double DLocal = D; @@ -626,7 +652,8 @@ namespace CapMachine.Wpf.Services if (Math.Abs(denom) < 1e-9) { error = "饱和焓差过小,可能临界附近"; return false; } double val = (hMix_kJkg - hl_kJkg) / denom; if (double.IsNaN(val) || double.IsInfinity(val)) { error = "干度数值异常"; return false; } - x = Math.Max(0.0, Math.Min(1.0, val)); + // 不再夹紧到 [0,1],返回原始 x(可能 <0 或 >1),以避免跨界限时的阶跃 + x = val; return true; }