From 49d7bccb41cd6e0acaf7a4a37d480a484ddf8d6e Mon Sep 17 00:00:00 2001 From: "andrea.villa" Date: Tue, 3 Feb 2026 13:00:44 +0100 Subject: [PATCH] Prima versione (non funzionante) script creazione gruppi --- CAMAuto/BatchProcessWin.lua | 2 +- CAMAuto/CreateMachGroups.lua | 360 +++++++++++++++++++++++++++++++++++ CAMAuto/LuaLibs/WinLib.lua | 34 ++++ 3 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 CAMAuto/CreateMachGroups.lua diff --git a/CAMAuto/BatchProcessWin.lua b/CAMAuto/BatchProcessWin.lua index 5ff8d1d..82503cf 100644 --- a/CAMAuto/BatchProcessWin.lua +++ b/CAMAuto/BatchProcessWin.lua @@ -387,7 +387,7 @@ if not bSimOk then WriteErrToLogFile( WIN.ERR, WIN.MSG, WIN.ROT, WIN.CUTID, WIN.TASKID) -- TODO gestire collisione su feature specifica!!!!!!!!!!!!!!!!!!!!!!!!!!!! BeamExec.AddApplyResultToGlobalList( WIN.ERR, WIN.CUTID, WIN.MSG) - WIN.RESULT = BeamLib.TableCopyDeep( RESULT) + WIN.RESULT = WinLib.TableCopyDeep( RESULT) WriteResultToJson( RESULT) return end diff --git a/CAMAuto/CreateMachGroups.lua b/CAMAuto/CreateMachGroups.lua new file mode 100644 index 0000000..d272f19 --- /dev/null +++ b/CAMAuto/CreateMachGroups.lua @@ -0,0 +1,360 @@ +-- 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) + +------------------------------------------------------------------------------------------------------------- +-- *** 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 + -- si processano solo BOTTOMRAIL o STANDARD + if EgtGetInfo( nId, 'PART_TYPE', 'd') == WIN_PART_TYPES.BOTTOMRAIL or EgtGetInfo( nId, 'PART_TYPE', 'd') == WIN_PART_TYPES.STD 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) + PARTS[i].b3Part = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[i].id, 'Geo') 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 ID finestra alla quale appartiene il pezzo + PARTS[i].idWindow = EgtGetInfo( PARTS[i].id, 'WIN_ID', 'd') or 0 + + -- 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 '') + + -- 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( PARTS) + for i = 1, #PARTS do + + -- prima di aggiungere sovramateriale al grezzo, calcolo dimensioni del finito + local b3Part = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[i].id, 'Geo') or GDB_ID.NULL, GDB_BB.STANDARD) + PARTS[i].b3Part = b3Part + PARTS[i].dPartLength = PARTS[i].b3Part:getDimX() + PARTS[i].dPartWidth = PARTS[i].b3Part:getDimY() + PARTS[i].dPartHeight = PARTS[i].b3Part:getDimZ() + + -- recupero sovramateriale + PARTS[i].RawOffset = {} + -- recupero info sovramateriale + PARTS[i].RawOffset.dOverMatIn = EgtGetInfo( PARTS[i].id, 'OVERMAT_IN', 'd') or 5 + PARTS[i].RawOffset.dOverMatOut = EgtGetInfo( PARTS[i].id, 'OVERMAT_OUT', 'd') or 5 + PARTS[i].RawOffset.dOverMatLeft = EgtGetInfo( PARTS[i].id, 'OVERMAT_LEFT', 'd') or 5 + PARTS[i].RawOffset.dOverMatRight = EgtGetInfo( PARTS[i].id, 'OVERMAT_RIGHT', 'd') or 5 + + PARTS[i].dRawLength = PARTS[i].RawOffset.dOverMatLeft + PARTS[i].dPartLength + PARTS[i].RawOffset.dOverMatRight + PARTS[i].dRawWidth = PARTS[i].RawOffset.dOverMatOut + PARTS[i].dPartWidth + PARTS[i].RawOffset.dOverMatIn + PARTS[i].dRawHeight = PARTS[i].dPartHeight + + EgtModifyRawPartSize( PARTS[i].idRaw, PARTS[i].dRawLength, PARTS[i].dRawWidth, PARTS[i].dPartHeight) + + local vtMove = Vector3d( PARTS[i].RawOffset.dOverMatLeft, PARTS[i].RawOffset.dOverMatOut, 0) + EgtMovePartInRawPart( PARTS[i].id, vtMove) + + -- TODO da controllare, se ruotato di 180° bisognerebbe prendere dOverMatIn. Lo stesso per la X + PARTS[i].OffsetPartToRaw = {} + PARTS[i].OffsetPartToRaw.X = PARTS[i].RawOffset.dOverMatLeft + PARTS[i].OffsetPartToRaw.Y = PARTS[i].RawOffset.dOverMatOut + end +end + +--------------------------------------------------------------------- +-- Crea il grezzo che verrà messo in macchina +--------------------------------------------------------------------- +local function CreateRaws( PARTS) + for i = 1, #PARTS do + -- si crea un grezzo "finto" (un cubo da 100mm) nell'origine del pezzo, verrà poi dimensionato uan volta adeguato con i vari sovramateriali + PARTS[i].idRaw = EgtAddRawPart( PARTS[i].frame:getOrigin(), 100, 100, 100, ColA) + EgtAddPartToRawPart( PARTS[i].id, ORIG(), PARTS[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( PARTS) + for i = 1, #PARTS do + -- allineo il pezzo all'interno del grezzo + local dRotX, dRotY, dRotZ = GetFixedAxesRotABCFromFrame( PARTS[i].frame) + -- se devo ruotare + if abs( dRotZ) > GEO.EPS_ANG_SMALL then + EgtRotatePartInRawPart( PARTS[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( PARTS[i].id, GDB_BB.ONLY_VISIBLE) + local dPartPosX = b3Part:getMin():getX() + local dPartPosY = b3Part:getMin():getY() + local dPartPosZ = b3Part:getMin():getZ() + local b3Raw = EgtGetRawPartBBox( PARTS[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( PARTS[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 *** +------------------------------------------------------------------------------------------------------------- + +-- 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 + + -- ordinamento dal pezzo più lungo + table.sort( PartsList, function( A, B) return A.dPartLength > B.dPartLength end) + + 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) + local VirtualList = WinLib.TableCopyDeep( Group) + table.insert( VirtualList, PartsList[j]) + bCanBeGrouped = bCanBeGrouped and WinData.VerifyPieces( VirtualList) + 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 + + 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 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( PARTS) + 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 + + diff --git a/CAMAuto/LuaLibs/WinLib.lua b/CAMAuto/LuaLibs/WinLib.lua index 64fc393..7990c5c 100644 --- a/CAMAuto/LuaLibs/WinLib.lua +++ b/CAMAuto/LuaLibs/WinLib.lua @@ -138,5 +138,39 @@ function WinLib.GetPathMinRadius( IdCurve) return dSmallRadius end +------------------------------------------------------------------------------------------------------------- +--- copia una tabella lua in modo ricorsivo, ossia mantiene indipendenti anche tutte le sottotabelle +--- ATTENZIONE: in caso di modifiche vanno gestiti anche i tipi custom; sarebbe meglio metterla nel LuaLibs +function WinLib.TableCopyDeep( OriginalTable) + -- controllo se oggetto passato è valido, altrimenti errore. Non deve mai succedere + if not OriginalTable then + error( "TableCopyDeep : can't copy nil object") + end + local CopiedTable = {} + for key, value in pairs( OriginalTable) do + if type( value) == "table" then + if isBBox3d( value) then + CopiedTable[ key] = BBox3d( value) + elseif isColor3d( value) then + CopiedTable[ key] = Color3d( value) + elseif isFrame3d( value) then + CopiedTable[ key] = Frame3d( value) + elseif isPoint3d( value) then + CopiedTable[ key] = Point3d( value) + elseif isQuaternion( value) then + CopiedTable[ key] = Quaternion( value) + elseif isVector3d( value) then + CopiedTable[ key] = Vector3d( value) + else + CopiedTable[ key] = BeamLib.TableCopyDeep( value) + end + else + CopiedTable[ key] = value + end + end + + return CopiedTable +end + ------------------------------------------------------------------------------------------------------------- return WinLib \ No newline at end of file