changed the folder and added index.js

This commit is contained in:
znetsixe
2025-06-10 12:36:39 +02:00
parent fda8cb33db
commit bc9e3cda90
24 changed files with 3848 additions and 2 deletions

View File

@@ -0,0 +1,187 @@
// Add unit conversion support
const convertModule = require('../../../convert/dependencies/index');
class Measurement {
constructor(type, variant, position, windowSize) {
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
// Place all data inside an array
this.values = []; // Array to store all values
this.timestamps = []; // Array to store all timestamps
// Unit tracking
this.unit = null; // Current unit of measurement
// For tracking differences if this is a calculated difference measurement
this.isDifference = false;
this.sourcePositions = [];
}
// -- Updater methods --
setType(type) {
this.type = type;
return this;
}
setVariant(variant) {
this.variant = variant;
return this;
}
setPosition(position) {
this.position = position;
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){
this.values.shift();
this.timestamps.shift();
}
//push the new value
this.values.push(value);
this.timestamps.push(timestamp);
return this;
}
setUnit(unit) {
this.unit = unit;
return this;
}
// -- Getter methods --
getCurrentValue() {
if (this.values.length === 0) return null;
return this.values[this.values.length - 1];
}
getAverage() {
if (this.values.length === 0) return null;
const sum = this.values.reduce((acc, val) => acc + val, 0);
return sum / this.values.length;
}
getMin() {
if (this.values.length === 0) return null;
return Math.min(...this.values);
}
getMax() {
if (this.values.length === 0) return null;
return Math.max(...this.values);
}
getAllValues() {
return {
values: [...this.values],
timestamps: [...this.timestamps],
unit: this.unit
};
}
// -- Position-based methods --
// Create a new measurement that is the difference between two positions
static createDifference(upstreamMeasurement, downstreamMeasurement) {
console.log('hello:');
if (upstreamMeasurement.type !== downstreamMeasurement.type ||
upstreamMeasurement.variant !== downstreamMeasurement.variant) {
throw new Error('Cannot calculate difference between different measurement types or variants');
}
// Ensure units match
let downstream = downstreamMeasurement;
if (upstreamMeasurement.unit && downstream.unit &&
upstreamMeasurement.unit !== downstream.unit) {
downstream = downstream.convertTo(upstreamMeasurement.unit);
}
// Create a new difference measurement
const diffMeasurement = new Measurement(
upstreamMeasurement.type,
upstreamMeasurement.variant,
'difference',
Math.min(upstreamMeasurement.windowSize, downstream.windowSize)
);
// Mark as a difference measurement and keep track of sources
diffMeasurement.isDifference = true;
diffMeasurement.sourcePositions = ['upstream', 'downstream'];
// Calculate all differences where timestamps match
const upValues = upstreamMeasurement.getAllValues();
const downValues = downstream.getAllValues();
// Match timestamps and calculate differences
for (let i = 0; i < upValues.timestamps.length; i++) {
const upTimestamp = upValues.timestamps[i];
const downIndex = downValues.timestamps.indexOf(upTimestamp);
if (downIndex !== -1) {
const diff = upValues.values[i] - downValues.values[downIndex];
diffMeasurement.setValue(diff, upTimestamp);
}
}
diffMeasurement.setUnit(upstreamMeasurement.unit);
return diffMeasurement;
}
// -- Additional getter methods --
getLatestTimestamp() {
if (this.timestamps.length === 0) return null;
return this.timestamps[this.timestamps.length - 1];
}
getValueAtTimestamp(timestamp) {
const index = this.timestamps.indexOf(timestamp);
if (index === -1) return null;
return this.values[index];
}
// -- Unit conversion methods --
convertTo(targetUnit) {
if (!this.unit) {
throw new Error('Current unit not set, cannot convert');
}
try {
const convertedValues = this.values.map(value =>
convertModule.convert(value).from(this.unit).to(targetUnit)
);
const newMeasurement = new Measurement(
this.type,
this.variant,
this.position,
this.windowSize
);
// Copy values and timestamps
newMeasurement.values = convertedValues;
newMeasurement.timestamps = [...this.timestamps];
newMeasurement.unit = targetUnit;
return newMeasurement;
} catch (error) {
throw new Error(`Unit conversion failed: ${error.message}`);
}
}
}
module.exports = Measurement;

