-- BatchProcessNew.lua by Egalware s.r.l. 2025/04/24 -- Intestazioni require( 'EgtBase') _ENV = EgtProtectGlobal() EgtEnableDebug( false) -- TODO DA CANCELLARE!!!! quando verrà passato automaticamente da programma local WIN = {} WIN.BASEDIR = 'C:\\EgtData\\Window\\CAMAuto' WIN.FILE = 'C:\\Temp\\Window.nge' -- Imposto direttorio libreria specializzata per serramenti EgtAddToPackagePath( WIN.BASEDIR .. '\\LuaLibs\\?.lua') -- Imposto direttorio strategie. N.B. Le strategie dovranno essere caricate con il nome del direttorio padre EgtAddToPackagePath( WIN.BASEDIR .. '\\Strategies\\?.lua') local sTxtLogFile = EgtChangePathExtension( WIN.FILE, '.txt') EgtOpenFile( WIN.FILE) -- 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 _G.package.loaded.WinData = nil _G.package.loaded.WinExec = nil _G.package.loaded.WinLib = nil _G.package.loaded.FeatureData = nil _G.package.loaded.MachiningLib = nil local WinExec = require( 'WinExec') -- Carico i dati globali local WinData = require( 'WinData') -- Variabili globali PARTS = {} -- tabella contenente tutte le informazioni di ogni pezzo -- Colore del grezzo local ColA = Color3d( 255, 165, 0, 30) ------------------------------------------------------------------------------------------------------------- local function GetDataConfig() -- recupero utensili dal magazzino WinExec.GetToolsFromDB() -- TODO da gestire eventuali errori bloccanti return true end ------------------------------------------------------------------------------------------------------------- local function WriteErrToLogFile( nErr, sMsg, nRot, idCut, idTask) local hFile = io.open( sTxtLogFile, 'a') hFile:write( 'ERR=' .. tostring( nErr) .. '\n') hFile:write( sMsg .. '\n') hFile:write( 'ROT=' .. tostring( nRot or 0) .. '\n') hFile:write( 'CUTID=' .. tostring( idCut or 0) .. '\n') hFile:write( 'TASKID=' .. tostring( idTask or 0) .. '\n') hFile:close() end ------------------------------------------------------------------------------------------------------------- -- *** Recupero i pezzi da processare *** ------------------------------------------------------------------------------------------------------------- local function MyProcessInputData() -- Recupero le travi selezionate local nId = EgtGetFirstInGroup( GDB_ID.ROOT) while nId do -- Per capire se è un pezzo controllo una nota, servirebbe info dedicata, oppure essere sicuri che vengano passati solo pezzi if EgtGetInfo( nId, 'OFFY_1', 'd') then table.insert( PARTS, { id = nId, sName = ( EgtGetName( nId) or ( 'Id=' .. tonumber( nId)))}) -- TODO RIMUOVERE! Bisogna sempre prendere tuttti i pezzi all'interno del file. Devono arrivare solo i pezzi da mettere nella macchinata if #PARTS == 2 then break end end nId = EgtGetNext( 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) sOut = sOut .. PARTS[i].sName .. ', ' end sOut = sOut:sub( 1, -3) EgtOutLog( 'Pezzi selezionati : ' .. sOut, 1) end EgtDeselectAll() return true end ------------------------------------------------------------------------------------------------------------- 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 ------------------------------------------------------------------------------------------------------------- local function GetDispOffsetFromInput( nPieceIndex) -- assegno alle stringhe i valori letti, in modo che vengano proposti quelli nel dialogo local sOffYPh1 = EgtIf( PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetY, tostring( PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetY), tostring( PARTS[nPieceIndex].dPartWidth / 2)) local sOffZPh1 = EgtIf( PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetZ, tostring( PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetZ), '0') local sOffYPh2 = EgtIf( PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetY, tostring( PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetY), tostring( PARTS[nPieceIndex].dPartWidth / 2)) local sOffZPh2 = EgtIf( PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetZ, tostring( PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetZ), '0') local vInp = EgtDialogBox( 'Dati di disposizione pezzo: ' .. PARTS[nPieceIndex].sName, {'Sporgenza laterale FASE1', sOffYPh1}, {'Posizione Z FASE1', sOffZPh1}, {'Sporgenza laterale FASE2', sOffYPh2}, {'Posizione Z FASE2', sOffZPh2}) if not vInp or #vInp == 0 then return false end -- salvo input nei valori che utilizzerò dopo PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetY = tonumber( vInp[1]) PARTS[nPieceIndex].DispOffsets.Phase1.dOffsetZ = tonumber( vInp[2]) PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetY = tonumber( vInp[3]) PARTS[nPieceIndex].DispOffsets.Phase2.dOffsetZ = tonumber( vInp[4]) return true end --------------------------------------------------------------------- -- Crea il grezzo che verrà messo in macchina --------------------------------------------------------------------- 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 ------------------------------------------------------------------------------------------------------------- -- *** Inserimento delle travi nel grezzo *** ------------------------------------------------------------------------------------------------------------- local function MyProcessPieces() -- creo macchinata local MachGroupName = NewMachGroupName() local nMachGroup = EgtAddMachGroup( MachGroupName) -- si crea il grezzo CreateRaws( PARTS) -- allineo i pezzi come orientamento richiesto dalla macchina AlignRawsToTable( PARTS) -- aggiungo sovramateriale ai grezzi AddOverMaterialToRaw( PARTS) -- recupero offset per posizionamento for i = 1, #PARTS do PARTS[i].DispOffsets = {} PARTS[i].DispOffsets.Phase1 = {} PARTS[i].DispOffsets.Phase2 = {} local bInsertedAllOffs = GetDispOffsetFromNotes( i) -- se non sono settati nelle note, li chiedo if not bInsertedAllOffs then bInsertedAllOffs = GetDispOffsetFromInput( i) end -- se non sono stati inseriti o c'è stato un errore esco subito if not bInsertedAllOffs then return false end end -- 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 EgtOutBox( sErr, 'ProcessWin', 'ERROR', 'OK') return false end -- Impostazione dell'attrezzaggio di default local bOk = EgtImportSetup() if not bOk then EgtImportSetup( 'Default') end return true end ------------------------------------------------------------------------------------------------------------- -- *** Inserimento delle lavorazioni *** ------------------------------------------------------------------------------------------------------------- local function MyProcessFeatures() local bOk = WinExec.ProcessFeatures( PARTS) return true end ------------------------------------------------------------------------------------------------------------- -- *** Esecuzione *** ------------------------------------------------------------------------------------------------------------- -- script principale local sFlag = '' if WIN.FLAG == 0 then sFlag = 'CHECK+GENERATE' else sFlag = 'FLAG='..tostring( WIN.FLAG) end local sLog = 'BatchProcess : ' .. WIN.FILE .. ', ' .. ( WIN.MACHINE or EgtGetCurrMachineName()) .. ', ' .. sFlag EgtOutLog( sLog) -- Se da elaborare EgtOutLog( ' +++ Processing Parts >>>') -- nascondo geometrie varie local vAuxId = { EgtGetFirstNameInGroup( GDB_ID.ROOT, 'Profile'), EgtGetFirstNameInGroup( GDB_ID.ROOT, 'Area*'), EgtGetFirstNameInGroup( GDB_ID.ROOT, 'Aux')} EgtSetStatus( vAuxId, GDB_ST.OFF) if not MyProcessInputData() then return end if not MyProcessPieces() then return end if not GetDataConfig() then return end -- Abilito Vmill EgtSetInfo( EgtGetCurrMachGroup(), 'Vm', '1') if not MyProcessFeatures() then return end -- Imposto Nome file CN local _, sName, _ = EgtSplitPath( WIN.FILE) EgtSetInfo( EgtGetCurrMachGroup(), 'NcName', sName .. '.cnc') -- Salvo il progetto EgtSaveFile( WIN.FILE) -- *** Eseguo simulazione con verifica collisione in cieco *** local bSimOk, nErr, sErr = EgtSimulate() if not bSimOk then if nErr == MCH_SHE.INIT then WIN.ERR = 19 WIN.MSG = 'Error starting simulation' elseif nErr == MCH_SHE.COLLISION then WIN.ERR = 22 WIN.MSG = 'Head-part collision' elseif nErr == MCH_SHE.OUTSTROKE then WIN.ERR = 23 WIN.MSG = 'Axis outstroke ' .. sErr elseif nErr == MCH_SHE.SPECIAL then WIN.ERR = 24 WIN.MSG = 'Special error ' .. sErr else WIN.ERR = 25 WIN.MSG = 'General failure (contact supplier)' end WIN.ROT = 0 WIN.CUTID = 0 WIN.TASKID = 0 local vItem = EgtSplitString( sErr, ';') or {} for i = 1, #vItem do vItem[i] = EgtTrim( vItem[i] or '') end 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 = WinLib.TableCopyDeep( RESULT) WriteResultToJson( RESULT) return end -- *** Genero programma CN *** ( se richiesto) local bIsGenerationEnabled = ( EgtVerifyKeyOption( 110) == false) if bIsGenerationEnabled then EgtOutLog( ' +++ Generating NC part program >>>') -- TODO gestire generazione end -- *** Eseguo stima tempi *** EgtOutLog( ' +++ Estimating T&L >>>') if not EgtEstimate( '', 'EgtCAM5 - ' .. WIN.FILE) then -- TODO gestire errori in stima end EgtOutLog( ' +++ BatchProcess completed')