chore: ignore test scripts and ifc files, update 3D viewer
This commit is contained in:
@@ -46,3 +46,8 @@ scripts/auto-synchome.ps1
|
|||||||
# Arquivos temporários
|
# Arquivos temporários
|
||||||
*.tmp
|
*.tmp
|
||||||
*.temp
|
*.temp
|
||||||
|
|
||||||
|
# Arquivos de teste e dados temporários
|
||||||
|
test_*.js
|
||||||
|
test_*.cjs
|
||||||
|
*.ifc
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface ProductionPieceStatus {
|
|||||||
pointedQtd: number;
|
pointedQtd: number;
|
||||||
currentProcessName?: string;
|
currentProcessName?: string;
|
||||||
processColor?: string;
|
processColor?: string;
|
||||||
|
processOrdem?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModelViewer3DProps {
|
interface ModelViewer3DProps {
|
||||||
@@ -252,27 +253,46 @@ export const ModelViewer3D: React.FC<ModelViewer3DProps> = ({
|
|||||||
const cleanAssMark = String(mesh.userData.cleanAssemblyMark || cleanMark).trim().toUpperCase();
|
const cleanAssMark = String(mesh.userData.cleanAssemblyMark || cleanMark).trim().toUpperCase();
|
||||||
const section = String(mesh.userData.section || '').trim().toUpperCase();
|
const section = String(mesh.userData.section || '').trim().toUpperCase();
|
||||||
|
|
||||||
// 1. Determinação da fase e vínculo de produção
|
// 1. Extração da fase deste elemento (Propriedade IFC > Regex na Marca)
|
||||||
|
let elementPhase = '';
|
||||||
|
if (mesh.userData.phase) {
|
||||||
|
elementPhase = String(mesh.userData.phase).trim();
|
||||||
|
}
|
||||||
|
if (!elementPhase) {
|
||||||
|
const raw = assMark || pmark;
|
||||||
|
const match = raw.match(/^(?:[A-Za-z0-9]+-)?(\d+)-/i);
|
||||||
|
if (match) {
|
||||||
|
elementPhase = match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Determinação do vínculo de produção (com matching rigoroso de fase)
|
||||||
let prod: ProductionPieceStatus | undefined = undefined;
|
let prod: ProductionPieceStatus | undefined = undefined;
|
||||||
|
|
||||||
if (colorMode === 'production' && productionData && productionData.size > 0) {
|
if (colorMode === 'production' && productionData && productionData.size > 0) {
|
||||||
// Prioridade 1: Busca pela Assembly Mark do conjunto estrutural
|
const searchKeys = [
|
||||||
if (assMark && assMark !== 'INDEFINIDO') {
|
elementPhase && selectedOF ? `${selectedOF}-${elementPhase}-${cleanAssMark}` : '',
|
||||||
prod = productionData.get(assMark) ||
|
elementPhase ? `${elementPhase}-${cleanAssMark}` : '',
|
||||||
productionData.get(cleanAssMark) ||
|
elementPhase && selectedOF ? `${selectedOF}-${elementPhase}-${cleanMark}` : '',
|
||||||
(selectedPhase && selectedPhase !== 'all' ? productionData.get(`${selectedPhase}-${cleanAssMark}`.toUpperCase()) : undefined) ||
|
elementPhase ? `${elementPhase}-${cleanMark}` : '',
|
||||||
(selectedOF ? productionData.get(`${selectedOF}-${cleanAssMark}`.toUpperCase()) : undefined);
|
assMark,
|
||||||
|
pmark,
|
||||||
|
cleanAssMark,
|
||||||
|
cleanMark
|
||||||
|
].filter(Boolean).map(k => k.toUpperCase());
|
||||||
|
|
||||||
|
for (const key of searchKeys) {
|
||||||
|
const match = productionData.get(key);
|
||||||
|
// Se encontramos, mas ele tem fase e nós temos fase, elas precisam bater.
|
||||||
|
if (match) {
|
||||||
|
if (!elementPhase || !match.fase || String(match.fase) === String(elementPhase)) {
|
||||||
|
prod = match;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prioridade 2: Busca pela marca própria da peça
|
// Fallback para perfis / bitolas
|
||||||
if (!prod && pmark && pmark !== 'INDEFINIDO') {
|
|
||||||
prod = productionData.get(pmark) ||
|
|
||||||
productionData.get(cleanMark) ||
|
|
||||||
(selectedPhase && selectedPhase !== 'all' ? productionData.get(`${selectedPhase}-${cleanMark}`.toUpperCase()) : undefined) ||
|
|
||||||
(selectedOF ? productionData.get(`${selectedOF}-${cleanMark}`.toUpperCase()) : undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prioridade 3: Fallback para perfis / bitolas
|
|
||||||
if (!prod && section && section !== '-' && section !== 'INDEFINIDO') {
|
if (!prod && section && section !== '-' && section !== 'INDEFINIDO') {
|
||||||
prod = productionData.get(`PERFIL:${section}`) ||
|
prod = productionData.get(`PERFIL:${section}`) ||
|
||||||
productionData.get(`DESC:${section}`);
|
productionData.get(`DESC:${section}`);
|
||||||
@@ -291,22 +311,6 @@ export const ModelViewer3D: React.FC<ModelViewer3DProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Extração da fase deste elemento (Banco > Propriedade IFC > Regex na Marca)
|
|
||||||
let elementPhase = '';
|
|
||||||
if (prod && prod.fase) {
|
|
||||||
elementPhase = String(prod.fase).trim();
|
|
||||||
}
|
|
||||||
if (!elementPhase && mesh.userData.phase) {
|
|
||||||
elementPhase = String(mesh.userData.phase).trim();
|
|
||||||
}
|
|
||||||
if (!elementPhase) {
|
|
||||||
const raw = assMark || pmark;
|
|
||||||
const match = raw.match(/^(?:[A-Za-z0-9]+-)?(\d+)-/i);
|
|
||||||
if (match) {
|
|
||||||
elementPhase = match[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Controle Estrito de Visibilidade por Fase
|
// 3. Controle Estrito de Visibilidade por Fase
|
||||||
const isVisibleByPhase =
|
const isVisibleByPhase =
|
||||||
!selectedPhase ||
|
!selectedPhase ||
|
||||||
@@ -330,10 +334,17 @@ export const ModelViewer3D: React.FC<ModelViewer3DProps> = ({
|
|||||||
|
|
||||||
if (prod && prod.pointedQtd > 0) {
|
if (prod && prod.pointedQtd > 0) {
|
||||||
pointedCount++;
|
pointedCount++;
|
||||||
// Peça apontada no banco: aplica com precisão a cor do estágio de fabricação
|
// Cores baseadas no progresso: verde claro -> verde escuro
|
||||||
const stageHex = prod.processColor || '#10b981';
|
const qtyPercent = Math.min(prod.pointedQtd / prod.totalQtd, 1.0);
|
||||||
|
const processPercent = Math.min((prod.processOrdem || 1) / 5, 1.0);
|
||||||
|
const percent = qtyPercent * processPercent;
|
||||||
|
|
||||||
|
const colorLight = new THREE.Color('#4ade80'); // Verde claro
|
||||||
|
const colorDark = new THREE.Color('#14532d'); // Verde escuro
|
||||||
|
const finalColor = new THREE.Color().lerpColors(colorLight, colorDark, percent);
|
||||||
|
|
||||||
mesh.material = new THREE.MeshStandardMaterial({
|
mesh.material = new THREE.MeshStandardMaterial({
|
||||||
color: new THREE.Color(stageHex),
|
color: finalColor,
|
||||||
metalness: 0.35,
|
metalness: 0.35,
|
||||||
roughness: 0.45,
|
roughness: 0.45,
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
|
|||||||
@@ -562,49 +562,49 @@ export async function loadAndAuditIFC(
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
// Extração inteligente de Fase
|
// Extração inteligente de Fase
|
||||||
const phaseCandidates = [
|
let chosenPhase = extractPhaseFromMark(assemblyMark || finalMark);
|
||||||
props.Phase,
|
|
||||||
props.Fase,
|
if (!chosenPhase) {
|
||||||
props['Phase Number'],
|
const phaseCandidates = [
|
||||||
props['Phase Name'],
|
props.Phase,
|
||||||
props['Tekla Assembly.Phase'],
|
props.Fase,
|
||||||
props['Tekla Common.Phase'],
|
props['Phase Number'],
|
||||||
props['Tekla Assembly.Phase Name'],
|
props['Phase Name'],
|
||||||
props.Etapa,
|
props['Tekla Assembly.Phase'],
|
||||||
props.Sequence,
|
props['Tekla Common.Phase'],
|
||||||
props.Batch
|
props['Tekla Assembly.Phase Name'],
|
||||||
];
|
props.Etapa,
|
||||||
let chosenPhase = '';
|
props.Sequence,
|
||||||
for (const pCand of phaseCandidates) {
|
props.Batch
|
||||||
if (pCand && isValidMark(pCand)) {
|
|
||||||
chosenPhase = String(pCand).trim();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!chosenPhase && childToAssembly.has(expressID)) {
|
|
||||||
const parentId = childToAssembly.get(expressID)!;
|
|
||||||
const parentProps = elementPropertiesMap.get(parentId) || {};
|
|
||||||
const parentPhaseCandidates = [
|
|
||||||
parentProps.Phase,
|
|
||||||
parentProps.Fase,
|
|
||||||
parentProps['Phase Number'],
|
|
||||||
parentProps['Phase Name'],
|
|
||||||
parentProps['Tekla Assembly.Phase'],
|
|
||||||
parentProps['Tekla Common.Phase'],
|
|
||||||
parentProps['Tekla Assembly.Phase Name'],
|
|
||||||
parentProps.Etapa,
|
|
||||||
parentProps.Sequence,
|
|
||||||
parentProps.Batch
|
|
||||||
];
|
];
|
||||||
for (const pCand of parentPhaseCandidates) {
|
for (const pCand of phaseCandidates) {
|
||||||
if (pCand && isValidMark(pCand)) {
|
if (pCand && isValidMark(pCand)) {
|
||||||
chosenPhase = String(pCand).trim();
|
chosenPhase = String(pCand).trim();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!chosenPhase && childToAssembly.has(expressID)) {
|
||||||
if (!chosenPhase) {
|
const parentId = childToAssembly.get(expressID)!;
|
||||||
chosenPhase = extractPhaseFromMark(assemblyMark || finalMark);
|
const parentProps = elementPropertiesMap.get(parentId) || {};
|
||||||
|
const parentPhaseCandidates = [
|
||||||
|
parentProps.Phase,
|
||||||
|
parentProps.Fase,
|
||||||
|
parentProps['Phase Number'],
|
||||||
|
parentProps['Phase Name'],
|
||||||
|
parentProps['Tekla Assembly.Phase'],
|
||||||
|
parentProps['Tekla Common.Phase'],
|
||||||
|
parentProps['Tekla Assembly.Phase Name'],
|
||||||
|
parentProps.Etapa,
|
||||||
|
parentProps.Sequence,
|
||||||
|
parentProps.Batch
|
||||||
|
];
|
||||||
|
for (const pCand of parentPhaseCandidates) {
|
||||||
|
if (pCand && isValidMark(pCand)) {
|
||||||
|
chosenPhase = String(pCand).trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -631,7 +631,7 @@ export async function loadAndAuditIFC(
|
|||||||
const markInfo = extractBestMark(assID);
|
const markInfo = extractBestMark(assID);
|
||||||
const children = assemblyToChildren.get(assID) || [];
|
const children = assemblyToChildren.get(assID) || [];
|
||||||
|
|
||||||
if (markInfo.phase) {
|
if (markInfo.phase && markInfo.mark && markInfo.mark !== 'indefinido') {
|
||||||
detectedPhasesSet.add(markInfo.phase);
|
detectedPhasesSet.add(markInfo.phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,7 +679,7 @@ export async function loadAndAuditIFC(
|
|||||||
const materialName = elementMaterialMap.get(expressID) ?? '';
|
const materialName = elementMaterialMap.get(expressID) ?? '';
|
||||||
const connectedGroup = weldedGroupMap.get(expressID) || [expressID];
|
const connectedGroup = weldedGroupMap.get(expressID) || [expressID];
|
||||||
|
|
||||||
if (markInfo.phase) {
|
if (markInfo.phase && markInfo.mark && markInfo.mark !== 'indefinido') {
|
||||||
detectedPhasesSet.add(markInfo.phase);
|
detectedPhasesSet.add(markInfo.phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,14 +856,13 @@ export function extractPhaseFromMark(mark: string): string {
|
|||||||
const parts = mark.split('-');
|
const parts = mark.split('-');
|
||||||
if (parts.length >= 3) {
|
if (parts.length >= 3) {
|
||||||
const p1 = parts[1].trim();
|
const p1 = parts[1].trim();
|
||||||
if (p1 && /^\d+$/.test(p1)) return p1;
|
if (p1 && /^[a-zA-Z0-9]+$/.test(p1)) return p1; // Can be 11, 12, A, etc.
|
||||||
return p1;
|
|
||||||
}
|
}
|
||||||
if (parts.length === 2) {
|
if (parts.length === 2) {
|
||||||
const p0 = parts[0].trim();
|
const p0 = parts[0].trim();
|
||||||
if (p0 && /^\d+$/.test(p0)) return p0;
|
if (p0 && /^\d+$/.test(p0)) return p0;
|
||||||
}
|
}
|
||||||
const match = mark.match(/(?:fase|phase|etapa)\s*(\d+)/i);
|
const match = mark.match(/(?:fase|phase|etapa)\s*([a-zA-Z0-9]+)/i);
|
||||||
if (match) return match[1];
|
if (match) return match[1];
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ export default function Visualizador3D() {
|
|||||||
|
|
||||||
if (pecasData) {
|
if (pecasData) {
|
||||||
pecasData.forEach((p: any) => {
|
pecasData.forEach((p: any) => {
|
||||||
const fase = String(p.etapa_fase || '1');
|
const fase = String(p.etapa_fase || '').trim();
|
||||||
phases.add(fase);
|
if (fase) phases.add(fase);
|
||||||
totalPecas += Number(p.quantidade || 0);
|
totalPecas += Number(p.quantidade || 0);
|
||||||
totalPeso += Number(p.peso_total || 0);
|
totalPeso += Number(p.peso_total || 0);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user