121 lines
4.2 KiB
JavaScript
121 lines
4.2 KiB
JavaScript
/**
|
||
* ROLLCALC V14 - STANDARD DEVIATION CALCULATOR
|
||
* =============================================
|
||
* Calculates diameter & weight ranges (±2σ = 95% confidence)
|
||
* For use with article-data.json containing thickness_stddev & area_weight_stddev
|
||
*/
|
||
|
||
class StdDevCalculator {
|
||
/**
|
||
* Calculate weight ranges from area weight and stddev
|
||
* @param {number} areaWeight - Area weight in g/m²
|
||
* @param {number} areaWeightStddev - Standard deviation in g/m²
|
||
* @param {number} length - Product length in meters
|
||
* @param {number} width - Roll width in meters
|
||
* @returns {Object} {min, nom, max, aw_min, aw_nom, aw_max}
|
||
*/
|
||
static calculateWeightRanges(areaWeight, areaWeightStddev, length, width) {
|
||
if (!areaWeight || !length || !width) return null;
|
||
|
||
const sigma = areaWeightStddev || 0;
|
||
const aw_min = Math.max(0, areaWeight - 2 * sigma); // Worst case (lightest)
|
||
const aw_nom = areaWeight; // Nominal
|
||
const aw_max = areaWeight + 2 * sigma; // Best case (heaviest)
|
||
|
||
return {
|
||
min: (aw_min * length * width / 1000), // kg
|
||
nom: (aw_nom * length * width / 1000), // kg
|
||
max: (aw_max * length * width / 1000), // kg
|
||
aw_min: aw_min,
|
||
aw_nom: aw_nom,
|
||
aw_max: aw_max
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Calculate diameter ranges from thickness and stddev
|
||
* NOTE: Thinner product → larger roll, thicker product → smaller roll
|
||
* @param {number} coreDiameter - Core diameter in mm
|
||
* @param {number} thickness - Product thickness in mm
|
||
* @param {number} thicknessStddev - Standard deviation in mm
|
||
* @param {number} productLength - Product length in meters
|
||
* @returns {Object} {min, nom, max, t_min, t_nom, t_max}
|
||
*/
|
||
static calculateDiameterRanges(coreDiameter, thickness, thicknessStddev, productLength) {
|
||
if (!coreDiameter || !thickness || !productLength) return null;
|
||
|
||
const d = coreDiameter;
|
||
const L = productLength * 1000; // Convert to mm
|
||
const sigma = thicknessStddev || 0;
|
||
|
||
// ±2σ = 95% confidence interval
|
||
const t_min = Math.max(0, thickness - 2 * sigma); // Thinnest product
|
||
const t_nom = thickness; // Nominal
|
||
const t_max = thickness + 2 * sigma; // Thickest product
|
||
|
||
// D = √(d² + 4Lt/π)
|
||
// Thinner product → larger D
|
||
// Thicker product → smaller D
|
||
const d_nom = Math.sqrt(d*d + (4*L*t_nom)/Math.PI);
|
||
const d_thin = Math.sqrt(d*d + (4*L*t_min)/Math.PI); // LARGEST roll
|
||
const d_thick = Math.sqrt(d*d + (4*L*t_max)/Math.PI); // SMALLEST roll
|
||
|
||
return {
|
||
min: d_thick, // SMALLEST (thickest product)
|
||
nom: d_nom, // NOMINAL
|
||
max: d_thin, // LARGEST (thinnest product)
|
||
t_min: t_min,
|
||
t_nom: t_nom,
|
||
t_max: t_max
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Format diameter result with ±2σ ranges and tolerance
|
||
*/
|
||
static formatDiameterResult(diameterRanges, tolerance = 0) {
|
||
if (!diameterRanges) return '';
|
||
|
||
const d_low = Math.min(diameterRanges.min, diameterRanges.max);
|
||
const d_high = Math.max(diameterRanges.min, diameterRanges.max);
|
||
|
||
let text = `${diameterRanges.nom.toFixed(1)} mm (±2σ: ${d_low.toFixed(1)}–${d_high.toFixed(1)} mm)`;
|
||
|
||
if (tolerance && tolerance > 0) {
|
||
const d_tol_min = diameterRanges.nom - tolerance;
|
||
const d_tol_max = diameterRanges.nom + tolerance;
|
||
text += ` | Tolerance: ${d_tol_min.toFixed(1)}–${d_tol_max.toFixed(1)} mm`;
|
||
}
|
||
|
||
return text;
|
||
}
|
||
|
||
/**
|
||
* Format weight result
|
||
*/
|
||
static formatWeightResult(weightRanges) {
|
||
if (!weightRanges) return '';
|
||
return `${weightRanges.nom.toFixed(1)} kg (${weightRanges.min.toFixed(1)}–${weightRanges.max.toFixed(1)} kg)`;
|
||
}
|
||
|
||
/**
|
||
* Get worst-case weight for safety checks (Forklift, etc.)
|
||
*/
|
||
static getWorstCaseWeight(weightRanges) {
|
||
if (!weightRanges) return 0;
|
||
return Math.max(weightRanges.nom, weightRanges.max);
|
||
}
|
||
|
||
/**
|
||
* Get nominal weight for capacity planning
|
||
*/
|
||
static getNominalWeight(weightRanges) {
|
||
if (!weightRanges) return 0;
|
||
return weightRanges.nom;
|
||
}
|
||
}
|
||
|
||
// Export globally
|
||
window.StdDevCalculator = StdDevCalculator;
|
||
console.log('[StdDevCalculator] Module loaded');
|