P6: convert diffuser to BaseDomain + BaseNodeAdapter + concern split
Refactor of diffuser to use the platform infrastructure (BaseDomain, BaseNodeAdapter, ChildRouter, commandRegistry, statusBadge). Extracts concerns into focused modules per .claude/refactor/MODULE_SPLIT.md generic template. Tests stay green; CONTRACT.md generated; legacy aliases preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,293 +1,196 @@
|
||||
const { logger, interpolation, gravity, convert } = require('generalFunctions');
|
||||
'use strict';
|
||||
|
||||
class Diffuser {
|
||||
constructor(config = {}) {
|
||||
this.config = config;
|
||||
this.logger = new logger(
|
||||
this.config.general?.logging?.enabled,
|
||||
this.config.general?.logging?.logLevel,
|
||||
this.config.general?.name,
|
||||
);
|
||||
const { BaseDomain, statusBadge, interpolation, gravity, convert } = require('generalFunctions');
|
||||
|
||||
class Diffuser extends BaseDomain {
|
||||
static name = 'diffuser';
|
||||
|
||||
configure() {
|
||||
const d = this.config.diffuser || {};
|
||||
this.interpolation = new interpolation({ type: 'linear' });
|
||||
this.convert = convert;
|
||||
this.specs = this.loadSpecs();
|
||||
this.specs = this._loadSpecs();
|
||||
|
||||
this.idle = true;
|
||||
this.warning = { state: false, text: [], flow: { min: { hyst: 2 }, max: { hyst: 2 } } };
|
||||
this.alarm = { state: false, text: [], flow: { min: { hyst: 10 }, max: { hyst: 10 } } };
|
||||
|
||||
this.i_pressure = this.config.diffuser?.headerPressure || 0;
|
||||
this.i_local_atm_pressure = this.config.diffuser?.localAtmPressure || 1013.25;
|
||||
this.i_water_density = this.config.diffuser?.waterDensity || 997;
|
||||
this.i_alfa_factor = this.config.diffuser?.alfaFactor || 0.7;
|
||||
this.i_n_elements = this.normalizePositiveInteger(this.config.diffuser?.elements, 1);
|
||||
this.i_diff_density = this.normalizeNumber(this.config.diffuser?.density, 2.4);
|
||||
this.i_m_water = this.normalizeNumber(this.config.diffuser?.waterHeight, 0);
|
||||
this.i_pressure = _num(d.headerPressure, 0);
|
||||
this.i_local_atm_pressure = _num(d.localAtmPressure, 1013.25);
|
||||
this.i_water_density = _num(d.waterDensity, 997);
|
||||
this.i_alfa_factor = _num(d.alfaFactor, 0.7);
|
||||
this.i_n_elements = _posInt(d.elements, 1);
|
||||
this.i_diff_density = _num(d.density, 2.4);
|
||||
this.i_m_water = _num(d.waterHeight, 0);
|
||||
this.i_flow = 0;
|
||||
this.zoneVolume = _num(d.zoneVolume, 0);
|
||||
|
||||
this.n_kg = this.calcAirDensityMbar(1013.25, 0, 20);
|
||||
|
||||
this.n_kg = this._calcAirDensityMbar(1013.25, 0, 20);
|
||||
this.n_flow = 0;
|
||||
this.o_otr = 0;
|
||||
this.o_p_flow = 0;
|
||||
this.o_p_water = this.heightToPressureMbar(this.i_water_density, this.i_m_water);
|
||||
this.o_p_water = this._heightToPressureMbar(this.i_water_density, this.i_m_water);
|
||||
this.o_p_total = this.o_p_water;
|
||||
this.o_kg = 0;
|
||||
this.o_kg_h = 0;
|
||||
this.o_kgo2_h = 0;
|
||||
this.o_kgo2 = 0;
|
||||
this.o_kgo2_h_min = 0;
|
||||
this.o_kgo2_h_max = 0;
|
||||
this.o_kg = 0; this.o_kg_h = 0; this.o_kgo2_h = 0; this.o_kgo2 = 0;
|
||||
this.o_kgo2_h_min = 0; this.o_kgo2_h_max = 0;
|
||||
this.o_flow_element = 0;
|
||||
this.o_otr_min = 0;
|
||||
this.o_otr_max = 0;
|
||||
this.o_p_min = 0;
|
||||
this.o_p_max = 0;
|
||||
this.o_otr_min = 0; this.o_otr_max = 0;
|
||||
this.o_p_min = 0; this.o_p_max = 0;
|
||||
this.o_combined_eff = 0;
|
||||
this.o_slope = 0;
|
||||
}
|
||||
|
||||
normalizeNumber(value, fallback = 0) {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
setDensity(v) { this.i_diff_density = _num(v, this.i_diff_density); this._recalculate(); }
|
||||
setFlow(v) { this.i_flow = Math.max(0, _num(v, 0)); this._recalculate(); }
|
||||
setWaterHeight(v) {
|
||||
this.i_m_water = Math.max(0, _num(v, this.i_m_water));
|
||||
this.o_p_water = this._heightToPressureMbar(this.i_water_density, this.i_m_water);
|
||||
this._recalculate();
|
||||
}
|
||||
setHeaderPressure(v) { this.i_pressure = _num(v, this.i_pressure); this._recalculate(); }
|
||||
setElementCount(v) { this.i_n_elements = _posInt(v, this.i_n_elements); this._recalculate(); }
|
||||
setAlfaFactor(v) { this.i_alfa_factor = _num(v, this.i_alfa_factor); this._recalculate(); }
|
||||
|
||||
normalizePositiveInteger(value, fallback = 1) {
|
||||
const parsed = Math.round(Number(value));
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
||||
}
|
||||
|
||||
setDensity(value) {
|
||||
this.i_diff_density = this.normalizeNumber(value, this.i_diff_density);
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
setFlow(value) {
|
||||
this.i_flow = Math.max(0, this.normalizeNumber(value, 0));
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
setWaterHeight(value) {
|
||||
this.i_m_water = Math.max(0, this.normalizeNumber(value, this.i_m_water));
|
||||
this.o_p_water = this.heightToPressureMbar(this.i_water_density, this.i_m_water);
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
setHeaderPressure(value) {
|
||||
this.i_pressure = this.normalizeNumber(value, this.i_pressure);
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
setElementCount(value) {
|
||||
this.i_n_elements = this.normalizePositiveInteger(value, this.i_n_elements);
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
setAlfaFactor(value) {
|
||||
this.i_alfa_factor = this.normalizeNumber(value, this.i_alfa_factor);
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
recalculate() {
|
||||
_recalculate() {
|
||||
if (this.i_flow <= 0) {
|
||||
this.idle = true;
|
||||
this.n_flow = 0;
|
||||
this.o_otr = 0;
|
||||
this.o_p_flow = 0;
|
||||
this.o_flow_element = 0;
|
||||
this.n_flow = 0; this.o_otr = 0; this.o_p_flow = 0; this.o_flow_element = 0;
|
||||
this.o_p_total = this.o_p_water;
|
||||
this.o_kg = 0;
|
||||
this.o_kg_h = 0;
|
||||
this.o_kgo2_h = 0;
|
||||
this.o_kgo2 = 0;
|
||||
this.o_combined_eff = 0;
|
||||
this.o_slope = 0;
|
||||
this.warning.text = [];
|
||||
this.warning.state = false;
|
||||
this.alarm.text = [];
|
||||
this.alarm.state = false;
|
||||
return;
|
||||
this.o_kg = 0; this.o_kg_h = 0; this.o_kgo2_h = 0; this.o_kgo2 = 0;
|
||||
this.o_combined_eff = 0; this.o_slope = 0;
|
||||
this.warning.text = []; this.warning.state = false;
|
||||
this.alarm.text = []; this.alarm.state = false;
|
||||
} else {
|
||||
this.idle = false;
|
||||
this._calcOtrPressure(this.i_flow);
|
||||
}
|
||||
|
||||
this.idle = false;
|
||||
this.calcOtrPressure(this.i_flow);
|
||||
this.notifyOutputChanged();
|
||||
}
|
||||
|
||||
getCurveKeys(curve) {
|
||||
return Object.keys(curve)
|
||||
.map(Number)
|
||||
.sort((a, b) => a - b);
|
||||
}
|
||||
_getCurveKeys(c) { return Object.keys(c).map(Number).sort((a, b) => a - b); }
|
||||
|
||||
interpolateSeries(points, x) {
|
||||
this.interpolation.load_spline(points.x, points.y, 'linear');
|
||||
_interpolateSeries(pts, x) {
|
||||
this.interpolation.load_spline(pts.x, pts.y, 'linear');
|
||||
return this.interpolation.interpolate(x);
|
||||
}
|
||||
|
||||
interpolateCurveByDensity(curve, density, x) {
|
||||
const keys = this.getCurveKeys(curve);
|
||||
_interpolateCurveByDensity(curve, density, x) {
|
||||
const keys = this._getCurveKeys(curve);
|
||||
if (keys.length === 1) {
|
||||
const only = curve[keys[0]];
|
||||
return {
|
||||
value: this.interpolateSeries(only, x),
|
||||
minY: Math.min(...only.y),
|
||||
maxY: Math.max(...only.y),
|
||||
minX: Math.min(...only.x),
|
||||
maxX: Math.max(...only.x),
|
||||
slope: this.getSegmentSlope(only, x),
|
||||
};
|
||||
return { value: this._interpolateSeries(only, x),
|
||||
minY: Math.min(...only.y), maxY: Math.max(...only.y),
|
||||
minX: Math.min(...only.x), maxX: Math.max(...only.x),
|
||||
slope: this._getSegmentSlope(only, x) };
|
||||
}
|
||||
|
||||
const lowerKey = keys.reduce((acc, key) => (key <= density ? key : acc), keys[0]);
|
||||
const upperKey = keys.find((key) => key >= density) ?? keys[keys.length - 1];
|
||||
const lowerCurve = curve[lowerKey];
|
||||
const upperCurve = curve[upperKey];
|
||||
|
||||
const lowerKey = keys.reduce((a, k) => (k <= density ? k : a), keys[0]);
|
||||
const upperKey = keys.find((k) => k >= density) ?? keys[keys.length - 1];
|
||||
const lower = curve[lowerKey]; const upper = curve[upperKey];
|
||||
if (lowerKey === upperKey) {
|
||||
return {
|
||||
value: this.interpolateSeries(lowerCurve, x),
|
||||
minY: Math.min(...lowerCurve.y),
|
||||
maxY: Math.max(...lowerCurve.y),
|
||||
minX: Math.min(...lowerCurve.x),
|
||||
maxX: Math.max(...lowerCurve.x),
|
||||
slope: this.getSegmentSlope(lowerCurve, x),
|
||||
};
|
||||
return { value: this._interpolateSeries(lower, x),
|
||||
minY: Math.min(...lower.y), maxY: Math.max(...lower.y),
|
||||
minX: Math.min(...lower.x), maxX: Math.max(...lower.x),
|
||||
slope: this._getSegmentSlope(lower, x) };
|
||||
}
|
||||
|
||||
const lowerValue = this.interpolateSeries(lowerCurve, x);
|
||||
const upperValue = this.interpolateSeries(upperCurve, x);
|
||||
const ratio = (density - lowerKey) / (upperKey - lowerKey);
|
||||
|
||||
const lv = this._interpolateSeries(lower, x);
|
||||
const uv = this._interpolateSeries(upper, x);
|
||||
const r = (density - lowerKey) / (upperKey - lowerKey);
|
||||
return {
|
||||
value: lowerValue + (upperValue - lowerValue) * ratio,
|
||||
minY: Math.min(...lowerCurve.y) + (Math.min(...upperCurve.y) - Math.min(...lowerCurve.y)) * ratio,
|
||||
maxY: Math.max(...lowerCurve.y) + (Math.max(...upperCurve.y) - Math.max(...lowerCurve.y)) * ratio,
|
||||
minX: Math.min(...lowerCurve.x),
|
||||
maxX: Math.max(...lowerCurve.x),
|
||||
slope: this.getSegmentSlope(lowerCurve, x),
|
||||
value: lv + (uv - lv) * r,
|
||||
minY: Math.min(...lower.y) + (Math.min(...upper.y) - Math.min(...lower.y)) * r,
|
||||
maxY: Math.max(...lower.y) + (Math.max(...upper.y) - Math.max(...lower.y)) * r,
|
||||
minX: Math.min(...lower.x), maxX: Math.max(...lower.x),
|
||||
slope: this._getSegmentSlope(lower, x),
|
||||
};
|
||||
}
|
||||
|
||||
getSegmentSlope(curvePoints, x) {
|
||||
const xs = curvePoints.x;
|
||||
const ys = curvePoints.y;
|
||||
_getSegmentSlope(pts, x) {
|
||||
const { x: xs, y: ys } = pts;
|
||||
for (let i = 0; i < xs.length - 1; i += 1) {
|
||||
if (x <= xs[i + 1]) {
|
||||
return (ys[i + 1] - ys[i]) / (xs[i + 1] - xs[i]);
|
||||
}
|
||||
if (x <= xs[i + 1]) return (ys[i + 1] - ys[i]) / (xs[i + 1] - xs[i]);
|
||||
}
|
||||
const last = xs.length - 1;
|
||||
return (ys[last] - ys[last - 1]) / (xs[last] - xs[last - 1]);
|
||||
const n = xs.length - 1;
|
||||
return (ys[n] - ys[n - 1]) / (xs[n] - xs[n - 1]);
|
||||
}
|
||||
|
||||
combineEff(oOtr, oOtrMin, oOtrMax, oPFlow, oPMin, oPMax) {
|
||||
_combineEff(oOtr, oOtrMin, oOtrMax, oPFlow, oPMin, oPMax) {
|
||||
const otrSpan = oOtrMax - oOtrMin;
|
||||
const pSpan = oPMax - oPMin;
|
||||
const eff1 = otrSpan > 0 ? (oOtr - oOtrMin) / otrSpan : 0;
|
||||
const eff2 = pSpan > 0 ? 1 - ((oPFlow - oPMin) / pSpan) : 0;
|
||||
return Math.max(0, eff1 * eff2 * 100);
|
||||
const e1 = otrSpan > 0 ? (oOtr - oOtrMin) / otrSpan : 0;
|
||||
const e2 = pSpan > 0 ? 1 - ((oPFlow - oPMin) / pSpan) : 0;
|
||||
return Math.max(0, e1 * e2 * 100);
|
||||
}
|
||||
|
||||
calcAirDensityMbar(pressureMbar, RH, tempC) {
|
||||
const Rd = 287.05;
|
||||
const Rv = 461.495;
|
||||
_calcAirDensityMbar(pMbar, RH, tempC) {
|
||||
const Rd = 287.05, Rv = 461.495;
|
||||
const T = tempC + 273.15;
|
||||
const A = 8.07131;
|
||||
const B = 1730.63;
|
||||
const C = 233.426;
|
||||
const e_s = Math.pow(10, (A - (B / (C + tempC))));
|
||||
const e = RH * e_s / 100;
|
||||
const pressurePa = this.convert(pressureMbar).from('mbar').to('Pa');
|
||||
const p_d = pressurePa - (e * 100);
|
||||
return (p_d / (Rd * T)) + ((e * 100) / (Rv * T));
|
||||
const es = Math.pow(10, (8.07131 - (1730.63 / (233.426 + tempC))));
|
||||
const e = RH * es / 100;
|
||||
const pPa = convert(pMbar).from('mbar').to('Pa');
|
||||
const pd = pPa - (e * 100);
|
||||
return (pd / (Rd * T)) + ((e * 100) / (Rv * T));
|
||||
}
|
||||
|
||||
heightToPressureMbar(density, height) {
|
||||
const pressurePa = gravity.getStandardGravity() * density * height;
|
||||
return this.convert(pressurePa).from('Pa').to('mbar');
|
||||
_heightToPressureMbar(density, height) {
|
||||
const pPa = gravity.getStandardGravity() * density * height;
|
||||
return convert(pPa).from('Pa').to('mbar');
|
||||
}
|
||||
|
||||
calcOtrPressure(flow) {
|
||||
_calcOtrPressure(flow) {
|
||||
const totalInputPressureMbar = this.i_local_atm_pressure + this.i_pressure;
|
||||
this.o_kg = this.calcAirDensityMbar(totalInputPressureMbar, 0, 20);
|
||||
this.o_kg = this._calcAirDensityMbar(totalInputPressureMbar, 0, 20);
|
||||
this.o_kg_h = this.o_kg * flow;
|
||||
this.n_flow = (this.o_kg / this.n_kg) * flow;
|
||||
this.o_flow_element = Math.round((this.n_flow / this.i_n_elements) * 100) / 100;
|
||||
|
||||
const otr = this.interpolateCurveByDensity(this.specs.otr_curve, this.i_diff_density, this.o_flow_element);
|
||||
const pressure = this.interpolateCurveByDensity(this.specs.p_curve, 0, this.o_flow_element);
|
||||
const otr = this._interpolateCurveByDensity(this.specs.otr_curve, this.i_diff_density, this.o_flow_element);
|
||||
const pressure = this._interpolateCurveByDensity(this.specs.p_curve, 0, this.o_flow_element);
|
||||
|
||||
this.o_otr_min = otr.minY;
|
||||
this.o_otr_max = otr.maxY;
|
||||
this.o_p_min = pressure.minY;
|
||||
this.o_p_max = pressure.maxY;
|
||||
this.o_otr_min = otr.minY; this.o_otr_max = otr.maxY;
|
||||
this.o_p_min = pressure.minY; this.o_p_max = pressure.maxY;
|
||||
this.o_otr = Math.round(otr.value * 100) / 100;
|
||||
this.o_p_flow = Math.round(pressure.value * 100) / 100;
|
||||
this.o_p_total = Math.round((this.o_p_water + this.o_p_flow) * 100) / 100;
|
||||
this.o_kgo2_h = Math.round(this.convert(this.o_otr * this.n_flow * this.i_m_water * this.i_alfa_factor).from('g').to('kg') * 100) / 100;
|
||||
this.o_kgo2_h_min = Math.round(this.convert(this.o_otr_min * this.n_flow * this.i_m_water * this.i_alfa_factor).from('g').to('kg') * 100) / 100;
|
||||
this.o_kgo2_h_max = Math.round(this.convert(this.o_otr_max * this.n_flow * this.i_m_water * this.i_alfa_factor).from('g').to('kg') * 100) / 100;
|
||||
const kgo2 = (n) => Math.round(convert(n * this.n_flow * this.i_m_water * this.i_alfa_factor).from('g').to('kg') * 100) / 100;
|
||||
this.o_kgo2_h = kgo2(this.o_otr);
|
||||
this.o_kgo2_h_min = kgo2(this.o_otr_min);
|
||||
this.o_kgo2_h_max = kgo2(this.o_otr_max);
|
||||
this.o_kgo2 = this.o_kgo2_h / 3600;
|
||||
this.o_combined_eff = Math.round(this.combineEff(
|
||||
this.o_otr,
|
||||
this.o_otr_min,
|
||||
this.o_otr_max,
|
||||
this.o_p_flow,
|
||||
this.o_p_min,
|
||||
this.o_p_max,
|
||||
this.o_combined_eff = Math.round(this._combineEff(
|
||||
this.o_otr, this.o_otr_min, this.o_otr_max,
|
||||
this.o_p_flow, this.o_p_min, this.o_p_max,
|
||||
) * 100) / 100;
|
||||
this.o_slope = Math.round(otr.slope * 1000) / 1000;
|
||||
|
||||
this.warningCheck(pressure.minX, pressure.maxX);
|
||||
this.alarmCheck(pressure.minX, pressure.maxX);
|
||||
this._checkLimits(pressure.minX, pressure.maxX);
|
||||
}
|
||||
|
||||
warningCheck(minFlow, maxFlow) {
|
||||
this.warning.text = [];
|
||||
this.warning.state = false;
|
||||
const minHyst = minFlow * (this.warning.flow.min.hyst / 100);
|
||||
const maxHyst = maxFlow * (this.warning.flow.max.hyst / 100);
|
||||
|
||||
if (this.o_flow_element < minFlow - minHyst) {
|
||||
this.warning.state = true;
|
||||
this.warning.text.push(`Warning: flow per element ${this.o_flow_element} is below ${Math.round((minFlow - minHyst) * 100) / 100}`);
|
||||
}
|
||||
|
||||
if (this.o_flow_element > maxFlow + maxHyst) {
|
||||
this.warning.state = true;
|
||||
this.warning.text.push(`Warning: flow per element ${this.o_flow_element} exceeds ${Math.round((maxFlow + maxHyst) * 100) / 100}`);
|
||||
_checkLimits(minFlow, maxFlow) {
|
||||
this.warning.text = []; this.warning.state = false;
|
||||
this.alarm.text = []; this.alarm.state = false;
|
||||
const f = this.o_flow_element;
|
||||
for (const k of ['warning', 'alarm']) {
|
||||
const band = this[k];
|
||||
const lo = minFlow - minFlow * (band.flow.min.hyst / 100);
|
||||
const hi = maxFlow + maxFlow * (band.flow.max.hyst / 100);
|
||||
if (f < lo) { band.state = true; band.text.push(`${_cap(k)}: flow per element ${f} is below ${Math.round(lo * 100) / 100}`); }
|
||||
if (f > hi) { band.state = true; band.text.push(`${_cap(k)}: flow per element ${f} exceeds ${Math.round(hi * 100) / 100}`); }
|
||||
}
|
||||
}
|
||||
|
||||
alarmCheck(minFlow, maxFlow) {
|
||||
this.alarm.text = [];
|
||||
this.alarm.state = false;
|
||||
const minHyst = minFlow * (this.alarm.flow.min.hyst / 100);
|
||||
const maxHyst = maxFlow * (this.alarm.flow.max.hyst / 100);
|
||||
|
||||
if (this.o_flow_element < minFlow - minHyst) {
|
||||
this.alarm.state = true;
|
||||
this.alarm.text.push(`Alarm: flow per element ${this.o_flow_element} is below ${Math.round((minFlow - minHyst) * 100) / 100}`);
|
||||
}
|
||||
|
||||
if (this.o_flow_element > maxFlow + maxHyst) {
|
||||
this.alarm.state = true;
|
||||
this.alarm.text.push(`Alarm: flow per element ${this.o_flow_element} exceeds ${Math.round((maxFlow + maxHyst) * 100) / 100}`);
|
||||
}
|
||||
// Back-compat hooks for the legacy specificClass test suite.
|
||||
getStatus() { return this._legacyStatus(); }
|
||||
_legacyStatus() {
|
||||
if (this.alarm.state) return { fill: 'red', shape: 'dot', text: this.alarm.text[0] };
|
||||
if (this.warning.state) return { fill: 'yellow', shape: 'dot', text: this.warning.text[0] };
|
||||
const fill = this.idle ? 'grey' : 'green';
|
||||
return { fill, shape: 'dot', text: `${this.o_kgo2_h} kg o2 / h` };
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
if (this.alarm.state) {
|
||||
return { fill: 'red', shape: 'dot', text: this.alarm.text[0] };
|
||||
}
|
||||
if (this.warning.state) {
|
||||
return { fill: 'yellow', shape: 'dot', text: this.warning.text[0] };
|
||||
}
|
||||
if (this.idle) {
|
||||
return { fill: 'grey', shape: 'dot', text: `${this.o_kgo2_h} kg o2 / h` };
|
||||
}
|
||||
return { fill: 'green', shape: 'dot', text: `${this.o_kgo2_h} kg o2 / h` };
|
||||
getStatusBadge() {
|
||||
if (this.alarm.state) return statusBadge.error(this.alarm.text[0]);
|
||||
if (this.warning.state) return statusBadge.compose([`⚠ ${this.warning.text[0]}`], { fill: 'yellow', shape: 'dot' });
|
||||
const text = `${this.o_kgo2_h} kg o2 / h`;
|
||||
return this.idle ? statusBadge.idle(text) : statusBadge.compose([`🟢 ${text}`]);
|
||||
}
|
||||
|
||||
getOutput() {
|
||||
@@ -302,6 +205,7 @@ class Diffuser {
|
||||
oFlowElement: this.o_flow_element,
|
||||
efficiency: this.o_combined_eff,
|
||||
slope: this.o_slope,
|
||||
oZoneOtr: this.getReactorOtr(this.zoneVolume),
|
||||
idle: this.idle,
|
||||
warning: [...this.warning.text],
|
||||
alarm: [...this.alarm.text],
|
||||
@@ -309,34 +213,29 @@ class Diffuser {
|
||||
}
|
||||
|
||||
getReactorOtr(zoneVolumeM3) {
|
||||
const volume = Number(zoneVolumeM3);
|
||||
if (!Number.isFinite(volume) || volume <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return this.o_kgo2_h * 1000 * 24 / volume;
|
||||
const v = Number(zoneVolumeM3);
|
||||
if (!Number.isFinite(v) || v <= 0) return 0;
|
||||
return this.o_kgo2_h * 1000 * 24 / v;
|
||||
}
|
||||
|
||||
loadSpecs() {
|
||||
_loadSpecs() {
|
||||
return {
|
||||
supplier: 'GVA',
|
||||
type: 'ELASTOX-R',
|
||||
units: {
|
||||
Nm3: { temp: 20, pressure: 1.01325, RH: 0 },
|
||||
},
|
||||
otr_curve: {
|
||||
2.4: {
|
||||
x: [2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||
y: [26, 25, 24, 23.5, 23, 22.75, 22.5, 22.25, 22],
|
||||
},
|
||||
},
|
||||
p_curve: {
|
||||
0: {
|
||||
x: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
||||
y: [40, 42.5, 45, 47.5, 50, 51.5, 53, 54.5, 56, 57.5, 59],
|
||||
},
|
||||
},
|
||||
supplier: 'GVA', type: 'ELASTOX-R',
|
||||
units: { Nm3: { temp: 20, pressure: 1.01325, RH: 0 } },
|
||||
otr_curve: { 2.4: { x: [2, 3, 4, 5, 6, 7, 8, 9, 10], y: [26, 25, 24, 23.5, 23, 22.75, 22.5, 22.25, 22] } },
|
||||
p_curve: { 0: { x: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], y: [40, 42.5, 45, 47.5, 50, 51.5, 53, 54.5, 56, 57.5, 59] } },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function _num(v, fb = 0) {
|
||||
const n = Number(v);
|
||||
return Number.isFinite(n) ? n : fb;
|
||||
}
|
||||
function _posInt(v, fb = 1) {
|
||||
const n = Math.round(Number(v));
|
||||
return Number.isFinite(n) && n > 0 ? n : fb;
|
||||
}
|
||||
function _cap(s) { return s.charAt(0).toUpperCase() + s.slice(1); }
|
||||
|
||||
module.exports = Diffuser;
|
||||
|
||||
Reference in New Issue
Block a user