using CapMachine.Core;
using CapMachine.Wpf.Models.PPCalc;
using CapMachine.Wpf.Models.Tag;
using CapMachine.Wpf.PPCalculation;
using ImTools;
using MaterialDesignThemes.Wpf;
using NLog;
using NPOI.XWPF.UserModel;
using Prism.Events;
using Prism.Mvvm;
using Prism.Services.Dialogs;
using SixLabors.ImageSharp.ColorSpaces;
using System.Collections.Generic;
using System.Text;
namespace CapMachine.Wpf.Services
{
///
/// 物性计算的服务
///
public class PPCService : BindableBase
{
///
/// 计算扫描 Task
///
private static Task CalcTask { get; set; }
public ConfigService ConfigService { get; }
private IEventAggregator _EventAggregator { get; set; }
public DataRecordService DataRecordService { get; }
public SysRunService SysRunServer { get; }
public ILogService Logger { get; }
public MachineRtDataService MachineRtDataService { get; }
public IDialogService DialogService { get; }
private readonly IDrynessCalculator _drynessCalculator;
///
/// 标签中心
///
public TagManager TagManager { get; set; }
///
/// 实例化
///
public PPCService(ConfigService configService, IEventAggregator eventAggregator,
DataRecordService dataRecordService, SysRunService sysRunService, ILogService logService,
MachineRtDataService machineRtDataService, IDialogService dialogService,
IDrynessCalculator drynessCalculator)
{
ConfigService = configService;
//事件服务
_EventAggregator = eventAggregator;
DataRecordService = dataRecordService;
SysRunServer = sysRunService;
Logger = logService;
MachineRtDataService = machineRtDataService;
DialogService = dialogService;
_drynessCalculator = drynessCalculator;
TagManager = MachineRtDataService.TagManger;
//SpeedTag = TagManager.DicTags.GetValueOrDefault("转速[rpm]");
//ExPressTag = TagManager.DicTags.GetValueOrDefault("排气压力[BarA]");
//ExTempTag = TagManager.DicTags.GetValueOrDefault("排气温度[℃]");
if (TagManager.TryGetShortTagByName("吸气压力[BarA]", out ShortValueTag? InhPressShortControlTag))
{
InhPressTag = InhPressShortControlTag!;
}
if (TagManager.TryGetShortTagByName("吸气温度[℃]", out ShortValueTag? InhTempShortControlTag))
{
InhTempTag = InhTempShortControlTag!;
}
//InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]")!;
//ComCapBusVolTag = TagManager.DicTags.GetValueOrDefault("通讯母线电压[V]");
//ComCapBusCurTag = TagManager.DicTags.GetValueOrDefault("通讯母线电流[A]");
//ComCapPwTag = TagManager.DicTags.GetValueOrDefault("通讯功率[W]");
//OS2TempTag = TagManager.DicTags.GetValueOrDefault("吸气混合器温度[℃]");
//TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!;
//TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!;
if (TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out ShortValueTag? TxvFrTempShortTag))
{
TxvFrTempTag = TxvFrTempShortTag!;
}
if (TagManager.TryGetShortTagByName("膨胀阀前压力[BarA]", out ShortValueTag? TxvFrPressShortTag))
{
TxvFrPressTag = TxvFrPressShortTag!;
}
if (TagManager.TryGetShortTagByName("液冷媒流量[kg/h]", out ShortValueTag? LiqRefFlowShortTag))
{
LiqRefFlowTag = LiqRefFlowShortTag!;
}
//kg/h
if (TagManager.TryGetShortTagByName("冷媒流量[kg/h]", out ShortValueTag? VRVShortTag))
{
VRVTag = VRVShortTag!;
}
//润滑油流量
if (TagManager.TryGetShortTagByName("润滑油流量[kg/h]", out ShortValueTag? LubeFlowShortTag))
{
LubeFlowTag = LubeFlowShortTag!;
}
// 气路阀前 P/T(用于质量流量加权混合焓的计算,LabVIEW 流程)
if (TagManager.TryGetShortTagByName("气路阀前压力[BarA]", out ShortValueTag? gasPreP))
{
GasPreValvePressTag = gasPreP!;
}
if (TagManager.TryGetShortTagByName("气路阀前温度[℃]", out ShortValueTag? gasPreT))
{
GasPreValveTempTag = gasPreT!;
}
//Cond1TempTag = TagManager.DicTags.GetValueOrDefault("冷凝器出口水温[℃]");
//CondInTempTag = TagManager.DicTags.GetValueOrDefault("冷凝器进口温度[℃]");
//Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]");
//Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]");
if (TagManager.TryGetShortTagByName("过热度[K]", out ShortValueTag? SuperheatShortTag))
{
Superheat = SuperheatShortTag!;
}
if (TagManager.TryGetShortTagByName("过冷度[K]", out ShortValueTag? SubcoolShortTag))
{
Subcool = SubcoolShortTag!;
}
// 干度标签(如不存在则仅跳过写入,不抛异常)。优先使用“干度[%]”,其次“干度[-]”。
if (TagManager.TryGetShortTagByName("干度[%]", out ShortValueTag? drynessPct))
{
DrynessTag = drynessPct!;
}
SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath");
SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen");
RtScanDeviceStart();
}
///
/// 当前的配置
///
public SuperHeatCoolConfigModel SuperHeatCoolConfig { get; set; } = new SuperHeatCoolConfigModel();
///
/// 保存配置信息
///
public void SaveSuperHeatCoolConfig()
{
ConfigHelper.SetValue("FluidsPath", SuperHeatCoolConfig.FluidsPath);
ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen);
}
///
/// 吸气压力
///
public ShortValueTag InhPressTag { get; set; }
///
/// 吸气温度
///
public ShortValueTag InhTempTag { get; set; }
///
/// 液体阀前温度
///
public ShortValueTag TxvFrTempTag { get; set; }
///
/// 液体阀前压力
///
public ShortValueTag TxvFrPressTag { get; set; }
///
/// 过热度
///
public ShortValueTag Superheat { get; set; }
///
/// 过冷度
///
public ShortValueTag Subcool { get; set; }
///
/// 干度(无量纲 [-])
///
public ShortValueTag DrynessTag { get; set; }
private double _DrynessTag2Value;
///
/// 干度2(无量纲 [-])
///
public double DrynessTag2Value
{
get { return _DrynessTag2Value; }
set { _DrynessTag2Value = value; RaisePropertyChanged(); }
}
///
/// 气路阀前压力(BarA)
///
public ShortValueTag GasPreValvePressTag { get; set; }
///
/// 气路阀前温度(℃)
///
public ShortValueTag GasPreValveTempTag { get; set; }
///
/// 冷媒流量kg/h
///
public ShortValueTag VRVTag { get; set; }
///
/// 液体流量(kg/h)
/// 液冷媒流量kg/h=液体流量kg/h
///
public ShortValueTag LiqRefFlowTag { get; set; }
///
/// 润滑油流量(kg/h)
///
public ShortValueTag LubeFlowTag { get; set; }
///
/// 风量数据-乘以系数的后的最终结果
///
private double AirVolumeData { get; set; } = 0.0;
///
/// 风量数据-公式计算的原始数据
///
private double AirVolumeDataSource { get; set; } = 0;
///
/// 风量喷嘴启用信息数据
///
private string RozzleEnableInfo { get; set; } = "";
///
/// 启用计算
///
private bool RtCalcEnable { get; set; } = true;
///
/// 触发日志
///
private bool DebugLog { get; set; } = false;
///
/// PLC扫描线程
///
private void RtScanDeviceStart()
{
CalcTask = Task.Run(async () =>
{
while (RtCalcEnable)
{
await Task.Delay(1000);
try
{
long iErr, kph = 1;
double te = 0.0, te1 = 0.0, p = 0.0, p1 = 0.0, d = 0.0, Dl = 0.0, Dv = 0.0, q = 0.0, h = 0.0, ee = 0.0, hh = 0.0, ss = 0.0, cp = 0.0, cv = 0.0, w = 0.0;
double[] x = new double[20], xliq = new double[20], xvap = new double[20];
double[] xlkg = new double[20], xvkg = new double[20];
double tk = 0.0, wm = 0.0, prevDeltaH = 0.0;//prevDeltaH 未使用
//textBox5.Text = "";
// 幂等初始化:仅首次或工质/路径变化时执行 SETPATH/SETUP,提高每秒循环效率
if (!EnsureRefpropInitialized(out var initErr))
{
// 初始化失败,跳过本周期
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.吸气压力(kpa)
p = (InhPressTag.PVModel.EngValue) * 100.0;// 保持你原有流程:将 BarA 当作 MPa? 历史代码为 *1000.0,不改变你的算法
kph = 1;
p1 = (TxvFrPressTag.PVModel.EngValue) * 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.PVModel.EngValue = InhTempTag.PVModel.EngValue - (te - 273.15);
else
Superheat.PVModel.EngValue = 0;
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.PVModel.EngValue = TxvFrTempTag.PVModel.EngValue - (te1 - 273.15);
else
Subcool.PVModel.EngValue = 0;
//干度技术
//气体流量kg/h=冷媒流量kg/h-液冷媒流量kg/h
var GasFlowKgPerH = VRVTag.PVModel.EngValue - LiqRefFlowTag.PVModel.EngValue;//气体流量kg/h
//摩尔质量(kg/mol)
var molarMassKgPerMol = GetMolarMass();
//定义气相质量焓 kJ/(kg·K)
var Gas_kJkgK = 0.0;
//步骤1:计算气路阀前气相焓h vap(单相气相)
if (TryTPRHO_VaporDensity_ByTP_MPa_C(GasPreValvePressTag.PVModel.EngValue * 0.1, GasPreValveTempTag.PVModel.EngValue, out var D_molL, out var D_molLErr1))
{
if (TryTHERM_VaporEntropy_ByTD(GasPreValveTempTag.PVModel.EngValue, D_molL, out var s_kJkgK, out var D_molLErr2))
{
// s_kJkgK 即为图片中的“气相质量熵 kJ/(kg·K)”
Gas_kJkgK = s_kJkgK;
}
}
//定义液相质量焓 kJ/(kg·K)
var Liquid_kJkg = 0.0;
//步骤2:计算液路阀前气相焓hlig(单相液相) TxvFrTempTag 液体阀前温度 TxvFrPressTag 液体阀前压力
// 1) 先求 D_liq
if (TryTPRHO_LiquidDensity_ByTP_MPa_C(TxvFrPressTag.PVModel.EngValue * 0.1, TxvFrTempTag.PVModel.EngValue, out var D_liq_molL, out var D_liqErr1))
{
// 2) 再用 THERM 求 h_liq
if (TryTHERM_LiquidEnthalpy_ByTD(TxvFrTempTag.PVModel.EngValue, D_liq_molL, out var h_liq_kJkg, out var D_liqErr2))
{
// h_liq_kJkg 即为图片中的“液相质量焓 h_liq kJ/kg”
Liquid_kJkg = h_liq_kJkg;
}
else
{
// 处理 err2
}
}
else
{
// 处理 err1
}
//定义饱和液质量焓hl kJ/kg
var Liquid_h_liq = 0.0;
//定义饱和气质量焓hl k)/kg
var Gas_h_vap = 0.0;
if (TryGetSaturationLiquidEnthalpy_ByP_MPa(InhPressTag.PVModel.EngValue * 0.1, out var h_liq, out var h_liqErr1) &&
TryGetSaturationVaporEnthalpy_ByP_MPa(InhPressTag.PVModel.EngValue * 0.1, out var h_vap, out var h_vapErr2))
{
// h_liq / h_vap 即为图片右侧的两个“饱和液/饱和气 质量焓 kJ/kg”
Liquid_h_liq = h_liq;
Gas_h_vap = h_vap;
}
// 气相焓 h_vap_kJkg(由 TPRHO 气相 + THERM)
// 液相焓 h_liq_kJkg(由 TPRHO 液相 + THERM)
// 饱和液 / 气焓 h_l / h_v(由 SATP +THERM)
//气/液质量流量 mg/ml
if (TryComputeDrynessByEnthalpy(
Gas_kJkgK, Liquid_kJkg,//气相质量焓 h vap [k/kg] 液相质量焓 h liq [kJ/kg]
GasFlowKgPerH, LiqRefFlowTag.PVModel.EngValue,//气体质量流量 mg [kg/h] 液体质量流量 ml [kg/h]
Liquid_h_liq, Gas_h_vap, //饱和液质量焓 h liq [kJ/kg] 饱和气质量焓 h vap [kJ/kg]
out var GasValue, out var hMix, out var err))
{
// x 为最终干度 [0..1],hMix 为混合后比焓
DrynessTag.PVModel.EngValue = GasValue * 100.0;
}
else
{
// 处理 err
}
//计算干度2
// 气相焓 h_vap_kJkg(由 TPRHO 气相 + THERM)
// 液相焓 h_liq_kJkg(由 TPRHO 液相 + THERM)
// 饱和液 / 气焓 h_l / h_v(由 SATP +THERM)
//气/液质量流量 mg/ml
if (TryComputeDrynessByEnthalpy2(
Gas_kJkgK, Liquid_kJkg,//气相质量焓 h vap [k/kg] 液相质量焓 h liq [kJ/kg]
GasFlowKgPerH, LubeFlowTag.PVModel.EngValue, //气体质量流量 mg [kg/h] 润滑油流量
LiqRefFlowTag.PVModel.EngValue,// 液体质量流量 ml [kg/h]
Liquid_h_liq, Gas_h_vap, //饱和液质量焓 h liq [kJ/kg] 饱和气质量焓 h vap [kJ/kg]
out var GasValue2, out var hMix2, out var err2))
{
// x 为最终干度 [0..1],hMix 为混合后比焓
DrynessTag2Value = GasValue2 * 100.0;
}
else
{
// 处理 err2
}
}
catch (Exception ex)
{
Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString()));
}
}
});
}
///
/// 获取指定组分的摩尔质量(kg/mol)
///
/// 组分ID(icomp)
/// 摩尔质量(kg/mol)
public static double GetMolarMass()
{
// 初始化所有变量,因为我们将使用ref传递
double wmm = 0, Trp = 0, Tnbpt = 0, Tc = 0, Pc = 0, Dc = 0, Zc = 0, acf = 0, dip = 0, Rgas = 0;
long componentId = 1;
try
{
// 调用INFOdll函数获取物性参数,使用ref
IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas);
// 将wmm从g/mol转换为kg/mol(乘以0.001)
double molarMassKgPerMol = wmm * 0.001;
return molarMassKgPerMol;
}
catch (Exception ex)
{
throw new Exception($"获取组分{componentId}的摩尔质量时出错: {ex.Message}", ex);
}
}
// 若类中尚未定义,请添加全局互斥锁,串行化所有 REFPROP 调用
private static readonly object _refpropLock = new object();
// 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
///
/// 气体阀前压力mpa
/// 气体阀前温度 C
/// “气相”摩尔密度 D [mol/L] / 气相密度D_vap mol/l
/// ERR
///
public bool TryTPRHO_VaporDensity_ByTP_MPa_C(
double pressureMPa,
double temperatureC,
out double densityMolPerL,
out string error)
{
densityMolPerL = double.NaN;
error = string.Empty;
// 输入换算
double tK = temperatureC + 273.15; // K
double pKPa = pressureMPa * 1000.0; // MPa -> kPa
// 纯工质:x[0]=1
double[] x = new double[20];
x[0] = 1.0;
// TPRHO 参数
long kph = 2; // 气相
long kguess = 0; // 让例程自行选择初值
double D = 0.0; // 输出:mol/L
long ierr = 0;
long herrLen = 255;
string herr = new string(' ', 255);
lock (_refpropLock)
{
IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kph, ref kguess, ref D, ref ierr, ref herr, ref herrLen);
}
if (ierr != 0)
{
error = $"TPRHO 错误: {herr.Trim()} (ierr={ierr})";
return false;
}
densityMolPerL = D; // mol/L
return true;
}
///
/// 计算气路阀前气相焓h_vap(单相气相)
///
/// THERMdll 封装:按 T(℃) 与上一步得到的 D[mol/L] 计算气相熵 s [kJ/(kg·K)]
/// LabVIEW 对应:THERM(T, D) -> s[J/(mol·K)],再 /M[kg/mol] 并 *0.001
///
/// 气体阀前温度 C
/// 气相密度D_vap mol/l
///
///
///
public bool TryTHERM_VaporEntropy_ByTD(
double temperatureC,
double densityMolPerL,
out double entropy_kJ_per_kgK,
out string error)
{
entropy_kJ_per_kgK = double.NaN;
error = string.Empty;
// 输入换算
double tK = temperatureC + 273.15; // K
double D = densityMolPerL; // mol/L
// 纯工质:x[0]=1
double[] x = new double[20];
x[0] = 1.0;
// THERM 输出
double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0;
// 摩尔质量(kg/mol)——使用你已封装的 GetMolarMass()
double molarMassKgPerMol = GetMolarMass();
if (molarMassKgPerMol <= 0)
{
error = "无效的摩尔质量";
return false;
}
lock (_refpropLock)
{
IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt);
// THERM 不返回 ierr,如输入越界会在上游 TPRHO 就失败
}
// J/(mol·K) -> kJ/(kg·K):先除以 kg/mol,再乘 0.001
entropy_kJ_per_kgK = (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);
}
///
/// 按图片的最终流程计算干度:
/// 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;
}
}
}