@@ -1,5 +1,6 @@
const EventEmitter = require ( 'events' ) ;
const { loadCurve , gravity , logger, configUtils , configManager , state , nrmse , MeasurementContainer , predict , interpolation , childRegistrationUtils , coolprop } = require ( 'generalFunctions' ) ;
const { loadCurve , logger , configUtils , configManager , state , nrmse , MeasurementContainer , predict , interpolation , childRegistrationUtils } = require ( 'generalFunctions' ) ;
const { name } = require ( '../../generalFunctions/src/convert/lodash/lodash._shimkeys' ) ;
class Machine {
@@ -16,7 +17,7 @@ class Machine {
// Load a specific curve
this . model = machineConfig . asset . model ; // Get the model from the machineConfig
this . curve = this . model ? loadCurve ( this . model ) : null ; // we need to convert the curve and add units to the curve information
this . curve = this . model ? loadCurve ( this . model ) : null ;
//Init config and check if it is valid
this . config = this . configUtils . initConfig ( machineConfig ) ;
@@ -34,8 +35,10 @@ class Machine {
}
else {
this . hasCurve = true ;
this . config = this . configUtils . updateConfig ( this . config , { asset : { ... this . config . asset , machineCurve : this . curve } } ) ;
//machineConfig = { ...machineConfig, asset: { ...machineConfig.asset, machineCurve: this.curve } }; // Merge curve into machineConfig
this . config = this . configUtils . updateConfig ( this . config , {
asset : { ... this . config . asset , machineCurve : this . curve }
} ) ;
machineConfig = { ... machineConfig , asset : { ... machineConfig . asset , machineCurve : this . curve } } ; // Merge curve into machineConfig
this . predictFlow = new predict ( { curve : this . config . asset . machineCurve . nq } ) ; // load nq (x : ctrl , y : flow relationship)
this . predictPower = new predict ( { curve : this . config . asset . machineCurve . np } ) ; // load np (x : ctrl , y : power relationship)
this . predictCtrl = new predict ( { curve : this . reverseCurve ( this . config . asset . machineCurve . nq ) } ) ; // load reversed nq (x: flow, y: ctrl relationship)
@@ -81,33 +84,86 @@ class Machine {
this . _updateState ( ) ;
} ) ;
//perform init for certain values
this . _init ( ) ;
this . child = { } ; // object to hold child information so we know on what to subscribe
this . childRegistrationUtils = new childRegistrationUtils ( this ) ; // Child registration utility
// --- KPI tracking ---
this . kpi = {
failures : 0 ,
totalRuntimeHours : 0 ,
totalDowntimeHours : 0 ,
lastFailureTime : null ,
lastRepairTime : null ,
MTBF : 0 ,
MTTR : 0 ,
availability : 0
} ;
this . assetHealth = {
index : 0 // 0 = optimal, 5 = failure
} ;
this . state . emitter . on ( 'stateChange' , ( payload ) => {
const stateStr = typeof payload === 'string'
? payload
: ( payload ? . state ? ? payload ? . newState ? ? payload ) ;
if ( typeof stateStr !== 'string' ) {
this . logger . warn ( ` stateChange event without parsable state: ${ JSON . stringify ( payload ) } ` ) ;
return ;
}
this . _handleStateChangeForKPI ( stateStr ) ;
} ) ;
// --- KPI tracking ---
this . kpi = {
failures : 0 ,
totalRuntimeHours : 0 ,
totalDowntimeHours : 0 ,
lastFailureTime : null ,
lastRepairTime : null ,
MTBF : 0 ,
MTTR : 0 ,
availability : 0
} ;
this . assetHealth = {
index : 0 // 0 = optimal, 5 = failure
} ;
this . state . emitter . on ( 'stateChange' , ( payload ) => {
const stateStr = typeof payload === 'string'
? payload
: ( payload ? . state ? ? payload ? . newState ? ? payload ) ;
if ( typeof stateStr !== 'string' ) {
this . logger . warn ( ` stateChange event without parsable state: ${ JSON . stringify ( payload ) } ` ) ;
return ;
}
this . _handleStateChangeForKPI ( stateStr ) ;
} ) ;
}
_init ( ) {
//assume standard temperature is 20degrees
this . measurements . type ( 'temperature' ) . variant ( 'measured' ) . position ( 'atEquipment' ) . value ( 15 ) . unit ( 'C' ) ;
//assume standard atm pressure is at sea level
this . measurements . type ( 'atmPressure' ) . variant ( 'measured' ) . position ( 'atEquipment' ) . value ( 101325 ) . unit ( 'Pa' ) ;
//populate min and max
const flowunit = this . config . general . unit ;
this . measurements . type ( 'flow' ) . variant ( 'predicted' ) . position ( 'max' ) . value ( this . predictFlow . currentFxyYMax , Date . now ( ) , flowunit )
this . measurements . type ( 'flow' ) . variant ( 'predicted' ) . position ( 'min' ) . value ( this . predictFlow . currentFxyYMin ) . unit ( this . config . general . unit ) ;
}
_updateState ( ) {
const isOperational = this . _isOperationalState ( ) ;
if ( ! isOperational ) {
//overrule the last prediction this should be 0 now
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( 0 , Date . now ( ) , this . config . general . unit );
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "atEquipment" ) . value ( 0 , Date . now ( ) , this . config . general . unit ) ;
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( 0 ) ;
}
}
_updateState ( ) {
const isOperational = this . _isOperationalState ( ) ;
if ( ! isOperational ) {
//overrule the last prediction this should be 0 now
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( 0 ) ;
}
}
@@ -129,7 +185,7 @@ class Machine {
this . logger . debug ( ` 🔄 ${ position } ${ measurementType } from ${ eventData . childName } : ${ eventData . value } ${ eventData . unit } ` ) ;
this . logger . debu g( ` Emitting... ${ eventName } with data: ` ) ;
console . lo g( ` Emitting... ${ eventName } with data: ` ) ;
// Store directly in parent's measurement container
this . measurements
. type ( measurementType )
@@ -154,10 +210,6 @@ _callMeasurementHandler(measurementType, value, position, context) {
this . updateMeasuredFlow ( value , position , context ) ;
break ;
case 'temperature' :
this . updateMeasuredTemperature ( value , position , context ) ;
break ;
default :
this . logger . warn ( ` No handler for measurement type: ${ measurementType } ` ) ;
// Generic handler - just update position
@@ -223,6 +275,7 @@ _callMeasurementHandler(measurementType, value, position, context) {
async handleInput ( source , action , parameter ) {
this . logger . debug ( "hello" ) ;
//sanitize input
if ( typeof action !== 'string' ) { this . logger . error ( ` Action must be string ` ) ; return ; }
//convert to lower case to avoid to many mistakes in commands
@@ -232,6 +285,8 @@ _callMeasurementHandler(measurementType, value, position, context) {
if ( ! this . isValidActionForMode ( action , this . currentMode ) ) { return ; }
if ( ! this . isValidSourceForMode ( source , this . currentMode ) ) { return ; }
this . logger . debug ( "hello2" ) ;
this . logger . info ( ` Handling input from source ' ${ source } ' with action ' ${ action } ' in mode ' ${ this . currentMode } '. ` ) ;
try {
@@ -348,23 +403,21 @@ _callMeasurementHandler(measurementType, value, position, context) {
calcFlow ( x ) {
if ( this . hasCurve ) {
if ( ! this . _isOperationalState ( ) ) {
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( 0 , Date . now ( ) , this . config . general . unit );
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "atEquipment" ) . value ( 0 , Date . now ( ) , this . config . general . unit ) ;
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( 0 ) ;
this . logger . debug ( ` Machine is not operational. Setting predicted flow to 0. ` ) ;
return 0 ;
}
//this.predictFlow.currentX = x; Decrepated
const cFlow = this . predictFlow . y ( x ) ;
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( cFlow , Date . now ( ) , this . config . general . unit );
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "atEquipment" ) . value ( cFlow , Date . now ( ) , this . config . general . unit ) ;
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( cFlow ) ;
//this.logger.debug(`Calculated flow: ${cFlow} for pressure: ${this.getMeasuredPressure()} and position: ${x}`);
return cFlow ;
}
// If no curve data is available, log a warning and return 0
this . logger . warn ( ` No curve data available for flow calculation. Returning 0. ` ) ;
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( 0 , Date . now ( ) , this . config . general . unit );
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "atEquipment" ) . value ( 0 , Date . now ( ) , this . config . general . unit ) ;
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( 0 ) ;
return 0 ;
}
@@ -429,11 +482,6 @@ _callMeasurementHandler(measurementType, value, position, context) {
// returns the best available pressure measurement to use in the prediction calculation
// this will be either the differential pressure, downstream or upstream pressure
getMeasuredPressure ( ) {
if ( this . hasCurve === false ) {
this . logger . error ( ` No valid curve available to calculate prediction using last known pressure ` ) ;
return 0 ;
}
const pressureDiff = this . measurements . type ( 'pressure' ) . variant ( 'measured' ) . difference ( ) ;
// Both upstream & downstream => differential
@@ -482,9 +530,6 @@ _callMeasurementHandler(measurementType, value, position, context) {
const efficiency = this . calcEfficiency ( this . predictPower . outputY , this . predictFlow . outputY , "predicted" ) ;
//update the distance from peak
this . calcDistanceBEP ( efficiency , cog , minEfficiency ) ;
//place min and max flow capabilities in containerthis.predictFlow.currentFxyYMax - this.predictFlow.currentFxyYMin
this . measurements . type ( 'flow' ) . variant ( 'predicted' ) . position ( 'max' ) . value ( this . predictFlow . currentFxyYMax ) . unit ( this . config . general . unit ) ;
this . measurements . type ( 'flow' ) . variant ( 'predicted' ) . position ( 'min' ) . value ( this . predictFlow . currentFxyYMin ) . unit ( this . config . general . unit ) ;
return 0 ;
}
@@ -572,7 +617,6 @@ _callMeasurementHandler(measurementType, value, position, context) {
// Update predicted flow if you have prediction capability
if ( this . predictFlow ) {
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "downstream" ) . value ( this . predictFlow . outputY || 0 ) ;
this . measurements . type ( "flow" ) . variant ( "predicted" ) . position ( "atEquipment" ) . value ( this . predictFlow . outputY || 0 ) ;
}
}
@@ -603,9 +647,138 @@ _callMeasurementHandler(measurementType, value, position, context) {
this . calcDistanceBEP ( efficiency , cog , minEfficiency ) ;
}
}
/////////////////////////////
/**
* Compute a single drift score in [0..1] using predicted vs measured series.
* Uses min/max of the *predicted* window as normalization range.
* If no usable data -> returns 0 (neutral).
*/
_computeDriftScore ( ) {
try {
const metrics = [
{ key : "pressure" , pos : "downstream" } ,
{ key : "flow" , pos : "downstream" } ,
{ key : "power" , pos : "atEquipment" }
] ;
const values = [ ] ;
for ( const m of metrics ) {
const pred = this . measurements . type ( m . key ) . variant ( "predicted" ) . position ( m . pos ) . getAllValues ( ) ? . values ;
const meas = this . measurements . type ( m . key ) . variant ( "measured" ) . position ( m . pos ) . getAllValues ( ) ? . values ;
if ( ! Array . isArray ( pred ) || ! Array . isArray ( meas ) || pred . length < 2 || meas . length < 2 ) continue ;
const expectedMin = Math . min ( ... pred ) ;
const expectedMax = Math . max ( ... pred ) ;
if ( ! Number . isFinite ( expectedMin ) || ! Number . isFinite ( expectedMax ) || expectedMax === expectedMin ) continue ;
const drift = this . errorMetrics . assessDrift ( pred , meas , expectedMin , expectedMax ) ;
if ( Number . isFinite ( drift ) ) {
// assessDrift is already normalized; keep it in [0..1]
values . push ( Math . max ( 0 , Math . min ( 1 , Math . abs ( drift ) ) ) ) ;
}
}
if ( values . length === 0 ) return 0 ; // neutral if no data
const avg = values . reduce ( ( s , v ) => s + v , 0 ) / values . length ;
return Math . max ( 0 , Math . min ( 1 , avg ) ) ;
} catch ( e ) {
this . logger ? . warn ? . ( ` Drift score error: ${ e . message } ` ) ;
return 0 ;
}
}
_calculateAssetHealthIndex ( ) {
try {
// 1) Hard fail -> worst health
// if (this.state?.getCurrentState && this.state.getCurrentState() === "failed")
if ( [ "off" ] . includes ( this . state ? . getCurrentState ? . ( ) ) ) {
this . assetHealth . index = 5 ;
return 5 ;
}
// 2) Inputs (clamped to 0..1)
const availability = typeof this . kpi ? . availability === 'number' ? this . kpi . availability : 1 ;
const unavailability = 1 - Math . max ( 0 , Math . min ( 1 , availability ) ) ;
const effPenalty = Math . max ( 0 , Math . min ( 1 , typeof this . relDistFromPeak === 'number' ? this . relDistFromPeak : 0 ) ) ;
const driftScore = this . _computeDriftScore ( ) ; // 0..1
// 3) Blend (weights sum to 1.0)
// Tweak these if you like: e.g. make drift more/less important.
const wAvail = 0.4 ; // unavailability weight
const wDrift = 0.4 ; // drift weight
const wEff = 0.2 ; // efficiency distance weight
const score01 = ( wAvail * unavailability ) + ( wDrift * driftScore ) + ( wEff * effPenalty ) ;
// 4) Scale to 0..5 integer, clamp
const index = Math . max ( 0 , Math . min ( 5 , Math . round ( score01 * 5 ) ) ) ;
this . assetHealth . index = index ;
return index ;
} catch ( err ) {
this . logger ? . error ? . ( ` AHI calc error: ${ err . message } ` ) ;
this . assetHealth . index = 0 ;
return 0 ;
}
}
_handleStateChangeForKPI ( newState ) {
const now = Date . now ( ) ;
const runtime = this . state . getRunTimeHours ( ) ;
const lastState = this . state . getPreviousState ? . ( ) || "unknown" ;
// --- Treat OFF as failure and start of downtime ---
if ( newState === "off" ) {
this . kpi . failures ++ ; // always count a new failure when OFF
this . kpi . lastFailureTime = now ; // mark the start of downtime
this . logger . warn ( ` Machine OFF (counted as failure). Total failures: ${ this . kpi . failures } ` ) ;
}
// --- When we leave OFF and become OPERATIONAL, book downtime ---
if ( newState === "operational" ) {
// Only calculate downtime if we had an OFF period before
if ( this . kpi . lastFailureTime != null ) {
const downtimeHours = ( now - this . kpi . lastFailureTime ) / 3600000 ;
this . kpi . totalDowntimeHours += downtimeHours ;
this . kpi . lastRepairTime = now ; // moment of "repaired"
this . kpi . lastFailureTime = null ; // close downtime window
this . logger . info ( ` OFF → OPERATIONAL. Added ${ downtimeHours . toFixed ( 2 ) } h downtime. ` ) ;
}
}
// --- Compute KPI Metrics ---
const failures = this . kpi . failures ;
const downtime = this . kpi . totalDowntimeHours ;
// If no failures yet: MTBF = total runtime; MTTR = 0
this . kpi . MTBF = failures > 0 ? runtime / failures : runtime ;
this . kpi . MTTR = failures > 0 ? downtime / failures : 0 ;
// --- Compute Availability ---
const mtbf = this . kpi . MTBF ? ? 0 ;
const mttr = this . kpi . MTTR ? ? 0 ;
if ( mtbf <= 0 && mttr <= 0 ) {
this . kpi . availability = 1 ; // Default: 100% if no data
} else {
const availability = mtbf / ( mtbf + mttr ) ;
this . kpi . availability = Math . min ( 1 , Math . max ( 0 , availability ) ) ; // clamp 0– 1
}
this . logger . debug (
` KPI updated — MTBF: ${ this . kpi . MTBF . toFixed ( 2 ) } h, MTTR: ${ this . kpi . MTTR . toFixed ( 2 ) } h, ` +
` Availability: ${ ( this . kpi . availability * 100 ) . toFixed ( 2 ) } % `
) ;
}
//////////////////////////////////////////////
calcDistanceFromPeak ( currentEfficiency , peakEfficiency ) {
return Math . abs ( currentEfficiency - peakEfficiency ) ;
}
@@ -643,7 +816,7 @@ _callMeasurementHandler(measurementType, value, position, context) {
const { efficiencyCurve , peak , peakIndex , minEfficiency } = this . calcEfficiencyCurve ( powerCurve , flowCurve ) ;
// Calculate the normalized center of gravity
const NCog = ( flowCurve . y [ peakIndex ] - this . predictFlow . currentFxyYMin ) / ( this . predictFlow . currentFxyYMax - this . predictFlow . currentFxyYMin ) ; //
const NCog = ( flowCurve . y [ peakIndex ] - this . predictFlow . currentFxyYMin ) / ( this . predictFlow . currentFxyYMax - this . predictFlow . currentFxyYMin ) ;
//store in object for later retrieval
this . currentEfficiencyCurve = efficiencyCurve ;
@@ -693,38 +866,15 @@ _callMeasurementHandler(measurementType, value, position, context) {
return { cPower , cFlow } ;
}
calcEfficiency ( power , flow , variant ) {
const pressureDiff = this . measurements . type ( 'pressure' ) . variant ( 'measured' ) . difference ( 'Pa' ) ;
const g = gravity . getStandardGravity ( ) ;
const temp = this . measurements . type ( 'temperature' ) . variant ( 'measured' ) . position ( 'atEquipment' ) . getCurrentValue ( 'K' ) ;
const atmPressure = this . measurements . type ( 'atmPressure' ) . variant ( 'measured' ) . position ( 'atEquipment' ) . getCurrentValue ( 'Pa' ) ;
console . log ( ` --------------------calc efficiency : Pressure diff: ${ pressureDiff } , ${ temp } , ${ g } ` ) ;
const rho = coolprop . PropsSI ( 'D' , 'T' , temp , 'P' , atmPressure , 'WasteWater' ) ;
this . logger . debug ( ` temp: ${ temp } atmPressure : ${ atmPressure } rho : ${ rho } pressureDiff: ${ pressureDiff ? . value || 0 } ` ) ;
const flowM3s = this . measurements . type ( 'flow' ) . variant ( 'predicted' ) . position ( 'atEquipment' ) . getCurrentValue ( 'm3/s' ) ;
const powerWatt = this . measurements . type ( 'power' ) . variant ( 'predicted' ) . position ( 'atEquipment' ) . getCurrentValue ( 'W' ) ;
this . logger . debug ( ` Flow : ${ flowM3s } power: ${ powerWatt } ` ) ;
calcEfficiency ( power , flow , variant ) {
if ( power != 0 && flow != 0 ) {
const specificFlow = flow / power ;
const specificEnergyConsumption = power / flow ;
this . measurements . type ( "efficiency" ) . variant ( variant ) . position ( 'atEquipment' ) . value ( specificFlow ) ;
this . measurements . type ( "specificEnergyConsumption" ) . variant ( variant ) . position ( 'atEquipment' ) . value ( specificEnergyConsumption ) ;
if ( pressureDiff ? . value != null && flowM3s != null && powerWatt != null ) {
const meterPerBar = pressureDiff . value / rho * g ;
const nHydraulicEfficiency = rho * g * flowM3s * ( pressureDiff . value * meterPerBar ) / powerWatt ;
this . measurements . type ( "nHydraulicEfficiency" ) . variant ( variant ) . position ( 'atEquipment' ) . value ( nHydraulicEfficiency ) ;
}
// Calculate efficiency after measurements update
this . measurements . type ( "efficiency" ) . variant ( variant ) . position ( 'atEquipment' ) . value ( ( flow / power ) ) ;
} else {
this . measurements . type ( "efficiency" ) . variant ( variant ) . position ( 'atEquipment' ) . value ( null ) ;
}
//change this to nhydrefficiency ?
return this . measurements . type ( "efficiency" ) . variant ( variant ) . position ( 'atEquipment' ) . getCurrentValue ( ) ;
}
@@ -771,8 +921,15 @@ _callMeasurementHandler(measurementType, value, position, context) {
getOutput ( ) {
// Improved output object generation
const output = { } ;
const output = this . measurements . getFlattenedOutput ( ) ;
Object . entries ( this . measurements . measurements ) . forEach ( ( [ type , variants ] ) => {
Object . entries ( variants ) . forEach ( ( [ variant , positions ] ) => {
Object . entries ( positions ) . forEach ( ( [ position , measurement ] ) => {
output [ ` ${ type } . ${ variant } . ${ position } ` ] = measurement . getCurrentValue ( ) ;
} ) ;
} ) ;
} ) ;
//fill in the rest of the output object
output [ "state" ] = this . state . getCurrentState ( ) ;
@@ -783,6 +940,13 @@ _callMeasurementHandler(measurementType, value, position, context) {
output [ "cog" ] = this . cog ; // flow / power efficiency
output [ "NCog" ] = this . NCog ; // normalized cog
output [ "NCogPercent" ] = Math . round ( this . NCog * 100 * 100 ) / 100 ;
output [ "kpi_MTBF" ] = this . kpi . MTBF ;
output [ "kpi_MTTR" ] = this . kpi . MTTR ;
output [ "kpi_assetAvailability" ] = Math . round ( this . kpi . availability * 100 * 100 ) / 100 ;
output [ "kpi_totalFailuresCount" ] = this . kpi . failures ;
output [ "asset_tag_number" ] = 'L001' ;
// output["asset_tag_number"] = this.assetTagNumber;
output [ "maintenanceTime" ] = this . state . getMaintenanceTimeHours ( ) ;
if ( this . flowDrift != null ) {
@@ -798,6 +962,21 @@ _callMeasurementHandler(measurementType, value, position, context) {
output [ "effRelDistFromPeak" ] = this . relDistFromPeak ;
//this.logger.debug(`Output: ${JSON.stringify(output)}`);
/////////////////////////////////
// this._calculateAssetHealthIndex();
// output["assetHealthIndex"] = this.assetHealth.index;
this . _calculateAssetHealthIndex ( ) ;
output [ "assetHealthIndex" ] = this . assetHealth . index ;
// 0 = darkgreen, 1 = green, 2 = yellow, 3 = orange, 4 = red, 5 = darkred
// const healthColors = ["darkgreen", "green", "yellow", "orange", "red", "darkred"];
const healthColors = [ "#006400" , "#008000" , "#FFFF00" , "#FFA500" , "#FF0000" , "#8B0000" ] ;
output [ "assetHealthColor" ] = healthColors [ this . assetHealth . index ] || "unknown" ;
//////////////////////////
return output ;
}
@@ -809,7 +988,7 @@ module.exports = Machine;
/*------------------- Testing -------------------*/
/*
curve = require('C:/Users/zn375/.node-red/public/fallbackData.json');
// curve = require('C:/Users/zn375/.node-red/public/fallbackData.json');
//import a child
const Child = require('../../measurement/src/specificClass');
@@ -825,7 +1004,6 @@ const PT1 = new Child(config={
},
functionality:{
softwareType:"measurement",
positionVsParent:"upstream",
},
asset:{
supplier:"Vega",
@@ -847,7 +1025,6 @@ const PT2 = new Child(config={
},
functionality:{
softwareType:"measurement",
positionVsParent:"upstream",
},
asset:{
supplier:"Vega",
@@ -863,18 +1040,17 @@ console.log(`Creating machine...`);
const machineConfig = {
general: {
name: "Hy drostal",
name: "Hi drostal",
logging: {
enabled: true,
logLevel: "debug",
}
},
asset: {
supplier: "Hy drostal",
supplier: "Hi drostal",
type: "pump",
category: "centrifugal",
model: "H05K-S03R+HGM1X-X280KO ", // Ensure this field is present.
machineCurve: curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"],
model: "hidrostal-H05K-S03R ", // Ensure this field is present.
}
}
@@ -903,18 +1079,17 @@ const machine = new Machine(machineConfig, stateConfig);
machine.logger.info(`Registering child...`);
machine.childRegistrationUtils.registerChild(PT1, "upstream");
machine.childRegistrationUtils.registerChild(PT2, "downstream");
//feed curve to the machine class
//machine.updateCurve(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]);
/*
PT1.logger.info(`Enable sim...`);
PT1.toggleSimulation();
PT2.logger.info(`Enable sim...`);
PT2.toggleSimulation();
machine.getOutput();
*/
//manual test
//machine.handleInput("parent", "execSequence", "startup");
/*
machine.measurements.type("pressure").variant("measured").position('upstream').value(-200);
machine.measurements.type("pressure").variant("measured").position('downstream').value(1000);
@@ -924,8 +1099,8 @@ const tickLoop = setInterval(changeInput,1000);
function changeInput(){
PT1.logger.info(`tick...`);
PT1.tick();
PT2.tick();
// PT1.tick();
// PT2.tick();
}
async function testingSequences(){