357 lines
12 KiB
Lua
357 lines
12 KiB
Lua
-- 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')
|