-- BeamNestProcess.lua by Egaltech s.r.l. 2021/06/14 -- Gestione nesting automatico travi -- 2022/10/05 Piccole modifiche per far funzionare correttamente i compilati -- 2022/10/06 Corretto bug che moltiplicava i pezzi se erano presenti più grezzi della stessa sezione -- Intestazioni require( 'EgtBase') _ENV = EgtProtectGlobal() EgtEnableDebug( false) -- Per test --NEST = {} --NEST.FILE = 'c:\\TechnoEssetre7\\EgtData\\Prods\\0010\\Bar_10_1.btl' --NEST.MACHINE = 'Essetre-90480019_MW' --NEST.FLAG = 3 local sLog = ' +++ BeamNestProcess : ' .. NEST.FILE .. ', ' .. NEST.MACHINE .. ', ' .. LEN["1"] EgtOutLog( sLog) -- Cancello file di log specifico local sLogFile = EgtChangePathExtension( NEST.FILE, '.txt') EgtEraseFile( sLogFile) -- Funzioni per scrittura su file di log specifico local function WriteErrToLogFile( nErr, sMsg, nRot, nCutId, nTaskId) local hFile = io.open( sLogFile, 'a') hFile:write( 'ERR=' .. tostring( nErr) .. '\n') hFile:write( sMsg .. '\n') hFile:write( 'ROT=' .. tostring( nRot or 0) .. '\n') hFile:write( 'CUTID=' .. tostring( nCutId or 0) .. '\n') hFile:write( 'TASKID=' .. tostring( nTaskId or 0) .. '\n') hFile:close() end local function WriteTimeToLogFile( dTime) local hFile = io.open( sLogFile, 'a') hFile:write( 'TIME=' .. EgtNumToString( dTime) .. '\n') hFile:close() end -- Funzione per gestire visualizzazione dopo errore local function PostErrView( nErr, sMsg) if nErr ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then EgtSetView( SCE_VD.ISO_SW, false) EgtZoom( SCE_ZM.ALL) EgtOutBox( sMsg, 'BatchProcess (err=' .. tostring( nErr) .. ')', 'ERRORS') end end -- Funzione per gestire visualizzazione dopo warning local function PostWarnView( nWarn, sMsg) if nWarn ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then EgtSetView( SCE_VD.ISO_SW, false) EgtZoom( SCE_ZM.ALL) EgtOutBox( sMsg, 'BatchProcess (wrn=' .. tostring( nWarn) .. ')', 'WARNINGS') end end -- Funzione per aggiornare dati ausiliari local function UpdateAuxData( sAuxFile) local bModif = false -- Se definito LOAD90, aggiorno local sLoad90 = EgtGetStringFromIni( 'AuxData', 'LOAD90', '', sAuxFile) if sLoad90 ~= '' then local BtlInfoId = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'BtlInfo') or GDB_ID.NULL EgtSetInfo( BtlInfoId, 'LOAD90', sLoad90) bModif = true end return bModif end local function PartsToFill( Parts) local nToFill = 0 for i = 1, #Parts do if Parts[i].Cnt > 0 then nToFill = nToFill + Parts[i].Cnt end end return nToFill end local function ExecMaximumFilling( Raw, Parts) -- Inizializzo maximum filler EgtMaxFillerStart() -- Inserisco i pezzi for i = 1, #Parts do EgtMaxFillerAddPart( i, Parts[i].Len, Parts[i].DispLen or Parts[i].Len, Parts[i].Cnt or 1) end -- Eseguo l'ottimizzazione EgtStartCounter() EgtMaxFillerCompute( Raw.LenToFill, Raw.StartGap, Raw.MidGap, Raw.EndGap, Raw.SortType) local dTime = EgtStopCounter() -- Recupero i risultati local nFilledParts, nDiffParts, dTotFillRatio = EgtMaxFillerGetResults() local OneRes = {} for i = 0, nDiffParts - 1 do local nPartId, nCount = EgtMaxFillerGetOneResult( i) table.insert( OneRes, { Id=nPartId, Count=nCount}) end return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, Time=dTime, Data=OneRes} 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 sMachGroupName = EgtGetMachGroupName(nMachGroupId) local nMachGroupName = tonumber(sMachGroupName) if nMachGroupName > nMaxMachGroup then nMaxMachGroup = nMachGroupName end nMachGroupId = EgtGetNextMachGroup(nMachGroupId) end return nMaxMachGroup + 1 end local function TotRawCount(Raws) local nTotRaws = 0 for RawIndex = 1, #Raws do nTotRaws = nTotRaws + Raws[RawIndex].Count end return nTotRaws end local function TotPartLen(Parts) local nTotPartLen = 0 for PartIndex = 1, #Parts do nTotPartLen = nTotPartLen + ( Parts[PartIndex].Len * Parts[PartIndex].Cnt) end return nTotPartLen end -- Imposto direttorio libreria specializzata per Travi EgtAddToPackagePath( NEST.BASEDIR .. '\\LuaLibs\\?.lua') -- Imposto la macchina corrente e verifico sia abilitata per la lavorazione delle Travi EgtSetCurrMachine( NEST.MACHINE) local sMachDir = EgtGetCurrMachineDir() if not EgtExistsFile( sMachDir .. '\\Beam\\BeamData.lua') then NEST.ERR = 12 NEST.MSG = 'Error not configured for beam machine : ' .. sMachine WriteErrToLogFile( NEST.ERR, NEST.MSG) PostErrView( NEST.ERR, NEST.MSG) return end -- Elimino direttori altre macchine e imposto direttorio macchina corrente per ricerca librerie EgtRemoveBaseMachineDirFromPackagePath() EgtAddToPackagePath( sMachDir .. '\\Beam\\?.lua') -- Inizializzo contatori errori e avvisi local nErrCnt = 0 local nWarnCnt = 0 -- Grezzi -- lista dei grezzi local Raws = {} -- creo tabella dei grezzi for nIndex, nLen in pairs( LEN) do table.insert(Raws, {LenToFill = nLen, StartGap = NEST.STARTOFFSET, MidGap = NEST.OFFSET, EndGap = 0, SortType = -1}) end for nIndex, nQty in pairs( QTY) do Raws[tonumber(nIndex)].Count = nQty end --local nTotRaws = Raws.Count -- Pezzi local Parts = {} -- cerco il grezzo con la lunghezza maggiore, epurata dello start gap local maxRawLenToFillNoStartGap = 0 for RawIndex = 1, #Raws do maxRawLenToFillNoStartGap = max( maxRawLenToFillNoStartGap, Raws[RawIndex].LenToFill - Raws[RawIndex].StartGap) end -- ciclo su pezzi per aggiungerli al nesting for nPartId, nCount in pairs( PART) do -- recupero lunghezza pezzo local Len = EgtGetInfo( nPartId, "L", 'd') local DispLen = EgtIf( Len <= 1000, 2000, 0) --EgtIf( Len <= 2000, max( 2000, 6000 - Len), 0) -- aggiungo il pezzo solo se ci sta nel grezzo più lungo a disposizione if Len < maxRawLenToFillNoStartGap then table.insert( Parts, {Id = nPartId, Len = Len, DispLen = DispLen, Cnt = nCount}) end end -- lunghezza totale pezzi local dTotPartLen = TotPartLen( Parts) -- calcolo media delle barre necessarie local NeededRawsForType = {} for RawIndex = 1, #Raws do NeededRawsForType[RawIndex] = min( ceil( dTotPartLen / Raws[RawIndex].LenToFill), Raws[RawIndex].Count) end local RawQtySum = 0 for NeededRawIndex = 1, #NeededRawsForType do RawQtySum = RawQtySum + NeededRawsForType[NeededRawIndex] end local MediumRawQty = ceil( RawQtySum / #NeededRawsForType) if MediumRawQty > 1 then MediumRawQty = MediumRawQty - 1 end -- recupero pezzi piu' corti di mille local ShortList = {} local LongList = {} for PartIndex = 1, #Parts do if Parts[PartIndex].Len <= 1000 then table.insert( ShortList, Parts[PartIndex]) else table.insert( LongList, Parts[PartIndex]) end end -- numero di pezzi piccoli per barra local ShortCount = 0 for ShortIndex = 1, #ShortList do ShortCount = ShortCount + ShortList[ShortIndex].Cnt end local ShortForRaw = floor( ShortCount / MediumRawQty) local ExtraShortForRaw = 0 if MediumRawQty > 0 then ExtraShortForRaw = fmod( ShortCount, MediumRawQty) end -- creo lista pezzi corti singoli local SingleShortList = {} for ShortIndex = 1, #ShortList do for ShortCount = 1, ShortList[ShortIndex].Cnt do table.insert( SingleShortList, {Id = ShortList[ShortIndex].Id, Len = ShortList[ShortIndex].Len, DispLen = ShortList[ShortIndex].DispLen, Cnt = 1}) end end -- li divido per le barre previste local RawsShortList = {} local RawIndex = 0 local ShortRawIndex = 0 for ShortIndex = 1, #SingleShortList do if ShortRawIndex > 0 then table.insert( RawsShortList[RawIndex], SingleShortList[ShortIndex]) ShortRawIndex = ShortRawIndex - 1 else table.insert( RawsShortList, {SingleShortList[ShortIndex]}) RawIndex = RawIndex + 1 ShortRawIndex = ShortForRaw + EgtIf( RawIndex <= ExtraShortForRaw, 1, 0) - 1 end end -- Ciclo fino ad esaurimento pezzi o barre local nRawTot = 0 local dTime = 0 local nCycle = 1 while TotRawCount( Raws) > 0 and PartsToFill( Parts) > 0 do -- creo lista pezzi con pezzi lunghi e pezzi corti di questo Cycle local PartsToNest = {} for PartIndex = 1, #LongList do table.insert( PartsToNest, LongList[PartIndex]) end for CycleIndex = 1, #RawsShortList do if CycleIndex <= nCycle then for PartIndex = 1, #RawsShortList[CycleIndex] do table.insert( PartsToNest, RawsShortList[CycleIndex][PartIndex]) end end end -- se non ci sono pezzi da nestare, esco if PartsToFill( PartsToNest) <= 0 then break end -- Eseguo ottimizzazione per ogni lunghezza di barra local Results = {} for RawIndex = 1, #Raws do if Raws[RawIndex].Count > 0 then Results[RawIndex] = ExecMaximumFilling( Raws[RawIndex], PartsToNest) else Results[RawIndex] = { FillRatio = 0.001, LenToFill = 1000} end end -- verifico quale e' quella con meno scarto local nMinWasteRawIndex = GDB_ID.NULL local dMinWaste = 100000 for ResultIndex = 1, #Results do if Results[ResultIndex] then local dWaste = (1 - Results[ResultIndex].FillRatio) * Raws[ResultIndex].LenToFill if dWaste < dMinWaste then dMinWaste = dWaste nMinWasteRawIndex = ResultIndex end end end -- verifico se ci sono pezzi if nMinWasteRawIndex > 0 and Results[nMinWasteRawIndex] and Results[nMinWasteRawIndex].DiffParts > 0 then -- creo gruppo di lavorazione local MachGroupName = NewMachGroupName() nMachGroup = EgtAddMachGroup( MachGroupName) EgtSetInfo( nMachGroup, "BARLEN", Raws[nMinWasteRawIndex].LenToFill) EgtSetInfo( nMachGroup, "MATERIAL", NEST.MATERIAL) EgtSetInfo( nMachGroup, "AUTONEST", 1) -- scrivo dati per variabili P di comunicazione con la macchina in gruppo di lavorazione EgtSetInfo( nMachGroup, "PRODID", NEST.PRODID) EgtSetInfo( nMachGroup, "PATTID", nMachGroup) -- Disegno i pezzi local CurrX = Raws[nMinWasteRawIndex].StartGap local nInfoIndex = 1 for i = 1, Results[nMinWasteRawIndex].DiffParts do local PartIndex = Results[nMinWasteRawIndex].Data[i].Id local PartId = PartsToNest[PartIndex].Id local dLen = PartsToNest[PartIndex].Len for j = 1, Results[nMinWasteRawIndex].Data[i].Count do -- creo pezzo copia local nPartDuploId = EgtDuploNew( PartId) EgtSetInfo( nMachGroup, "PART" .. nInfoIndex, nPartDuploId .. "," .. CurrX) CurrX = CurrX + dLen + Raws[nMinWasteRawIndex].MidGap nInfoIndex = nInfoIndex + 1 end end nRawTot = nRawTot + 1 -- Aggiorno per prossima iterazione Raws[nMinWasteRawIndex].Count = Raws[nMinWasteRawIndex].Count - 1 for i = 1, Results[nMinWasteRawIndex].DiffParts do local PartId = Results[nMinWasteRawIndex].Data[i].Id PartsToNest[PartId].Cnt = PartsToNest[PartId].Cnt - Results[nMinWasteRawIndex].Data[i].Count end end nCycle = nCycle + 1 end -- creo grezzi per ogni gruppo di lavorazione local nRawCnt = 0 _G.BEAM = {} BEAM.FILE = NEST.FILE BEAM.MACHINE = NEST.MACHINE BEAM.FLAG = 6 -- CREATE_PANEL BEAM.BASEDIR = NEST.BASEDIR nMachGroup = EgtGetFirstMachGroup() while nMachGroup do local nNextMachGroup = EgtGetNextMachGroup( nMachGroup) EgtSetCurrMachGroup( nMachGroup) if EgtGetInfo( nMachGroup, "AUTONEST",'i') == 1 then EgtRemoveInfo( nMachGroup, "AUTONEST") EgtSetInfo( nMachGroup, "UPDATEUI", 1) local bOk, sErr = pcall( dofile, BEAM.BASEDIR .. "\\BatchProcessNew.lua") if not bOk then EgtOutLog( 'Error in BatchProcessNew.lua call (' .. ( sErr or '') ..')') end nRawCnt = nRawCnt + 1 -- aggiorno interfaccia EgtProcessEvents( 200 + ( nRawCnt / nRawTot * 100), 0) end nMachGroup = nNextMachGroup end EgtResetCurrMachGroup() NEST.ERR = 0 EgtOutLog( ' +++ BeamNestProcess completed')