Compare commits

..

4 Commits

Author SHA1 Message Date
2fb73e6713 Remove printing of EventData to prevent console spam 2025-10-10 11:12:38 +02:00
znetsixe
de0b947c56 Updated distance in measurement helper so its a settable compoment. See example.js file in measurement helper folder 2025-10-05 09:34:00 +02:00
Rene De ren
d99561fa80 need to further update measurement emit function 2025-10-03 15:37:08 +02:00
znetsixe
44033da15d Added logging data on menu and distance
Added helper functionality to abort movements in state class and safeguards to NOT be able to abort in protected states.
some caps removal
2025-10-02 17:29:31 +02:00
11 changed files with 337 additions and 229 deletions

View File

@@ -66,15 +66,6 @@
"units": ["g/m³", "mol/m³"]
}
]
},
{
"name": "Quantity (TSS)",
"models": [
{
"name": "VegaSolidsProbe",
"units": ["g/m³"]
}
]
}
]
}

View File

@@ -412,14 +412,6 @@
],
"description": "The frequency at which calculations are performed."
}
},
"flowNumber": {
"default": 1,
"rules": {
"type": "number",
"nullable": false,
"description": "Defines which effluent flow of the parent node to handle."
}
}
}

View File

@@ -5,18 +5,14 @@ class ChildRegistrationUtils {
this.registeredChildren = new Map();
}
async registerChild(child, positionVsParent) {
async registerChild(child, positionVsParent, distance) {
const { softwareType } = child.config.functionality;
const { name, id } = child.config.general;
this.logger.debug(`Registering child: ${name} (${id}) as ${softwareType} at ${positionVsParent}`);
// Enhanced child setup - multiple parents
if (Array.isArray(child.parent)) {
child.parent.push(this.mainClass);
} else {
child.parent = [this.mainClass];
}
// Enhanced child setup
child.parent = this.mainClass;
child.positionVsParent = positionVsParent;
// Enhanced measurement container with rich context

View File

@@ -2,11 +2,12 @@
const convertModule = require('../convert/index');
class Measurement {
constructor(type, variant, position, windowSize) {
constructor(type, variant, position, windowSize, distance = null) {
this.type = type; // e.g. 'pressure', 'flow', etc.
this.variant = variant; // e.g. 'predicted' or 'measured', etc..
this.position = position; // Downstream or upstream of parent object
this.windowSize = windowSize; // Rolling window size
this.distance = distance; // Distance from parent, if applicable
// Place all data inside an array
this.values = []; // Array to store all values
@@ -36,13 +37,12 @@ class Measurement {
return this;
}
setDistance(distance) {
this.distance = distance;
return this;
}
setValue(value, timestamp = Date.now()) {
/*
if (value === undefined || value === null) {
value = null ;
//throw new Error('Value cannot be null or undefined');
}
*/
//shift the oldest value
if(this.values.length >= this.windowSize){
@@ -168,7 +168,8 @@ class Measurement {
this.type,
this.variant,
this.position,
this.windowSize
this.windowSize,
this.distance
);
// Copy values and timestamps

View File

@@ -5,6 +5,7 @@ class MeasurementBuilder {
this.type = null;
this.variant = null;
this.position = null;
this.distance = null;
this.windowSize = 10; // Default window size
}
@@ -32,6 +33,11 @@ class MeasurementBuilder {
return this;
}
setDistance(distance) {
this.distance = distance;
return this;
}
build() {
// Validate required fields
if (!this.type) {
@@ -43,12 +49,14 @@ class MeasurementBuilder {
if (!this.position) {
throw new Error('Measurement position is required');
}
// distance is not a requirement as it can be derived from position
return new Measurement(
this.type,
this.variant,
this.position,
this.windowSize
this.windowSize,
this.distance
);

View File

@@ -3,7 +3,7 @@ const EventEmitter = require('events');
const convertModule = require('../convert/index');
class MeasurementContainer {
constructor(options = {}) {
constructor(options = {},logger) {
this.emitter = new EventEmitter();
this.measurements = {};
this.windowSize = options.windowSize || 10; // Default window size
@@ -12,6 +12,7 @@ class MeasurementContainer {
this._currentType = null;
this._currentVariant = null;
this._currentPosition = null;
this._currentDistance = null;
this._unit = null;
// Default units for each measurement type
@@ -93,16 +94,22 @@ class MeasurementContainer {
throw new Error('Variant must be specified before position');
}
// Turn string positions into numeric values
if (typeof positionValue == "string") {
positionValue = this._convertPositionStr2Num(positionValue);
}
this._currentPosition = positionValue;
return this;
}
distance(distance) {
// If distance is not provided, derive from positionVsParent
if(distance === null) {
distance = this._convertPositionStr2Num(this._currentPosition);
}
this._currentDistance = distance;
return this;
}
// ENHANCED: Update your existing value method
value(val, timestamp = Date.now(), sourceUnit = null) {
if (!this._ensureChainIsValid()) return this;
@@ -145,17 +152,18 @@ class MeasurementContainer {
sourceUnit: sourceUnit,
timestamp,
position: this._currentPosition,
distance: this._currentDistance,
variant: this._currentVariant,
type: this._currentType,
// NEW: Enhanced context
childId: this.childId,
childName: this.childName,
parentRef: this.parentRef
parentRef: this.parentRef,
};
// Emit the exact event your parent expects
this.emitter.emit(`${this._currentType}.${this._currentVariant}.${this._currentPosition}`, eventData);
//console.log(`Emitted event: ${this._currentType}.${this._currentVariant}.${this._currentPosition}`, eventData);
console.log(`Emitted event: ${this._currentType}.${this._currentVariant}.${this._currentPosition}`);
return this;
}
@@ -243,23 +251,13 @@ class MeasurementContainer {
// Difference calculations between positions
difference(requestedUnit = null) {
if (!this._currentType || !this._currentVariant) {
throw new Error('Type and variant must be specified for difference calculation');
}
const savedPosition = this._currentPosition;
// Get upstream and downstream measurements
const positions = this.getPositions();
}
const upstream = this.measurements?.[this._currentType]?.[this._currentVariant]?.['upstream'] || null;
const downstream = this.measurements?.[this._currentType]?.[this._currentVariant]?.['downstream'] || null;
this._currentPosition = Math.min(...positions);
const upstream = this.get();
this._currentPosition = Math.max(...positions);
const downstream = this.get();
this._currentPosition = savedPosition;
if (!upstream || !downstream || upstream.values.length === 0 || downstream.values.length === 0) {
return null;
}
@@ -309,6 +307,7 @@ class MeasurementContainer {
.setVariant(this._currentVariant)
.setPosition(this._currentPosition)
.setWindowSize(this.windowSize)
.setDistance(this._currentDistance)
.build();
}
@@ -328,7 +327,7 @@ class MeasurementContainer {
Object.keys(this.measurements[this._currentType]) : [];
}
getPositions(asNumber = false) {
getPositions() {
if (!this._currentType || !this._currentVariant) {
throw new Error('Type and variant must be specified before listing positions');
}
@@ -338,11 +337,7 @@ class MeasurementContainer {
return [];
}
if (asNumber) {
return Object.keys(this.measurements[this._currentType][this._currentVariant]);
}
return Object.keys(this.measurements[this._currentType][this._currentVariant]).map(this._convertPositionNum2Str);
return Object.keys(this.measurements[this._currentType][this._currentVariant]);
}
clear() {
@@ -435,18 +430,15 @@ class MeasurementContainer {
}
_convertPositionNum2Str(positionValue) {
if (positionValue === 0) {
switch (positionValue) {
case 0:
return "atEquipment";
}
if (positionValue < 0) {
case (positionValue < 0):
return "upstream";
}
if (positionValue > 0) {
case (positionValue > 0):
return "downstream";
}
if (this.logger) {
this.logger.error(`Invalid position provided: ${positionValue}`);
default:
console.log(`Invalid position provided: ${positionValue}`);
}
}

View File

@@ -7,249 +7,325 @@ console.log('retrieving, and converting measurement data with automatic unit han
// ====================================
// BASIC SETUP EXAMPLES
// ====================================
console.log('--- Example 1: Basic Setup & Event Subscription ---');
console.log('--- Example 1: Basic Setup & Distance ---');
// Create a basic container
const basicContainer = new MeasurementContainer({ windowSize: 20 });
// Subscribe to flow events to monitor changes
// Subscribe to events to monitor changes
basicContainer.emitter.on('flow.predicted.upstream', (data) => {
console.log(`📡 Event: Flow predicted upstream update: ${data.value} at ${new Date(data.timestamp).toLocaleTimeString()}`);
console.log(`📡 Event: Flow predicted upstream = ${data.value} ${data.unit || ''} (distance=${data.distance ?? 'n/a'}m)`);
});
//show all flow values from variant measured
// Subscribe to all measured flow events using wildcard
basicContainer.emitter.on('flow.measured.*', (data) => {
console.log(`📡 Event---------- I DID IT: Flow measured ${data.position} update: ${data.value}`)
console.log(`📡 Event: Flow measured ${data.position} = ${data.value} ${data.unit || ''} (distance=${data.distance ?? 'n/a'}m)`);
});
// Basic value setting with chaining
console.log('Setting basic pressure values...');
basicContainer.type('pressure').variant('measured').position('upstream').value(100).unit('psi');
basicContainer.type('pressure').variant('measured').position('downstream').value(95).unit('psi');
basicContainer.type('pressure').variant('measured').position('downstream').value(80); // Additional value
// Basic value setting with distance
console.log('\nSetting pressure values with distances:');
basicContainer
.type('pressure')
.variant('measured')
.position('upstream')
.distance(1.5)
.value(100)
.unit('psi');
basicContainer
.type('pressure')
.variant('measured')
.position('downstream')
.distance(5.2)
.value(95)
.unit('psi');
// Distance persists - no need to set it again for same position
basicContainer
.type('pressure')
.variant('measured')
.position('downstream')
.value(90); // distance 5.2 is automatically reused
console.log('✅ Basic setup complete\n');
// Retrieve and display the distance
const upstreamPressure = basicContainer
.type('pressure')
.variant('measured')
.position('upstream')
.get();
console.log(`Retrieved upstream pressure: ${upstreamPressure.getCurrentValue()} ${upstreamPressure.unit}`);
console.log(`Distance from parent: ${upstreamPressure.distance ?? 'not set'} m\n`);
// ====================================
// AUTO-CONVERSION SETUP EXAMPLES
// AUTO-CONVERSION SETUP
// ====================================
console.log('--- Example 2: Auto-Conversion Setup ---');
console.log('Setting up a container with automatic unit conversion...\n');
// Create container with auto-conversion enabled
const autoContainer = new MeasurementContainer({
autoConvert: true,
windowSize: 50,
defaultUnits: {
pressure: 'bar', // Default pressure unit
flow: 'l/min', // Default flow unit
power: 'kW', // Default power unit
temperature: 'C' // Default temperature unit
pressure: 'bar',
flow: 'l/min',
power: 'kW',
temperature: 'C'
},
preferredUnits: {
pressure: 'psi' // Override: store pressure in PSI instead of bar
pressure: 'psi'
}
});
// Values are automatically converted to preferred units
console.log('Adding pressure data with auto-conversion:');
autoContainer.type('pressure').variant('measured').position('upstream')
.value(1.5, Date.now(), 'bar'); // Input: 1.5 bar → Auto-stored as ~21.76 psi
// Values automatically convert to preferred units
console.log('Adding pressure with auto-conversion:');
autoContainer
.type('pressure')
.variant('measured')
.position('upstream')
.distance(0.5)
.value(1.5, Date.now(), 'bar'); // Input: 1.5 bar → Auto-stored as ~21.76 psi
autoContainer.type('pressure').variant('measured').position('downstream')
.value(20, Date.now(), 'psi'); // Input: 20 psi → Stored as 20 psi (already in preferred unit)
const converted = autoContainer
.type('pressure')
.variant('measured')
.position('upstream')
.get();
// Check what was actually stored
const storedPressure = autoContainer.type('pressure').variant('measured').position('upstream').get();
console.log(` Stored upstream pressure: ${storedPressure.getCurrentValue()} ${storedPressure.unit}`);
console.log(' Auto-conversion setup complete\n');
console.log(`Stored as: ${converted.getCurrentValue()} ${converted.unit} (distance=${converted.distance}m)`);
console.log('✅ Auto-conversion complete\n');
// ====================================
// UNIT CONVERSION EXAMPLES
// UNIT CONVERSION ON RETRIEVAL
// ====================================
console.log('--- Example 3: Unit Conversion on Retrieval ---');
console.log('Getting values in different units without changing stored data...\n');
// Add flow data in different units
autoContainer.type('flow').variant('predicted').position('upstream')
.value(100, Date.now(), 'l/min'); // Stored in l/min (default)
autoContainer
.type('flow')
.variant('predicted')
.position('upstream')
.distance(2.4)
.value(100, Date.now(), 'l/min');
autoContainer.type('flow').variant('predicted').position('downstream')
.value(6, Date.now(), 'm3/h'); // Auto-converted from m3/h to l/min
const flowMeasurement = autoContainer
.type('flow')
.variant('predicted')
.position('upstream')
.get();
// Retrieve the same data in different units
const flowLPM = autoContainer.type('flow').variant('predicted').position('upstream').getCurrentValue('l/min');
const flowM3H = autoContainer.type('flow').variant('predicted').position('upstream').getCurrentValue('m3/h');
const flowGPM = autoContainer.type('flow').variant('predicted').position('upstream').getCurrentValue('gal/min');
console.log(`Flow in l/min: ${flowLPM}`);
console.log(`Flow in m³/h: ${flowM3H.toFixed(2)}`);
console.log(`Flow in gal/min: ${flowGPM.toFixed(2)}`);
console.log('Unit conversion examples complete\n');
console.log(`Flow in l/min: ${flowMeasurement.getCurrentValue('l/min')}`);
console.log(`Flow in m³/h: ${flowMeasurement.getCurrentValue('m3/h').toFixed(2)}`);
console.log(`Flow in gal/min: ${flowMeasurement.getCurrentValue('gal/min').toFixed(2)}`);
console.log(`Distance: ${flowMeasurement.distance}m\n`);
// ====================================
// SMART UNIT SELECTION
// ====================================
console.log('--- Example 4: Smart Unit Selection ---');
console.log('Automatically finding the best unit for readability...\n');
// Add a very small pressure value
autoContainer.type('pressure').variant('test').position('sensor')
autoContainer
.type('pressure')
.variant('test')
.position('sensor')
.distance(0.2)
.value(0.001, Date.now(), 'bar');
// Get the best unit for this small value
const bestUnit = autoContainer.type('pressure').variant('test').position('sensor').getBestUnit();
const bestUnit = autoContainer
.type('pressure')
.variant('test')
.position('sensor')
.getBestUnit();
if (bestUnit) {
console.log(`Best unit representation: ${bestUnit.val} ${bestUnit.unit}`);
console.log(`Best unit: ${bestUnit.val.toFixed(2)} ${bestUnit.unit}`);
}
// Get all available units for pressure
const availableUnits = autoContainer.getAvailableUnits('pressure');
console.log(`Available pressure units: ${availableUnits.slice(0, 8).join(', ')}... (${availableUnits.length} total)`);
console.log('Smart unit selection complete\n');
console.log(`Available units: ${availableUnits.slice(0, 5).join(', ')}...\n`);
// ====================================
// BASIC RETRIEVAL AND CALCULATIONS
// BASIC RETRIEVAL
// ====================================
console.log('--- Example 5: Basic Value Retrieval ---');
console.log('Getting individual values and their units...\n');
// Using basic container for clear examples
const upstreamValue = basicContainer.type('pressure').variant('measured').position('upstream').getCurrentValue();
const upstreamUnit = basicContainer.type('pressure').variant('measured').position('upstream').get().unit;
console.log(`Upstream pressure: ${upstreamValue} ${upstreamUnit}`);
const upstreamVal = basicContainer
.type('pressure')
.variant('measured')
.position('upstream')
.getCurrentValue();
const downstreamValue = basicContainer.type('pressure').variant('measured').position('downstream').getCurrentValue();
const downstreamUnit = basicContainer.type('pressure').variant('measured').position('downstream').get().unit;
console.log(`Downstream pressure: ${downstreamValue} ${downstreamUnit}`);
console.log('Basic retrieval complete\n');
const upstreamData = basicContainer
.type('pressure')
.variant('measured')
.position('upstream')
.get();
console.log(`Upstream: ${upstreamVal} ${upstreamData.unit} at ${upstreamData.distance}m`);
const downstreamVal = basicContainer
.type('pressure')
.variant('measured')
.position('downstream')
.getCurrentValue();
const downstreamData = basicContainer
.type('pressure')
.variant('measured')
.position('downstream')
.get();
console.log(`Downstream: ${downstreamVal} ${downstreamData.unit} at ${downstreamData.distance}m\n`);
// ====================================
// CALCULATIONS AND STATISTICS
// CALCULATIONS & STATISTICS
// ====================================
console.log('--- Example 6: Calculations & Statistics ---');
console.log('Using built-in calculation methods...\n');
// Add flow data for calculations
basicContainer.type('flow').variant('predicted').position('upstream').value(200).unit('gpm');
basicContainer.type('flow').variant('predicted').position('downstream').value(195).unit('gpm');
basicContainer
.type('flow')
.variant('predicted')
.position('upstream')
.distance(3.0)
.value(200)
.unit('gpm');
const flowAvg = basicContainer.type('flow').variant('predicted').position('upstream').getAverage();
console.log(`Average upstream flow: ${flowAvg} gpm`);
basicContainer
.type('flow')
.variant('predicted')
.position('downstream')
.distance(8.5)
.value(195)
.unit('gpm');
// Calculate pressure difference between upstream and downstream
const pressureDiff = basicContainer.type('pressure').variant('measured').difference();
console.log(`Pressure difference: ${pressureDiff.value} ${pressureDiff.unit}`);
console.log('Calculations complete\n');
const flowAvg = basicContainer
.type('flow')
.variant('predicted')
.position('upstream')
.getAverage();
console.log(`Average upstream flow: ${flowAvg.toFixed(1)} gpm`);
const pressureDiff = basicContainer
.type('pressure')
.variant('measured')
.difference();
console.log(`Pressure difference: ${pressureDiff.value} ${pressureDiff.unit}\n`);
// ====================================
// ADVANCED STATISTICS
// ADVANCED STATISTICS & HISTORY
// ====================================
console.log('--- Example 7: Advanced Statistics & History ---');
console.log('Adding multiple values and getting comprehensive statistics...\n');
// Add several flow measurements to build history
basicContainer.type('flow').variant('measured').position('upstream')
.value(210).value(215).value(205).value(220).value(200).unit('m3/h');
basicContainer.type('flow').variant('measured').position('downstream')
.value(190).value(195).value(185).value(200).value(180).unit('m3/h');
basicContainer
.type('flow')
.variant('measured')
.position('upstream')
.distance(3.0)
.value(210)
.value(215)
.value(205)
.value(220)
.value(200)
.unit('m3/h');
const stats = basicContainer
.type('flow')
.variant('measured')
.position('upstream');
const statsData = stats.get();
// Get comprehensive statistics
const measurement = basicContainer.type('flow').variant('measured').position('upstream');
console.log('Flow Statistics:');
console.log(`- Current value: ${measurement.getCurrentValue()} ${measurement.get().unit}`);
console.log(`- Average: ${measurement.getAverage().toFixed(1)} ${measurement.get().unit}`);
console.log(`- Minimum: ${measurement.getMin()} ${measurement.get().unit}`);
console.log(`- Maximum: ${measurement.getMax()} ${measurement.get().unit}`);
console.log(` Current: ${stats.getCurrentValue()} ${statsData.unit}`);
console.log(` Average: ${stats.getAverage().toFixed(1)} ${statsData.unit}`);
console.log(` Min: ${stats.getMin()} ${statsData.unit}`);
console.log(` Max: ${stats.getMax()} ${statsData.unit}`);
console.log(` Distance: ${statsData.distance}m`);
// Show all values with timestamps
const allValues = measurement.getAllValues();
console.log(`- Total samples: ${allValues.values.length}`);
console.log(`- Value history: [${allValues.values.join(', ')}]`);
console.log('Advanced statistics complete\n');
const allValues = stats.getAllValues();
console.log(` Samples: ${allValues.values.length}`);
console.log(` History: [${allValues.values.join(', ')}]\n`);
// ====================================
// DYNAMIC UNIT MANAGEMENT
// ====================================
console.log('--- Example 8: Dynamic Unit Management ---');
console.log('Changing preferred units at runtime...\n');
// Change preferred unit for flow measurements
autoContainer.setPreferredUnit('flow', 'm3/h');
console.log('Changed preferred flow unit to m³/h');
// Add new flow data - will auto-convert to new preferred unit
autoContainer.type('flow').variant('realtime').position('inlet')
.value(150, Date.now(), 'l/min'); // Input in l/min, stored as m³/h
autoContainer
.type('flow')
.variant('realtime')
.position('inlet')
.distance(1.2)
.value(150, Date.now(), 'l/min');
const realtimeFlow = autoContainer.type('flow').variant('realtime').position('inlet');
console.log(`Stored as: ${realtimeFlow.getCurrentValue()} ${realtimeFlow.get().unit}`);
console.log(`Original unit: ${realtimeFlow.getCurrentValue('l/min')} l/min`);
console.log('Dynamic unit management complete\n');
const realtimeFlow = autoContainer
.type('flow')
.variant('realtime')
.position('inlet')
.get();
console.log(`Stored as: ${realtimeFlow.getCurrentValue()} ${realtimeFlow.unit}`);
console.log(`Original: ${realtimeFlow.getCurrentValue('l/min').toFixed(1)} l/min`);
console.log(`Distance: ${realtimeFlow.distance}m\n`);
// ====================================
// DATA EXPLORATION
// ====================================
console.log('--- Example 9: Data Exploration ---');
console.log('Discovering what data is available in the container...\n');
console.log('Available measurement types:', basicContainer.getTypes());
console.log('Available types:', basicContainer.getTypes());
console.log('Pressure variants:', basicContainer.type('pressure').getVariants());
console.log('Measured pressure positions:', basicContainer.type('pressure').variant('measured').getPositions());
// Show data structure overview
console.log('\nData Structure Overview:');
console.log('\nData Structure:');
basicContainer.getTypes().forEach(type => {
console.log(`${type.toUpperCase()}:`);
const variants = basicContainer.type(type).getVariants();
variants.forEach(variant => {
const positions = basicContainer.type(type).variant(variant).getPositions();
positions.forEach(position => {
const measurement = basicContainer.type(type).variant(variant).position(position).get();
if (measurement && measurement.values.length > 0) {
console.log(` └── ${variant}.${position}: ${measurement.values.length} values (${measurement.unit || 'no unit'})`);
}
if (variants.length > 0) {
console.log(`${type.toUpperCase()}:`);
variants.forEach(variant => {
const positions = basicContainer.type(type).variant(variant).getPositions();
positions.forEach(position => {
const m = basicContainer.type(type).variant(variant).position(position).get();
if (m && m.values.length > 0) {
console.log(` └─ ${variant}.${position}: ${m.values.length} values, ${m.unit || 'no unit'}, dist=${m.distance ?? 'n/a'}m`);
}
});
});
});
}
});
console.log('Data exploration complete\n');
console.log('\n✅ All examples complete!\n');
// ====================================
// BEST PRACTICES SUMMARY
// BEST PRACTICES
// ====================================
console.log('--- Best Practices Summary ---');
console.log('BEST PRACTICES FOR NEW USERS:\n');
console.log('--- Best Practices Summary ---\n');
console.log('1. SETUP:');
console.log(' • Enable auto-conversion for consistent units');
console.log(' • Define default units for your measurement types');
console.log(' • Set appropriate window size for your data needs\n');
console.log('SETUP:');
console.log(' • Enable autoConvert for consistent units');
console.log(' • Define defaultUnits for your measurement types');
console.log(' • Set windowSize based on your data retention needs\n');
console.log('2. STORING DATA:');
console.log(' Always use the full chain: type().variant().position().value()');
console.log(' • Specify source unit when adding values: .value(100, timestamp, "psi")');
console.log(' • Set units immediately after first value: .value(100).unit("psi")\n');
console.log('STORING DATA:');
console.log(' • Chain methods: type().variant().position().distance().value()');
console.log(' • Set distance once - it persists for that position');
console.log(' • Specify source unit: .value(100, timestamp, "psi")');
console.log(' • Set unit immediately: .value(100).unit("psi")\n');
console.log('3. RETRIEVING DATA:');
console.log(' • Use .getCurrentValue("unit") to get values in specific units');
console.log(' • Use .getBestUnit() for automatic unit selection');
console.log(' • Use .difference() for automatic upstream/downstream calculations\n');
console.log('RETRIEVING DATA:');
console.log(' • Use .getCurrentValue("unit") for specific units');
console.log(' • Use .getBestUnit() for automatic selection');
console.log(' • Use .difference() for upstream/downstream deltas');
console.log(' • Access .get().distance for physical positioning\n');
console.log('4. MONITORING:');
console.log(' • Subscribe to events for real-time updates');
console.log(' Use .emitter.on("type.variant.position", callback)');
console.log(' • Explore available data with .getTypes(), .getVariants(), .getPositions()\n');
console.log('MONITORING:');
console.log(' • Subscribe: .emitter.on("type.variant.position", callback)');
console.log(' • Event data includes: value, unit, timestamp, distance');
console.log(' • Explore data: .getTypes(), .getVariants(), .getPositions()\n');
console.log('All examples complete! Ready to use MeasurementContainer');
// Export for programmatic use
module.exports = {
runExamples: () => {
console.log('Measurement Container Examples - Complete Guide for New Users');
console.log('This file demonstrates all features with practical examples.');
},
// Export containers for testing
basicContainer,
autoContainer
};
module.exports = { basicContainer, autoContainer };

View File

@@ -180,33 +180,66 @@ getSaveInjectionCode(nodeName) {
return `
// PhysicalPosition Save injection for ${nodeName}
window.EVOLV.nodes.${nodeName}.positionMenu.saveEditor = function(node) {
console.log("=== PhysicalPosition Save Debug ===");
const sel = document.getElementById('node-input-positionVsParent');
const hasDistanceCheck = document.getElementById('node-input-hasDistance');
const distanceInput = document.getElementById('node-input-distance');
// Save existing position data
node.positionVsParent = sel ? sel.value : 'atEquipment';
node.positionLabel = sel ? sel.options[sel.selectedIndex].textContent : 'At Equipment';
node.positionIcon = sel ? sel.options[sel.selectedIndex].getAttribute('data-icon') : 'fa fa-cog';
console.log("→ sel element found:", !!sel);
console.log("→ sel:", sel);
console.log("→ sel.value:", sel ? sel.value : "NO ELEMENT");
console.log("→ sel.selectedIndex:", sel ? sel.selectedIndex : "NO ELEMENT");
console.log("→ sel.options:", sel ? Array.from(sel.options).map(o => ({value: o.value, text: o.textContent})) : "NO OPTIONS");
if (!sel) {
console.error("→ positionMenu.saveEditor FAILED: select element not found!");
return false;
}
// Save existing position data
const positionValue = sel.value;
const selectedOption = sel.options[sel.selectedIndex];
console.log("→ positionValue:", positionValue);
console.log("→ selectedOption:", selectedOption);
console.log("→ selectedOption.textContent:", selectedOption ? selectedOption.textContent : "NO OPTION");
console.log("→ selectedOption data-icon:", selectedOption ? selectedOption.getAttribute('data-icon') : "NO ICON");
node.positionVsParent = positionValue || 'atEquipment';
node.positionLabel = selectedOption ? selectedOption.textContent : 'At Equipment';
node.positionIcon = selectedOption ? selectedOption.getAttribute('data-icon') : 'fa fa-cog';
console.log("→ node.positionVsParent set to:", node.positionVsParent);
console.log("→ node.positionLabel set to:", node.positionLabel);
// Save distance data
console.log("→ hasDistanceCheck found:", !!hasDistanceCheck);
console.log("→ hasDistanceCheck.checked:", hasDistanceCheck ? hasDistanceCheck.checked : "NO ELEMENT");
// Save distance data (NEW)
node.hasDistance = hasDistanceCheck ? hasDistanceCheck.checked : false;
if (node.hasDistance && distanceInput && distanceInput.value) {
console.log("→ distanceInput.value:", distanceInput.value);
node.distance = parseFloat(distanceInput.value) || 0;
node.distanceUnit = 'm'; // Fixed to meters for now
// Generate distance description based on position
const contexts = window.EVOLV.nodes.${nodeName}.menuData.position.distanceContexts;
const context = contexts && contexts[node.positionVsParent];
node.distanceDescription = context ? context.description : 'Distance from parent';
console.log("→ distance set to:", node.distance);
} else {
// Clear distance data if not specified
console.log("→ clearing distance data");
delete node.distance;
delete node.distanceUnit;
delete node.distanceDescription;
}
console.log("→ positionMenu.saveEditor result: SUCCESS");
console.log("→ final node.positionVsParent:", node.positionVsParent);
return true;
};
`;

View File

@@ -1,7 +1,7 @@
{
"general": {
"name": {
"default": "Interpolation Configuration",
"default": "interpolation configuration",
"rules": {
"type": "string",
"description": "A human-readable name or label for this interpolation configuration."
@@ -70,7 +70,7 @@
}
},
"role": {
"default": "Interpolator",
"default": "interpolator",
"rules": {
"type": "string",
"description": "Indicates the role of this configuration (e.g., 'Interpolator', 'DataCurve', etc.)."

View File

@@ -52,6 +52,7 @@ class state{
return this.stateManager.getRunTimeHours();
}
async moveTo(targetPosition) {
// Check for invalid conditions and throw errors
@@ -86,14 +87,33 @@ class state{
// -------- State Transition Methods -------- //
abortCurrentMovement(reason = "group override") {
if (this.abortController && !this.abortController.signal.aborted) {
this.logger.warn(`Aborting movement: ${reason}`);
this.abortController.abort();
}
}
async transitionToState(targetState, signal) {
const fromState = this.getCurrentState();
const position = this.getCurrentPosition();
// Define states that cannot be aborted for safety reasons
const protectedStates = ['warmingup', 'coolingdown'];
const isProtectedTransition = protectedStates.includes(fromState);
try {
this.logger.debug(`Starting transition from ${fromState} to ${targetState}.`);
if( isProtectedTransition){
//overrule signal to prevent abortion
signal = null; // Disable abortion for protected states
//spit warning
this.logger.warn(`Transition from ${fromState} to ${targetState} is protected and cannot be aborted.`);
}
// Await the state transition and pass signal for abortion
const feedback = await this.stateManager.transitionTo(targetState,signal);
this.logger.info(`Statemanager: ${feedback}`);
@@ -108,7 +128,6 @@ class state{
//trigger move
await this.moveTo(this.delayedMove,signal);
this.delayedMove = null;
this.logger.info(`moveTo : ${feedback} `);
}

View File

@@ -1,7 +1,7 @@
{
"general": {
"name": {
"default": "State Configuration",
"default": "state configuration",
"rules": {
"type": "string",
"description": "A human-readable name for the state configuration."
@@ -65,7 +65,7 @@
}
},
"role": {
"default": "StateController",
"default": "statecontroller",
"rules": {
"type": "string",
"description": "Functional role within the system."