-- Process.lua by Egalware s.r.l. 2024/06/13 -- Gestione calcolo disposizione e lavorazioni per serramenti -- Si opera sulla macchina corrente -- 2024/06/13 PRIMA VERSIONE -- Intestazioni require( 'EgtBase') _ENV = EgtProtectGlobal() EgtEnableDebug( false) -- Verifico che la macchina corrente sia abilitata per la lavorazione delle Travi local sMachDir = EgtGetCurrMachineDir() if not sMachDir then EgtOutBox( 'Errore nel caricamento della macchina corrente', 'Lavora Serramenti', 'ERROR') return end if not EgtExistsFile( sMachDir .. '\\Window\\WinData.lua') then EgtOutBox( 'La macchina corrente non è configurata per lavorare serramenti', 'Lavora Serramenti', 'ERROR') return end -- Elimino direttori altre macchine e imposto direttorio macchina corrente per ricerca librerie EgtRemoveBaseMachineDirFromPackagePath() EgtAddToPackagePath( sMachDir .. '\\Window\\?.lua') -- Segnalazione avvio EgtOutLog( '*** Window Process Start ***', 1) -- Carico le librerie -- Carico i dati globali local WinData = require( 'WinData') -- Variabili globali PARTS = {} GROUPS = {} -- Colore del grezzo local ColA = Color3d( 255, 165, 0, 30) -- TODO DA RIMUOVERE, il parametro deve essere passato local WIN = {} WIN.OPT_TYPE = 'PIECE' ------------------------------------------------------------------------------------------------------------- -- *** Recupero offset applicato al profilo *** ------------------------------------------------------------------------------------------------------------- local function GetDispOffsetFromNotes( nPieceIndex) local bAllOffsetsAreOk = false PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetX = 0 -- dovrà essere calcolato in base alle lavorazioni PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetY = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFY_1', 'd') PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetZ = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFZ_1', 'd') PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetX = 0 -- dovrà essere calcolato in base alle lavorazioni PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetY = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFY_2', 'd') PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetZ = EgtGetInfo( PARTS[nPieceIndex].id, 'OFFZ_2', 'd') -- controllo se tutti gli offset siano settati if PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetY and PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetZ and PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetY and PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetZ then bAllOffsetsAreOk = true end return bAllOffsetsAreOk end ------------------------------------------------------------------------------------------------------------- -- *** Recupero i pezzi da processare *** ------------------------------------------------------------------------------------------------------------- local function GetInfoPieces() -- Recupero le travi selezionate local nId = EgtGetFirstPart() while nId do -- TODO caricare il file di costanti -- si processano solo BOTTOMRAIL (WIN_PART_TYPES.BOTTOMRAIL = 2) o STANDARD (WIN_PART_TYPES.STD = 3) if EgtGetInfo( nId, 'PART_TYPE', 'd') == 2 or EgtGetInfo( nId, 'PART_TYPE', 'd') == 3 then table.insert( PARTS, { id = nId, sName = ( EgtGetName( nId) or ( 'Id=' .. tonumber( nId)))}) end nId = EgtGetNextPart( nId) end if #PARTS == 0 then EgtOutBox( 'Non ci sono pezzi', 'Lavora Pezzi', 'ERROR') return false else -- recupero tutte le dimensioni necessarie local sOut = '' for i = 1, #PARTS do PARTS[i].b3Raw = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[i].id, 'GeoRaw') or GDB_ID.NULL, GDB_BB.STANDARD) local idFrame = EgtGetFirstNameInGroup( EgtGetFirstNameInGroup( PARTS[i].id, 'GeoRaw'), 'AuxFrame') PARTS[i].frame = EgtFR( idFrame) -- recupero offset per posizionamento PARTS[i].DispOffsets = {} PARTS[i].DispOffsets.Phase1 = {} PARTS[i].DispOffsets.Phase2 = {} local bInsertedAllOffs = GetDispOffsetFromNotes( i) -- recupero info profili PARTS[i].SemiProfiles = {} table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_IN', 's') or '') table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_OUT', 's') or '') table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_LEFT', 's') or '') table.insert( PARTS[i].SemiProfiles, EgtGetInfo( PARTS[i].id, 'PROFILE_RIGHT', 's') or '') -- TODO info deve essere passata da programma -- recupero info finestra PARTS[i].sWindow = EgtGetInfo( PARTS[i].id, 'WIN_ID', 's') or '' -- TODO è lo stesso del PART_TYPE o serve altro??? da verificare! -- tipo di pezzo PARTS[i].sPieceType = EgtGetInfo( PARTS[i].id, 'PART_TYPE', 'd') or '' -- definisco il pezzo per famiglia ( TODO da recuperare dal nome del pezzo, o da una nota) -- se non sono stati inseriti o c'è stato un errore esco subito if not bInsertedAllOffs then return false end sOut = sOut .. PARTS[i].sName .. ', ' end sOut = sOut:sub( 1, -3) EgtOutLog( 'Pezzi : ' .. sOut, 1) end EgtDeselectAll() return true end --------------------------------------------------------------------- -- Aggiunge al grezzo i sovramateriali pre-impostati --------------------------------------------------------------------- local function AddOverMaterialToRaw( PiecesInMachGroup) for i = 1, #PiecesInMachGroup do -- prima di aggiungere sovramateriale al grezzo, calcolo dimensioni del finito local b3Part = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PiecesInMachGroup[i].id, 'Geo') or GDB_ID.NULL, GDB_BB.STANDARD) PiecesInMachGroup[i].b3Part = b3Part PiecesInMachGroup[i].dPartLength = PiecesInMachGroup[i].b3Part:getDimX() PiecesInMachGroup[i].dPartWidth = PiecesInMachGroup[i].b3Part:getDimY() PiecesInMachGroup[i].dPartHeight = PiecesInMachGroup[i].b3Part:getDimZ() -- recupero sovramateriale PiecesInMachGroup[i].RawOffset = {} -- recupero info sovramateriale PiecesInMachGroup[i].RawOffset.dOverMatIn = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_IN', 'd') or 5 PiecesInMachGroup[i].RawOffset.dOverMatOut = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_OUT', 'd') or 5 PiecesInMachGroup[i].RawOffset.dOverMatLeft = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_LEFT', 'd') or 5 PiecesInMachGroup[i].RawOffset.dOverMatRight = EgtGetInfo( PiecesInMachGroup[i].id, 'OVERMAT_RIGHT', 'd') or 5 PiecesInMachGroup[i].dRawLength = PiecesInMachGroup[i].RawOffset.dOverMatLeft + PiecesInMachGroup[i].dPartLength + PiecesInMachGroup[i].RawOffset.dOverMatRight PiecesInMachGroup[i].dRawWidth = PiecesInMachGroup[i].RawOffset.dOverMatOut + PiecesInMachGroup[i].dPartWidth + PiecesInMachGroup[i].RawOffset.dOverMatIn PiecesInMachGroup[i].dRawHeight = PiecesInMachGroup[i].dPartHeight EgtModifyRawPartSize( PiecesInMachGroup[i].idRaw, PiecesInMachGroup[i].dRawLength, PiecesInMachGroup[i].dRawWidth, PiecesInMachGroup[i].dPartHeight) local vtMove = Vector3d( PiecesInMachGroup[i].RawOffset.dOverMatLeft, PiecesInMachGroup[i].RawOffset.dOverMatOut, 0) EgtMovePartInRawPart( PiecesInMachGroup[i].id, vtMove) -- TODO da controllare, se ruotato di 180° bisognerebbe prendere dOverMatIn. Lo stesso per la X PiecesInMachGroup[i].OffsetPartToRaw = {} PiecesInMachGroup[i].OffsetPartToRaw.X = PiecesInMachGroup[i].RawOffset.dOverMatLeft PiecesInMachGroup[i].OffsetPartToRaw.Y = PiecesInMachGroup[i].RawOffset.dOverMatOut end end --------------------------------------------------------------------- -- Crea il grezzo che verrà messo in macchina --------------------------------------------------------------------- local function CreateRaws( PiecesInMachGroup) for i = 1, #PiecesInMachGroup do -- si crea un grezzo "finto" (un cubo da 100mm) nell'origine del pezzo, verrà poi dimensionato uan volta adeguato con i vari sovramateriali PiecesInMachGroup[i].idRaw = EgtAddRawPart( PiecesInMachGroup[i].frame:getOrigin(), 100, 100, 100, ColA) EgtAddPartToRawPart( PiecesInMachGroup[i].id, ORIG(), PiecesInMachGroup[i].idRaw) end end --------------------------------------------------------------------- -- Allinea i pezzi in base a come devono essere disposti sulla tavola --------------------------------------------------------------------- -- TODO in questa fase bisogna già sapere qual è la prima fase e posizionare il pezzo di conseguenza -- TODO (bassa priorità) adesso allinea sempre in X, ma bisognerebbe farlo in base ad un parametro in WINDATA che dice come si dispongono in macchina local function AlignRawsToTable( PiecesInMachGroup) for i = 1, #PiecesInMachGroup do -- allineo il pezzo all'interno del grezzo local dRotX, dRotY, dRotZ = GetFixedAxesRotABCFromFrame( PiecesInMachGroup[i].frame) -- se devo ruotare if abs( dRotZ) > GEO.EPS_ANG_SMALL then EgtRotatePartInRawPart( PiecesInMachGroup[i].id, Z_AX(), -dRotZ) end -- sposto punto in basso a sinistra del pezzo sul punto in basso a sinistra del grezzo local b3Part = EgtGetBBoxGlob( PiecesInMachGroup[i].id, GDB_BB.ONLY_VISIBLE) local dPartPosX = b3Part:getMin():getX() local dPartPosY = b3Part:getMin():getY() local dPartPosZ = b3Part:getMin():getZ() local b3Raw = EgtGetRawPartBBox( PiecesInMachGroup[i].idRaw) local dRawPosX = b3Raw:getMin():getX() local dRawPosY = b3Raw:getMin():getY() local dRawPosZ = b3Raw:getMin():getZ() local vtMove = Vector3d( dRawPosX - dPartPosX, dRawPosY - dPartPosY, dRawPosZ - dPartPosZ) EgtMovePartInRawPart( PiecesInMachGroup[i].id, vtMove) end return true end ------------------------------------------------------------------------------------------------------------- -- *** Funzione per trovare nome MachGroup *** ------------------------------------------------------------------------------------------------------------- local function NewMachGroupName() local nMachGroupId = EgtGetFirstMachGroup() if not nMachGroupId then return '1' end local nMaxMachGroup = 0 while nMachGroupId do local sMachGroupName = EgtGetMachGroupName( nMachGroupId) local nMachGroupName = tonumber( sMachGroupName) if nMachGroupName > nMaxMachGroup then nMaxMachGroup = nMachGroupName end nMachGroupId = EgtGetNextMachGroup( nMachGroupId) end return tostring( nMaxMachGroup + 1) end ------------------------------------------------------------------------------------------------------------- -- *** Funzione per raggruppamento pezzi *** ------------------------------------------------------------------------------------------------------------- -- TODO punteggio da verificare!! -- Calcolo un punteggio di similitudine tra due pezzi local function CalculateSimilarity( Part1, Part2) -- peso lati: IN, OUT, LEFT, RIGHT (i lati lunghi hanno più peso rispetto alle teste) local ProfilesWeights = { 3, 3, 1, 1} local dScore = 0 for i = 1, 4 do if Part1.SemiProfiles[i] == Part2.SemiProfiles[i] then dScore = dScore + ProfilesWeights[i] end end -- punteggio "bonus" se il tipo di pezzo è lo stesso if Part1.sPieceType and Part2.sPieceType and Part1.sPieceType == Part2.sPieceType then dScore = dScore + 2 end return dScore end ------------------------------------------------------------------------------------------------------------- -- raggruppamento con vincoli ( lunghezza / numero pezzi) local function GroupParts( PartsList, sOptimisationType) -- TIPI DI OTTIMIZZAZIONE (sOptimisationType) -- 'PIECE' : pezzo singolo -- 'WINDOW' : ottimizza finestra -- 'FULL_LIST': ottimizza progetto local Groups = {} local Used = {} for i = 1, #PartsList do if not Used[i] then local Group = {} -- Add the main part table.insert( Group, PartsList[i]) Used[i] = true -- se non è ottimizzazione pezzo singolo, cerco altri pezzi che possono essere lavorati nella stessa macchinata if sOptimisationType ~= 'PIECE' then -- cerca un altro pezzo compatibile while true do local nBestIndex = nil local nBestScore = -1 for j = 1, #PartsList do if not Used[j] then local bCanBeGrouped = not( sOptimisationType == 'WINDOW' and PartsList[i].sWindow ~= PartsList[j].sWindow) -- se possono essere raggruppati per tipo di ottimizzazione if bCanBeGrouped then local VirtualList = WinLib.TableCopyDeep( Group) table.insert( VirtualList, PartsList[j]) bCanBeGrouped = WinData.VerifyPieces( VirtualList) -- se possono effettivamente essere messi nella stessa macchinata if bCanBeGrouped then -- il punteggio è sempre calcolato rispetto al primo pezzo del gruppo local nScore = CalculateSimilarity( PartsList[i], PartsList[j]) if nScore > nBestScore then nBestScore = nScore nBestIndex = j end end end end end if not nBestIndex then break end -- Add the best matching part table.insert( Group, PartsList[nBestIndex]) Used[nBestIndex] = true end end table.insert( Groups, Group) end end return Groups end ------------------------------------------------------------------------------------------------------------- -- *** Inserimento dei pezzi nelle macchinate *** ------------------------------------------------------------------------------------------------------------- local function CreateMachGroups() for j = 1, #GROUPS do -- creo macchinata local MachGroupName = NewMachGroupName() local nMachGroup = EgtAddMachGroup( MachGroupName) -- si setta gruppo appena creato come corrente EgtSetCurrMachGroup( nMachGroup) -- si crea il grezzo CreateRaws( GROUPS[j]) -- allineo i pezzi come orientamento richiesto dalla macchina AlignRawsToTable( GROUPS[j]) -- aggiungo sovramateriale ai grezzi AddOverMaterialToRaw( GROUPS[j]) -- si dispongono i pezzi sulla tavola local bDispOk, sErr = WinData.ExecDisposition( GROUPS[j]) if not bDispOk then if not sErr then sErr = 'Errore non gestito in WinData.ExecDisposition' end return false end -- Impostazione dell'attrezzaggio di default local bOk = EgtImportSetup() if not bOk then EgtImportSetup( 'Default') end end return true end ------------------------------------------------------------------------------------------------------------- -- *** Esecuzione *** ------------------------------------------------------------------------------------------------------------- if not GetInfoPieces() then return end GROUPS = GroupParts( PARTS, WIN.OPT_TYPE) if not GROUPS or #GROUPS == 0 then return end if not CreateMachGroups() then return end