From 40580cdc69f65a3c0f1ceac43671c21d8616610f Mon Sep 17 00:00:00 2001 From: "luca.mazzoleni" Date: Mon, 11 May 2026 12:42:34 +0200 Subject: [PATCH] - NestProcess attuale rinominata Old per test nesting obliqui, con NestProcess ripulita --- NestProcess.lua | 476 +------------------------------------------- NestProcessOld.lua | 477 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 480 insertions(+), 473 deletions(-) create mode 100644 NestProcessOld.lua diff --git a/NestProcess.lua b/NestProcess.lua index aaace66..49a325f 100644 --- a/NestProcess.lua +++ b/NestProcess.lua @@ -1,477 +1,7 @@ --- BeamNestProcess.lua by Egaltech s.r.l. 2023/01/15 --- 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. --- 2023/01/15 Piccole correzioni. +-- BeamNestProcess.lua by Egalware s.r.l. 2026/05/11 +-- Gestione nesting automatico travi anche oblique -- 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) - --- flag per abilitare statistiche in log -local bLogStat = false - --- 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 - EgtMaxFillerCompute( Raw.LenToFill, Raw.StartGap, Raw.MidGap, Raw.EndGap, Raw.SortType) - -- 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} - return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, 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 -local Raws = {} --- creo tabella dei grezzi -for nIndex, nLen in pairs( LEN) do - Raws[tonumber(nIndex)] = {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 --- cerco il grezzo con la lunghezza maggiore, epurata dello start gap -local maxRawLenToFillNoStartGap = 0 -for RawIndex = 1, #Raws do - if Raws[RawIndex].Count > 0 then - maxRawLenToFillNoStartGap = max( maxRawLenToFillNoStartGap, Raws[RawIndex].LenToFill - Raws[RawIndex].StartGap) - end -end - --- Pezzi -local Parts = {} --- ciclo su pezzi per aggiungerli al nesting -local dTotLen = 0 -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 - for nCntIndex = 1 , nCount do - table.insert( Parts, {Id = nPartId, Len = Len, DispLen = DispLen, Cnt = 1}) - dTotLen = dTotLen + Len - end - 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 - --- lista dei risultati -local ResultList = {} -local BestResult = nil -local BestResultIndex = nil --- riordino lista pezzi per lunghezza -table.sort( Parts, function( B1, B2) return B1.Len < B2.Len end) - -local function NestSolutionByIndex( Index) - - -- creo copia lista raw - local TempRaws = {} - for TempRawIndex = 1, #Raws do - table.insert(TempRaws, {LenToFill = Raws[TempRawIndex].LenToFill, StartGap = Raws[TempRawIndex].StartGap, MidGap = Raws[TempRawIndex].MidGap, EndGap = Raws[TempRawIndex].EndGap, SortType = Raws[TempRawIndex].SortType, Count = Raws[TempRawIndex].Count}) - end - - -- recupero pezzi corti - local ShortList = {} - local LongList = {} - - for PartIndex = 1, #Parts do - if PartIndex <= Index then - table.insert( ShortList, Parts[PartIndex]) - else - table.insert( LongList, Parts[PartIndex]) - end - Parts[PartIndex].Cnt = 1 - end - -- numero di pezzi piccoli per barra - local ShortCount = Index - 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 dTotPartInRawLen = 0 - local nRawTot = 0 - local dRawTotLen = 0 - local dTime = 0 - local nCycle = 1 - local CurrResult = {} - while TotRawCount( TempRaws) > 0 and PartsToFill( Parts) > 0 do - - -- creo lista 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, #TempRaws do - if TempRaws[RawIndex].Count > 0 then - Results[RawIndex] = ExecMaximumFilling( TempRaws[RawIndex], PartsToNest) - else - Results[RawIndex] = { FillRatio = 0.001, LenToFill = 1000, DiffParts = 0} - 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) * TempRaws[ResultIndex].LenToFill - if Results[ResultIndex].DiffParts > 0 and 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 - -- riporto barra e pezzi nel risultato corrente - local CurrBar = { BarLen = TempRaws[nMinWasteRawIndex].LenToFill, Parts = {}} - local CurrX = TempRaws[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 - CurrPart = { Index = nInfoIndex, PartId = PartId, PosX = CurrX} - table.insert( CurrBar.Parts, CurrPart) - CurrX = CurrX + dLen + TempRaws[nMinWasteRawIndex].MidGap - nInfoIndex = nInfoIndex + 1 - end - end - table.insert( CurrResult, CurrBar) - dTotPartInRawLen = dTotPartInRawLen + ( Results[nMinWasteRawIndex].FillRatio * TempRaws[nMinWasteRawIndex].LenToFill) - nRawTot = nRawTot + 1 - dRawTotLen = dRawTotLen + TempRaws[nMinWasteRawIndex].LenToFill - -- Aggiorno per prossima iterazione - TempRaws[nMinWasteRawIndex].Count = TempRaws[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 - else - -- se non sono riuscito ad inserire alcun pezzo esco dal ciclo perche' non ci sono pezzi inseribili - break - end - nCycle = nCycle + 1 - end - -- riporto risultato in lista - ResultList[Index] = dTotPartInRawLen - if not BestResult or not BestResultIndex or - ( dTotPartInRawLen > ResultList[BestResultIndex] + 0.02 or ( abs( dTotPartInRawLen - ResultList[BestResultIndex]) < 0.02 and dRawTotLen < BestResult.RawTotLen - 0.02)) then - BestResult = CurrResult - BestResult.RawTotLen = dRawTotLen - BestResultIndex = Index - end -end - -local CycleCount = 0 - -local MinTime = 10 + pow( 3, ceil( log10( #Parts)) - 1) -if bLogStat then EgtOutLog('MinTime: ' .. MinTime ) end -local MaxTime = 30 + pow( 7, ceil( log10( #Parts)) - 1) -if bLogStat then EgtOutLog('MaxTime: ' .. MaxTime ) end -local TargetRatio = 0.98 -local dTargetRatioLen = TargetRatio * dTotLen -if bLogStat then EgtOutLog('TargetRatioLen: ' .. dTargetRatioLen ) end -local CurrTime = 0 - -local function NestSolutionFromSP( StartingPoint, OscillationStep) - -- ciclo sulle possibilita' da un punto di origine con uno step fisso - local CurrResultIndex = StartingPoint - NestSolutionByIndex( StartingPoint) - if OscillationStep == 0 then return end - local CycleIndex = 1 - local nOutOfBoundary = 0 - while nOutOfBoundary ~= 3 do - CurrTime = EgtStopCounter() / 1000 - if bLogStat then EgtOutLog('CurrTime: ' .. CurrTime ) end - if bLogStat then EgtOutLog('BestRatio: ' .. dTotLen / BestResult.RawTotLen ) end - -- se e' passato il tempo massimo, o e' passato il tempo minimo, ha inserito tutti i pezzi e la percentuale di utilizzo del materiale e' maggiore della soglia - if CurrTime > MaxTime or ( CurrTime > MinTime and ResultList[BestResultIndex] > dTotLen - 0.1 and ( dTotLen / BestResult.RawTotLen ) >= TargetRatio) then - if bLogStat then EgtOutLog('Brake') end - break - end - local bCurrOutOfBoundary = false - if CurrResultIndex < 0 then - bCurrOutOfBoundary = true - if nOutOfBoundary == 2 then - nOutOfBoundary = 3 - else - nOutOfBoundary = 1 - end - end - if CurrResultIndex > #Parts then - bCurrOutOfBoundary = true - if nOutOfBoundary == 1 then - nOutOfBoundary = 3 - else - nOutOfBoundary = 2 - end - end - if not bCurrOutOfBoundary and not ResultList[CurrResultIndex] then - NestSolutionByIndex( CurrResultIndex) - if bLogStat then EgtOutLog('CurrResultIndex: ' .. CurrResultIndex ) end - if bLogStat then EgtOutLog('Result: ' .. ResultList[CurrResultIndex]) end - CycleCount = CycleCount + 1 - end - CurrResultIndex = StartingPoint + EgtIf( CycleIndex % 2 == 0, (CycleIndex / 2) * OscillationStep, -( ( CycleIndex + 1) / 2) * OscillationStep ) - CycleIndex = CycleIndex + 1 - end -end - --- lancio calcolo -EgtStartCounter() -local StartingResult = floor( #Parts * 0.3) -if bLogStat then EgtOutLog('StartingResult: ' .. StartingResult ) end ---local Step = floor( #Parts / 10) * floor( log10( #Parts)) -local nDividendo = pow( 10, floor( log10( #Parts)) - 1) -nDividendo = EgtIf( nDividendo ~= 1, nDividendo, 10) -local Step = floor( #Parts / nDividendo) * floor( log10( #Parts)) -if bLogStat then EgtOutLog('Step: ' .. Step ) end -NestSolutionFromSP( StartingResult, Step) -if Step > 1 then - NestSolutionFromSP( StartingResult, 1) -end - --- creo gruppi di lavorazione per risultato -for MachGroupIndex = 1, #BestResult do - local CurrMachGroup = BestResult[ MachGroupIndex] - -- creo gruppo di lavorazione - local MachGroupName = NewMachGroupName() - nMachGroup = EgtAddMachGroup( MachGroupName) - EgtSetInfo( nMachGroup, "BARLEN", CurrMachGroup.BarLen) - 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 - for i = 1, #CurrMachGroup.Parts do - local CurrPart = CurrMachGroup.Parts[ i] - -- creo pezzo copia - local nPartDuploId = EgtDuploNew( CurrPart.PartId) - EgtSetInfo( nMachGroup, "PART" .. CurrPart.Index, nPartDuploId .. "," .. CurrPart.PosX) - end -end - --- creo grezzi per ogni gruppo di lavorazione -local nRawCnt = 0 -local nRawTot = ResultList[BestResultIndex] -_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') +EgtEnableDebug( false) \ No newline at end of file diff --git a/NestProcessOld.lua b/NestProcessOld.lua new file mode 100644 index 0000000..aaace66 --- /dev/null +++ b/NestProcessOld.lua @@ -0,0 +1,477 @@ +-- BeamNestProcess.lua by Egaltech s.r.l. 2023/01/15 +-- 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. +-- 2023/01/15 Piccole correzioni. + +-- 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) + +-- flag per abilitare statistiche in log +local bLogStat = false + +-- 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 + EgtMaxFillerCompute( Raw.LenToFill, Raw.StartGap, Raw.MidGap, Raw.EndGap, Raw.SortType) + -- 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} + return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, 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 +local Raws = {} +-- creo tabella dei grezzi +for nIndex, nLen in pairs( LEN) do + Raws[tonumber(nIndex)] = {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 +-- cerco il grezzo con la lunghezza maggiore, epurata dello start gap +local maxRawLenToFillNoStartGap = 0 +for RawIndex = 1, #Raws do + if Raws[RawIndex].Count > 0 then + maxRawLenToFillNoStartGap = max( maxRawLenToFillNoStartGap, Raws[RawIndex].LenToFill - Raws[RawIndex].StartGap) + end +end + +-- Pezzi +local Parts = {} +-- ciclo su pezzi per aggiungerli al nesting +local dTotLen = 0 +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 + for nCntIndex = 1 , nCount do + table.insert( Parts, {Id = nPartId, Len = Len, DispLen = DispLen, Cnt = 1}) + dTotLen = dTotLen + Len + end + 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 + +-- lista dei risultati +local ResultList = {} +local BestResult = nil +local BestResultIndex = nil +-- riordino lista pezzi per lunghezza +table.sort( Parts, function( B1, B2) return B1.Len < B2.Len end) + +local function NestSolutionByIndex( Index) + + -- creo copia lista raw + local TempRaws = {} + for TempRawIndex = 1, #Raws do + table.insert(TempRaws, {LenToFill = Raws[TempRawIndex].LenToFill, StartGap = Raws[TempRawIndex].StartGap, MidGap = Raws[TempRawIndex].MidGap, EndGap = Raws[TempRawIndex].EndGap, SortType = Raws[TempRawIndex].SortType, Count = Raws[TempRawIndex].Count}) + end + + -- recupero pezzi corti + local ShortList = {} + local LongList = {} + + for PartIndex = 1, #Parts do + if PartIndex <= Index then + table.insert( ShortList, Parts[PartIndex]) + else + table.insert( LongList, Parts[PartIndex]) + end + Parts[PartIndex].Cnt = 1 + end + -- numero di pezzi piccoli per barra + local ShortCount = Index + 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 dTotPartInRawLen = 0 + local nRawTot = 0 + local dRawTotLen = 0 + local dTime = 0 + local nCycle = 1 + local CurrResult = {} + while TotRawCount( TempRaws) > 0 and PartsToFill( Parts) > 0 do + + -- creo lista 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, #TempRaws do + if TempRaws[RawIndex].Count > 0 then + Results[RawIndex] = ExecMaximumFilling( TempRaws[RawIndex], PartsToNest) + else + Results[RawIndex] = { FillRatio = 0.001, LenToFill = 1000, DiffParts = 0} + 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) * TempRaws[ResultIndex].LenToFill + if Results[ResultIndex].DiffParts > 0 and 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 + -- riporto barra e pezzi nel risultato corrente + local CurrBar = { BarLen = TempRaws[nMinWasteRawIndex].LenToFill, Parts = {}} + local CurrX = TempRaws[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 + CurrPart = { Index = nInfoIndex, PartId = PartId, PosX = CurrX} + table.insert( CurrBar.Parts, CurrPart) + CurrX = CurrX + dLen + TempRaws[nMinWasteRawIndex].MidGap + nInfoIndex = nInfoIndex + 1 + end + end + table.insert( CurrResult, CurrBar) + dTotPartInRawLen = dTotPartInRawLen + ( Results[nMinWasteRawIndex].FillRatio * TempRaws[nMinWasteRawIndex].LenToFill) + nRawTot = nRawTot + 1 + dRawTotLen = dRawTotLen + TempRaws[nMinWasteRawIndex].LenToFill + -- Aggiorno per prossima iterazione + TempRaws[nMinWasteRawIndex].Count = TempRaws[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 + else + -- se non sono riuscito ad inserire alcun pezzo esco dal ciclo perche' non ci sono pezzi inseribili + break + end + nCycle = nCycle + 1 + end + -- riporto risultato in lista + ResultList[Index] = dTotPartInRawLen + if not BestResult or not BestResultIndex or + ( dTotPartInRawLen > ResultList[BestResultIndex] + 0.02 or ( abs( dTotPartInRawLen - ResultList[BestResultIndex]) < 0.02 and dRawTotLen < BestResult.RawTotLen - 0.02)) then + BestResult = CurrResult + BestResult.RawTotLen = dRawTotLen + BestResultIndex = Index + end +end + +local CycleCount = 0 + +local MinTime = 10 + pow( 3, ceil( log10( #Parts)) - 1) +if bLogStat then EgtOutLog('MinTime: ' .. MinTime ) end +local MaxTime = 30 + pow( 7, ceil( log10( #Parts)) - 1) +if bLogStat then EgtOutLog('MaxTime: ' .. MaxTime ) end +local TargetRatio = 0.98 +local dTargetRatioLen = TargetRatio * dTotLen +if bLogStat then EgtOutLog('TargetRatioLen: ' .. dTargetRatioLen ) end +local CurrTime = 0 + +local function NestSolutionFromSP( StartingPoint, OscillationStep) + -- ciclo sulle possibilita' da un punto di origine con uno step fisso + local CurrResultIndex = StartingPoint + NestSolutionByIndex( StartingPoint) + if OscillationStep == 0 then return end + local CycleIndex = 1 + local nOutOfBoundary = 0 + while nOutOfBoundary ~= 3 do + CurrTime = EgtStopCounter() / 1000 + if bLogStat then EgtOutLog('CurrTime: ' .. CurrTime ) end + if bLogStat then EgtOutLog('BestRatio: ' .. dTotLen / BestResult.RawTotLen ) end + -- se e' passato il tempo massimo, o e' passato il tempo minimo, ha inserito tutti i pezzi e la percentuale di utilizzo del materiale e' maggiore della soglia + if CurrTime > MaxTime or ( CurrTime > MinTime and ResultList[BestResultIndex] > dTotLen - 0.1 and ( dTotLen / BestResult.RawTotLen ) >= TargetRatio) then + if bLogStat then EgtOutLog('Brake') end + break + end + local bCurrOutOfBoundary = false + if CurrResultIndex < 0 then + bCurrOutOfBoundary = true + if nOutOfBoundary == 2 then + nOutOfBoundary = 3 + else + nOutOfBoundary = 1 + end + end + if CurrResultIndex > #Parts then + bCurrOutOfBoundary = true + if nOutOfBoundary == 1 then + nOutOfBoundary = 3 + else + nOutOfBoundary = 2 + end + end + if not bCurrOutOfBoundary and not ResultList[CurrResultIndex] then + NestSolutionByIndex( CurrResultIndex) + if bLogStat then EgtOutLog('CurrResultIndex: ' .. CurrResultIndex ) end + if bLogStat then EgtOutLog('Result: ' .. ResultList[CurrResultIndex]) end + CycleCount = CycleCount + 1 + end + CurrResultIndex = StartingPoint + EgtIf( CycleIndex % 2 == 0, (CycleIndex / 2) * OscillationStep, -( ( CycleIndex + 1) / 2) * OscillationStep ) + CycleIndex = CycleIndex + 1 + end +end + +-- lancio calcolo +EgtStartCounter() +local StartingResult = floor( #Parts * 0.3) +if bLogStat then EgtOutLog('StartingResult: ' .. StartingResult ) end +--local Step = floor( #Parts / 10) * floor( log10( #Parts)) +local nDividendo = pow( 10, floor( log10( #Parts)) - 1) +nDividendo = EgtIf( nDividendo ~= 1, nDividendo, 10) +local Step = floor( #Parts / nDividendo) * floor( log10( #Parts)) +if bLogStat then EgtOutLog('Step: ' .. Step ) end +NestSolutionFromSP( StartingResult, Step) +if Step > 1 then + NestSolutionFromSP( StartingResult, 1) +end + +-- creo gruppi di lavorazione per risultato +for MachGroupIndex = 1, #BestResult do + local CurrMachGroup = BestResult[ MachGroupIndex] + -- creo gruppo di lavorazione + local MachGroupName = NewMachGroupName() + nMachGroup = EgtAddMachGroup( MachGroupName) + EgtSetInfo( nMachGroup, "BARLEN", CurrMachGroup.BarLen) + 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 + for i = 1, #CurrMachGroup.Parts do + local CurrPart = CurrMachGroup.Parts[ i] + -- creo pezzo copia + local nPartDuploId = EgtDuploNew( CurrPart.PartId) + EgtSetInfo( nMachGroup, "PART" .. CurrPart.Index, nPartDuploId .. "," .. CurrPart.PosX) + end +end + +-- creo grezzi per ogni gruppo di lavorazione +local nRawCnt = 0 +local nRawTot = ResultList[BestResultIndex] +_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')