View File

@@ -0,0 +1,56 @@
const Measurement = require('./Measurement');
class MeasurementBuilder {
constructor() {
this.type = null;
this.variant = null;
this.position = null;
this.windowSize = 10; // Default window size
}
// e.g. 'pressure', 'flow', etc.
setType(type) {
this.type = type;
return this;
}
// e.g. 'predicted' or 'measured', etc..
setVariant(variant) {
this.variant = variant;
return this;
}
// Downstream or upstream of parent object
setPosition(position) {
this.position = position;
return this;
}
// default size of the data that gets tracked
setWindowSize(windowSize) {
this.windowSize = windowSize;
return this;
}
build() {
// Validate required fields
if (!this.type) {
throw new Error('Measurement type is required');
}
if (!this.variant) {
throw new Error('Measurement variant is required');
}
if (!this.position) {
throw new Error('Measurement position is required');
}
return new Measurement(
this.type,
this.variant,
this.position,
this.windowSize
);
}
}
module.exports = MeasurementBuilder;

View File

@@ -0,0 +1,200 @@
const MeasurementBuilder = require('./MeasurementBuilder');
class MeasurementContainer {
constructor(options = {}, logger) {
this.logger = logger;
this.measurements = {};
this.windowSize = options.windowSize || 10; // Default window size
// For chaining context
this._currentType = null;
this._currentVariant = null;
this._currentPosition = null;
}
// Chainable methods
type(typeName) {
this._currentType = typeName;
this._currentVariant = null;
this._currentPosition = null;
return this;
}
variant(variantName) {
if (!this._currentType) {
throw new Error('Type must be specified before variant');
}
this._currentVariant = variantName;
this._currentPosition = null;
return this;
}
position(positionName) {
if (!this._currentVariant) {
throw new Error('Variant must be specified before position');
}
this._currentPosition = positionName;
return this;
}
// Core methods that complete the chain
value(val, timestamp = Date.now()) {
if (!this._ensureChainIsValid()) return this;
const measurement = this._getOrCreateMeasurement();
measurement.setValue(val, timestamp);
return this;
}
unit(unitName) {
if (!this._ensureChainIsValid()) return this;
const measurement = this._getOrCreateMeasurement();
measurement.setUnit(unitName);
return this;
}
// Terminal operations - get data out
get() {
if (!this._ensureChainIsValid()) return null;
return this._getOrCreateMeasurement();
}
getCurrentValue() {
const measurement = this.get();
return measurement ? measurement.getCurrentValue() : null;
}
getAverage() {
const measurement = this.get();
return measurement ? measurement.getAverage() : null;
}
getMin() {
const measurement = this.get();
return measurement ? measurement.getMin() : null;
}
getMax() {
const measurement = this.get();
return measurement ? measurement.getMax() : null;
}
getAllValues() {
const measurement = this.get();
return measurement ? measurement.getAllValues() : null;
}
// Difference calculations between positions
difference() {
if (!this._currentType || !this._currentVariant) {
throw new Error('Type and variant must be specified for difference calculation');
}
// Save position to restore chain state after operation
const savedPosition = this._currentPosition;
// Get upstream measurement
this._currentPosition = 'upstream';
const upstream = this.get();
// Get downstream measurement
this._currentPosition = 'downstream';
const downstream = this.get();
// Restore chain state
this._currentPosition = savedPosition;
if (!upstream || !downstream || upstream.values.length === 0 || downstream.values.length === 0) {
return null;
}
// Ensure units match
let downstreamForCalc = downstream;
if (upstream.unit && downstream.unit && upstream.unit !== downstream.unit) {
try {
downstreamForCalc = downstream.convertTo(upstream.unit);
} catch (error) {
if (this.logger) {
this.logger.error(`Unit conversion failed: ${error.message}`);
}
return null;
}
}
return {
value: downstreamForCalc.getCurrentValue() - upstream.getCurrentValue() ,
avgDiff: downstreamForCalc.getAverage() - upstream.getAverage() ,
unit: upstream.unit
};
}
// Helper methods
_ensureChainIsValid() {
if (!this._currentType || !this._currentVariant || !this._currentPosition) {
if (this.logger) {
this.logger.error('Incomplete measurement chain, required: type, variant, and position');
}
return false;
}
return true;
}
_getOrCreateMeasurement() {
// Initialize nested structure if needed
if (!this.measurements[this._currentType]) {
this.measurements[this._currentType] = {};
}
if (!this.measurements[this._currentType][this._currentVariant]) {
this.measurements[this._currentType][this._currentVariant] = {};
}
if (!this.measurements[this._currentType][this._currentVariant][this._currentPosition]) {
this.measurements[this._currentType][this._currentVariant][this._currentPosition] =
new MeasurementBuilder()
.setType(this._currentType)
.setVariant(this._currentVariant)
.setPosition(this._currentPosition)
.setWindowSize(this.windowSize)
.build();
}
return this.measurements[this._currentType][this._currentVariant][this._currentPosition];
}
// Additional utility methods
getTypes() {
return Object.keys(this.measurements);
}
getVariants() {
if (!this._currentType) {
throw new Error('Type must be specified before listing variants');
}
return this.measurements[this._currentType] ?
Object.keys(this.measurements[this._currentType]) : [];
}
getPositions() {
if (!this._currentType || !this._currentVariant) {
throw new Error('Type and variant must be specified before listing positions');
}
if (!this.measurements[this._currentType] ||
!this.measurements[this._currentType][this._currentVariant]) {
return [];
}
return Object.keys(this.measurements[this._currentType][this._currentVariant]);
}
clear() {
this.measurements = {};
this._currentType = null;
this._currentVariant = null;
this._currentPosition = null;
}
}
module.exports = MeasurementContainer;

