CSV 的报错的更改

This commit is contained in:
2026-04-29 20:51:27 +08:00
parent 53ded58da3
commit 74bf47ee62
10 changed files with 497 additions and 100 deletions

View File

@@ -0,0 +1,173 @@
# PPCalculation 物性计算说明
本文档用于说明当前工程中 **PPC 物性计算** 的整体数据流、输入输出、单位约定、关键规则,以及 REFPROP 调用链路,便于后续维护与复用。
## 1. 代码入口与职责边界
- **入口服务**`CapMachine.Wpf.Services.PPCService`
- 负责:
-`TagManager` 读取实时数据(压力、温度、功率、流量、转速等)。
- 调用物性计算器Calculator完成计算。
- 将结果写回 Tag干度、过热/过冷、Qh/Qc、COP、效率等
- 负责 REFPROP 初始化SETPATH/SETUP与全局串行化锁。
- 不负责:
- 具体的热力性质求解细节(已下沉到 Calculator
- **干度计算器**`CapMachine.Wpf.PPCalculation.EnthalpyDrynessCalculator`
- 职责:基于“加权混合焓 + 吸气压力下饱和焓差”的方法,计算干度。
- **六参数计算器**`CapMachine.Wpf.PPCalculation.ThermodynamicSixResultsCalculator`
- 职责:计算六个核心性能/效率结果:
- `Qc``Qh``COPc``COPh``ηs``ηv`
- 内部封装 REFPROP 计算链路、单位换算、以及关键规则(如 h3 的 Tsat-10°C
## 2. 单位约定(非常重要)
### 2.1 Tag 输入单位(来自 PLC/界面)
- 压力:`BarA`
- 温度:`℃`
- 功率:`W`HV[W]
- 冷媒总流量:`kg/h`
- 转速:`rpm`
- 排量:`cc`cm³/rev
### 2.2 REFPROP 常用单位(在 Calculator 内部处理)
REFPROP 不同接口参数单位不完全一致,工程中统一由 Calculator 进行换算。
- 温度:`K`
- 压力:常见为 `kPa`(例如 SATP/TPRHO 采用 kPa
- 摩尔密度:`mol/L`
- 焓:`J/mol`THERM 输出),最终换算为 `kJ/kg`
- 熵:`J/(mol·K)`THERM 输出),最终换算为 `kJ/(kg·K)`
典型换算:
- `BarA -> MPa`:乘 `0.1`
- `MPa -> kPa`:乘 `1000`
- `℃ -> K`:加 `273.15`
- `J/mol -> kJ/kg``(J/mol) / (kg/mol) * 0.001`
## 3. 运行周期与整体数据流
`PPCService.RtScanDeviceStart()` 周期性执行(约 100ms 一次),主要步骤:
1. **REFPROP 幂等初始化**(第一次进入或未初始化时)
- 调用 `EnsureRefpropInitialized()`
- 内部在 `_refpropLock` 下执行:
- `SETPATHdll` 设置流体库路径
- `SETUPdll` 载入工质(当前默认 `R134A.FLD`
2. **过热度/过冷度计算(当前仍在服务内直接调用 SATP**
- 吸气侧:由吸气压力求 `Tsat`,与吸气温度做差得到过热度
- 膨胀阀前侧:由膨胀阀前压力求 `Tsat`,与膨胀阀前温度做差得到过冷度
3. **干度计算**(调用 `EnthalpyDrynessCalculator`
- 读取:
- 气路阀前压力/温度GasPreValve P/T
- 膨胀阀前压力/温度TxvFr P/T
- 吸气压力InhP
- 冷媒总流量VRV
- 液体流量LiqRefFlow
- 当前实现中:润滑油流量 `lubeFlowKgPerH` 固定为 `0.0`(不参与干度计算)。
- 输出:
- `Dryness1` 写入 `干度[%]` Tag*100
- `Dryness2` 写入 `PPCService.DrynessTag2Value`*100用于界面绑定
4. **六参数计算**(调用 `ThermodynamicSixResultsCalculator.TryCalculate`
- 读取输入:
- 吸气 P/TBarA/℃)
- 排气 P/TBarA/℃)
- 膨胀阀前 P/TBarA/℃)
- 冷媒总流量kg/h
- HV 功率W
- 转速rpm
- 排量cc
- 输出写回:
- `制热量Qh[W]`kW * 1000
- `制冷量Qc[W]`kW * 1000
- `压缩机性能系数(制热)`COPh
- `压缩机性能系数(制冷)`COPc
- `等熵效率ns[%]`
- `容积效率nv[%]`
## 4. 干度计算EnthalpyDrynessCalculator概要
### 4.1 输入
- `GasPreValvePressBarA`
- `GasPreValveTempC`
- `TxvFrPressBarA`
- `TxvFrTempC`
- `InhPressBarA`
- `VrvFlowKgPerH`
- `LiqRefFlowKgPerH`
- `LubeFlowKgPerH`(当前传 0
### 4.2 核心思路
1. 分别求取:
- 气路阀前气相焓 `h_vap`
- 膨胀阀前液相焓 `h_liq`
2. 由总流量与液体流量推算气体流量,按质量流量计算混合焓 `h_mix`
3. 由吸气压力求饱和液/饱和气焓:`h_satL``h_satV`
4. 干度:
`x = (h_mix - h_satL) / (h_satV - h_satL)`
并进行必要的边界处理除零、NaN、限幅等由 Calculator 内实现)。
## 5. 六参数计算ThermodynamicSixResultsCalculator概要
### 5.1 输出结果
- `Qc`制冷量kW
- `Qh`制热量kW
- `COPc`:制冷 COP
- `COPh`:制热 COP
- `ηs`:等熵效率(%
- `ηv`:容积效率(%
### 5.2 h3液路阀前/冷凝出口关键规则Tsat-10°C
该规则已封装在 `ThermodynamicSixResultsCalculator` 内部:
- 由**排气压力**计算饱和温度 `Tsat_discharge`
-`T_for_h3 = Tsat_discharge - 10°C`
-`TxvFrPress``T_for_h3` 求液体点焓 `h3`
注意:这是为了对齐现场 LabVIEW 流程/截图对比,属于业务约束。
### 5.3 典型计算链路(概念级)
- h1/s1吸气点由吸气 P/T 求物性)
- h2排气点由排气 P/T 求物性)
- h2s等熵压缩出口点由排气压力 + s1 求物性)
- h3液体点由 TxvFrPress + (Tsat(DischargePress)-10°C) 求物性)
由焓差与质量流量计算 Qc/Qh并结合功率得到 COP。
## 6. REFPROP 初始化与线程安全
- `PPCService` 内使用静态锁:`_refpropLock`
- 原因REFPROP Fortran DLL 内部通常存在全局状态/非线程安全调用。
- 约束:
- `EnsureRefpropInitialized()` 必须在锁内进行。
- 所有直接 REFPROP 原生调用也必须串行化。
Calculator 内部同样通过外部传入的锁对象串行化 REFPROP 调用。
## 7. 结果写回约定
- `DrynessTag`:按百分比写回(`x * 100`)。
- `DrynessTag2Value`:按百分比用于界面绑定(`x * 100`)。
- `Qh/Qc`Tag 单位为 `W`Calculator 输出为 `kW`,写回时乘 `1000`
## 8. 扩展/维护建议
- 如后续希望让 `PPCService` 更纯粹:
- 可将过热度/过冷度计算也下沉到独立 Calculator避免在 Service 里直接调用 SATP
- 工质切换:
- 当前 `EnsureRefpropInitialized()` 默认只加载 `R134A.FLD`
- 若要支持 `R1234yf` 等,需要根据配置选择对应 `.FLD` 文件。

