Files
CapMachine/CapMachine.Wpf/Services/PPCService.cs
Tyrone CT 802348a743 csv导入和导出的问题
排量配置的问题
物性参数回差值配置的问题
2026-04-30 11:35:27 +08:00

944 lines
36 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using CapMachine.Core;
using CapMachine.Wpf.Models.PPCalc;
using CapMachine.Wpf.Models.Tag;
using CapMachine.Wpf.PPCalculation;
using Prism.Mvvm;
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace CapMachine.Wpf.Services
{
/// <summary>
/// 物性计算的服务
/// </summary>
public class PPCService : BindableBase
{
/// <summary>
/// 计算扫描 Task
/// </summary>
private static Task CalcTask { get; set; } = Task.CompletedTask;
public ConfigService ConfigService { get; }
public ILogService Logger { get; }
public MachineRtDataService MachineRtDataService { get; }
private readonly EnthalpyDrynessCalculator _enthalpyDrynessCalculator;
private readonly ThermodynamicSixResultsCalculator _thermodynamicSixResultsCalculator;
private readonly SuperheatSubcoolCalculator _superheatSubcoolCalculator;
/// <summary>
/// 标签中心
/// </summary>
public TagManager TagManager { get; set; }
/// <summary>
/// 实例化
/// </summary>
public PPCService(ConfigService configService, ILogService logService,
MachineRtDataService machineRtDataService)
{
ConfigService = configService;
Logger = logService;
MachineRtDataService = machineRtDataService;
_enthalpyDrynessCalculator = new EnthalpyDrynessCalculator(_refpropLock);
_thermodynamicSixResultsCalculator = new ThermodynamicSixResultsCalculator(_refpropLock);
_superheatSubcoolCalculator = new SuperheatSubcoolCalculator(_refpropLock);
TagManager = MachineRtDataService.TagManger;
if (TagManager.TryGetShortTagByName("转速[rpm]", out ShortValueTag? speedShortTag))
{
SpeedTag = speedShortTag!;
}
if (TagManager.TryGetShortTagByName("排气压力[BarA]", out ShortValueTag? exPressShortTag))
{
ExPressTag = exPressShortTag!;
}
if (TagManager.TryGetShortTagByName("排气温度[℃]", out ShortValueTag? exTempShortTag))
{
ExTempTag = exTempShortTag!;
}
if (TagManager.TryGetShortTagByName("HV[W]", out ShortValueTag? hvPwShortTag))
{
HVPwTag = hvPwShortTag!;
}
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!;
}
if (TagManager.TryGetShortTagByName("制热量Qh[W]", out ShortValueTag? heatingCapacityTag))
{
HeatingCapacityTag = heatingCapacityTag!;
}
if (TagManager.TryGetShortTagByName("压缩机性能系数(制热COP)", out ShortValueTag? copHeatTag))
{
COPHeatTag = copHeatTag!;
}
if (TagManager.TryGetShortTagByName("等熵效率ns[%]", out ShortValueTag? isentrpEffTag))
{
IsentrpEffTag = isentrpEffTag!;
}
if (TagManager.TryGetShortTagByName("制冷量Qc[W]", out ShortValueTag? coolCapacityTag))
{
CoolCapacityTag = coolCapacityTag!;
}
if (TagManager.TryGetShortTagByName("压缩机性能系数(制冷COP)", out ShortValueTag? copCoolTag))
{
COPCoolTag = copCoolTag!;
}
if (TagManager.TryGetShortTagByName("容积效率nv[%]", out ShortValueTag? voltricEffTag))
{
VoltricEffTag = voltricEffTag!;
}
SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath");
SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen");
ReloadTherdyH3TempOffset();
// 订阅 ConfigService.CurExpInfo 属性变化,实验切换时自动刷新排量缓存
ConfigService.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == nameof(ConfigService.CurExpInfo))
{
RefreshDisplacementCache();
}
};
// 首次初始化排量缓存(在订阅事件前初始化,避免首次触发不必要的刷新)
RefreshDisplacementCache();
RtScanDeviceStart();
}
/// <summary>
/// 当前的配置
/// </summary>
public SuperHeatCoolConfigModel SuperHeatCoolConfig { get; set; } = new SuperHeatCoolConfigModel();
private const string TherdyH3TempOffsetConfigKey = "Therdy_H3TempOffset_C";
private double _therdyH3TempOffset_C = -10.0;
public double TherdyH3TempOffset_C
{
get { return _therdyH3TempOffset_C; }
private set
{
_therdyH3TempOffset_C = value;
RaisePropertyChanged();
}
}
/// <summary>
/// 保存配置信息
/// </summary>
public void SaveSuperHeatCoolConfig()
{
ConfigHelper.SetValue("FluidsPath", SuperHeatCoolConfig.FluidsPath ?? string.Empty);
ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen ?? string.Empty);
}
public void ReloadTherdyH3TempOffset()
{
double offsetC = -10.0;
try
{
string raw = ConfigHelper.GetValue(TherdyH3TempOffsetConfigKey);
if (!string.IsNullOrWhiteSpace(raw) && double.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed))
{
offsetC = parsed;
}
}
catch
{
}
TherdyH3TempOffset_C = offsetC;
_thermodynamicSixResultsCalculator.SetH3TempOffset_C(offsetC);
}
/// <summary>
/// 吸气压力
/// </summary>
public ShortValueTag InhPressTag { get; set; } = null!;
/// <summary>
/// 转速标签
/// </summary>
public ShortValueTag? SpeedTag { get; set; }
/// <summary>
/// 排气压力
/// </summary>
public ShortValueTag? ExPressTag { get; set; }
/// <summary>
/// 排气温度
/// </summary>
public ShortValueTag? ExTempTag { get; set; }
/// <summary>
/// 压缩机功率HV 电源)
/// </summary>
public ShortValueTag? HVPwTag { get; set; }
/// <summary>
/// 吸气温度
/// </summary>
public ShortValueTag InhTempTag { get; set; } = null!;
/// <summary>
/// 液体阀前温度
/// </summary>
public ShortValueTag TxvFrTempTag { get; set; } = null!;
/// <summary>
/// 液体阀前压力
/// </summary>
public ShortValueTag TxvFrPressTag { get; set; } = null!;
/// <summary>
/// 过热度
/// </summary>
public ShortValueTag Superheat { get; set; } = null!;
/// <summary>
/// 过冷度
/// </summary>
public ShortValueTag Subcool { get; set; } = null!;
/// <summary>
/// 干度(无量纲 [-]
/// </summary>
public ShortValueTag DrynessTag { get; set; } = null!;
public ShortValueTag? HeatingCapacityTag { get; set; }
public ShortValueTag? COPHeatTag { get; set; }
public ShortValueTag? IsentrpEffTag { get; set; }
public ShortValueTag? CoolCapacityTag { get; set; }
public ShortValueTag? COPCoolTag { get; set; }
public ShortValueTag? VoltricEffTag { get; set; }
private double _DrynessTag2Value;
/// <summary>
/// 干度2无量纲 [-]
/// </summary>
public double DrynessTag2Value
{
get { return _DrynessTag2Value; }
set { _DrynessTag2Value = value; RaisePropertyChanged(); }
}
/// <summary>
/// 气路阀前压力BarA
/// </summary>
public ShortValueTag GasPreValvePressTag { get; set; } = null!;
/// <summary>
/// 气路阀前温度(℃)
/// </summary>
public ShortValueTag GasPreValveTempTag { get; set; } = null!;
/// <summary>
/// 冷媒流量kg/h
/// 气体流量+液体流量=冷媒流量
/// </summary>
public ShortValueTag VRVTag { get; set; } = null!;
/// <summary>
/// 液体流量kg/h
/// 液冷媒流量kg/h=液体流量kg/h
/// </summary>
public ShortValueTag LiqRefFlowTag { get; set; } = null!;
/// <summary>
/// 润滑油流量kg/h
/// </summary>
public ShortValueTag LubeFlowTag { get; set; } = null!;
/// <summary>
/// 启用计算
/// </summary>
private bool RtCalcEnable { get; set; } = true;
/// <summary>
/// PLC扫描线程
/// </summary>
private void RtScanDeviceStart()
{
CalcTask = Task.Run(async () =>
{
while (RtCalcEnable)
{
await Task.Delay(100);
try
{
double[] x = new double[20];
double wm = 0.0;
// 幂等初始化:仅首次或工质/路径变化时执行 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);
UpdateSuperheatAndSubcool_BySatp();
//这里写死干度的输入数据
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(
gasPreValvePressBarA,
gasPreValveTempC,
txvFrPressBarA,
txvFrTempC,
inhPressBarA,
vrvFlowKgPerH,
liqRefFlowKgPerH,
lubeFlowKgPerH));
//以前会受 LubeFlowTag 影响,现在由于固定为 0会与 Dryness1 更接近/一致(只要两者公式差异仅在油这一项)
if (drynessResult.IsDryness1Success)
{
if (DrynessTag != null)
{
//DrynessTag.PVModel.EngValue = drynessResult.Dryness1_01 * 100.0;
DrynessTag.PVModel.EngValue = Math.Round(drynessResult.Dryness1_01, 4);
}
//DrynessTag2Value = drynessResult.Dryness1_01 * 100.0;
//if (drynessResult.Dryness1_01 <= 0)
//{
// LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy(SuccessButZero)",
// string.Empty,
// drynessResult.Dryness1_01,
// drynessResult.HMix1_kJkg,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// 0,
// LiqRefFlowTag.PVModel.EngValue);
//}
}
else
{
//LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy(Fail)",
// drynessResult.Error1,
// double.NaN,
// double.NaN,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// 0,
// LiqRefFlowTag.PVModel.EngValue);
}
if (drynessResult.IsDryness2Success)
{
//DrynessTag.PVModel.EngValue = drynessResult.Dryness2_01 * 100.0;
DrynessTag2Value = Math.Round(drynessResult.Dryness2_01,4) ;
//if (drynessResult.Dryness2_01 <= 0)
//{
// LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy2(SuccessButZero)",
// string.Empty,
// drynessResult.Dryness2_01,
// drynessResult.HMix2_kJkg,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// LubeFlowTag.PVModel.EngValue,
// LiqRefFlowTag.PVModel.EngValue);
//}
}
else
{
//LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy2(Fail)",
// drynessResult.Error2,
// double.NaN,
// double.NaN,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// LubeFlowTag.PVModel.EngValue,
// 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))
{
//Logger?.Warn($"六个物性结果计算警告: {thermoErr}");
}
}
else
{
if (!string.IsNullOrWhiteSpace(thermoErr))
{
//Logger?.Error($"六个物性结果计算失败: {thermoErr}");
}
}
}
catch (Exception ex)
{
Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace ?? string.Empty, ex.Message));
}
}
});
}
private void UpdateSuperheatAndSubcool_BySatp()
{
_superheatSubcoolCalculator.Calculate(
inhPressBarA: InhPressTag.PVModel.EngValue,
inhTempC: InhTempTag.PVModel.EngValue,
txvFrPressBarA: TxvFrPressTag.PVModel.EngValue,
txvFrTempC: TxvFrTempTag.PVModel.EngValue,
out var superheatK,
out var subcoolK);
Superheat.PVModel.EngValue = superheatK;
Subcool.PVModel.EngValue = subcoolK;
}
// 若类中尚未定义,请添加全局互斥锁,串行化所有 REFPROP 调用
private static readonly object _refpropLock = new object();
// REFPROP 初始化状态(全局、幂等)
private static volatile bool _rpInitialized = false;
/// <summary>
/// 幂等初始化:设置流体路径/工质/参考态;确保全局只初始化一次。
/// 注意:所有 REFPROP 原生调用都需在 _refpropLock 下串行化,包括初始化调用。
/// </summary>
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;
return true;
}
}
catch (Exception ex)
{
error = $"REFPROP 初始化异常: {ex.Message}";
Logger.Error(error);
_rpInitialized = false;
return false;
}
}
///制热量、压缩机性能系数COP制热、等熵效率、制冷量、压缩机性能系数COP(制冷)、容积效率 计算
#region
/// <summary>
/// 缓存的压缩机排量值cc在实验切换时自动刷新
/// </summary>
private double _cachedDisplacement_cc = double.NaN;
/// <summary>
/// 缓存数据来源标识用于调试ExpInfo=实验信息, Config=配置文件, Default=默认值
/// </summary>
private string _cachedSource = string.Empty;
private int _CurDisplacementCc;
/// <summary>
/// 当前的排量信息(供 UI 展示)
/// </summary>
public int CurDisplacementCc
{
get { return _CurDisplacementCc; }
set { _CurDisplacementCc = value; RaisePropertyChanged(); }
}
private double _HeatingCapacityQh_kW;
/// <summary>
/// 制热量 Qh [kW]
/// </summary>
public double HeatingCapacityQh_kW
{
get { return _HeatingCapacityQh_kW; }
set { _HeatingCapacityQh_kW = value; RaisePropertyChanged(); }
}
private double _COPHeating;
/// <summary>
/// 压缩机性能系数 COP制热[-]
/// </summary>
public double COPHeating
{
get { return _COPHeating; }
set { _COPHeating = value; RaisePropertyChanged(); }
}
private double _IsentropicEfficiencyPct;
/// <summary>
/// 等熵效率 ηs [%]
/// </summary>
public double IsentropicEfficiencyPct
{
get { return _IsentropicEfficiencyPct; }
set { _IsentropicEfficiencyPct = value; RaisePropertyChanged(); }
}
private double _CoolingCapacityQc_kW;
/// <summary>
/// 制冷量 Qc [kW]
/// </summary>
public double CoolingCapacityQc_kW
{
get { return _CoolingCapacityQc_kW; }
set { _CoolingCapacityQc_kW = value; RaisePropertyChanged(); }
}
private double _COPCooling;
/// <summary>
/// 压缩机性能系数 COP制冷[-]
/// </summary>
public double COPCooling
{
get { return _COPCooling; }
set { _COPCooling = value; RaisePropertyChanged(); }
}
private double _VolumetricEfficiencyPct;
/// <summary>
/// 容积效率 ηv [%]
/// </summary>
public double VolumetricEfficiencyPct
{
get { return _VolumetricEfficiencyPct; }
set { _VolumetricEfficiencyPct = value; RaisePropertyChanged(); }
}
/// <summary>
/// 按流程图更新制热量、COP(制热)、等熵效率、制冷量、COP(制冷)、容积效率。
/// </summary>
/// <param name="error">
/// 错误/警告信息输出。
/// - 当方法返回 <see langword="false"/> 时,<paramref name="error"/> 为失败原因,调用方应视为本周期计算无效。
/// - 当方法返回 <see langword="true"/> 但 <paramref name="error"/> 非空时,表示仅部分结果无法计算(例如缺少排量导致容积效率为 NaN
/// </param>
/// <returns>
/// 是否成功完成本周期的结果更新。
/// - <see langword="true"/>:至少已成功更新 Qh/Qc/COP/ηs 等主要结果;容积效率可能因缺失排量而为 NaN。
/// - <see langword="false"/>:关键输入或 REFPROP 计算失败,本周期结果不更新。
/// </returns>
private bool TryUpdateThermodynamicSixResults(out string error)
{
error = string.Empty;
if (InhPressTag == null || InhTempTag == null)
{
error = "缺少吸气压力/吸气温度标签";
return false;
}
if (TxvFrPressTag == null || TxvFrTempTag == null)
{
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 -> BarAkW -> 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
if (!double.IsNaN(w_W) && !double.IsInfinity(w_W) && w_W == 0)
{
HeatingCapacityQh_kW = 0;
CoolingCapacityQc_kW = 0;
COPHeating = 0;
COPCooling = 0;
IsentropicEfficiencyPct = 0;
VolumetricEfficiencyPct = 0;
if (HeatingCapacityTag != null)
{
HeatingCapacityTag.PVModel.EngValue = 0;
}
if (COPHeatTag != null)
{
COPHeatTag.PVModel.EngValue = 0;
}
if (IsentrpEffTag != null)
{
IsentrpEffTag.PVModel.EngValue = 0;
}
if (CoolCapacityTag != null)
{
CoolCapacityTag.PVModel.EngValue = 0;
}
if (COPCoolTag != null)
{
COPCoolTag.PVModel.EngValue = 0;
}
if (VoltricEffTag != null)
{
VoltricEffTag.PVModel.EngValue = 0;
}
return true;
}
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 = calcErr;
return false;
}
HeatingCapacityQh_kW = r.HeatingCapacityQh_kW;
CoolingCapacityQc_kW = r.CoolingCapacityQc_kW;
COPHeating = r.COPHeating;
COPCooling = r.COPCooling;
IsentropicEfficiencyPct = r.IsentropicEfficiencyPct;
if (HeatingCapacityTag != null)
{
HeatingCapacityTag.PVModel.EngValue = HeatingCapacityQh_kW * 1000.0;
}
if (COPHeatTag != null)
{
COPHeatTag.PVModel.EngValue = COPHeating;
}
if (IsentrpEffTag != null)
{
IsentrpEffTag.PVModel.EngValue = IsentropicEfficiencyPct;
}
if (CoolCapacityTag != null)
{
CoolCapacityTag.PVModel.EngValue = CoolingCapacityQc_kW * 1000.0;
}
if (COPCoolTag != null)
{
COPCoolTag.PVModel.EngValue = COPCooling;
}
if (double.IsNaN(r.VolumetricEfficiencyPct) || double.IsInfinity(r.VolumetricEfficiencyPct))
{
VolumetricEfficiencyPct = double.NaN;
error = calcErr;
return true;
}
VolumetricEfficiencyPct = r.VolumetricEfficiencyPct;
if (VoltricEffTag != null)
{
VoltricEffTag.PVModel.EngValue = VolumetricEfficiencyPct;
}
error = calcErr;
return true;
}
/// <summary>
/// 获取压缩机排量(使用缓存机制,避免每次计算周期实时读取)
/// </summary>
/// <param name="displacement_cc">排量输出,单位 cccm³/rev。</param>
/// <param name="error">失败原因(仅在缓存异常时返回)。</param>
/// <returns>始终返回 true因为默认回退 35cc 保证缓存始终有效)。</returns>
private bool TryGetCompressorDisplacement_cc(out double displacement_cc, out string error)
{
displacement_cc = _cachedDisplacement_cc;
error = string.Empty;
return true;
}
/// <summary>
/// 刷新压缩机排量缓存,按优先级读取:实验信息 → 配置文件 → 默认值
/// 在实验切换时自动调用,保证缓存与当前实验信息一致
/// </summary>
private void RefreshDisplacementCache()
{
const double defaultDisplacementCc = 35d;
double displacementCc = defaultDisplacementCc;
string source = "Default";
// 优先级1从当前实验信息读取
if (ConfigService?.CurExpInfo != null &&
TryParseCompressorDisplacementTextToCc(ConfigService.CurExpInfo.CapDisplacement, out var expCc) &&
expCc > 0)
{
displacementCc = expCc;
source = "ExpInfo";
}
// 优先级2从 App.config 配置读取
else if (ConfigHelper.IsExist("CompressorDisplacementCc") &&
TryParseCompressorDisplacementTextToCc(ConfigHelper.GetValue("CompressorDisplacementCc"), out var cfgCc) &&
cfgCc > 0)
{
displacementCc = cfgCc;
source = "Config";
}
// 优先级3使用默认值
else
{
displacementCc = defaultDisplacementCc;
source = "Default";
}
// 更新缓存字段
_cachedDisplacement_cc = displacementCc;
_cachedSource = source;
// 同步更新 UI 展示属性
CurDisplacementCc = (int)displacementCc;
// 记录日志(便于调试)
Logger?.Info($"压缩机排量缓存已刷新: {displacementCc}cc (来源: {source})");
}
/// <summary>
/// 强制刷新压缩机排量缓存(供外部主动调用,用于调试或特殊情况如 App.config 配置修改后)
/// </summary>
public void ForceRefreshDisplacementCache()
{
RefreshDisplacementCache();
}
private static bool TryParseCompressorDisplacementTextToCc(string? text, out double displacementCc)
{
displacementCc = double.NaN;
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
var normalized = text.Trim().ToLowerInvariant();
normalized = normalized.Replace(',', '.');
var match = Regex.Match(normalized, @"[-+]?\d+(\.\d+)?");
if (!match.Success)
{
return false;
}
if (!double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var v))
{
return false;
}
displacementCc = v;
return true;
}
#endregion
}
}