124 lines
3.9 KiB
JavaScript
124 lines
3.9 KiB
JavaScript
|
|
'use strict';
|
||
|
|
|
||
|
|
const test = require('node:test');
|
||
|
|
const assert = require('node:assert/strict');
|
||
|
|
const EventEmitter = require('events');
|
||
|
|
const Settler = require('../../src/specificClass');
|
||
|
|
|
||
|
|
const NUM_SPECIES = 13;
|
||
|
|
|
||
|
|
function makeSettler() {
|
||
|
|
return new Settler({
|
||
|
|
general: { name: 'TestSettler', id: 'settler-test-1', logging: { enabled: false, logLevel: 'error' } },
|
||
|
|
functionality: { softwareType: 'settler', positionVsParent: 'downstream' },
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('constructor sets default state', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
assert.equal(s.F_in, 0);
|
||
|
|
assert.deepEqual(s.Cs_in, new Array(NUM_SPECIES).fill(0));
|
||
|
|
assert.equal(s.C_TS, 2500);
|
||
|
|
assert.equal(s.upstreamReactor, null);
|
||
|
|
assert.equal(s.returnPump, null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('getEffluent conserves total flow (mass balance)', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
s.F_in = 200;
|
||
|
|
s.C_TS = 3000;
|
||
|
|
const C = new Array(NUM_SPECIES).fill(5);
|
||
|
|
C[12] = 2000;
|
||
|
|
s.Cs_in = C;
|
||
|
|
const [eff, sur, ret] = s.getEffluent;
|
||
|
|
assert.equal(eff.topic, 'Fluent');
|
||
|
|
assert.ok(Math.abs(eff.payload.F + sur.payload.F + ret.payload.F - 200) < 1e-6);
|
||
|
|
for (let i = 7; i <= 12; i++) assert.equal(eff.payload.C[i], 0);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('getEffluent clamps F_s to F_in when X_TS exceeds C_TS', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
s.F_in = 100;
|
||
|
|
s.C_TS = 1000;
|
||
|
|
s.Cs_in = new Array(NUM_SPECIES).fill(10);
|
||
|
|
s.Cs_in[12] = 5000;
|
||
|
|
const [eff] = s.getEffluent;
|
||
|
|
assert.equal(eff.payload.F, 0);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('reactor stateChange pulls effluent (preserves _connectReactor integration)', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
let outputChanges = 0;
|
||
|
|
s.emitter.on('output-changed', () => outputChanges++);
|
||
|
|
|
||
|
|
const reactor = {
|
||
|
|
config: { general: { name: 'r', id: 'r-1' }, functionality: { positionVsParent: 'upstream' } },
|
||
|
|
emitter: new EventEmitter(),
|
||
|
|
measurements: { emitter: new EventEmitter() },
|
||
|
|
// Mirror the array shape the reactor produces in production.
|
||
|
|
get getEffluent() {
|
||
|
|
const C = new Array(NUM_SPECIES).fill(2);
|
||
|
|
C[12] = 3500;
|
||
|
|
return [{ topic: 'Fluent', payload: { inlet: 0, F: 150, C } }];
|
||
|
|
},
|
||
|
|
};
|
||
|
|
s.router.dispatchRegister(reactor, 'reactor');
|
||
|
|
reactor.emitter.emit('stateChange');
|
||
|
|
|
||
|
|
assert.equal(s.upstreamReactor, reactor);
|
||
|
|
assert.equal(s.F_in, 150);
|
||
|
|
assert.equal(s.Cs_in[12], 3500);
|
||
|
|
assert.ok(outputChanges >= 1, 'reactor stateChange should trigger output-changed');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('reactor stateChange handles single-envelope getEffluent (not array)', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
const reactor = {
|
||
|
|
config: { general: { name: 'r', id: 'r-1' }, functionality: { positionVsParent: 'upstream' } },
|
||
|
|
emitter: new EventEmitter(),
|
||
|
|
measurements: { emitter: new EventEmitter() },
|
||
|
|
get getEffluent() {
|
||
|
|
const C = new Array(NUM_SPECIES).fill(1);
|
||
|
|
C[12] = 800;
|
||
|
|
return { topic: 'Fluent', payload: { inlet: 0, F: 42, C } };
|
||
|
|
},
|
||
|
|
};
|
||
|
|
s.router.dispatchRegister(reactor, 'reactor');
|
||
|
|
reactor.emitter.emit('stateChange');
|
||
|
|
|
||
|
|
assert.equal(s.F_in, 42);
|
||
|
|
assert.equal(s.Cs_in[12], 800);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('TSS measurement updates C_TS via _updateMeasurement', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
s._updateMeasurement('quantity (tss)', 7000);
|
||
|
|
assert.equal(s.C_TS, 7000);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('downstream machine becomes returnPump', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
const pump = {
|
||
|
|
config: { general: { name: 'pump', id: 'p-1' }, functionality: { positionVsParent: 'downstream' } },
|
||
|
|
measurements: { emitter: new EventEmitter() },
|
||
|
|
};
|
||
|
|
s.router.dispatchRegister(pump, 'machine');
|
||
|
|
assert.equal(s.returnPump, pump);
|
||
|
|
assert.equal(pump.upstreamSource, s);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('getStatusBadge returns idle when F_in=0, green when flowing', () => {
|
||
|
|
const s = makeSettler();
|
||
|
|
const idle = s.getStatusBadge();
|
||
|
|
assert.equal(idle.fill, 'blue');
|
||
|
|
|
||
|
|
s.F_in = 100;
|
||
|
|
s.C_TS = 5000;
|
||
|
|
const C = new Array(NUM_SPECIES).fill(10);
|
||
|
|
C[12] = 3000;
|
||
|
|
s.Cs_in = C;
|
||
|
|
const active = s.getStatusBadge();
|
||
|
|
assert.equal(active.fill, 'green');
|
||
|
|
assert.ok(active.text.includes('F_in'));
|
||
|
|
});
|