806 lines
28 KiB
C#
806 lines
28 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.Globalization;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
|
||
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 SuperheatSubcoolCalculator _superheatSubcoolCalculator;
|
||
private readonly ThermodynamicSixResultsCalculator _thermodynamicSixResultsCalculator;
|
||
|
||
/// <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
|
||
)
|
||
{
|
||
ConfigService = configService;
|
||
//事件服务
|
||
_EventAggregator = eventAggregator;
|
||
DataRecordService = dataRecordService;
|
||
SysRunServer = sysRunService;
|
||
Logger = logService;
|
||
MachineRtDataService = machineRtDataService;
|
||
DialogService = dialogService;
|
||
TagManager = MachineRtDataService.TagManger;
|
||
|
||
SpeedTag = TagManager.DicTags.GetValueOrDefault("转速[rpm]");
|
||
|
||
ExPressTag = TagManager.DicTags.GetValueOrDefault("排气压力[BarA]");
|
||
|
||
ExTempTag = TagManager.DicTags.GetValueOrDefault("排气温度[℃]");
|
||
|
||
HVPwTag = TagManager.DicTags.GetValueOrDefault("HV[W]");
|
||
|
||
InhPressTag = TagManager.DicTags.GetValueOrDefault("吸气压力[BarA]");
|
||
|
||
InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]");
|
||
|
||
TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]");
|
||
|
||
TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]");
|
||
|
||
GasPreValvePressTag = TagManager.DicTags.GetValueOrDefault("气路阀前压力[BarA]");
|
||
GasPreValveTempTag = TagManager.DicTags.GetValueOrDefault("气路阀前温度[℃]");
|
||
|
||
DrynessTag = TagManager.DicTags.GetValueOrDefault("干度[-]");
|
||
if (DrynessTag == null)
|
||
{
|
||
DrynessTag = TagManager.DicTags.GetValueOrDefault("干度");
|
||
}
|
||
|
||
VRVTag = TagManager.DicTags.GetValueOrDefault("冷媒流量[kg/h]");
|
||
if (VRVTag == null)
|
||
{
|
||
VRVTag = TagManager.DicTags.GetValueOrDefault("冷媒流量[kg/h]");
|
||
}
|
||
|
||
LiqRefFlowTag = TagManager.DicTags.GetValueOrDefault("液冷媒流量[kg/h]");
|
||
if (LiqRefFlowTag == null)
|
||
{
|
||
LiqRefFlowTag = TagManager.DicTags.GetValueOrDefault("液体流量[kg/h]");
|
||
}
|
||
|
||
LubeFlowTag = TagManager.DicTags.GetValueOrDefault("润滑油流量[kg/h]");
|
||
if (LubeFlowTag == null)
|
||
{
|
||
LubeFlowTag = TagManager.DicTags.GetValueOrDefault("润滑油流量[kg/h]");
|
||
}
|
||
|
||
Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]");
|
||
|
||
Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]");
|
||
|
||
HeatingCapacityTag = TagManager.DicTags.GetValueOrDefault("制热量Qh[KW]")
|
||
?? TagManager.DicTags.GetValueOrDefault("制热量Qh[W]");
|
||
COPHeatTag = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制热)[K]")
|
||
?? TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制热COP)");
|
||
IsentrpEffTag = TagManager.DicTags.GetValueOrDefault("等熵效率ns[%]");
|
||
CoolCapacityTag = TagManager.DicTags.GetValueOrDefault("制冷量Qc[KW]")
|
||
?? TagManager.DicTags.GetValueOrDefault("制冷量Qc[W]");
|
||
COPCoolTag = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制冷)[K]")
|
||
?? TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制冷COP)");
|
||
VoltricEffTag = TagManager.DicTags.GetValueOrDefault("容积效率nv[%]");
|
||
|
||
|
||
_superheatSubcoolCalculator = new SuperheatSubcoolCalculator(_refpropLock);
|
||
_thermodynamicSixResultsCalculator = new ThermodynamicSixResultsCalculator(_refpropLock);
|
||
|
||
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();
|
||
}
|
||
|
||
private const string TherdyH3TempOffsetConfigKey = "Therdy_H3TempOffset_C";
|
||
|
||
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 SuperHeatCoolConfigModel SuperHeatCoolConfig { get; set; } = new SuperHeatCoolConfigModel();
|
||
|
||
/// <summary>
|
||
/// 保存配置信息
|
||
/// </summary>
|
||
public void SaveSuperHeatCoolConfig()
|
||
{
|
||
ConfigHelper.SetValue("FluidsPath", SuperHeatCoolConfig.FluidsPath);
|
||
ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen);
|
||
}
|
||
|
||
private double _therdyH3TempOffset_C = -10.0;
|
||
public double TherdyH3TempOffset_C
|
||
{
|
||
get { return _therdyH3TempOffset_C; }
|
||
private set
|
||
{
|
||
_therdyH3TempOffset_C = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 吸气压力
|
||
/// </summary>
|
||
public ITag InhPressTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 转速标签
|
||
/// </summary>
|
||
public ITag? SpeedTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 排气压力
|
||
/// </summary>
|
||
public ITag? ExPressTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 排气温度
|
||
/// </summary>
|
||
public ITag? ExTempTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 压缩机功率(HV 电源)
|
||
/// </summary>
|
||
public ITag? HVPwTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 吸气温度
|
||
/// </summary>
|
||
public ITag InhTempTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 液体阀前温度
|
||
/// </summary>
|
||
public ITag TxvFrTempTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 液体阀前压力
|
||
/// </summary>
|
||
public ITag TxvFrPressTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 过热度
|
||
/// </summary>
|
||
public ITag Superheat { get; set; }
|
||
|
||
/// <summary>
|
||
/// 过冷度
|
||
/// </summary>
|
||
public ITag Subcool { get; set; }
|
||
|
||
/// <summary>
|
||
/// 干度(无量纲 [-])
|
||
/// </summary>
|
||
public ITag DrynessTag { get; set; }
|
||
|
||
private double _DrynessTag2Value;
|
||
/// <summary>
|
||
/// 干度2(无量纲 [-])
|
||
/// </summary>
|
||
public double DrynessTag2Value
|
||
{
|
||
get { return _DrynessTag2Value; }
|
||
set { _DrynessTag2Value = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 气路阀前压力(BarA)
|
||
/// </summary>
|
||
public ITag GasPreValvePressTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 气路阀前温度(℃)
|
||
/// </summary>
|
||
public ITag GasPreValveTempTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 冷媒流量kg/h
|
||
/// </summary>
|
||
public ITag VRVTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 液体流量(kg/h)
|
||
/// 液冷媒流量kg/h=液体流量kg/h
|
||
/// </summary>
|
||
public ITag LiqRefFlowTag { get; set; }
|
||
|
||
/// <summary>
|
||
/// 润滑油流量(kg/h)
|
||
/// </summary>
|
||
public ITag LubeFlowTag { get; set; }
|
||
|
||
|
||
public ITag? HeatingCapacityTag { get; set; }
|
||
|
||
public ITag? COPHeatTag { get; set; }
|
||
|
||
public ITag? IsentrpEffTag { get; set; }
|
||
|
||
public ITag? CoolCapacityTag { get; set; }
|
||
|
||
public ITag? COPCoolTag { get; set; }
|
||
|
||
public ITag? VoltricEffTag { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// 启用计算
|
||
/// </summary>
|
||
private bool RtCalcEnable { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// 触发日志
|
||
/// </summary>
|
||
private bool DebugLog { get; set; } = false;
|
||
|
||
/// <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>
|
||
/// PLC扫描线程
|
||
/// </summary>
|
||
private void RtScanDeviceStart()
|
||
{
|
||
CalcTask = Task.Run(async () =>
|
||
{
|
||
while (RtCalcEnable)
|
||
{
|
||
await Task.Delay(300);
|
||
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();
|
||
|
||
|
||
|
||
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.ToString(), ex.Message.ToString()));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
/// <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.EngPvValue;
|
||
double suctionTemp_C = InhTempTag.EngPvValue;
|
||
double dischargePress_BarA = ExPressTag.EngPvValue;
|
||
double dischargeTemp_C = ExTempTag.EngPvValue;
|
||
double txvFrPress_BarA = TxvFrPressTag.EngPvValue;
|
||
double txvFrTemp_C = TxvFrTempTag.EngPvValue;
|
||
double totalFlow_kg_h = VRVTag.EngPvValue;
|
||
double w_W = HVPwTag.EngPvValue;
|
||
|
||
double speed_rpm = SpeedTag?.EngPvValue ?? 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
|
||
|
||
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.EngPvValue = 0;
|
||
}
|
||
if (COPHeatTag != null)
|
||
{
|
||
COPHeatTag.EngPvValue = 0;
|
||
}
|
||
if (IsentrpEffTag != null)
|
||
{
|
||
IsentrpEffTag.EngPvValue = 0;
|
||
}
|
||
if (CoolCapacityTag != null)
|
||
{
|
||
CoolCapacityTag.EngPvValue = 0;
|
||
}
|
||
if (COPCoolTag != null)
|
||
{
|
||
COPCoolTag.EngPvValue = 0;
|
||
}
|
||
if (VoltricEffTag != null)
|
||
{
|
||
VoltricEffTag.EngPvValue = 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.EngPvValue = HeatingCapacityQh_kW * 1000.0;
|
||
}
|
||
if (COPHeatTag != null)
|
||
{
|
||
COPHeatTag.EngPvValue = COPHeating;
|
||
}
|
||
if (IsentrpEffTag != null)
|
||
{
|
||
IsentrpEffTag.EngPvValue = IsentropicEfficiencyPct;
|
||
}
|
||
if (CoolCapacityTag != null)
|
||
{
|
||
CoolCapacityTag.EngPvValue = CoolingCapacityQc_kW * 1000.0;
|
||
}
|
||
if (COPCoolTag != null)
|
||
{
|
||
COPCoolTag.EngPvValue = COPCooling;
|
||
}
|
||
|
||
if (double.IsNaN(r.VolumetricEfficiencyPct) || double.IsInfinity(r.VolumetricEfficiencyPct))
|
||
{
|
||
VolumetricEfficiencyPct = double.NaN;
|
||
error = calcErr;
|
||
return true;
|
||
}
|
||
|
||
VolumetricEfficiencyPct = r.VolumetricEfficiencyPct;
|
||
if (VoltricEffTag != null)
|
||
{
|
||
VoltricEffTag.EngPvValue = VolumetricEfficiencyPct;
|
||
}
|
||
|
||
error = calcErr;
|
||
return true;
|
||
}
|
||
|
||
|
||
private void UpdateSuperheatAndSubcool_BySatp()
|
||
{
|
||
_superheatSubcoolCalculator.Calculate(
|
||
inhPressBarA: InhPressTag.EngPvValue,
|
||
inhTempC: InhTempTag.EngPvValue,
|
||
txvFrPressBarA: TxvFrPressTag.EngPvValue,
|
||
txvFrTempC: TxvFrTempTag.EngPvValue,
|
||
out var superheatK,
|
||
out var subcoolK);
|
||
|
||
Superheat.EngPvValue = superheatK;
|
||
Subcool.EngPvValue = 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;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 获取压缩机排量(使用缓存机制,避免每次计算周期实时读取)
|
||
/// </summary>
|
||
/// <param name="displacement_cc">排量输出,单位 cc(cm³/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
|
||
{
|
||
string configValue = ConfigHelper.GetValue("CompressorDisplacementCc");
|
||
if (!string.IsNullOrWhiteSpace(configValue) &&
|
||
TryParseCompressorDisplacementTextToCc(configValue, out var cfgCc) &&
|
||
cfgCc > 0)
|
||
{
|
||
displacementCc = cfgCc;
|
||
source = "Config";
|
||
}
|
||
// 优先3:使用默认值(已在初始化时设置)
|
||
}
|
||
|
||
// 更新缓存字段
|
||
_cachedDisplacement_cc = displacementCc;
|
||
_cachedSource = source;
|
||
|
||
// 同步更新 UI 展示属性
|
||
CurDisplacementCc = (int)displacementCc;
|
||
|
||
// 记录日志(便于调试)
|
||
Logger?.Info($"压缩机排量缓存已刷新: {displacementCc}cc (来源: {source})");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 强制刷新压缩机排量缓存(供外部主动调用,用于调试或特殊情况如 App.config 配置修改后)
|
||
/// </summary>
|
||
public void ForceRefreshDisplacementCache()
|
||
{
|
||
RefreshDisplacementCache();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析压缩机排量文本,支持多种格式(如 34.5cc、34,5 cm3 等)
|
||
/// </summary>
|
||
/// <param name="text">排量文本</param>
|
||
/// <param name="displacementCc">解析出的排量值(cc)</param>
|
||
/// <returns>是否解析成功</returns>
|
||
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;
|
||
}
|
||
|
||
|
||
}
|
||
}
|