View File

@@ -0,0 +1,87 @@
using System;
namespace CapMachine.Wpf.PPCalculation
{
/// <summary>
/// 过热度/过冷度计算器。
///
/// 说明:该类仅负责封装 SATPdll 调用与计算公式,保持与历史流程一致。
/// - 压力换算保持原逻辑BarA * 100.0
/// - 温度换算保持原逻辑Tsat[K] - 273.15
/// - 失败时返回 0与原先在服务中直接写 Tag 的行为一致)
/// </summary>
public sealed class SuperheatSubcoolCalculator
{
private readonly object _refpropLock;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="refpropLock">REFPROP 全局锁REFPROP DLL 非线程安全,需串行化调用)。</param>
public SuperheatSubcoolCalculator(object refpropLock)
{
_refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock));
}
/// <summary>
/// 按既有流程计算过热度/过冷度。
/// </summary>
/// <param name="inhPressBarA">吸气压力BarA。</param>
/// <param name="inhTempC">吸气温度(℃)。</param>
/// <param name="txvFrPressBarA">膨胀阀前压力BarA。</param>
/// <param name="txvFrTempC">膨胀阀前温度(℃)。</param>
/// <param name="superheatK">过热度K。失败时为 0。</param>
/// <param name="subcoolK">过冷度K。失败时为 0。</param>
public void Calculate(
double inhPressBarA,
double inhTempC,
double txvFrPressBarA,
double txvFrTempC,
out double superheatK,
out double subcoolK)
{
superheatK = 0.0;
subcoolK = 0.0;
long iErr;
long kph = 1;
double te = 0.0;
double te1 = 0.0;
double p = 0.0;
double p1 = 0.0;
double Dl = 0.0;
double Dv = 0.0;
double[] x = new double[20], xliq = new double[20], xvap = new double[20];
x[0] = 1.0;
//p = Convert.ToDouble(textBox2.Text) * 1000.0;//textBox2 Comp.吸气压力kpa
p = inhPressBarA * 100.0;// 保持你原有流程:将 BarA 当作 MPa? 历史代码为 *1000.0,不改变你的算法
p1 = txvFrPressBarA * 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)
superheatK = Math.Abs(inhTempC - (te - 273.15));
else
superheatK = 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)
subcoolK = Math.Abs(txvFrTempC - (te1 - 273.15));
else
subcoolK = 0;
}
}
}