View File

@@ -0,0 +1,89 @@
# Measurement System Documentation
This system provides a flexible way to store, retrieve, and analyze measurement data using a chainable API.
## Basic Usage
```javascript
const { MeasurementContainer } = require('./index');
const container = new MeasurementContainer({ windowSize: 20 });
// Set values
container.type('pressure').variant('measured').position('upstream').value(100).unit('psi');
// Get values
const upstreamPressure = container.type('pressure').variant('measured').position('upstream').getCurrentValue();
console.log(`Upstream pressure: ${upstreamPressure}`);
```
## Chainable API Methods
### Setting Context
- `type(typeName)` - Set the measurement type (pressure, flow, etc.)
- `variant(variantName)` - Set the variant (measured, predicted, etc.)
- `position(positionName)` - Set the position (upstream, downstream, etc.)
### Setting Data
- `value(val, [timestamp])` - Add a value with optional timestamp
- `unit(unitName)` - Set the measurement unit
### Getting Data
- `get()` - Get the measurement object
- `getCurrentValue()` - Get the most recent value
- `getAverage()` - Calculate average of all values
- `getMin()` - Get minimum value
- `getMax()` - Get maximum value
### Calculations
- `difference()` - Calculate difference between upstream and downstream positions
### Listing Available Data
- `getTypes()` - Get all measurement types
- `listVariants()` - List variants for current type
- `listPositions()` - List positions for current type and variant
## Example Workflows
### Setting and retrieving values
```javascript
// Set a measurement
container.type('flow')
.variant('measured')
.position('upstream')
.value(120)
.unit('gpm');
// Retrieve the same measurement
const flow = container.type('flow')
.variant('measured')
.position('upstream')
.getCurrentValue();
```
### Calculating differences
```javascript
// Set upstream and downstream measurements
container.type('pressure').variant('measured').position('upstream').value(100).unit('psi');
container.type('pressure').variant('measured').position('downstream').value(95).unit('psi');
// Calculate the difference
const diff = container.type('pressure').variant('measured').difference();
console.log(`Pressure drop: ${diff.currentDiff} ${diff.unit}`);
```
### Working with historical data
```javascript
// Add multiple values
container.type('temperature')
.variant('measured')
.position('outlet')
.value(72)
.value(74)
.value(73)
.unit('F');
// Get statistics
const avg = container.type('temperature').variant('measured').position('outlet').getAverage();
const min = container.type('temperature').variant('measured').position('outlet').getMin();
const max = container.type('temperature').variant('measured').position('outlet').getMax();
```

