106 lines
3.1 KiB
JavaScript
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;
|
|
}
|