// Throwaway probe — exercises the exact path: // measurement child writes flow.measured.upstream → pumpingStation parent // subscribes → getOutput() (≡ what Port 0 emits). // Run with: node --test test/basic/_probe_upstream_emit.test.js const test = require('node:test'); const assert = require('node:assert/strict'); const PumpingStation = require('../../src/specificClass'); const { MeasurementContainer, configManager } = require('generalFunctions'); const EventEmitter = require('node:events'); // Minimal PumpingStation config — matches the editor defaults shape. function makePsConfig() { const ui = { name: 'PS', basinVolume: 50, basinHeight: 5, inflowLevel: 3, outflowLevel: 0.2, overflowLevel: 4.5, minHeightBasedOn: 'outlet', controlMode: 'levelbased', minLevel: 1, startLevel: 2, maxLevel: 4, levelCurveType: 'linear', processOutputFormat: 'process', dbaseOutputFormat: 'influxdb', }; const cm = new configManager(); // Use the same buildConfig pipeline the runtime uses. return cm.buildConfig('pumpingStation', ui, 'ps-probe', { basin: { volume: 50, height: 5, inflowLevel: 3, outflowLevel: 0.2, overflowLevel: 4.5, }, hydraulics: { minHeightBasedOn: 'outlet' }, control: { mode: 'levelbased', allowedModes: new Set(['levelbased']), levelbased: { minLevel: 1, startLevel: 2, maxLevel: 4, curveType: 'linear' }, }, safety: {}, }); } // Fake measurement child that looks exactly like the real one to the router: // - softwareType 'measurement' // - config.asset.type = 'flow' // - config.functionality.positionVsParent = 'upstream' // - .measurements is a real MeasurementContainer with a real emitter function makeMeasurementChild(id = 'meas-probe') { const measurements = new MeasurementContainer({ autoConvert: true, preferredUnits: { flow: 'm3/s' }, }); // Real container ships an emitter; sanity check. assert.ok(measurements.emitter instanceof EventEmitter || typeof measurements.emitter?.on === 'function'); return { id, source: { config: { general: { id, name: id }, functionality: { softwareType: 'measurement', positionVsParent: 'upstream' }, asset: { type: 'flow' }, }, measurements, }, }; } test('PROBE: measurement child writes flow.measured.upstream — parent surfaces it on getOutput()', () => { const ps = new PumpingStation(makePsConfig()); const child = makeMeasurementChild(); // Register the child the same way the runtime does. ps.childRegistrationUtils.registerChild(child.source, 'upstream'); // Drive a value through the child's MeasurementContainer the way Channel // does — type/variant/position chain then .value(). child.source.measurements .type('flow').variant('measured').position('upstream') .value(12, Date.now(), 'm3/h'); // 12 m³/h ≈ 0.00333 m³/s const out = ps.getOutput(); const upstreamKeys = Object.keys(out).filter((k) => k.startsWith('flow.measured.upstream')); console.log('flow.measured.upstream.* keys in Port 0 payload:', upstreamKeys); for (const k of upstreamKeys) console.log(` ${k} = ${out[k]}`); // The contract: the parent should surface the upstream measurement. assert.ok(upstreamKeys.length > 0, 'parent must surface flow.measured.upstream.* on Port 0'); });