diff --git a/package-lock.json b/package-lock.json index 8819c83..732b3a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,7 +58,7 @@ "tailwindcss-animate": "^1.0.7", "three": "^0.186.0", "three-stdlib": "^2.36.1", - "web-ifc": "^0.0.77", + "web-ifc": "0.0.57", "xlsx": "^0.18.5", "zod": "^3.25.76" }, @@ -7188,10 +7188,9 @@ } }, "node_modules/web-ifc": { - "version": "0.0.77", - "resolved": "https://registry.npmjs.org/web-ifc/-/web-ifc-0.0.77.tgz", - "integrity": "sha512-VzQ0W/Iiqbidxn1ECUvz6qJ6p2sXBVNcOsUOBCETzy77psAH6yFLKQm74aXabkx3JH4OvFVHe8k1qS6+Z2zl1w==", - "license": "MPL-2.0" + "version": "0.0.57", + "resolved": "https://registry.npmjs.org/web-ifc/-/web-ifc-0.0.57.tgz", + "integrity": "sha512-ozzfxiCNT4+VqIhm93QdTozOmkMSPRZzf1RoRMMQIQLqoidYdExnAmY1OLoq1emnbvHPqFNQJ5HnQoqxvYZeFA==" }, "node_modules/which": { "version": "2.0.2", diff --git a/package.json b/package.json index 6099740..b47d4ed 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "tailwindcss-animate": "^1.0.7", "three": "^0.186.0", "three-stdlib": "^2.36.1", - "web-ifc": "^0.0.77", + "web-ifc": "0.0.57", "xlsx": "^0.18.5", "zod": "^3.25.76" }, diff --git a/public/wasm/web-ifc-mt.wasm b/public/wasm/web-ifc-mt.wasm new file mode 100755 index 0000000..ccab27c Binary files /dev/null and b/public/wasm/web-ifc-mt.wasm differ diff --git a/public/wasm/web-ifc-node.wasm b/public/wasm/web-ifc-node.wasm new file mode 100755 index 0000000..fa38fef Binary files /dev/null and b/public/wasm/web-ifc-node.wasm differ diff --git a/src/lib/ifc/ifcLoaderService.ts b/src/lib/ifc/ifcLoaderService.ts index 878ef17..e80c190 100644 --- a/src/lib/ifc/ifcLoaderService.ts +++ b/src/lib/ifc/ifcLoaderService.ts @@ -74,21 +74,40 @@ export async function loadAndAuditIFC( // 1. Initialize Web-IFC WASM locally with fallback if (onProgress) onProgress(10, 'Carregando motor Web-IFC (WASM)...'); - const wasmPaths = [ - '/wasm/', - 'https://unpkg.com/web-ifc@0.0.57/', - 'https://cdn.jsdelivr.net/npm/web-ifc@0.0.57/' - ]; + const LOCAL_WASM_PATH = '/wasm/'; + const CDN_WASM_PATH = 'https://unpkg.com/web-ifc@0.0.57/'; + + // Intercepta logs do console temporariamente para filtrar avisos internos da engine C++ + const originalConsoleError = console.error; + const originalConsoleWarn = console.warn; + console.error = (...args: any[]) => { + if (typeof args[0] === 'string' && args[0].includes('[WEB-IFC]')) { + console.debug(...args); + return; + } + originalConsoleError.apply(console, args); + }; + console.warn = (...args: any[]) => { + if (typeof args[0] === 'string' && args[0].includes('[WEB-IFC]')) { + console.debug(...args); + return; + } + originalConsoleWarn.apply(console, args); + }; let wasmInitSuccess = false; - for (const path of wasmPaths) { + try { + ifcApi.SetWasmPath(LOCAL_WASM_PATH, true); + await ifcApi.Init(); + wasmInitSuccess = true; + } catch (localErr) { + console.warn('[IFC Loader] Falha ao carregar WASM local (/wasm/), tentando CDN...', localErr); try { - ifcApi.SetWasmPath(path); + ifcApi.SetWasmPath(CDN_WASM_PATH, true); await ifcApi.Init(); wasmInitSuccess = true; - break; - } catch { - // try next + } catch (cdnErr) { + console.error('[IFC Loader] Falha ao inicializar Web-IFC via CDN:', cdnErr); } } @@ -212,6 +231,53 @@ export async function loadAndAuditIFC( } } + // 4.3 Element Properties Map from IFCRELDEFINESBYPROPERTIES + const elementPropertiesMap = new Map>(); + try { + const relsProp = ifcApi.GetLineIDsWithType(modelID, WebIFC.IFCRELDEFINESBYPROPERTIES); + for (let i = 0; i < relsProp.size(); i++) { + const relId = relsProp.get(i); + if (relId && relId > 0) { + const rel = ifcApi.GetLine(modelID, relId); + if (rel && rel.RelatedObjects && rel.RelatingPropertyDefinition) { + const propDefId = rel.RelatingPropertyDefinition.value; + if (propDefId && typeof propDefId === 'number' && propDefId > 0) { + const propDef = ifcApi.GetLine(modelID, propDefId); + if (propDef && propDef.HasProperties) { + const props: Record = {}; + for (const pRef of propDef.HasProperties) { + const pId = pRef?.value; + if (pId && typeof pId === 'number' && pId > 0) { + const pLine = ifcApi.GetLine(modelID, pId); + if (pLine && pLine.Name) { + const name = pLine.Name.value; + let value = ''; + if (pLine.NominalValue) { + value = String(pLine.NominalValue.value); + } + if (name && value) { + props[name] = value; + } + } + } + } + const relatedObjs = Array.isArray(rel.RelatedObjects) ? rel.RelatedObjects : [rel.RelatedObjects]; + for (const objRef of relatedObjs) { + const objId = objRef?.value || objRef; + if (objId && typeof objId === 'number') { + const existing = elementPropertiesMap.get(objId) || {}; + elementPropertiesMap.set(objId, { ...existing, ...props }); + } + } + } + } + } + } + } + } catch (propErr) { + console.debug('[IFC Loader] Property indexing note:', propErr); + } + // Collect piece marks & properties const piecesByMark = new Map(); const pieceByExpressID = new Map(); @@ -224,8 +290,9 @@ export async function loadAndAuditIFC( const assID = assemblies.get(i); try { const assObj = ifcApi.GetLine(modelID, assID); - const props = await getPropertiesForElement(ifcApi, modelID, assID); - const pmark = props.PieceMark || props.Reference || props.Mark || props.Tag || 'indefinido'; + const props = elementPropertiesMap.get(assID) || {}; + const rawProps = getSyncProperties(ifcApi, modelID, assID); + const pmark = props.PieceMark || props.Reference || props.Mark || props.Tag || rawProps.Name || 'indefinido'; const children = assemblyToChildren.get(assID) || []; const piece: PieceInfo = { @@ -273,15 +340,10 @@ export async function loadAndAuditIFC( const placedGeometries = flatMesh.geometries; // Check properties for this element - let pmark = ''; - let section = ''; - try { - const rawProps = getSyncProperties(ifcApi, modelID, expressID); - pmark = rawProps.PieceMark || rawProps.Reference || rawProps.Mark || ''; - section = rawProps.Section || rawProps.Profile || ''; - } catch { - // fallback - } + const props = elementPropertiesMap.get(expressID) || {}; + const rawProps = getSyncProperties(ifcApi, modelID, expressID); + const pmark = props.PieceMark || props.Reference || props.Mark || props.Tag || rawProps.Name || ''; + const section = props.Section || props.Profile || props.Description || rawProps.Name || ''; // Determine section color const colorKey = section || 'PADRAO'; @@ -412,25 +474,6 @@ export async function loadAndAuditIFC( } // Helpers -async function getPropertiesForElement(ifcApi: WebIFC.IfcAPI, modelID: number, expressID: number): Promise> { - const result: Record = {}; - try { - const psets = ifcApi.GetPropertySetsForLines(modelID, [expressID]); - for (const pset of psets) { - if (pset.HasProperties) { - for (const propRef of pset.HasProperties) { - const prop = ifcApi.GetLine(modelID, propRef.value); - if (prop && prop.Name && prop.NominalValue) { - result[prop.Name.value] = String(prop.NominalValue.value); - } - } - } - } - } catch { - // fallback - } - return result; -} function getSyncProperties(ifcApi: WebIFC.IfcAPI, modelID: number, expressID: number): Record { const result: Record = {}; diff --git a/src/pages/Visualizador3D.tsx b/src/pages/Visualizador3D.tsx index 0233973..6e61266 100644 --- a/src/pages/Visualizador3D.tsx +++ b/src/pages/Visualizador3D.tsx @@ -65,13 +65,19 @@ export default function Visualizador3D() { try { const { data, error } = await supabase .from('ordens_fabricacao' as any) - .select('id, of_number, descritivo, cliente, peso_total') - .order('created_at', { ascending: false }); + .select('id, num_of, descritivo, peso_total') + .order('num_of', { ascending: false }); if (!error && data) { - setOfs(data as any); - if (data.length > 0 && !selectedOF) { - setSelectedOF((data[0] as any).of_number); + const list: OFOption[] = (data as any[]).map(item => ({ + id: item.id, + of_number: item.num_of || '', + descritivo: item.descritivo || '', + peso_total: Number(item.peso_total || 0) + })); + setOfs(list); + if (list.length > 0 && !selectedOF) { + setSelectedOF(list[0].of_number); } } } catch (err) { @@ -90,13 +96,17 @@ export default function Visualizador3D() { // Fetch pecas const { data: pecasData } = await supabase .from('pecas' as any) - .select('*') + .select('id, of_number, etapa_fase, marca, descricao, quantidade, peso_unitario, peso_total, perfil_principal, material') .eq('of_number', selectedOF); - // Fetch apontamentos + // Fetch apontamentos com join de peca e processo const { data: apontamentosData } = await supabase .from('apontamentos_producao' as any) - .select('*') + .select(` + id, of_number, peca_id, quantidade_produzida, + peca:pecas(marca, etapa_fase), + processo:processos_fabricacao(nome, cor, ordem) + `) .eq('of_number', selectedOF); // Build phases list @@ -109,15 +119,16 @@ export default function Visualizador3D() { pecasData.forEach((p: any) => { const fase = String(p.etapa_fase || '1'); phases.add(fase); - totalPecas += p.quantidade || 0; + totalPecas += Number(p.quantidade || 0); totalPeso += Number(p.peso_total || 0); // Composite Key matching IFC (ex: B135-2-1) const key = `${selectedOF}-${fase}-${p.marca}`; prodMap.set(key, { + pecaId: p.id, marca: p.marca, fase, - totalQtd: p.quantidade || 1, + totalQtd: Number(p.quantidade || 1), pointedQtd: 0, currentProcessName: 'Pendente', processColor: '#64748b' @@ -128,15 +139,19 @@ export default function Visualizador3D() { let pointedTotal = 0; if (apontamentosData) { apontamentosData.forEach((ap: any) => { - const key = `${selectedOF}-${ap.etapa_fase || '1'}-${ap.marca}`; - const existing = prodMap.get(key); - if (existing) { - const qty = Number(ap.quantidade_apontada || 0); - existing.pointedQtd += qty; - pointedTotal += qty; - if (ap.processo_nome) { - existing.currentProcessName = ap.processo_nome; - existing.processColor = PROCESS_COLORS[ap.processo_nome] || '#10b981'; + const marca = ap.peca?.marca; + const fase = String(ap.peca?.etapa_fase || '1'); + if (marca) { + const key = `${selectedOF}-${fase}-${marca}`; + const existing = prodMap.get(key); + if (existing) { + const qty = Number(ap.quantidade_produzida || 0); + existing.pointedQtd += qty; + pointedTotal += qty; + if (ap.processo?.nome) { + existing.currentProcessName = ap.processo.nome; + existing.processColor = ap.processo.cor || PROCESS_COLORS[ap.processo.nome] || '#10b981'; + } } } });