Files
RollCalcPython/static/rollcalc_stddev_ranges.js

211 lines
7.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ROLLCALC V14 - QUALITY OF LIFE UPDATE
* Standard Deviation Ranges (±2σ = 95% confidence)
* ====================================================
*
* Features:
* 1. Direct Calc: Show MIN/NOM/MAX for diameter & weight (±2σ)
* 2. Forklift Checker: Use worst-case weight (MAX)
* 3. Load Optimizer: Use nominal values (current behavior)
*/
// ===================================================================
// ENHANCED WEIGHT CALCULATION WITH STDDEV
// ===================================================================
function 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
const aw_nom = areaWeight; // Nominal
const aw_max = areaWeight + 2 * sigma; // Best case
return {
min: (aw_min * length * width / 1000),
nom: (aw_nom * length * width / 1000),
max: (aw_max * length * width / 1000),
aw_min: aw_min,
aw_nom: aw_nom,
aw_max: aw_max
};
}
// ===================================================================
// ENHANCED DIAMETER CALCULATION WITH STDDEV
// ===================================================================
function 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 (largest diameter)
const t_nom = thickness; // Nominal
const t_max = thickness + 2 * sigma; // Thickest (smallest diameter)
// D = √(d² + 4Lt/π)
const d_nom = Math.sqrt(d*d + (4*L*t_nom)/Math.PI);
const d_min = Math.sqrt(d*d + (4*L*t_min)/Math.PI); // Thinnest product → largest roll
const d_max = Math.sqrt(d*d + (4*L*t_max)/Math.PI); // Thickest product → smallest roll
return {
min: d_min, // Actually MAX diameter (thinnest product)
nom: d_nom, // Nominal diameter
max: d_max, // Actually MIN diameter (thickest product)
t_min: t_min,
t_nom: t_nom,
t_max: t_max,
note: "Note: Min/Max are swapped (thinner product → larger roll)"
};
}
// ===================================================================
// DISPLAY RESULT WITH STDDEV RANGES
// ===================================================================
function displayDiameterResult(diameterRanges, weightRanges, tolerance = 0) {
if (!diameterRanges || !weightRanges) return false;
const resBox = document.getElementById('d-result');
const resLabel = document.getElementById('d-res-label');
const resVal = document.getElementById('d-res-val');
const resRange = document.getElementById('d-res-range');
if (!resBox || !resLabel || !resVal || !resRange) {
console.warn('[StdDev] Result elements not found');
return false;
}
resLabel.textContent = 'Roll Diameter (D)';
resVal.textContent = diameterRanges.nom.toFixed(1) + ' mm';
// Build range text with ±2σ
const d_low = Math.min(diameterRanges.min, diameterRanges.max);
const d_high = Math.max(diameterRanges.min, diameterRanges.max);
let rangeText = `Range (±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;
rangeText += ` | Tol: ${d_tol_min.toFixed(1)} – ${d_tol_max.toFixed(1)} mm`;
}
resRange.textContent = rangeText;
resBox.style.display = 'block';
console.log('[StdDev] Diameter results displayed:', {
nom: diameterRanges.nom.toFixed(1),
min: d_low.toFixed(1),
max: d_high.toFixed(1)
});
return true;
}
// ===================================================================
// DISPLAY WEIGHT WITH STDDEV RANGES
// ===================================================================
function displayWeightRanges(weightRanges) {
if (!weightRanges) return false;
const woField = document.getElementById('d-wo');
if (!woField) {
console.warn('[StdDev] Weight field not found');
return false;
}
// Display nominal weight with data attributes for stddev
woField.value = weightRanges.nom.toFixed(1);
// Store ranges in data attributes for Forklift Checker to use
woField.dataset.weight_nom = weightRanges.nom.toFixed(1);
woField.dataset.weight_min = weightRanges.min.toFixed(1);
woField.dataset.weight_max = weightRanges.max.toFixed(1);
console.log('[StdDev] Weight ranges stored:', {
nom: weightRanges.nom.toFixed(1),
min: weightRanges.min.toFixed(1),
max: weightRanges.max.toFixed(1)
});
return true;
}
// ===================================================================
// ENHANCED FORKLIFT CHECKER - Use worst-case weight
// ===================================================================
function evaluateForkliftWithStddev(coreMm, category) {
const woField = document.getElementById('d-wo');
if (!woField) return;
// Get worst-case weight (maximum)
const weight_nom = parseFloat(woField.value) || 0;
const weight_max = parseFloat(woField.dataset.weight_max) || weight_nom;
// Use worst-case for safety
const weightToCheck = Math.max(weight_nom, weight_max);
console.log('[StdDev-Forklift] Evaluating with worst-case weight:', {
nominal: weight_nom.toFixed(1),
worst_case: weight_max.toFixed(1),
using: weightToCheck.toFixed(1)
});
if (window.ForkliftChecker) {
return window.ForkliftChecker.evaluate(weightToCheck, coreMm, category);
}
return true;
}
// ===================================================================
// EXPORT FOR USE IN MAIN CALCULATOR
// ===================================================================
window.StdDevCalculator = {
calculateWeightRanges: calculateWeightRanges,
calculateDiameterRanges: calculateDiameterRanges,
displayDiameterResult: displayDiameterResult,
displayWeightRanges: displayWeightRanges,
evaluateForkliftWithStddev: evaluateForkliftWithStddev
};
console.log('[StdDevCalculator] Module loaded');
// ===================================================================
// INTEGRATION PATCH FOR DIRECT CALCULATION
// ===================================================================
// This should be added to the main d-calc button handler
/*
USAGE IN DIRECT CALC:
// After article selection, when user clicks "Calculate":
const mode = document.querySelector('.mode-btn.active')?.dataset.mode || 'diameter';
const d_mm = parseFloat(document.getElementById('d-co').value) || 0;
const t_mm = parseFloat(document.getElementById('d-th').value) || 0;
const t_stddev = parseFloat(document.getElementById('d-th-stddev')?.value) || 0; // NEW
const L_m = parseFloat(document.getElementById('d-le').value) || 0;
const wi = parseFloat(document.getElementById('de-wi').value) || 0;
const aw = parseFloat(document.getElementById('d-aw').value) || 0;
const aw_stddev = parseFloat(document.getElementById('d-aw-stddev')?.value) || 0; // NEW
const category = (document.getElementById('d-cat')?.value || '').toLowerCase();
// Calculate ranges
const diameterRanges = window.StdDevCalculator.calculateDiameterRanges(
d_mm, t_mm, t_stddev, L_m
);
const weightRanges = window.StdDevCalculator.calculateWeightRanges(
aw, aw_stddev, L_m, wi
);
// Display results with ranges
window.StdDevCalculator.displayDiameterResult(diameterRanges, weightRanges);
window.StdDevCalculator.displayWeightRanges(weightRanges);
// Forklift check with worst-case weight
window.StdDevCalculator.evaluateForkliftWithStddev(d_mm, category);
*/