diff --git a/NestProcess.lua b/NestProcess.lua index be7ea92..ace4f83 100644 --- a/NestProcess.lua +++ b/NestProcess.lua @@ -17,6 +17,9 @@ EgtEnableDebug( false) 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) @@ -87,9 +90,9 @@ local function ExecMaximumFilling( Raw, Parts) EgtMaxFillerAddPart( i, Parts[i].Len, Parts[i].DispLen or Parts[i].Len, Parts[i].Cnt or 1) end -- Eseguo l'ottimizzazione - EgtStartCounter() + --EgtStartCounter() EgtMaxFillerCompute( Raw.LenToFill, Raw.StartGap, Raw.MidGap, Raw.EndGap, Raw.SortType) - local dTime = EgtStopCounter() + --local dTime = EgtStopCounter() -- Recupero i risultati local nFilledParts, nDiffParts, dTotFillRatio = EgtMaxFillerGetResults() local OneRes = {} @@ -97,7 +100,8 @@ local function ExecMaximumFilling( Raw, Parts) 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, Time=dTime, Data=OneRes} + return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, Data=OneRes} end -- Funzione per trovare nome MachGroup @@ -155,7 +159,6 @@ local nErrCnt = 0 local nWarnCnt = 0 -- Grezzi --- lista dei grezzi local Raws = {} -- creo tabella dei grezzi for nIndex, nLen in pairs( LEN) do @@ -165,8 +168,6 @@ for nIndex, nQty in pairs( QTY) do Raws[tonumber(nIndex)].Count = nQty end ---local nTotRaws = Raws.Count - -- Pezzi local Parts = {} @@ -176,13 +177,17 @@ for RawIndex = 1, #Raws do maxRawLenToFillNoStartGap = max( maxRawLenToFillNoStartGap, Raws[RawIndex].LenToFill - Raws[RawIndex].StartGap) end -- 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 - table.insert( Parts, {Id = nPartId, Len = Len, DispLen = DispLen, Cnt = nCount}) + for nCntIndex = 1 , nCount do + table.insert( Parts, {Id = nPartId, Len = Len, DispLen = DispLen, Cnt = 1}) + dTotLen = dTotLen + Len + end end end @@ -201,131 +206,247 @@ 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]) + +-- 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 -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}) + + -- 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 -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 + -- 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} + 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 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 --- 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 +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 - -- creo lista pezzi con pezzi lunghi e pezzi corti di questo Cycle - local PartsToNest = {} - for PartIndex = 1, #LongList do - table.insert( PartsToNest, LongList[PartIndex]) +-- 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 - 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 +local nRawTot = ResultList[BestResultIndex] _G.BEAM = {} BEAM.FILE = NEST.FILE BEAM.MACHINE = NEST.MACHINE