909 lines
38 KiB
JavaScript
909 lines
38 KiB
JavaScript
// ========================================
|
||
// CONTINUATION OF CALCULATION FUNCTIONS
|
||
// ========================================
|
||
|
||
// Bolts Calculation
|
||
function calcularParafusos() {
|
||
const fy = parseFloat(document.getElementById('bolt-type').value) || 400;
|
||
const d = parseFloat(document.getElementById('bolt-d').value) || 20;
|
||
const qty = parseInt(document.getElementById('bolt-qty').value) || 1;
|
||
const planes = parseInt(document.getElementById('bolt-planes').value) || 1;
|
||
const force = parseFloat(document.getElementById('bolt-force').value) || 0;
|
||
|
||
const area = Math.PI * Math.pow(d / 2, 2);
|
||
const Fv = 0.6 * fy * area * planes / 1000;
|
||
const capacity = Fv * qty;
|
||
const utilization = (force / capacity) * 100;
|
||
|
||
let alertClass = 'alert-success';
|
||
let status = '✅ OK - Capacidade adequada';
|
||
if (utilization > 100) {
|
||
alertClass = 'alert-error';
|
||
status = '❌ FALHA - Capacidade insuficiente';
|
||
} else if (utilization > 80) {
|
||
alertClass = 'alert-warning';
|
||
status = '⚠️ ATENÇÃO - Utilização elevada';
|
||
}
|
||
|
||
document.getElementById('bolt-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Verificação ao Cisalhamento</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Área Parafuso</div>
|
||
<div class="result-value">${area.toFixed(0)} mm²</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Fv por Parafuso</div>
|
||
<div class="result-value">${Fv.toFixed(1)} kN</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Capacidade Total</div>
|
||
<div class="result-value">${capacity.toFixed(1)} kN</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Utilização</div>
|
||
<div class="result-value">${utilization.toFixed(1)}%</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert ${alertClass}" style="margin-top: 16px;">
|
||
<strong>${status}</strong>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Ligações Parafusadas', `${qty} parafusos Ø${d}mm, Utilização = ${utilization.toFixed(1)}%`);
|
||
}
|
||
|
||
// Layout Verification
|
||
function verificarLayout() {
|
||
const d = parseFloat(document.getElementById('layout-d').value) || 20;
|
||
const edge = parseFloat(document.getElementById('layout-edge').value) || 0;
|
||
const spacing = parseFloat(document.getElementById('layout-spacing').value) || 0;
|
||
|
||
const minEdge = 1.5 * d;
|
||
const maxEdge = 12 * 10;
|
||
const minSpacing = 2.67 * d;
|
||
const maxSpacing = 300;
|
||
|
||
let edgeStatus = '✅ Conforme';
|
||
let edgeClass = 'alert-success';
|
||
if (edge < minEdge) {
|
||
edgeStatus = `❌ Abaixo do mínimo (${minEdge.toFixed(1)}mm)`;
|
||
edgeClass = 'alert-error';
|
||
} else if (edge > maxEdge) {
|
||
edgeStatus = `⚠️ Acima do máximo (${maxEdge}mm)`;
|
||
edgeClass = 'alert-warning';
|
||
}
|
||
|
||
let spacingStatus = '✅ Conforme';
|
||
let spacingClass = 'alert-success';
|
||
if (spacing < minSpacing) {
|
||
spacingStatus = `❌ Abaixo do mínimo (${minSpacing.toFixed(1)}mm)`;
|
||
spacingClass = 'alert-error';
|
||
} else if (spacing > maxSpacing) {
|
||
spacingStatus = `⚠️ Acima do máximo (${maxSpacing}mm)`;
|
||
spacingClass = 'alert-warning';
|
||
}
|
||
|
||
document.getElementById('layout-result').innerHTML = `
|
||
<div class="card">
|
||
<div class="card-title">Verificação NBR 8800</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Dist. Borda Mín</div>
|
||
<div class="result-value" style="font-size: 18px;">${minEdge.toFixed(1)} mm</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Dist. Borda Máx</div>
|
||
<div class="result-value" style="font-size: 18px;">${maxEdge} mm</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Espaç. Mínimo</div>
|
||
<div class="result-value" style="font-size: 18px;">${minSpacing.toFixed(1)} mm</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Espaç. Máximo</div>
|
||
<div class="result-value" style="font-size: 18px;">${maxSpacing} mm</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert ${edgeClass}" style="margin-top: 16px;">
|
||
<strong>Distância de Borda: ${edgeStatus}</strong>
|
||
</div>
|
||
<div class="alert ${spacingClass}">
|
||
<strong>Espaçamento: ${spacingStatus}</strong>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Layout de Furação', `Ø${d}mm, Borda: ${edge}mm, Espaç: ${spacing}mm`);
|
||
}
|
||
|
||
// Bolt vs Weld Comparison
|
||
function compararParafusoSolda() {
|
||
const force = parseFloat(document.getElementById('comp-force').value) || 0;
|
||
const length = parseFloat(document.getElementById('comp-length').value) || 0;
|
||
const fy = parseFloat(document.getElementById('comp-fy').value) || 345;
|
||
|
||
// Parafusos A325 Ø20mm
|
||
const boltCapacity = 60;
|
||
const boltQty = Math.ceil(force / boltCapacity);
|
||
const boltCost = boltQty * 15;
|
||
|
||
// Solda
|
||
const weldLeg = (force * 1000) / (0.707 * length * 0.65 * fy);
|
||
const weldLegRounded = Math.ceil(weldLeg);
|
||
const weldCost = (weldLegRounded * length / 1000) * 25;
|
||
|
||
document.getElementById('comparison-result').innerHTML = `
|
||
<div class="card">
|
||
<div class="card-title">Comparação de Soluções</div>
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
|
||
<div style="background: var(--color-bg-1); padding: 20px; border-radius: 12px;">
|
||
<h3 style="color: var(--color-primary); margin-bottom: 16px;">🔩 Solução Parafusada</h3>
|
||
<p><strong>Tipo:</strong> A325 Ø20mm</p>
|
||
<p><strong>Quantidade:</strong> ${boltQty} parafusos</p>
|
||
<p><strong>Capacidade:</strong> ${(boltQty * boltCapacity).toFixed(1)} kN</p>
|
||
<p><strong>Custo estimado:</strong> R$ ${boltCost.toFixed(2)}</p>
|
||
<p><strong>Vantagens:</strong> Desmontável, inspeção visual</p>
|
||
<p><strong>Desvantagens:</strong> Maior tempo de instalação</p>
|
||
</div>
|
||
<div style="background: var(--color-bg-2); padding: 20px; border-radius: 12px;">
|
||
<h3 style="color: var(--color-warning); margin-bottom: 16px;">🔥 Solução Soldada</h3>
|
||
<p><strong>Tipo:</strong> Solda de filete</p>
|
||
<p><strong>Perna:</strong> ${weldLegRounded} mm</p>
|
||
<p><strong>Comprimento:</strong> ${length} mm</p>
|
||
<p><strong>Custo estimado:</strong> R$ ${weldCost.toFixed(2)}</p>
|
||
<p><strong>Vantagens:</strong> Melhor rigidez, econômica</p>
|
||
<p><strong>Desvantagens:</strong> Permanente, requer qualificação</p>
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-success" style="margin-top: 20px;">
|
||
<strong>Recomendação:</strong> ${weldCost < boltCost ? 'Solda de filete é mais econômica' : 'Parafusos mais econômicos'}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Parafuso vs Solda', `${boltQty} parafusos vs solda ${weldLegRounded}mm`);
|
||
}
|
||
|
||
// Preheat Calculation
|
||
function calcularPreaquecimento() {
|
||
const cev = parseFloat(document.getElementById('preheat-cev').value) || 0;
|
||
const thickness = parseFloat(document.getElementById('preheat-thickness').value) || 0;
|
||
const ambient = parseFloat(document.getElementById('preheat-ambient').value) || 20;
|
||
|
||
const preheatTemp = 50 + (cev * 100) + (thickness / 10 * 20) + ((20 - ambient) / 2);
|
||
const maxInterpass = preheatTemp + 100;
|
||
|
||
let pwhtRecommendation = '';
|
||
if (thickness > 50 || cev > 0.60) {
|
||
pwhtRecommendation = '⚠️ PWHT (Tratamento Térmico Pós-Soldagem) recomendado';
|
||
} else {
|
||
pwhtRecommendation = '✅ PWHT não obrigatório';
|
||
}
|
||
|
||
document.getElementById('preheat-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Temperatura de Pré-Aquecimento (AWS D1.1)</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Temp. Mínima</div>
|
||
<div class="result-value">${Math.round(preheatTemp)}°C</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Temp. Interpasse Máx</div>
|
||
<div class="result-value">${Math.round(maxInterpass)}°C</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-warning" style="margin-top: 16px;">
|
||
<strong>${pwhtRecommendation}</strong>
|
||
</div>
|
||
<div class="expert-only" style="margin-top: 16px; padding: 16px; background: var(--color-bg-3); border-radius: 8px;">
|
||
<strong>Procedimento:</strong><br>
|
||
1. Aquecer uniformemente até ${Math.round(preheatTemp)}°C<br>
|
||
2. Medir temperatura a 75mm da junta<br>
|
||
3. Manter durante toda a soldagem<br>
|
||
4. Temperatura interpasse máxima: ${Math.round(maxInterpass)}°C
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Pré-Aquecimento', `CEV=${cev}, esp=${thickness}mm → ${Math.round(preheatTemp)}°C`);
|
||
}
|
||
|
||
// Weld Fillet Calculation
|
||
function calcularSoldaFilete() {
|
||
const force = parseFloat(document.getElementById('weld-force').value) || 0;
|
||
const length = parseFloat(document.getElementById('weld-length').value) || 0;
|
||
const fy = parseFloat(document.getElementById('weld-fy').value) || 345;
|
||
|
||
const fyWeld = fy * 0.6;
|
||
const leg = (force * 1000) / (0.707 * length * 0.65 * fyWeld);
|
||
const throat = leg * 0.707;
|
||
const legCommercial = Math.ceil(leg);
|
||
|
||
const passes = legCommercial <= 5 ? 1 : legCommercial <= 8 ? 2 : 3;
|
||
|
||
document.getElementById('weld-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Dimensionamento da Solda de Filete</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Perna Calculada</div>
|
||
<div class="result-value">${leg.toFixed(2)} mm</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Perna Adotada</div>
|
||
<div class="result-value">${legCommercial} mm</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Garganta Efetiva</div>
|
||
<div class="result-value">${throat.toFixed(2)} mm</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Número de Passes</div>
|
||
<div class="result-value">${passes}</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-success" style="margin-top: 16px;">
|
||
<strong>Eletrodo recomendado:</strong> E${Math.round(fy * 1.15)} (resistência compatível com o aço base)
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Solda de Filete', `Perna ${legCommercial}mm, ${passes} passe(s)`);
|
||
}
|
||
|
||
// Heat Input Calculation
|
||
function calcularEnergiaSoldagem() {
|
||
const voltage = parseFloat(document.getElementById('hi-voltage').value) || 0;
|
||
const current = parseFloat(document.getElementById('hi-current').value) || 0;
|
||
const speed = parseFloat(document.getElementById('hi-speed').value) || 0;
|
||
|
||
const heatInput = (voltage * current * 60) / (speed * 1000);
|
||
|
||
let interpretation = '';
|
||
let alertClass = '';
|
||
if (heatInput < 1.0) {
|
||
interpretation = 'Energia baixa - Risco de falta de fusão ou trincas a frio';
|
||
alertClass = 'alert-warning';
|
||
} else if (heatInput <= 2.0) {
|
||
interpretation = 'Energia adequada - Dentro da faixa recomendada';
|
||
alertClass = 'alert-success';
|
||
} else {
|
||
interpretation = 'Energia alta - Risco de fragilização da ZTA e distorção';
|
||
alertClass = 'alert-error';
|
||
}
|
||
|
||
document.getElementById('hi-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Energia de Soldagem (Heat Input)</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Energia</div>
|
||
<div class="result-value">${heatInput.toFixed(2)} kJ/mm</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert ${alertClass}" style="margin-top: 16px;">
|
||
<strong>${interpretation}</strong>
|
||
</div>
|
||
<div class="expert-only" style="margin-top: 16px; padding: 16px; background: var(--color-bg-4); border-radius: 8px;">
|
||
<strong>Fórmula:</strong> HI = (V × I × 60) / (v × 1000)<br>
|
||
<strong>Recomendações:</strong><br>
|
||
• Aços carbono: 0.8 - 2.0 kJ/mm<br>
|
||
• Alta resistência: 0.8 - 1.5 kJ/mm<br>
|
||
• Baixa liga: 1.0 - 2.5 kJ/mm
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Energia de Soldagem', `HI = ${heatInput.toFixed(2)} kJ/mm`);
|
||
}
|
||
|
||
// Electrode Consumption
|
||
function calcularConsumoEletrodos() {
|
||
const leg = parseFloat(document.getElementById('elec-leg').value) || 0;
|
||
const length = parseFloat(document.getElementById('elec-length').value) || 0;
|
||
const factor = parseFloat(document.getElementById('elec-type').value) || 1.10;
|
||
const loss = parseFloat(document.getElementById('elec-loss').value) || 15;
|
||
|
||
const throat = leg * 0.707;
|
||
const volume = throat * leg * length * 1000;
|
||
const mass = (volume / 1000000) * 7850 / 1000;
|
||
const consumption = mass * factor * (1 + loss / 100);
|
||
|
||
document.getElementById('elec-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Consumo de Eletrodos</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Volume de Solda</div>
|
||
<div class="result-value">${(volume / 1000).toFixed(1)} cm³</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Massa de Solda</div>
|
||
<div class="result-value">${mass.toFixed(2)} kg</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Consumo Total</div>
|
||
<div class="result-value">${consumption.toFixed(2)} kg</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 16px; padding: 16px; background: var(--color-bg-5); border-radius: 8px;">
|
||
<strong>Estimativa de embalagens:</strong><br>
|
||
Eletrodos Ø3,25mm (1 kg cada): ${Math.ceil(consumption)} embalagens
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Consumo de Eletrodos', `${consumption.toFixed(2)} kg para ${length}m de solda`);
|
||
}
|
||
|
||
// Hardness Converter
|
||
function converterDureza(source) {
|
||
let HB = 0;
|
||
|
||
if (source === 'hb') {
|
||
HB = parseFloat(document.getElementById('hard-hb').value) || 0;
|
||
} else if (source === 'hrc') {
|
||
const HRC = parseFloat(document.getElementById('hard-hrc').value) || 0;
|
||
HB = (HRC + 9.8) / 0.0338;
|
||
document.getElementById('hard-hb').value = Math.round(HB);
|
||
} else if (source === 'hv') {
|
||
const HV = parseFloat(document.getElementById('hard-hv').value) || 0;
|
||
HB = HV / 0.95;
|
||
document.getElementById('hard-hb').value = Math.round(HB);
|
||
}
|
||
|
||
if (HB === 0) return;
|
||
|
||
const HRC = HB * 0.0338 - 9.8;
|
||
const HV = HB * 0.95;
|
||
const fu = HB * 10;
|
||
const fy = fu * 0.7;
|
||
|
||
if (source !== 'hrc') document.getElementById('hard-hrc').value = HRC.toFixed(1);
|
||
if (source !== 'hv') document.getElementById('hard-hv').value = Math.round(HV);
|
||
|
||
document.getElementById('hardness-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Conversão de Dureza</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">HB (Brinell)</div>
|
||
<div class="result-value">${Math.round(HB)}</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">HRC (Rockwell C)</div>
|
||
<div class="result-value">${HRC.toFixed(1)}</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">HV (Vickers)</div>
|
||
<div class="result-value">${Math.round(HV)}</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 16px; padding: 16px; background: var(--color-bg-6); border-radius: 8px;">
|
||
<strong>Estimativa de Resistência:</strong><br>
|
||
fu ≈ ${fu.toFixed(0)} MPa<br>
|
||
fy ≈ ${fy.toFixed(0)} MPa (aproximado)
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// Charpy Analysis
|
||
function analisarCharpy() {
|
||
const temps = [
|
||
parseFloat(document.getElementById('charpy-t1').value),
|
||
parseFloat(document.getElementById('charpy-t2').value),
|
||
parseFloat(document.getElementById('charpy-t3').value),
|
||
parseFloat(document.getElementById('charpy-t4').value)
|
||
];
|
||
|
||
const energies = [
|
||
parseFloat(document.getElementById('charpy-e1').value),
|
||
parseFloat(document.getElementById('charpy-e2').value),
|
||
parseFloat(document.getElementById('charpy-e3').value),
|
||
parseFloat(document.getElementById('charpy-e4').value)
|
||
];
|
||
|
||
const validPoints = temps.map((t, i) => ({ temp: t, energy: energies[i] }))
|
||
.filter(p => !isNaN(p.temp) && !isNaN(p.energy))
|
||
.sort((a, b) => a.temp - b.temp);
|
||
|
||
if (validPoints.length < 2) {
|
||
alert('Insira pelo menos 2 pontos válidos');
|
||
return;
|
||
}
|
||
|
||
let ttdf = null;
|
||
for (let i = 0; i < validPoints.length - 1; i++) {
|
||
if ((validPoints[i].energy >= 27 && validPoints[i+1].energy < 27) ||
|
||
(validPoints[i].energy < 27 && validPoints[i+1].energy >= 27)) {
|
||
const t1 = validPoints[i].temp;
|
||
const e1 = validPoints[i].energy;
|
||
const t2 = validPoints[i+1].temp;
|
||
const e2 = validPoints[i+1].energy;
|
||
ttdf = t1 + (27 - e1) * (t2 - t1) / (e2 - e1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
document.getElementById('charpy-result').innerHTML = `
|
||
<div class="card">
|
||
<div class="card-title">Curva de Transição Dúctil-Frágil</div>
|
||
<div class="chart-container">
|
||
<canvas id="charpy-chart"></canvas>
|
||
</div>
|
||
${ttdf !== null ? `
|
||
<div class="alert alert-success" style="margin-top: 16px;">
|
||
<strong>TTDF (Temperatura de Transição):</strong> ${ttdf.toFixed(1)}°C<br>
|
||
Temperatura onde a energia de impacto = 27J
|
||
</div>
|
||
` : `
|
||
<div class="alert alert-warning" style="margin-top: 16px;">
|
||
<strong>Não foi possível calcular TTDF</strong><br>
|
||
A curva não intercepta 27J no intervalo medido
|
||
</div>
|
||
`}
|
||
</div>
|
||
`;
|
||
|
||
if (currentChart) {
|
||
currentChart.destroy();
|
||
}
|
||
|
||
const ctx = document.getElementById('charpy-chart').getContext('2d');
|
||
currentChart = new Chart(ctx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: validPoints.map(p => p.temp + '°C'),
|
||
datasets: [{
|
||
label: 'Energia (J)',
|
||
data: validPoints.map(p => p.energy),
|
||
borderColor: '#1FB8CD',
|
||
backgroundColor: '#1FB8CD40',
|
||
tension: 0.4,
|
||
fill: true
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: 'Curva de Transição Charpy'
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
title: {
|
||
display: true,
|
||
text: 'Energia (J)'
|
||
}
|
||
},
|
||
x: {
|
||
title: {
|
||
display: true,
|
||
text: 'Temperatura (°C)'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
addToHistory('Análise Charpy', `${validPoints.length} pontos, TTDF = ${ttdf ? ttdf.toFixed(1) : 'N/A'}°C`);
|
||
}
|
||
|
||
// Certificate Checklist
|
||
function gerarChecklistCertificado() {
|
||
const norm = document.getElementById('cert-norm').value;
|
||
const requirements = certRequirements[norm] || [];
|
||
|
||
document.getElementById('cert-result').innerHTML = `
|
||
<div class="card">
|
||
<div class="card-title">Checklist de Requisitos - ${norm.toUpperCase().replace('_', ' ')}</div>
|
||
${requirements.map((req, index) => `
|
||
<div style="padding: 12px; background: var(--color-background); border-radius: 8px; margin-bottom: 8px; display: flex; align-items: center; gap: 12px;">
|
||
<input type="checkbox" id="req-${index}" style="width: 20px; height: 20px; cursor: pointer;">
|
||
<label for="req-${index}" style="cursor: pointer; flex: 1;">${req}</label>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// Paint Area Calculation
|
||
function updatePaintFields() {
|
||
const type = document.getElementById('paint-type').value;
|
||
const field3 = document.getElementById('paint-field3');
|
||
|
||
if (type === 'chapa') {
|
||
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
|
||
document.getElementById('paint-label2').textContent = 'Largura (mm)';
|
||
field3.style.display = 'none';
|
||
} else if (type === 'perfilW') {
|
||
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
|
||
document.getElementById('paint-label2').textContent = 'Altura (mm)';
|
||
field3.style.display = 'none';
|
||
} else if (type === 'tubo') {
|
||
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
|
||
document.getElementById('paint-label2').textContent = 'Diâmetro (mm)';
|
||
field3.style.display = 'none';
|
||
} else if (type === 'rhs') {
|
||
document.getElementById('paint-label1').textContent = 'Comprimento (mm)';
|
||
document.getElementById('paint-label2').textContent = 'Largura (mm)';
|
||
document.getElementById('paint-label3').textContent = 'Altura (mm)';
|
||
field3.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
function calcularAreaPintura() {
|
||
const type = document.getElementById('paint-type').value;
|
||
const dim1 = parseFloat(document.getElementById('paint-dim1').value) || 0;
|
||
const dim2 = parseFloat(document.getElementById('paint-dim2').value) || 0;
|
||
const dim3 = parseFloat(document.getElementById('paint-dim3').value) || 0;
|
||
const qty = parseInt(document.getElementById('paint-qty').value) || 1;
|
||
|
||
let area = 0;
|
||
|
||
if (type === 'chapa') {
|
||
area = (dim1 * dim2 * 2) / 1000000;
|
||
} else if (type === 'perfilW') {
|
||
const perimeter = dim2 * 3.5;
|
||
area = (perimeter * dim1) / 1000000;
|
||
} else if (type === 'tubo') {
|
||
area = (Math.PI * dim2 * dim1) / 1000000;
|
||
} else if (type === 'rhs') {
|
||
const perimeter = 2 * (dim2 + dim3);
|
||
area = (perimeter * dim1) / 1000000;
|
||
}
|
||
|
||
const totalArea = area * qty;
|
||
|
||
document.getElementById('paint-area-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Área de Pintura</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Área Unitária</div>
|
||
<div class="result-value">${area.toFixed(2)} m²</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Área Total</div>
|
||
<div class="result-value">${totalArea.toFixed(2)} m²</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('tinta-area').value = totalArea.toFixed(2);
|
||
|
||
addToHistory('Área de Pintura', `${totalArea.toFixed(2)}m² (${qty} unidades)`);
|
||
}
|
||
|
||
// Paint Consumption
|
||
function calcularConsumoTinta() {
|
||
const area = parseFloat(document.getElementById('tinta-area').value) || 0;
|
||
const dft = parseFloat(document.getElementById('tinta-dft').value) || 0;
|
||
const solids = parseFloat(document.getElementById('tinta-solids').value) || 0;
|
||
const loss = parseFloat(document.getElementById('tinta-loss').value) || 0;
|
||
const coats = parseInt(document.getElementById('tinta-coats').value) || 1;
|
||
const cost = parseFloat(document.getElementById('tinta-cost').value) || 0;
|
||
|
||
const volumeTheoretical = (dft * area) / (1000 * (solids / 100));
|
||
const volumeWithLoss = volumeTheoretical / (1 - loss / 100);
|
||
const volumePerCoat = volumeWithLoss;
|
||
const volumeTotal = volumePerCoat * coats;
|
||
const totalCost = volumeTotal * cost;
|
||
|
||
document.getElementById('tinta-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Consumo de Tinta</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Volume Teórico</div>
|
||
<div class="result-value">${volumeTheoretical.toFixed(2)} L</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Volume por Demão</div>
|
||
<div class="result-value">${volumePerCoat.toFixed(2)} L</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Volume Total</div>
|
||
<div class="result-value">${volumeTotal.toFixed(2)} L</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Custo Total</div>
|
||
<div class="result-value">R$ ${totalCost.toFixed(2)}</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 16px; padding: 16px; background: var(--color-bg-7); border-radius: 8px;">
|
||
<strong>Recomendação de embalagens:</strong><br>
|
||
${volumeTotal > 20 ? `Galões 20L: ${Math.ceil(volumeTotal / 20)} unidades` :
|
||
volumeTotal > 5 ? `Galões 5L: ${Math.ceil(volumeTotal / 5)} unidades` :
|
||
`Latas 1L: ${Math.ceil(volumeTotal)} unidades`}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Consumo de Tinta', `${volumeTotal.toFixed(2)}L para ${area}m²`);
|
||
}
|
||
|
||
// Galvanization
|
||
function calcularGalvanizacao() {
|
||
const env = document.getElementById('galv-env').value;
|
||
const area = parseFloat(document.getElementById('galv-area').value) || 0;
|
||
const thickness = parseFloat(document.getElementById('galv-thickness').value) || 85;
|
||
|
||
const corrosionRate = {
|
||
'interno': 0.5,
|
||
'urbano': 1.5,
|
||
'marinho': 3.0,
|
||
'industrial': 2.5
|
||
};
|
||
|
||
const rate = corrosionRate[env] || 1.5;
|
||
const lifeYears = thickness / rate;
|
||
|
||
const zincDensity = 7140;
|
||
const zincMass = (area * thickness / 1000) * (zincDensity / 1000000);
|
||
const zincCost = zincMass * 12;
|
||
|
||
document.getElementById('galv-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Galvanização a Quente</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Taxa de Corrosão</div>
|
||
<div class="result-value">${rate.toFixed(1)} μm/ano</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Vida Útil Estimada</div>
|
||
<div class="result-value">${lifeYears.toFixed(0)} anos</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Consumo de Zinco</div>
|
||
<div class="result-value">${zincMass.toFixed(2)} kg</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Custo Estimado</div>
|
||
<div class="result-value">R$ ${(zincCost + area * 180).toFixed(2)}</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-success" style="margin-top: 16px;">
|
||
<strong>Normas aplicáveis:</strong> ASTM A123, ISO 1461, NBR 6323
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Galvanização', `${area}m², ${thickness}μm → ${lifeYears.toFixed(0)} anos`);
|
||
}
|
||
|
||
// Budget
|
||
function adicionarItemOrcamento() {
|
||
const type = document.getElementById('budget-type').value;
|
||
const spec = document.getElementById('budget-spec').value;
|
||
const qty = parseFloat(document.getElementById('budget-qty').value) || 0;
|
||
const unit = document.getElementById('budget-unit').value;
|
||
const price = parseFloat(document.getElementById('budget-price').value) || 0;
|
||
|
||
if (!spec || qty <= 0 || price <= 0) {
|
||
alert('Preencha todos os campos corretamente');
|
||
return;
|
||
}
|
||
|
||
const item = {
|
||
id: Date.now(),
|
||
type: type,
|
||
spec: spec,
|
||
qty: qty,
|
||
unit: unit,
|
||
price: price,
|
||
total: qty * price
|
||
};
|
||
|
||
appState.budgetItems.push(item);
|
||
atualizarTabelaOrcamento();
|
||
|
||
document.getElementById('budget-spec').value = '';
|
||
document.getElementById('budget-qty').value = '1';
|
||
document.getElementById('budget-price').value = '0';
|
||
}
|
||
|
||
function removerItemOrcamento(id) {
|
||
appState.budgetItems = appState.budgetItems.filter(item => item.id !== id);
|
||
atualizarTabelaOrcamento();
|
||
}
|
||
|
||
function atualizarTabelaOrcamento() {
|
||
const tbody = document.getElementById('budget-tbody');
|
||
|
||
if (appState.budgetItems.length === 0) {
|
||
tbody.innerHTML = '<tr><td colspan="7" style="text-align: center; color: var(--color-text-secondary);">Nenhum item adicionado</td></tr>';
|
||
} else {
|
||
tbody.innerHTML = appState.budgetItems.map(item => `
|
||
<tr>
|
||
<td>${item.type}</td>
|
||
<td>${item.spec}</td>
|
||
<td>${item.qty.toFixed(2)}</td>
|
||
<td>${item.unit}</td>
|
||
<td>R$ ${item.price.toFixed(2)}</td>
|
||
<td><strong>R$ ${item.total.toFixed(2)}</strong></td>
|
||
<td><button class="btn btn-secondary" style="padding: 6px 12px; font-size: 12px;" onclick="removerItemOrcamento(${item.id})">Remover</button></td>
|
||
</tr>
|
||
`).join('');
|
||
}
|
||
|
||
atualizarTotalOrcamento();
|
||
}
|
||
|
||
function atualizarTotalOrcamento() {
|
||
const subtotal = appState.budgetItems.reduce((sum, item) => sum + item.total, 0);
|
||
const bdi = parseFloat(document.getElementById('budget-bdi').value) || 0;
|
||
const total = subtotal * (1 + bdi / 100);
|
||
|
||
const resultDiv = document.getElementById('budget-total');
|
||
resultDiv.innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Subtotal</div>
|
||
<div class="result-value">R$ ${subtotal.toFixed(2)}</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">BDI (${bdi}%)</div>
|
||
<div class="result-value">R$ ${(total - subtotal).toFixed(2)}</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">TOTAL GERAL</div>
|
||
<div class="result-value" style="color: var(--color-success);">R$ ${total.toFixed(2)}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// Weight & Rigging
|
||
function updateWeightFields() {
|
||
const type = document.getElementById('weight-type').value;
|
||
const field3 = document.getElementById('weight-field3');
|
||
const field4 = document.getElementById('weight-field4');
|
||
|
||
if (type === 'perfilW') {
|
||
document.getElementById('weight-label1').textContent = 'Altura (mm)';
|
||
document.getElementById('weight-label2').textContent = 'Comprimento (m)';
|
||
field3.style.display = 'none';
|
||
field4.style.display = 'none';
|
||
} else if (type === 'chapa') {
|
||
document.getElementById('weight-label1').textContent = 'Largura (mm)';
|
||
document.getElementById('weight-label2').textContent = 'Altura (mm)';
|
||
document.getElementById('weight-label3').textContent = 'Espessura (mm)';
|
||
field3.style.display = 'block';
|
||
field4.style.display = 'none';
|
||
} else if (type === 'tubo') {
|
||
document.getElementById('weight-label1').textContent = 'Diâmetro Externo (mm)';
|
||
document.getElementById('weight-label2').textContent = 'Comprimento (m)';
|
||
document.getElementById('weight-label3').textContent = 'Espessura Parede (mm)';
|
||
field3.style.display = 'block';
|
||
field4.style.display = 'none';
|
||
} else if (type === 'barra') {
|
||
document.getElementById('weight-label1').textContent = 'Diâmetro (mm)';
|
||
document.getElementById('weight-label2').textContent = 'Comprimento (m)';
|
||
field3.style.display = 'none';
|
||
field4.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
function calcularPeso() {
|
||
const type = document.getElementById('weight-type').value;
|
||
const dim1 = parseFloat(document.getElementById('weight-dim1').value) || 0;
|
||
const dim2 = parseFloat(document.getElementById('weight-dim2').value) || 0;
|
||
const dim3 = parseFloat(document.getElementById('weight-dim3').value) || 0;
|
||
|
||
let weight = 0;
|
||
|
||
if (type === 'perfilW') {
|
||
weight = (dim1 / 100) * 31.8 * dim2;
|
||
} else if (type === 'chapa') {
|
||
weight = (dim1 / 1000) * (dim2 / 1000) * (dim3 / 1000) * 7850;
|
||
} else if (type === 'tubo') {
|
||
const dExt = dim1;
|
||
const dInt = dExt - 2 * dim3;
|
||
const area = Math.PI * (Math.pow(dExt/2, 2) - Math.pow(dInt/2, 2));
|
||
weight = area / 1000000 * dim2 * 7850;
|
||
} else if (type === 'barra') {
|
||
const area = Math.PI * Math.pow(dim1/2, 2);
|
||
weight = area / 1000000 * dim2 * 7850;
|
||
}
|
||
|
||
document.getElementById('weight-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Cálculo de Peso</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Peso Total</div>
|
||
<div class="result-value">${weight.toFixed(2)} kg</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('rigging-weight').value = weight.toFixed(0);
|
||
|
||
addToHistory('Cálculo de Peso', `${weight.toFixed(2)}kg`);
|
||
}
|
||
|
||
function calcularRigging() {
|
||
const weight = parseFloat(document.getElementById('rigging-weight').value) || 0;
|
||
const points = parseInt(document.getElementById('rigging-points').value) || 2;
|
||
const angle = parseInt(document.getElementById('rigging-angle').value) || 60;
|
||
const fs = parseFloat(document.getElementById('rigging-fs').value) || 4;
|
||
|
||
const angleRad = angle * Math.PI / 180;
|
||
const forcePerCable = (weight * 9.81 / 1000) / (points * Math.cos(angleRad)) * fs;
|
||
|
||
const cableSteelCapacity = 21;
|
||
const chainCapacity = 15;
|
||
const syntheticCapacity = 12;
|
||
|
||
let recommendation = '';
|
||
let alertClass = 'alert-success';
|
||
if (forcePerCable <= syntheticCapacity) {
|
||
recommendation = '✅ Cabo sintético (12 kN)';
|
||
} else if (forcePerCable <= chainCapacity) {
|
||
recommendation = '✅ Corrente grau 80 (15 kN)';
|
||
} else if (forcePerCable <= cableSteelCapacity) {
|
||
recommendation = '⚠️ Cabo de aço (21 kN)';
|
||
alertClass = 'alert-warning';
|
||
} else {
|
||
recommendation = '❌ Requer cabo especial ou mais pontos de içamento';
|
||
alertClass = 'alert-error';
|
||
}
|
||
|
||
document.getElementById('rigging-result').innerHTML = `
|
||
<div class="result-box">
|
||
<div class="result-title">Plano de Rigging</div>
|
||
<div class="result-grid">
|
||
<div class="result-item">
|
||
<div class="result-label">Força por Cabo</div>
|
||
<div class="result-value">${forcePerCable.toFixed(1)} kN</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Ângulo</div>
|
||
<div class="result-value">${angle}°</div>
|
||
</div>
|
||
<div class="result-item">
|
||
<div class="result-label">Fator Segurança</div>
|
||
<div class="result-value">${fs}</div>
|
||
</div>
|
||
</div>
|
||
<div class="alert ${alertClass}" style="margin-top: 16px;">
|
||
<strong>Recomendação: ${recommendation}</strong>
|
||
</div>
|
||
<div style="margin-top: 16px; padding: 16px; background: var(--color-bg-8); border-radius: 8px;">
|
||
<strong>Capacidades de Referência:</strong><br>
|
||
• Cabo sintético: 12 kN<br>
|
||
• Corrente grau 80: 15 kN<br>
|
||
• Cabo de aço: 21 kN
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
addToHistory('Rigging', `${weight}kg, ${points} pontos, ${angle}° → ${forcePerCable.toFixed(1)}kN/cabo`);
|
||
}
|
||
|
||
// ========================================
|
||
// INITIALIZATION
|
||
// ========================================
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
showSection('cev');
|
||
mostrarEquivalencias();
|
||
gerarChecklistCertificado();
|
||
updatePaintFields();
|
||
updateWeightFields();
|
||
});
|