Files

106 lines
3.1 KiB
JavaScript

/**
* Injectable fault profiles.
*
* Each profile turns "time since injection" into a set of physics modifiers that
* stations.js and line.js consume. Faults are progressive where a real fault
* would be progressive - the bearing does not fail the instant you press the
* button, it degrades, which is the whole point of showing trend extrapolation.
*/
export const FAULT_PROFILES = [
{
id: 'bearing-degradation',
station: 'CNC-02',
label: 'CNC-02 bearing degradation',
short: 'Bearing wear',
severity: 'major',
headline: true,
description:
'Spindle bearing begins to spall. Vibration rises exponentially, spindle load creeps up, and out-of-tolerance parts start reaching inspection.',
},
{
id: 'oven-burner',
station: 'OVN-03',
label: 'OVN-03 zone 2 burner fault',
short: 'Burner fault',
severity: 'major',
description:
'Zone 2 loses roughly 40% of its heating capacity. The controller saturates trying to hold setpoint, the cure runs cold, and quality falls downstream.',
},
{
id: 'packer-jam',
station: 'PKG-05',
label: 'PKG-05 film jam',
short: 'Film jam',
severity: 'critical',
description:
'Film binds then tears, stopping the packer. Work in progress backs up through the line and upstream stations block.',
},
{
id: 'sensor-dropout',
station: 'INS-04',
label: 'INS-04 sensor dropout',
short: 'Sensor dropout',
severity: 'minor',
description:
'The inspection station stops reporting. Values go stale rather than to zero, which is what a real dropout looks like and what a twin has to handle honestly.',
},
];
export function faultProfile(id) {
return FAULT_PROFILES.find((f) => f.id === id);
}
/** Time constant for the bearing ramp, in simulated seconds. */
const BEARING_TAU = 420;
const BEARING_GAIN = 0.35;
const BEARING_CAP = 3.2;
/**
* Collapse the set of active faults into physics modifiers.
*
* active: Map of faultId -> { injectedAt } in simulated seconds.
*/
export function computeModifiers(active, simTime) {
const mods = {
bearingVibration: 0,
ovenZone2Capacity: 1,
packerJamPhase: null,
faultedStations: new Set(),
offlineStations: new Set(),
};
for (const [id, info] of active) {
const t = Math.max(0, simTime - info.injectedAt);
switch (id) {
case 'bearing-degradation':
// Exponential ramp: slow to start, unmistakable once it moves. Crosses
// the 3.5 mm/s warn band around 13 simulated minutes.
mods.bearingVibration = Math.min(
BEARING_CAP,
BEARING_GAIN * (Math.exp(t / BEARING_TAU) - 1),
);
break;
case 'oven-burner': {
// Capacity degrades over the first minute rather than cliff-edging.
const frac = Math.min(1, t / 60);
mods.ovenZone2Capacity = 1 - 0.38 * frac;
break;
}
case 'packer-jam':
mods.faultedStations.add('PKG-05');
mods.packerJamPhase = t < 8 ? 'bind' : 'tear';
break;
case 'sensor-dropout':
mods.offlineStations.add('INS-04');
break;
}
}
return mods;
}