View File

@@ -0,0 +1,58 @@
const { MeasurementContainer } = require('./index');
// Create a container
const container = new MeasurementContainer({ windowSize: 20 });
// Example 1: Setting values with chaining
console.log('--- Example 1: Setting values ---');
container.type('pressure').variant('measured').position('upstream').value(100).unit('psi');
container.type('pressure').variant('measured').position('downstream').value(95).unit('psi');
container.type('pressure').variant('measured').position('downstream').value(80);
// Example 2: Getting values with chaining
console.log('--- Example 2: Getting values ---');
const upstreamValue = container.type('pressure').variant('measured').position('upstream').getCurrentValue();
const upstreamUnit = container.type('pressure').variant('measured').position('upstream').get().unit;
console.log(`Upstream pressure: ${upstreamValue} ${upstreamUnit}`);
const downstreamValue = container.type('pressure').variant('measured').position('downstream').getCurrentValue();
const downstreamUnit = container.type('pressure').variant('measured').position('downstream').get().unit;
console.log(`Downstream pressure: ${downstreamValue} ${downstreamUnit}`);
// Example 3: Calculations using chained methods
console.log('--- Example 3: Calculations ---');
container.type('flow').variant('predicted').position('upstream').value(200).unit('gpm');
container.type('flow').variant('predicted').position('downstream').value(195).unit('gpm');
const flowAvg = container.type('flow').variant('predicted').position('upstream').getAverage();
console.log(`Average upstream flow: ${flowAvg} gpm`);
// Example 4: Getting pressure difference
console.log('--- Example 4: Difference calculations ---');
const pressureDiff = container.type('pressure').variant('measured').difference();
console.log(`Pressure difference: ${pressureDiff.value} ${pressureDiff.unit}`);
// Example 5: Adding multiple values to track history
console.log('--- Example 5: Multiple values ---');
// Add several values to upstream flow
container.type('flow').variant('measured').position('upstream')
.value(210).value(215).value(205).unit('gpm');
// Then get statistics
console.log('Flow statistics:');
console.log(`- Current: ${container.type('flow').variant('measured').position('upstream').getCurrentValue()} gpm`);
console.log(`- Average: ${container.type('flow').variant('measured').position('upstream').getAverage()} gpm`);
console.log(`- Min: ${container.type('flow').variant('measured').position('upstream').getMin()} gpm`);
console.log(`- Max: ${container.type('flow').variant('measured').position('upstream').getMax()} gpm`);
console.log(`Show all values : ${JSON.stringify(container.type('flow').variant('measured').position('upstream').getAllValues())}`);
// Example 6: Listing available data
console.log('--- Example 6: Listing available data ---');
console.log('Types:', container.getTypes());
console.log('Pressure variants:', container.type('pressure').getVariants());
console.log('Measured pressure positions:', container.type('pressure').variant('measured').getPositions());
module.exports = {
runExamples: () => {
console.log('Examples of the measurement chainable API');
}
};

View File

@@ -0,0 +1,9 @@
const MeasurementContainer = require('./MeasurementContainer');
const Measurement = require('./Measurement');
const MeasurementBuilder = require('./MeasurementBuilder');
module.exports = {
MeasurementContainer,
Measurement,
MeasurementBuilder
};