211 lines
7.7 KiB
JavaScript
211 lines
7.7 KiB
JavaScript
/**
|
||
* 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);
|
||
*/
|