Atualização automática - 2026-06-24 10:23:23
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
import { ReportData, Standard, ComplianceItem } from '../types';
|
||||
|
||||
/**
|
||||
* Extracts a numeric value from a string like "0.22%", "415 MPa", etc.
|
||||
*/
|
||||
function extractNumber(val: string): number | null {
|
||||
if (!val) return null;
|
||||
const match = val.match(/-?\d+(\.\d+)?/);
|
||||
return match ? parseFloat(match[0]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the product string to try and find a thickness in mm.
|
||||
* E.g., "CHAPA 50MM", "PLATE 25.4 MM"
|
||||
*/
|
||||
function parseThickness(productDesc: string): number {
|
||||
if (!productDesc) return 0;
|
||||
const match = productDesc.match(/(\d+(\.\d+)?)\s*(mm)/i);
|
||||
if (match) return parseFloat(match[1]);
|
||||
return 0; // Default or unknown
|
||||
}
|
||||
|
||||
function normalizeElementName(element: string): string {
|
||||
const map: Record<string, string> = {
|
||||
'c': 'carbono',
|
||||
'carbon': 'carbono',
|
||||
'carbono': 'carbono',
|
||||
'mn': 'manganes',
|
||||
'manganese': 'manganes',
|
||||
'manganes': 'manganes',
|
||||
'si': 'silicio',
|
||||
'silicon': 'silicio',
|
||||
'silicio': 'silicio',
|
||||
'p': 'fosforo',
|
||||
'phosphorus': 'fosforo',
|
||||
'fosforo': 'fosforo',
|
||||
's': 'enxofre',
|
||||
'sulfur': 'enxofre',
|
||||
'enxofre': 'enxofre',
|
||||
'cu': 'cobre',
|
||||
'copper': 'cobre',
|
||||
'cobre': 'cobre',
|
||||
'ni': 'niquel',
|
||||
'nickel': 'niquel',
|
||||
'niquel': 'niquel',
|
||||
'cr': 'cromo',
|
||||
'chromium': 'cromo',
|
||||
'cromo': 'cromo',
|
||||
'mo': 'molibdenio',
|
||||
'molybdenum': 'molibdenio',
|
||||
'molibdenio': 'molibdenio',
|
||||
'v': 'vanadio',
|
||||
'vanadium': 'vanadio',
|
||||
'vanadio': 'vanadio',
|
||||
'nb': 'niobio',
|
||||
'columbium': 'niobio',
|
||||
'niobium': 'niobio',
|
||||
'niobio': 'niobio',
|
||||
'ti': 'titanio',
|
||||
'titanium': 'titanio',
|
||||
'titanio': 'titanio',
|
||||
'al': 'aluminio',
|
||||
'aluminum': 'aluminio',
|
||||
'aluminio': 'aluminio'
|
||||
};
|
||||
|
||||
const lower = element.toLowerCase().trim();
|
||||
// Remove content in parentheses, e.g. "C (Carbon)" -> "c"
|
||||
const clean = lower.replace(/\(.*?\)/g, '').trim();
|
||||
return map[clean] || clean;
|
||||
}
|
||||
|
||||
function getTolerance(elementName: string, productVal: number, thickness: number, norm: Standard): number {
|
||||
if (!norm.tolerancias_analise_produto_astma6) return 0;
|
||||
|
||||
const tolRules = norm.tolerancias_analise_produto_astma6[elementName];
|
||||
if (!tolRules) return 0;
|
||||
|
||||
if (tolRules.tabela && tolRules.tabela.length > 0) {
|
||||
for (const faixa of tolRules.tabela) {
|
||||
if (faixa.espessura_ate_mm && thickness <= faixa.espessura_ate_mm) {
|
||||
return faixa.tolerancia_mais || 0;
|
||||
}
|
||||
if (faixa.espessura_maior_que_mm && thickness > faixa.espessura_maior_que_mm) {
|
||||
return faixa.tolerancia_mais || 0;
|
||||
}
|
||||
if (faixa.limite_corrida_ate && productVal <= faixa.limite_corrida_ate) {
|
||||
return faixa.tolerancia_mais || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tolRules.tolerancia_mais || 0;
|
||||
}
|
||||
|
||||
export function validateCertificateAgainstStandard(extractedReport: ReportData, standard: Standard): ReportData {
|
||||
const report = JSON.parse(JSON.stringify(extractedReport)) as ReportData;
|
||||
const thickness = parseThickness(report.identification.product);
|
||||
|
||||
let allOk = true;
|
||||
|
||||
// Encontrar limites baseados no produto (simplificado: pegamos o primeiro grupo que atende a espessura, ou o flat limits)
|
||||
let activeLimits: Record<string, number | null> = {};
|
||||
|
||||
if (standard.limites_quimicos_corrida) {
|
||||
const grupos = Object.values(standard.limites_quimicos_corrida)[0] || [];
|
||||
for (const limite of grupos) {
|
||||
if (!limite.espessura_max_mm || thickness <= limite.espessura_max_mm) {
|
||||
// Encontrou a faixa certa
|
||||
for (const [key, val] of Object.entries(limite)) {
|
||||
if (typeof val === 'number') {
|
||||
activeLimits[key] = val;
|
||||
}
|
||||
}
|
||||
break; // Pega o primeiro correspondente
|
||||
}
|
||||
}
|
||||
} else if (standard.parametros_controle?.quimicos) {
|
||||
activeLimits = { ...standard.parametros_controle.quimicos };
|
||||
}
|
||||
|
||||
// Validate Chemical
|
||||
if (report.compliance.chemical) {
|
||||
for (const item of report.compliance.chemical) {
|
||||
if (!item.element) continue;
|
||||
|
||||
const normElement = normalizeElementName(item.element);
|
||||
const val = extractNumber(item.certificate);
|
||||
|
||||
if (val !== null) {
|
||||
// Look for max limit
|
||||
const maxLimit = activeLimits[`${normElement}_max`] || activeLimits[normElement];
|
||||
const minLimit = activeLimits[`${normElement}_min`];
|
||||
|
||||
let status: 'OK' | 'FALHA' = 'OK';
|
||||
let normStr = [];
|
||||
|
||||
// Product tolerance
|
||||
const tolerance = getTolerance(normElement, maxLimit as number || 0, thickness, standard);
|
||||
|
||||
if (minLimit !== undefined && minLimit !== null) {
|
||||
normStr.push(`Min: ${minLimit}`);
|
||||
if (val < (minLimit - tolerance)) status = 'FALHA';
|
||||
}
|
||||
|
||||
if (maxLimit !== undefined && maxLimit !== null) {
|
||||
normStr.push(`Max: ${maxLimit}`);
|
||||
if (val > (maxLimit + tolerance)) status = 'FALHA';
|
||||
}
|
||||
|
||||
if (normStr.length > 0) {
|
||||
item.norm = normStr.join(' - ');
|
||||
} else {
|
||||
item.norm = 'Não especificado';
|
||||
}
|
||||
|
||||
item.status = status;
|
||||
if (status === 'FALHA') allOk = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate Mechanical
|
||||
const mechLimits = standard.parametros_controle?.mecanicos || {};
|
||||
if (report.compliance.mechanical) {
|
||||
for (const item of report.compliance.mechanical) {
|
||||
if (!item.property) continue;
|
||||
|
||||
const val = extractNumber(item.certificate);
|
||||
if (val !== null) {
|
||||
const propLower = item.property.toLowerCase();
|
||||
let maxLimit: number | null = null;
|
||||
let minLimit: number | null = null;
|
||||
|
||||
if (propLower.includes('yield') || propLower.includes('escoamento')) {
|
||||
minLimit = mechLimits['limite_escoamento_min'] || mechLimits['yield_strength_min'] || null;
|
||||
maxLimit = mechLimits['limite_escoamento_max'] || mechLimits['yield_strength_max'] || null;
|
||||
} else if (propLower.includes('tensile') || propLower.includes('resistencia')) {
|
||||
minLimit = mechLimits['limite_resistencia_min'] || mechLimits['tensile_strength_min'] || null;
|
||||
maxLimit = mechLimits['limite_resistencia_max'] || mechLimits['tensile_strength_max'] || null;
|
||||
} else if (propLower.includes('elongation') || propLower.includes('alongamento')) {
|
||||
minLimit = mechLimits['alongamento_min'] || mechLimits['elongation_min'] || null;
|
||||
}
|
||||
|
||||
let status: 'OK' | 'FALHA' = 'OK';
|
||||
let normStr = [];
|
||||
|
||||
if (minLimit !== null) {
|
||||
normStr.push(`Min: ${minLimit}`);
|
||||
if (val < minLimit) status = 'FALHA';
|
||||
item.differencePercentage = `${val >= minLimit ? '+' : ''}${(((val - minLimit) / minLimit) * 100).toFixed(1)}%`;
|
||||
}
|
||||
|
||||
if (maxLimit !== null) {
|
||||
normStr.push(`Max: ${maxLimit}`);
|
||||
if (val > maxLimit) status = 'FALHA';
|
||||
}
|
||||
|
||||
if (normStr.length > 0) {
|
||||
item.norm = normStr.join(' - ');
|
||||
} else {
|
||||
item.norm = 'Não especificado';
|
||||
}
|
||||
|
||||
item.status = status;
|
||||
if (status === 'FALHA') allOk = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report.compliance.status = allOk ? 'CONFORME' : 'NÃO CONFORME';
|
||||
if (!allOk) {
|
||||
report.compliance.nonComplianceReason = 'Valores fora dos limites da norma técnica considerando bitola e tolerâncias de produto.';
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
Reference in New Issue
Block a user