863 lines
34 KiB
C#
863 lines
34 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 物性计算的服务
|
||
/// </summary>
|
||
public class PPCService : BindableBase
|
||
{
|
||
/// <summary>
|
||
/// 计算扫描 Task
|
||
/// </summary>
|
||
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;
|
||
|
||
/// <summary>
|
||
/// 标签中心
|
||
/// </summary>
|
||
public TagManager TagManager { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实例化
|
||
/// </summary>
|
||
public PPCService(ConfigService configService, IEventAggregator eventAggregator,
|
||
DataRecordService dataRecordService, SysRunService sysRunService, ILogService logService,
|
||
MachineRtDataService machineRtDataService, IDialogService dialogService,
|
||
IDrynessCalculator drynessCalculator)
|
||
{
|
||
ConfigService = configService;
|
||
//事件服务
|
||
_EventAggregator = eventAggregator;
|
||
DataRecordService = dataRecordService;
|
||
SysRunServer = sysRunService;
|
||
Logger = logService;
|
||
MachineRtDataService = machineRtDataService;
|
||
DialogService = dialogService;
|
||
_drynessCalculator = drynessCalculator;
|
||
TagManager = MachineRtDataService.TagManger;
|
||
|
||
//SpeedTag = TagManager.DicTags.GetValueOrDefault("转速[rpm]");
|
||
//ExPressTag = TagManager.DicTags.GetValueOrDefault("排气压力[BarA]");
|
||
//ExTempTag = TagManager.DicTags.GetValueOrDefault("排气温度[℃]");
|
||
if (TagManager.TryGetShortTagByName("吸气压力[BarA]", out ShortValueTag? InhPressShortControlTag))
|
||
{
|
||
InhPressTag = InhPressShortControlTag!;
|
||
}
|
||
|
||
if (TagManager.TryGetShortTagByName("吸气温度[℃]", out ShortValueTag? InhTempShortControlTag))
|
||
{
|
||
InhTempTag = InhTempShortControlTag!;
|
||
}
|
||
|
||
//InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]")!;
|
||
//ComCapBusVolTag = TagManager.DicTags.GetValueOrDefault("通讯母线电压[V]");
|
||
//ComCapBusCurTag = TagManager.DicTags.GetValueOrDefault("通讯母线电流[A]");
|
||
//ComCapPwTag = TagManager.DicTags.GetValueOrDefault("通讯功率[W]");
|
||
//OS2TempTag = TagManager.DicTags.GetValueOrDefault("吸气混合器温度[℃]");
|
||
|
||
//TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!;
|
||
//TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!;
|
||
|
||
if (TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out ShortValueTag? TxvFrTempShortTag))
|
||
{
|
||
TxvFrTempTag = TxvFrTempShortTag!;
|
||
}
|
||
if (TagManager.TryGetShortTagByName("膨胀阀前压力[BarA]", out ShortValueTag? TxvFrPressShortTag))
|
||
{
|
||
TxvFrPressTag = TxvFrPressShortTag!;
|
||
}
|
||
|
||
if (TagManager.TryGetShortTagByName("液冷媒流量[kg/h]", out ShortValueTag? LiqRefFlowShortTag))
|
||
{
|
||
LiqRefFlowTag = LiqRefFlowShortTag!;
|
||
}
|
||
//kg/h
|
||
if (TagManager.TryGetShortTagByName("冷媒流量[kg/h]", out ShortValueTag? VRVShortTag))
|
||
{
|
||
VRVTag = VRVShortTag!;
|
||
}
|
||
|
||
// 气路阀前 P/T(用于质量流量加权混合焓的计算,LabVIEW 流程)
|
||
if (TagManager.TryGetShortTagByName("气路阀前压力[BarA]", out ShortValueTag? gasPreP))
|
||
{
|
||
GasPreValvePressTag = gasPreP!;
|
||
}
|
||
if (TagManager.TryGetShortTagByName("气路阀前温度[℃]", out ShortValueTag? gasPreT))
|
||
{
|
||
GasPreValveTempTag = gasPreT!;
|
||
}
|
||
|
||
//Cond1TempTag = TagManager.DicTags.GetValueOrDefault("冷凝器出口水温[℃]");
|
||
//CondInTempTag = TagManager.DicTags.GetValueOrDefault("冷凝器进口温度[℃]");
|
||
|
||
//Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]");
|
||
//Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]");
|
||
|
||
if (TagManager.TryGetShortTagByName("过热度[K]", out ShortValueTag? SuperheatShortTag))
|
||
{
|
||
Superheat = SuperheatShortTag!;
|
||
}
|
||
if (TagManager.TryGetShortTagByName("过冷度[K]", out ShortValueTag? SubcoolShortTag))
|
||
{
|
||
Subcool = SubcoolShortTag!;
|
||
}
|
||
|
||
// 干度标签(如不存在则仅跳过写入,不抛异常)。优先使用“干度[%]”,其次“干度[-]”。
|
||
if (TagManager.TryGetShortTagByName("干度[%]", out ShortValueTag? drynessPct))
|
||
{
|
||
DrynessTag = drynessPct!;
|
||
}
|
||
|
||
SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath");
|
||
SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen");
|
||
|
||
RtScanDeviceStart();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前的配置
|
||
/// </summary>
|
||
public SuperHeatCoolConfigModel SuperHeatCoolConfig { get; set; } = new SuperHeatCoolConfigModel();
|
||
|
||
/// <summary>
|
||
/// 保存配置信息
|
||
/// </summary>
|
||
public void SaveSuperHeatCoolConfig()
|
||
{
|
||
ConfigHelper.SetValue("FluidsPath", SuperHeatCoolConfig.FluidsPath);
|
||
ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 吸气压力
|
||
/// </summary>
|
||
public ShortValueTag InhPressTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 吸气温度
|
||
/// </summary>
|
||
public ShortValueTag InhTempTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 液体阀前温度
|
||
/// </summary>
|
||
public ShortValueTag TxvFrTempTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 液体阀前压力
|
||
/// </summary>
|
||
public ShortValueTag TxvFrPressTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 过热度
|
||
/// </summary>
|
||
public ShortValueTag Superheat { get; set; }
|
||
|
||
/// <summary>
|
||
/// 过冷度
|
||
/// </summary>
|
||
public ShortValueTag Subcool { get; set; }
|
||
|
||
/// <summary>
|
||
/// 干度(无量纲 [-])
|
||
/// </summary>
|
||
public ShortValueTag DrynessTag { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// 气路阀前压力(BarA)
|
||
/// </summary>
|
||
public ShortValueTag GasPreValvePressTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 气路阀前温度(℃)
|
||
/// </summary>
|
||
public ShortValueTag GasPreValveTempTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 冷媒流量kg/h
|
||
/// </summary>
|
||
public ShortValueTag VRVTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 液体流量(kg/h)
|
||
/// 液冷媒流量kg/h=液体流量kg/h
|
||
/// </summary>
|
||
public ShortValueTag LiqRefFlowTag { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// 风量数据-乘以系数的后的最终结果
|
||
/// </summary>
|
||
private double AirVolumeData { get; set; } = 0.0;
|
||
|
||
/// <summary>
|
||
/// 风量数据-公式计算的原始数据
|
||
/// </summary>
|
||
private double AirVolumeDataSource { get; set; } = 0;
|
||
|
||
/// <summary>
|
||
/// 风量喷嘴启用信息数据
|
||
/// </summary>
|
||
private string RozzleEnableInfo { get; set; } = "";
|
||
|
||
/// <summary>
|
||
/// 启用计算
|
||
/// </summary>
|
||
private bool RtCalcEnable { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// 触发日志
|
||
/// </summary>
|
||
private bool DebugLog { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// PLC扫描线程
|
||
/// </summary>
|
||
private void RtScanDeviceStart()
|
||
{
|
||
CalcTask = Task.Run(async () =>
|
||
{
|
||
while (RtCalcEnable)
|
||
{
|
||
await Task.Delay(1000);
|
||
try
|
||
{
|
||
|
||
long iErr, kph = 1;
|
||
|
||
double te = 0.0, te1 = 0.0, p = 0.0, p1 = 0.0, d = 0.0, Dl = 0.0, Dv = 0.0, q = 0.0, h = 0.0, ee = 0.0, hh = 0.0, ss = 0.0, cp = 0.0, cv = 0.0, w = 0.0;
|
||
double[] x = new double[20], xliq = new double[20], xvap = new double[20];
|
||
double[] xlkg = new double[20], xvkg = new double[20];
|
||
|
||
double tk = 0.0, wm = 0.0, prevDeltaH = 0.0;//prevDeltaH 未使用
|
||
|
||
//textBox5.Text = "";
|
||
|
||
string hpath = SuperHeatCoolConfig.FluidsPath;
|
||
long size = hpath.Length;
|
||
|
||
hpath += new String(' ', 255 - (int)size);
|
||
IRefProp64.SETPATHdll(hpath, ref size);
|
||
|
||
long numComps = 1;//冷媒个数
|
||
//string hfld = "R1234YF.FLD"; R1234YF
|
||
string hfld = "";
|
||
if (SuperHeatCoolConfig.Cryogen == "R134a")
|
||
{
|
||
hfld = "R134A.FLD";
|
||
}
|
||
else
|
||
{
|
||
hfld = "R134A.FLD";
|
||
}
|
||
//string hfld = "R134A.FLD";
|
||
//string hfld = Convert.ToString(comboBox1.Text);//从控件获取数据
|
||
size = hfld.Length;
|
||
|
||
hfld += new String(' ', 10000 - (int)size);
|
||
|
||
//string hfmix = "hmx.bnc" + new String(' ', 255);//原来的
|
||
string hfmix = "hmx.bnc" + new String(' ', 255);//248
|
||
string hrf = "DEF";
|
||
string herr = new String(' ', 255);
|
||
|
||
iErr = 0;
|
||
long hfldLen = hfld.Length, hfmixLen = hfmix.Length, hrfLen = hrf.Length, herrLen = herr.Length;
|
||
|
||
//numComps = -1;
|
||
IRefProp64.SETUPdll(ref numComps, ref hfld, ref hfmix, ref hrf, ref iErr, ref herr, ref hfldLen, ref hfmixLen, ref hrfLen, ref herrLen);
|
||
|
||
double version = iErr / 10000.0;//错误信息
|
||
if (iErr != 0)
|
||
{
|
||
//MessageBox.Show(herr, "RefProp SETUPdll() Error ", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
return;
|
||
}
|
||
|
||
IRefProp64.WMOLdll(x, ref wm);
|
||
|
||
//p = Convert.ToDouble(textBox2.Text) * 1000.0;//textBox2 Comp.吸气压力(kpa)
|
||
p = (InhPressTag.PVModel.EngValue) * 100.0;//textBox2 Comp.吸气压力(kpa)
|
||
kph = 1;
|
||
|
||
p1 = (TxvFrPressTag.PVModel.EngValue) * 100.0;//textBox3 Evap.膨胀阀前压力(Mpa)
|
||
//p1 = Convert.ToDouble(textBox3.Text) * 1000.0;//textBox3 Evap.膨胀阀前压力(Mpa)
|
||
IRefProp64.SATPdll(ref p, x, ref kph, ref te, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen);
|
||
|
||
if (iErr == 0)
|
||
Superheat.PVModel.EngValue = InhTempTag.PVModel.EngValue - (te - 273.15);
|
||
//textBox5.Text = String.Format("{0:n4}", Convert.ToDouble(textBox1.Text) - (te - 273.15));//textBox1 Comp.吸气温度(℃)
|
||
else
|
||
Superheat.PVModel.EngValue = 0;
|
||
IRefProp64.SATPdll(ref p1, x, ref kph, ref te1, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen);
|
||
if (iErr == 0)
|
||
Subcool.PVModel.EngValue = TxvFrTempTag.PVModel.EngValue - (te1 - 273.15);//textBox4 Evap.膨胀阀前温度(℃)
|
||
//GuoLengDu = (te1 - 273.15) - ListKRLogCellValue.Find(a => a.Name == "膨胀阀前温度").Value;//textBox4 Evap.膨胀阀前温度(℃)
|
||
//textBox6.Text = String.Format("{0:n4}", Convert.ToDouble(textBox4.Text) - (te1 - 273.15));//textBox4 Evap.膨胀阀前温度(℃)
|
||
else
|
||
Subcool.PVModel.EngValue = 0;
|
||
|
||
|
||
|
||
//干度技术
|
||
|
||
//气体流量kg/h=冷媒流量kg/h-液冷媒流量kg/h
|
||
var GasFlowKgPerH = VRVTag.PVModel.EngValue - LiqRefFlowTag.PVModel.EngValue;//气体流量kg/h
|
||
|
||
//摩尔质量(kg/mol)
|
||
var molarMassKgPerMol = GetMolarMass();
|
||
|
||
//定义气相质量焓 kJ/(kg·K)
|
||
var Gas_kJkgK = 0.0;
|
||
|
||
//步骤1:计算气路阀前气相焓h vap(单相气相)
|
||
if (TryTPRHO_VaporDensity_ByTP_MPa_C(GasPreValvePressTag.PVModel.EngValue * 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
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString()));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定组分的摩尔质量(kg/mol)
|
||
/// </summary>
|
||
/// <param name="componentId">组分ID(icomp)</param>
|
||
/// <returns>摩尔质量(kg/mol)</returns>
|
||
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();
|
||
|
||
/// <summary>
|
||
///
|
||
/// 计算获取 气相密度D_vap mol/l
|
||
///
|
||
/// TPRHOdll 封装:按 T(℃)、P(MPa) 计算“气相”摩尔密度 D [mol/L]
|
||
/// LabVIEW 对应:kph=2(气相),kguess=0
|
||
/// </summary>
|
||
/// <param name="pressureMPa">气体阀前压力mpa</param>
|
||
/// <param name="temperatureC">气体阀前温度 C</param>
|
||
/// <param name="densityMolPerL">“气相”摩尔密度 D [mol/L] / 气相密度D_vap mol/l</param>
|
||
/// <param name="error">ERR</param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 计算气路阀前气相焓h_vap(单相气相)
|
||
///
|
||
/// THERMdll 封装:按 T(℃) 与上一步得到的 D[mol/L] 计算气相熵 s [kJ/(kg·K)]
|
||
/// LabVIEW 对应:THERM(T, D) -> s[J/(mol·K)],再 /M[kg/mol] 并 *0.001
|
||
/// </summary>
|
||
/// <param name="temperatureC">气体阀前温度 C</param>
|
||
/// <param name="densityMolPerL">气相密度D_vap mol/l</param>
|
||
/// <param name="entropy_kJ_per_kgK"></param>
|
||
/// <param name="error"></param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
///
|
||
/// 计算液路阀前液相密度D_liq(单相液相) 液相密度D_liq mol/L
|
||
///
|
||
/// TPRHOdll 封装(液相):按 T(℃)、P(MPa) 计算“液相”摩尔密度 D_liq [mol/L]
|
||
/// LabVIEW 对应:kph=1(液相),kguess=0
|
||
/// </summary>
|
||
/// <param name="pressureMPa"></param>
|
||
/// <param name="temperatureC"></param>
|
||
/// <param name="densityMolPerL"></param>
|
||
/// <param name="error"></param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 计算获取 液相质量焓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
|
||
/// </summary>
|
||
/// <param name="temperatureC">液体阀前温度'℃</param>
|
||
/// <param name="densityMolPerL">液相密度D_liq mol/L</param>
|
||
/// <param name="h_liq_kJ_per_kg">液相质量焓h lig k/kg</param>
|
||
/// <param name="error">Err</param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// SATPdll 封装:由压力 P(MPa) 求饱和温度 Tsat[K]、饱和液/气摩尔密度 Dl/Dv [mol/L]
|
||
/// LabVIEW 对应:P(MPa)*1000 -> kPa;kph=1(bubble)
|
||
/// </summary>
|
||
/// <param name="pressureMPa">王缩机吸气压力mpa</param>
|
||
/// <param name="tSatK">吸气压力下的饱和温度T_sat K</param>
|
||
/// <param name="Dl_molL">饱和液相密度Dl molL</param>
|
||
/// <param name="Dv_molL">饱和气相密度Dv mol/L</param>
|
||
/// <param name="error"></param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// THERMdll 封装:由 T[K] 与 D[mol/L] 计算质量比焓 h[kJ/kg]
|
||
/// LabVIEW 对应:THERM(T, D) -> h[J/mol],再 /M[kg/mol] * 0.001
|
||
/// </summary>
|
||
/// <param name="temperatureK"></param>
|
||
/// <param name="densityMolPerL"></param>
|
||
/// <param name="h_kJ_per_kg"></param>
|
||
/// <param name="error"></param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 便捷:由压力 P(MPa) 直接得到“饱和液”质量比焓 h_liq[kJ/kg]
|
||
/// 流程:SATP(P)->Tsat/Dl,再 THERM(Tsat, Dl)
|
||
/// </summary>
|
||
/// <param name="pressureMPa"></param>
|
||
/// <param name="h_liq_kJkg"></param>
|
||
/// <param name="error"></param>
|
||
/// <returns></returns>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 便捷:由压力 P(MPa) 直接得到“饱和气”质量比焓 h_vap[kJ/kg]
|
||
/// 流程:SATP(P)->Tsat/Dv,再 THERM(Tsat, Dv)
|
||
/// </summary>
|
||
/// <param name="pressureMPa"></param>
|
||
/// <param name="h_vap_kJkg"></param>
|
||
/// <param name="error"></param>
|
||
/// <returns></returns>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按图片的最终流程计算干度:
|
||
/// 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
|
||
/// </summary>
|
||
/// <param name="hVap_kJkg">气相质量焓 h_vap [kJ/kg]</param>
|
||
/// <param name="hLiq_kJkg">液相质量焓 h_liq [kJ/kg]</param>
|
||
/// <param name="mGas_kg_h">气体质量流量 mg [kg/h]</param>
|
||
/// <param name="mLiq_kg_h">液体质量流量 ml [kg/h]</param>
|
||
/// <param name="hSatL_kJkg">饱和液质量焓 h_l [kJ/kg]</param>
|
||
/// <param name="hSatV_kJkg">饱和气质量焓 h_v [kJ/kg]</param>
|
||
/// <param name="dryness">输出干度 x ∈ [0,1]</param>
|
||
/// <param name="hMix_kJkg">输出混合后总比焓 h_mix [kJ/kg]</param>
|
||
/// <param name="error">Err</param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
}
|
||
}
|