fix(3d-viewer): pin web-ifc 0.0.57 with matching WASM and fix ordens_fabricacao Supabase queries
This commit is contained in:
Generated
+4
-5
@@ -58,7 +58,7 @@
|
|||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"three": "^0.186.0",
|
"three": "^0.186.0",
|
||||||
"three-stdlib": "^2.36.1",
|
"three-stdlib": "^2.36.1",
|
||||||
"web-ifc": "^0.0.77",
|
"web-ifc": "0.0.57",
|
||||||
"xlsx": "^0.18.5",
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -7188,10 +7188,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/web-ifc": {
|
"node_modules/web-ifc": {
|
||||||
"version": "0.0.77",
|
"version": "0.0.57",
|
||||||
"resolved": "https://registry.npmjs.org/web-ifc/-/web-ifc-0.0.77.tgz",
|
"resolved": "https://registry.npmjs.org/web-ifc/-/web-ifc-0.0.57.tgz",
|
||||||
"integrity": "sha512-VzQ0W/Iiqbidxn1ECUvz6qJ6p2sXBVNcOsUOBCETzy77psAH6yFLKQm74aXabkx3JH4OvFVHe8k1qS6+Z2zl1w==",
|
"integrity": "sha512-ozzfxiCNT4+VqIhm93QdTozOmkMSPRZzf1RoRMMQIQLqoidYdExnAmY1OLoq1emnbvHPqFNQJ5HnQoqxvYZeFA=="
|
||||||
"license": "MPL-2.0"
|
|
||||||
},
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@
|
|||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"three": "^0.186.0",
|
"three": "^0.186.0",
|
||||||
"three-stdlib": "^2.36.1",
|
"three-stdlib": "^2.36.1",
|
||||||
"web-ifc": "^0.0.77",
|
"web-ifc": "0.0.57",
|
||||||
"xlsx": "^0.18.5",
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -74,21 +74,40 @@ export async function loadAndAuditIFC(
|
|||||||
// 1. Initialize Web-IFC WASM locally with fallback
|
// 1. Initialize Web-IFC WASM locally with fallback
|
||||||
if (onProgress) onProgress(10, 'Carregando motor Web-IFC (WASM)...');
|
if (onProgress) onProgress(10, 'Carregando motor Web-IFC (WASM)...');
|
||||||
|
|
||||||
const wasmPaths = [
|
const LOCAL_WASM_PATH = '/wasm/';
|
||||||
'/wasm/',
|
const CDN_WASM_PATH = 'https://unpkg.com/web-ifc@0.0.57/';
|
||||||
'https://unpkg.com/web-ifc@0.0.57/',
|
|
||||||
'https://cdn.jsdelivr.net/npm/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;
|
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 {
|
try {
|
||||||
ifcApi.SetWasmPath(path);
|
ifcApi.SetWasmPath(CDN_WASM_PATH, true);
|
||||||
await ifcApi.Init();
|
await ifcApi.Init();
|
||||||
wasmInitSuccess = true;
|
wasmInitSuccess = true;
|
||||||
break;
|
} catch (cdnErr) {
|
||||||
} catch {
|
console.error('[IFC Loader] Falha ao inicializar Web-IFC via CDN:', cdnErr);
|
||||||
// try next
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +231,53 @@ export async function loadAndAuditIFC(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4.3 Element Properties Map from IFCRELDEFINESBYPROPERTIES
|
||||||
|
const elementPropertiesMap = new Map<number, Record<string, string>>();
|
||||||
|
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<string, string> = {};
|
||||||
|
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
|
// Collect piece marks & properties
|
||||||
const piecesByMark = new Map<string, PieceInfo[]>();
|
const piecesByMark = new Map<string, PieceInfo[]>();
|
||||||
const pieceByExpressID = new Map<number, PieceInfo>();
|
const pieceByExpressID = new Map<number, PieceInfo>();
|
||||||
@@ -224,8 +290,9 @@ export async function loadAndAuditIFC(
|
|||||||
const assID = assemblies.get(i);
|
const assID = assemblies.get(i);
|
||||||
try {
|
try {
|
||||||
const assObj = ifcApi.GetLine(modelID, assID);
|
const assObj = ifcApi.GetLine(modelID, assID);
|
||||||
const props = await getPropertiesForElement(ifcApi, modelID, assID);
|
const props = elementPropertiesMap.get(assID) || {};
|
||||||
const pmark = props.PieceMark || props.Reference || props.Mark || props.Tag || 'indefinido';
|
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 children = assemblyToChildren.get(assID) || [];
|
||||||
const piece: PieceInfo = {
|
const piece: PieceInfo = {
|
||||||
@@ -273,15 +340,10 @@ export async function loadAndAuditIFC(
|
|||||||
const placedGeometries = flatMesh.geometries;
|
const placedGeometries = flatMesh.geometries;
|
||||||
|
|
||||||
// Check properties for this element
|
// Check properties for this element
|
||||||
let pmark = '';
|
const props = elementPropertiesMap.get(expressID) || {};
|
||||||
let section = '';
|
const rawProps = getSyncProperties(ifcApi, modelID, expressID);
|
||||||
try {
|
const pmark = props.PieceMark || props.Reference || props.Mark || props.Tag || rawProps.Name || '';
|
||||||
const rawProps = getSyncProperties(ifcApi, modelID, expressID);
|
const section = props.Section || props.Profile || props.Description || rawProps.Name || '';
|
||||||
pmark = rawProps.PieceMark || rawProps.Reference || rawProps.Mark || '';
|
|
||||||
section = rawProps.Section || rawProps.Profile || '';
|
|
||||||
} catch {
|
|
||||||
// fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine section color
|
// Determine section color
|
||||||
const colorKey = section || 'PADRAO';
|
const colorKey = section || 'PADRAO';
|
||||||
@@ -412,25 +474,6 @@ export async function loadAndAuditIFC(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
async function getPropertiesForElement(ifcApi: WebIFC.IfcAPI, modelID: number, expressID: number): Promise<Record<string, string>> {
|
|
||||||
const result: Record<string, string> = {};
|
|
||||||
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<string, string> {
|
function getSyncProperties(ifcApi: WebIFC.IfcAPI, modelID: number, expressID: number): Record<string, string> {
|
||||||
const result: Record<string, string> = {};
|
const result: Record<string, string> = {};
|
||||||
|
|||||||
@@ -65,13 +65,19 @@ export default function Visualizador3D() {
|
|||||||
try {
|
try {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('ordens_fabricacao' as any)
|
.from('ordens_fabricacao' as any)
|
||||||
.select('id, of_number, descritivo, cliente, peso_total')
|
.select('id, num_of, descritivo, peso_total')
|
||||||
.order('created_at', { ascending: false });
|
.order('num_of', { ascending: false });
|
||||||
|
|
||||||
if (!error && data) {
|
if (!error && data) {
|
||||||
setOfs(data as any);
|
const list: OFOption[] = (data as any[]).map(item => ({
|
||||||
if (data.length > 0 && !selectedOF) {
|
id: item.id,
|
||||||
setSelectedOF((data[0] as any).of_number);
|
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) {
|
} catch (err) {
|
||||||
@@ -90,13 +96,17 @@ export default function Visualizador3D() {
|
|||||||
// Fetch pecas
|
// Fetch pecas
|
||||||
const { data: pecasData } = await supabase
|
const { data: pecasData } = await supabase
|
||||||
.from('pecas' as any)
|
.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);
|
.eq('of_number', selectedOF);
|
||||||
|
|
||||||
// Fetch apontamentos
|
// Fetch apontamentos com join de peca e processo
|
||||||
const { data: apontamentosData } = await supabase
|
const { data: apontamentosData } = await supabase
|
||||||
.from('apontamentos_producao' as any)
|
.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);
|
.eq('of_number', selectedOF);
|
||||||
|
|
||||||
// Build phases list
|
// Build phases list
|
||||||
@@ -109,15 +119,16 @@ export default function Visualizador3D() {
|
|||||||
pecasData.forEach((p: any) => {
|
pecasData.forEach((p: any) => {
|
||||||
const fase = String(p.etapa_fase || '1');
|
const fase = String(p.etapa_fase || '1');
|
||||||
phases.add(fase);
|
phases.add(fase);
|
||||||
totalPecas += p.quantidade || 0;
|
totalPecas += Number(p.quantidade || 0);
|
||||||
totalPeso += Number(p.peso_total || 0);
|
totalPeso += Number(p.peso_total || 0);
|
||||||
|
|
||||||
// Composite Key matching IFC (ex: B135-2-1)
|
// Composite Key matching IFC (ex: B135-2-1)
|
||||||
const key = `${selectedOF}-${fase}-${p.marca}`;
|
const key = `${selectedOF}-${fase}-${p.marca}`;
|
||||||
prodMap.set(key, {
|
prodMap.set(key, {
|
||||||
|
pecaId: p.id,
|
||||||
marca: p.marca,
|
marca: p.marca,
|
||||||
fase,
|
fase,
|
||||||
totalQtd: p.quantidade || 1,
|
totalQtd: Number(p.quantidade || 1),
|
||||||
pointedQtd: 0,
|
pointedQtd: 0,
|
||||||
currentProcessName: 'Pendente',
|
currentProcessName: 'Pendente',
|
||||||
processColor: '#64748b'
|
processColor: '#64748b'
|
||||||
@@ -128,15 +139,19 @@ export default function Visualizador3D() {
|
|||||||
let pointedTotal = 0;
|
let pointedTotal = 0;
|
||||||
if (apontamentosData) {
|
if (apontamentosData) {
|
||||||
apontamentosData.forEach((ap: any) => {
|
apontamentosData.forEach((ap: any) => {
|
||||||
const key = `${selectedOF}-${ap.etapa_fase || '1'}-${ap.marca}`;
|
const marca = ap.peca?.marca;
|
||||||
const existing = prodMap.get(key);
|
const fase = String(ap.peca?.etapa_fase || '1');
|
||||||
if (existing) {
|
if (marca) {
|
||||||
const qty = Number(ap.quantidade_apontada || 0);
|
const key = `${selectedOF}-${fase}-${marca}`;
|
||||||
existing.pointedQtd += qty;
|
const existing = prodMap.get(key);
|
||||||
pointedTotal += qty;
|
if (existing) {
|
||||||
if (ap.processo_nome) {
|
const qty = Number(ap.quantidade_produzida || 0);
|
||||||
existing.currentProcessName = ap.processo_nome;
|
existing.pointedQtd += qty;
|
||||||
existing.processColor = PROCESS_COLORS[ap.processo_nome] || '#10b981';
|
pointedTotal += qty;
|
||||||
|
if (ap.processo?.nome) {
|
||||||
|
existing.currentProcessName = ap.processo.nome;
|
||||||
|
existing.processColor = ap.processo.cor || PROCESS_COLORS[ap.processo.nome] || '#10b981';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user