changes to RUL

This commit is contained in:
Woutverheijen
2025-12-15 15:18:51 +01:00
parent e457975be8
commit 1cf42b9a7e

View File

@@ -676,35 +676,40 @@ _callMeasurementHandler(measurementType, value, position, context) {
} }
_registerFailure(cause, runtimeOverride) { _registerFailure(cause, runtimeOverride) {
const runtime = runtimeOverride ?? this.state.getRunTimeHours(); const runtime = runtimeOverride ?? this.state.getRunTimeHours();
const maintenance_time = this.state.getMaintenanceTimeHours(); const maintenanceTime = this.state.getMaintenanceTimeHours();
this.kpi.failures += 1; this.kpi.failures += 1;
this.kpi.rulFailureTriggered = false; this.kpi.lastFailureTime = Date.now();
this.kpi.lastFailureTime = Date.now(); this.kpi.lastFailureRuntime = runtime;
this.kpi.lastFailureRuntime = runtime; this.kpi.lastFailureCause = cause;
this.kpi.lastFailureCause = cause;
const failures = this.kpi.failures; this.kpi.cycleMTBF =
this.kpi.failures > 0
? runtime / this.kpi.failures
: runtime;
// If no failures yet: MTBF = total runtime & MTTR = 0 this.kpi.lastRUL = null;
this.kpi.MTBF = failures > 0 ? runtime / failures : runtime; this.kpi.rulFailureTriggered = false;
this.kpi.MTTR = failures > 0 ? maintenance_time / failures : 0;
const mtbf = this.kpi.MTBF ?? 0; this.kpi.MTTR =
const mttr = this.kpi.MTTR ?? 0; this.kpi.failures > 0
? maintenanceTime / this.kpi.failures
: 0;
if (mtbf <= 0 && mttr <= 0) { const mtbf = this.kpi.cycleMTBF ?? 0;
this.kpi.availability = 1; const mttr = this.kpi.MTTR ?? 0;
} else {
const availability = mtbf / (mtbf + mttr);
this.kpi.availability = Math.min(1, Math.max(0, availability));
}
this.logger.warn( if (mtbf <= 0 && mttr <= 0) {
`Failure registered (cause=${cause}). Total failures=${this.kpi.failures}, runtime=${runtime.toFixed(2)}h` this.kpi.availability = 1;
} else {
this.kpi.availability = Math.min(
1,
Math.max(0, mtbf / (mtbf + mttr))
); );
} }
}
_handleStateChangeForKPI(newState) { _handleStateChangeForKPI(newState) {
const runtime = this.state.getRunTimeHours(); const runtime = this.state.getRunTimeHours();
@@ -839,65 +844,51 @@ _callMeasurementHandler(measurementType, value, position, context) {
} }
calculateSimpleRUL() { calculateSimpleRUL() {
const mtbf = this.kpi?.MTBF ?? 0; const mtbf =
const healthIndex = typeof this.assetHealth?.index === "number" this.kpi.cycleMTBF ??
? this.assetHealth.index this.kpi.MTBF ??
: 0; 0;
if (this.kpi?.rulFailureTriggered) { if (mtbf <= 0) {
this.kpi.lastRUL = 0; this.kpi.lastRUL = 0;
return 0; return 0;
} }
if (mtbf <= 0) { const runtime = this.state?.getRunTimeHours?.() ?? 0;
this.logger.debug("No MTBF available, RUL cannot be estimated."); const lastFailureRuntime = this.kpi?.lastFailureRuntime ?? 0;
this.kpi.lastRUL = 0; const age = Math.max(0, runtime - lastFailureRuntime);
return 0;
}
const clampedHI = Math.min(5, Math.max(0, healthIndex)); const baseFractionLeft = Math.max(0, 1 - age / mtbf);
const runtime = this.state?.getRunTimeHours?.() ?? 0; let rul = mtbf * baseFractionLeft;
const lastFailureRuntime = this.kpi?.lastFailureRuntime ?? 0;
const ageSinceLastFailure = Math.max(0, runtime - lastFailureRuntime);
const fractionByHealth = (5 - clampedHI) / 5; const healthIndex = Math.min(5, Math.max(0, this.assetHealth?.index ?? 0));
const fractionByAge = Math.max(0, 1 - ageSinceLastFailure / mtbf);
const fractionLeft = Math.min(fractionByHealth, fractionByAge);
let rul = mtbf * fractionLeft; const decayFactor = 1 + (healthIndex / 5) * 2;
if (rul < 0) rul = 0; const acceleratedAge = age * decayFactor;
const acceleratedFractionLeft = Math.max(0, 1 - acceleratedAge / mtbf);
rul = mtbf * acceleratedFractionLeft;
const prevRUL = this.kpi.lastRUL ?? null; if (this.kpi.lastRUL != null) {
const EPS = 1e-3; rul = Math.min(this.kpi.lastRUL, rul);
}
const state = this.state?.getCurrentState?.(); const EPS = 1e-3;
const isOperational = ["operational", "accelerating", "decelerating"].includes(state); const state = this.state?.getCurrentState?.();
const isOperational = ["operational", "accelerating", "decelerating"].includes(state);
if (isOperational) { if (isOperational && rul <= EPS && !this.kpi.rulFailureTriggered) {
const hadPrev = prevRUL !== null;
const wasPositive = hadPrev && prevRUL > EPS;
const isNowZeroish = rul <= EPS;
const isDecreasing = hadPrev && rul <= prevRUL + EPS;
if (wasPositive && isNowZeroish && isDecreasing) {
this.kpi.rulFailureTriggered = true; this.kpi.rulFailureTriggered = true;
this.kpi.lastFailureTime = Date.now(); this.kpi.lastFailureTime = Date.now();
this.kpi.lastFailureRuntime = runtime; this.kpi.lastFailureRuntime = runtime;
this.kpi.lastFailureCause = "RUL"; this.kpi.lastFailureCause = "RUL";
rul = 0; rul = 0;
} }
this.kpi.lastRUL = rul;
return rul;
} }
this.kpi.lastRUL = rul;
return rul;
}
updateCurve(newCurve) { updateCurve(newCurve) {
this.logger.info(`Updating machine curve`); this.logger.info(`Updating machine curve`);
const newConfig = { asset: { machineCurve: newCurve } }; const newConfig = { asset: { machineCurve: newCurve } };
@@ -968,9 +959,10 @@ calculateSimpleRUL() {
output["asset_tag_number"] = 'L001'; // hardcoded for now output["asset_tag_number"] = 'L001'; // hardcoded for now
output["maintenanceMode"] = this.kpi.maintenanceMode; output["maintenanceMode"] = this.kpi.maintenanceMode;
output["maintenanceTime"] = this.state.getMaintenanceTimeHours(); output["maintenanceTime"] = this.state.getMaintenanceTimeHours();
output["rul_hours"] = this.calculateSimpleRUL();
this._calculateAssetHealthIndex(); this._calculateAssetHealthIndex();
output["assetHealthIndex"] = this.assetHealth.index; output["assetHealthIndex"] = this.assetHealth.index;
output["rul_hours"] = this.calculateSimpleRUL();
const healthColors = ["#006400", "#008000", "#FFFF00", "#FFA500", "#FF0000", "#8B0000"]; // 0 = darkgreen, 1 = green, 2 = yellow, 3 = orange, 4 = red, 5 = darkred const healthColors = ["#006400", "#008000", "#FFFF00", "#FFA500", "#FF0000", "#8B0000"]; // 0 = darkgreen, 1 = green, 2 = yellow, 3 = orange, 4 = red, 5 = darkred
output["assetHealthColor"] = healthColors[this.assetHealth.index] || "unknown"; output["assetHealthColor"] = healthColors[this.assetHealth.index] || "unknown";