Files
databeamnew/NestProcess.lua
T

659 lines
23 KiB
Lua

-- BeamNestProcess.lua by Egalware s.r.l. 2026/05/11
-- Gestione nesting automatico travi anche oblique
-- Intestazioni
require( 'EgtBase')
_ENV = EgtProtectGlobal()
EgtEnableDebug( false)
-- Include
local BeamData = require( 'BeamDataNew')
local BeamLib = require( 'BeamLib')
-- 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 : machine' .. sMachine .. 'note configured for Beam'
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')
----------------------------------------------------------------------------------------------------------
-- Parametri di configurazione Nesting
local CONFIG = {
-- Strategia di ricerca
NUM_LONGEST_CANDIDATES = 3,
-- Punteggi
BONUS_PERFECT_FIT = 500, -- Se lo scarto è quasi zero (es: < 5mm)
BONUS_SHARED_CUT = 100, -- Se le inclinazioni combaciano perfettamente
PENALTY_TOO_SHORT = 100, -- Se lo scarto è troppo piccolo per essere utile ma non zero
PENALTY_BAD_FIT_END_PHASE = 1000, -- Se siamo alla fine (>80%) e si utilizza un pezzo corto con fit mediocre
-- Macchina
MIN_USABLE_REMNANT = BeamData.MINRAW_S, -- Sotto questa misura lo scarto è considerato "sfrido"
MAX_REMNANT_PERFECT_FIT = 20,
BLADE_THICKNESS = 5.4, -- TODO questo deve arrivare da interfaccia o da automatismo!!!!!
MIN_FILLER_LIMIT = ( BeamData.MINRAW_S + BeamData.MINRAW_L) / 2
}
----------------------------------------------------------------------------------------------------------
-- variabili per tutto il modulo
local dMaxJobLength = 0
local dMaxFillerLength = 0
local dGlobalProgress = 0
local t_insert = table.insert -- si mette via il riferimento locale per evitare continui lookup
local JobPool = {}
----------------------------------------------------------------------------------------------------------
-- inventario grezzi
local RawInventory = {
Stock = {},
ActiveBeams = {}
}
function RawInventory:GetNewBeam( dLength)
local NewBeam = {
dTotalLength = dLength,
dResidualLength = dLength - ( NEST.STARTOFFSET or 0),
LastOffsetX = { 0, 0, 0, 0},
LastVtN = Vector3d( 1, 0, 0),
vtNXabs = 1,
dLastHeadRecess = 0,
NestedParts = {}
}
return NewBeam
end
function RawInventory:BuildStock()
if #LEN ~= #QTY then
error( 'NestProcess: invalid stock data')
end
for i = 1, #LEN do
self.Stock[#self.Stock + 1] = {
dLength = LEN[i],
nCount = QTY[i]
}
end
return RawInventory
end
-- aggiunge una nuova barra attiva rimuovendo la corrispondente dalla lista stock disponibili
function RawInventory:AddActiveBeam( nStockIndex)
local CurrentStock = self.Stock[nStockIndex]
-- se barra disponibile posso aggiungerla a quelle attive
if CurrentStock and CurrentStock.nCount > 0 then
-- update quantità a stock
CurrentStock.nCount = CurrentStock.nCount - 1
-- aggiungo una nuova barra attiva
local NewBeam = self:GetNewBeam( CurrentStock.dLength)
t_insert( self.ActiveBeams, NewBeam)
return NewBeam
end
return nil
end
-- auditor della bontà del nesting calcolato. Scrive nel log i risultati.
function RawInventory:PrintDiagnosticReport()
local nBeamsUsed = #self.ActiveBeams
local dTotalStockLength = 0
local dTotalPartLength = 0
local dTotalScrap = 0
local dTotalUsableRemnants = 0
-- si contano i pezzi davvero nestati
local nActualNestedParts = 0
for i = 1, #JobPool do
if JobPool[i].bNested then
nActualNestedParts = nActualNestedParts + 1
end
end
local nUnnestedParts = #JobPool - nActualNestedParts
for i = 1, nBeamsUsed do
local Beam = self.ActiveBeams[i]
dTotalStockLength = dTotalStockLength + Beam.dTotalLength
-- Classificazione del residuo rimasto in fondo alla barra
if Beam.dResidualLength >= CONFIG.MIN_USABLE_REMNANT then
dTotalUsableRemnants = dTotalUsableRemnants + Beam.dResidualLength
else
dTotalScrap = dTotalScrap + Beam.dResidualLength
end
-- Somma delle lunghezze dei pezzi reali inseriti
for j = 1, #Beam.NestedParts do
local Part = Beam.NestedParts[j]
dTotalPartLength = dTotalPartLength + Part.dLength
end
end
-- Calcolo efficienze percentuali con protezione per divisione per zero
local dGrossEfficiency = 0
if dTotalStockLength > 0 then
dGrossEfficiency = (dTotalPartLength / dTotalStockLength) * 100
end
local dNetEfficiency = 0
local dNetDenominator = dTotalStockLength - dTotalUsableRemnants
if dNetDenominator > 0 then
dNetEfficiency = (dTotalPartLength / dNetDenominator) * 100
end
-- Stampa tramite il sistema di logging proprietario EgtLog
EgtOutLog("==========================================================")
EgtOutLog(" EGALWARE NESTING ENGINE DIAGNOSTIC ")
EgtOutLog("==========================================================")
EgtOutLog(string.format("Total Parts Demanded : %d", #JobPool))
EgtOutLog(string.format("Parts Successfully Nested: %d", nActualNestedParts))
-- Se ci sono pezzi rimasti fuori, spariamo un alert visibile nel log
if nUnnestedParts > 0 then
EgtOutLog(string.format("WARNING: Unnested Parts : %d <<<<<< MATERIAL SHORTAGE!", nUnnestedParts))
else
EgtOutLog("All Parts Nested : YES (Commessa completata)")
end
EgtOutLog(string.format("Total Bars Used : %d", nBeamsUsed))
EgtOutLog(string.format("Total Material Cut : %.2f mm", dTotalPartLength))
EgtOutLog("----------------------------------------------------------")
EgtOutLog(string.format("Gross Yield (Rendimento): %.2f%%", dGrossEfficiency))
EgtOutLog(string.format("Net Yield (Riutilizzato): %.2f%%", dNetEfficiency))
EgtOutLog("----------------------------------------------------------")
EgtOutLog(string.format("Total Usable Remnants : %.2f mm (Safe for Clamps)", dTotalUsableRemnants))
EgtOutLog(string.format("Total Pure Scrap (Sfrido): %.2f mm (Trash Zone)", dTotalScrap))
EgtOutLog("==========================================================")
end
----------------------------------------------------------------------------------------------------------
-- PartTemplates (informazioni geometriche pezzi univoci)
-- JobPool (lista di tutti i singoli pezzi, multipli compresi, da nestare)
local PartTemplates = {}
function PartTemplates:GetInfoFromPart( id, sStateWithSide)
local OffsetX = {}
local vtN
local sInfo = EgtGetInfo( id, sStateWithSide)
if not sInfo then
return
end
local Info = EgtSplitString( sInfo, ';')
OffsetX = EgtSplitString( Info[1], ',')
vtN = VectorFromString( Info[2])
for i = 1, #OffsetX do
OffsetX[i] = tonumber( OffsetX[i]) or 0
end
return OffsetX, vtN
end
function PartTemplates:AddPart( id)
self[id] = {}
self[id].dLength = EgtGetInfo( id, 'L', 'd')
self[id].States = {}
self[id].dMaxGlobalTailRecess = 0
-- si tiene via la lunghezza del pezzo massimo
if self[id].dLength > dMaxJobLength + 10 * GEO.EPS_SMALL then
dMaxJobLength = self[id].dLength
end
local States = { '1000', '0010', '1000_INV', '0010_INV' }
for _, sState in ipairs(States) do
local OffsetXHead, vtNHead = self:GetInfoFromPart( id, 'ALT' .. sState .. '_H')
local OffsetXTail, vtNTail = self:GetInfoFromPart( id, 'ALT' .. sState .. '_T')
if OffsetXHead or OffsetXTail then
if not OffsetXHead then
OffsetXHead = { 0, 0, 0, 0}
vtNHead = Vector3d( 1, 0, 0)
end
if not OffsetXTail then
OffsetXTail = { 0, 0, 0, 0}
vtNTail = Vector3d( -1, 0, 0)
end
self[id].States[sState] = {}
local State = self[id].States[sState]
State.Head = {
OffsetX = OffsetXHead,
vtN = vtNHead,
vtNXabs = abs( vtNHead:getX())
}
State.Tail = {
OffsetX = OffsetXTail,
vtN = vtNTail,
vtNXabs = abs( vtNTail:getX())
}
-- overlap massimi in testa e in coda per questo pezzo
local dMaxHeadRecess = 0
for i = 1, 4 do
if State.Head.OffsetX[i] > dMaxHeadRecess + 10 * GEO.EPS_SMALL then
dMaxHeadRecess = State.Head.OffsetX[i]
end
end
State.dMaxHeadRecess = dMaxHeadRecess
local dMaxTailRecess = 0
for i = 1, 4 do
if abs( State.Tail.OffsetX[i]) > dMaxTailRecess + 10 * GEO.EPS_SMALL then
dMaxTailRecess = abs( State.Tail.OffsetX[i])
end
end
State.dMaxTailRecess = dMaxTailRecess
if dMaxTailRecess > self[id].dMaxGlobalTailRecess + 10 * GEO.EPS_SMALL then
self[id].dMaxGlobalTailRecess = dMaxTailRecess
end
end
end
end
-- ordinamento JobPool per lunghezza decrescente dei pezzi
function JobPool:Sort()
local function SortByLengthDescending( Part1, Part2)
local dLength1 = PartTemplates[Part1.id].dLength
local dLength2 = PartTemplates[Part2.id].dLength
if dLength1 > dLength2 + 10 * GEO.EPS_SMALL then
return true
elseif dLength2 > dLength1 + 10 * GEO.EPS_SMALL then
return false
end
-- tie breaker
return Part1.id < Part2.id
end
table.sort( self, SortByLengthDescending)
end
-- creazione combinata (si cicla una sola volta) di entrambe le tabelle
local function BuildPartTemplatesAndJobPool()
for id, nCount in pairs( PART) do
PartTemplates:AddPart( id)
for _ = 1, nCount do
t_insert( JobPool, { id = id, bNested = false})
end
end
return PartTemplates, JobPool
end
----------------------------------------------------------------------------------------------------------
-- calcolo singola Move, ossia combinazione barra-pezzo-stato, con relativo punteggio
local function CalculateMove( Beam, dPartLength, sState, State)
local Move = {}
-- calcolo overlap pezzi (riduzione di lunghezza occupata)
local dSafeOverlap = GEO.INFINITO
for i = 1, 4 do
local dCornerDistance = Beam.LastOffsetX[i] - State.Tail.OffsetX[i]
if dCornerDistance < dSafeOverlap then
dSafeOverlap = dCornerDistance
end
end
-- il massimo overlap va ridotto dello spessore lama
local dProjectedBlade = CONFIG.BLADE_THICKNESS / min( Beam.vtNXabs, State.Tail.vtNXabs)
dSafeOverlap = dSafeOverlap - dProjectedBlade
-- lunghezza barra rimasta (se negativo non ci sta)
local dFutureResidualLength = Beam.dResidualLength + dSafeOverlap - dPartLength
if dFutureResidualLength < -10 * GEO.EPS_SMALL then
return nil
end
-- Calcolo punteggio
-- All'inizio (Ratio=1) si dà vantaggio ai pezzi più lunghi. Alla fine (Ratio=0) si dà vantaggio ai best fit.
local dEfficiency
-- barra nuova, si valuta l'efficienza prospettica
if #Beam.NestedParts == 0 then
-- È una barra vergine dallo stock! Valutiamo la sua efficienza PROSPETTICA
-- Quanti pezzi di questa lunghezza ci starebbero al massimo?
local nMaxPieces = math.floor(Beam.dResidualLength / dPartLength)
if nMaxPieces == 0 then nMaxPieces = 1 end
local dUltimateUsed = nMaxPieces * dPartLength
dEfficiency = dUltimateUsed / Beam.dTotalLength
-- barra avviata: efficienza reale
else
dEfficiency = dPartLength / ( dPartLength - dSafeOverlap)
end
local dNormalizedLength = dPartLength / dMaxJobLength
local dLengthScore = dNormalizedLength * ( 1 - dGlobalProgress) * 1000
local dEfficiencyScore = dEfficiency * dGlobalProgress * 1000
local dScore = dLengthScore + dEfficiencyScore
-- Bonus Perfect Fit
if dFutureResidualLength < CONFIG.MAX_REMNANT_PERFECT_FIT then
dScore = dScore + CONFIG.BONUS_PERFECT_FIT
-- Penalità Sliver (si evitano sfridi non riutilizzabili)
elseif dFutureResidualLength < CONFIG.MIN_USABLE_REMNANT then
dScore = dScore - CONFIG.PENALTY_TOO_SHORT
end
-- Bonus Shared Cut: se le normali sono opposte, si risparmia un taglio/posizionamento
if AreOppositeVectorApprox( Beam.LastVtN, State.Tail.vtN) then
dScore = dScore + CONFIG.BONUS_SHARED_CUT
end
-- Protezione finale pezzi corti: se siamo alla fine e il pezzo è corto, penalizziamo se non è un fit quasi perfetto
if ( dGlobalProgress >= 0.8) and ( dPartLength < dMaxFillerLength) and ( dFutureResidualLength > CONFIG.MAX_REMNANT_PERFECT_FIT) then
dScore = dScore - CONFIG.PENALTY_BAD_FIT_END_PHASE
end
Move = {
sState = sState,
dScore = dScore,
dSafeOverlap = dSafeOverlap,
dFutureResidualLength = dFutureResidualLength
}
return Move
end
----------------------------------------------------------------------------------------------------------
-- trova i migliori pezzi da inserire nella trave (N pezzi più lunghi e 2 pezzi di lunghezza più adeguata al restante della barra) e i migliori stati in cui metterli
local function FindBestPartForBeam( Beam)
local nLongestParts = 0
local Candidates = {}
local JobsAlreadyInCandidates = {}
local BestFitJob1 = { Job = nil, dGap = GEO.INFINITO }
local BestFitJob2 = { Job = nil, dGap = GEO.INFINITO }
-- 1 Scelta candidati --
for i = 1, #JobPool do
local Job = JobPool[i]
if not Job.bNested then
local PartTemplate = PartTemplates[Job.id]
local dGap = Beam.dResidualLength + Beam.dLastHeadRecess + PartTemplate.dMaxGlobalTailRecess - PartTemplate.dLength
if dGap > - 10 * GEO.EPS_SMALL then
-- JobPool è già ordinata: i primi N pezzi che entrano sono i più lunghi
if nLongestParts < CONFIG.NUM_LONGEST_CANDIDATES then
t_insert( Candidates, Job)
JobsAlreadyInCandidates[Job] = true
nLongestParts = nLongestParts + 1
end
-- si cercano i due pezzi con il best fit nella barra restante
if dGap < BestFitJob1.dGap then
-- il bestfit1 è il nuovo bestfit 2
BestFitJob2.Job = BestFitJob1.Job
BestFitJob2.dGap = BestFitJob1.dGap
-- la job corrente è la nuova bestfit1
BestFitJob1.dGap = dGap
BestFitJob1.Job = Job
elseif dGap < BestFitJob2.dGap then
BestFitJob2.Job = Job
BestFitJob2.dGap = dGap
end
end
end
end
-- i pezzi bestfit si aggiungono solo se non corrispondono a pezzi già inseriti
if BestFitJob1.Job and not JobsAlreadyInCandidates[BestFitJob1.Job] then
table.insert( Candidates, BestFitJob1.Job)
JobsAlreadyInCandidates[BestFitJob1.Job] = true
end
if BestFitJob2.Job and not JobsAlreadyInCandidates[BestFitJob2.Job] then
table.insert( Candidates, BestFitJob2.Job)
JobsAlreadyInCandidates[BestFitJob2.Job] = true
end
-- 2 Scelta miglior candidato --
local dHighestScore = -GEO.INFINITO
local BestMove
for i = 1, #Candidates do
local Candidate = Candidates[i]
local Template = PartTemplates[Candidate.id]
local dPartLength = Template.dLength
local dHighestCandidateScore = -GEO.INFINITO
local BestCandidateMove
-- si trova la Move migliore del singolo candidato (a CalculateMove si passano gli argomenti precalcolati per evitare di rallentare il calcolo)
for sState, State in pairs( Template.States) do
local Move = CalculateMove( Beam, dPartLength, sState, State)
if Move and Move.dScore > dHighestCandidateScore + 10 * GEO.EPS_SMALL then
BestCandidateMove = Move
dHighestCandidateScore = Move.dScore
BestCandidateMove.Job = Candidate
end
end
-- si trova la Move migliore in assoluto
if dHighestCandidateScore > dHighestScore + 10 * GEO.EPS_SMALL then
BestMove = BestCandidateMove
dHighestScore = dHighestCandidateScore
end
end
return BestMove
end
----------------------------------------------------------------------------------------------------------
-- Esegue la mossa scelta: aggiorna lo stato della trave e segna il pezzo come nestato
local function CommitBestMove( BestMove)
local Beam
-- recupero o creazione della barra
if BestMove.nActiveBeamIndex then
Beam = RawInventory.ActiveBeams[BestMove.nActiveBeamIndex]
elseif BestMove.nStockIndex then
Beam = RawInventory:AddActiveBeam( BestMove.nStockIndex)
end
if not Beam then return end
-- recupero dati pezzo e stato
local Job = BestMove.Job
local Template = PartTemplates[Job.id]
local State = Template.States[BestMove.sState]
-- update geometria barra
-- la nuova faccia della barra è ora la testa (Head) del pezzo appena inserito
Beam.dResidualLength = BestMove.dFutureResidualLength
Beam.LastOffsetX = State.Head.OffsetX
Beam.LastVtN = State.Head.vtN
Beam.vtNXabs = abs( State.Head.vtN:getX())
Beam.dLastHeadRecess = State.dMaxHeadRecess
-- registrazione pezzo
t_insert( Beam.NestedParts, {
id = Job.id,
sState = BestMove.sState,
dSafeOverlap = BestMove.dSafeOverlap,
dLength = Template.dLength,
dPosX = BestMove.dFutureResidualLength
})
-- chiusura job
Job.bNested = true
end
----------------------------------------------------------------------------------------------------------
-- script principale
-- preparazione tabelle lista grezzi (RawInventory), lista pezzi univoci (PartTemplates) e lista singoli pezzi da nestare (JobPool)
RawInventory:BuildStock()
BuildPartTemplatesAndJobPool()
JobPool:Sort()
-- calcolo lunghezza massima pezzi "filler"
local nTotalParts = #JobPool
local nFillerIndex = floor( nTotalParts * 0.8) + 1
if nFillerIndex > nTotalParts then nFillerIndex = nTotalParts end
dMaxFillerLength = PartTemplates[JobPool[nFillerIndex].id].dLength
dMaxFillerLength = EgtClamp( dMaxFillerLength, CONFIG.MIN_FILLER_LIMIT, BeamData.LEN_SHORT_PART)
-- loop principale: scorre le barre, sia già attive che a stock, e le riempie nel modo migliore possibile
-- per ogni giro sceglie la soluzione con punteggio più alto
local nDoneParts = 0
local VirtualBeam = RawInventory:GetNewBeam( 0)
while true do
local BestMove
local dHighestScore = -GEO.INFINITO
-- progresso calcolo
if nTotalParts > 0 then
dGlobalProgress = nDoneParts / nTotalParts
-- se non ci sono pezzi il calcolo è già finito
else
dGlobalProgress = 1
end
-- 1 Si provano le barre già attive
for i = 1, #RawInventory.ActiveBeams do
local CurrentMove = FindBestPartForBeam( RawInventory.ActiveBeams[i])
if CurrentMove and CurrentMove.dScore > dHighestScore then
dHighestScore = CurrentMove.dScore
BestMove = CurrentMove
BestMove.nActiveBeamIndex = i
end
end
-- 2 Si provano le barre ancora a stock SOLO SE NON TROVATA SOLUZIONE CON BARRE ATTIVE
-- VirtualBeam si resetta invece di crearne una nuova per velocizzare il calcolo
if not BestMove then
for i = 1, #RawInventory.Stock do
if RawInventory.Stock[i].nCount > 0 then
VirtualBeam.dTotalLength = RawInventory.Stock[i].dLength
VirtualBeam.dResidualLength = RawInventory.Stock[i].dLength - ( NEST.STARTOFFSET or 0)
VirtualBeam.LastOffsetX = { 0, 0, 0, 0}
VirtualBeam.LastVtN = Vector3d( 1, 0, 0)
VirtualBeam.vtNXabs = 1
VirtualBeam.dLastHeadRecess = 0
VirtualBeam.NestedParts = {}
local CurrentMove = FindBestPartForBeam( VirtualBeam)
if CurrentMove and CurrentMove.dScore > dHighestScore then
dHighestScore = CurrentMove.dScore
BestMove = CurrentMove
BestMove.nStockIndex = i
end
end
end
end
-- 3 Se BestMove trovata si aggiornano lista pezzi e barre
if BestMove then
CommitBestMove( BestMove)
nDoneParts = nDoneParts + 1
-- se non c'è più niente di compatibile si esce
else
break
end
end
-- creazione MachGroup e Duplo
for i = 1, #RawInventory.ActiveBeams do
local dStartOffset = NEST.STARTOFFSET or 0
local Beam = RawInventory.ActiveBeams[i]
-- creazione MachGroup
local sMachGroup = EgtGetMachGroupNewName( i)
local idMachGroup = EgtAddMachGroup( sMachGroup)
-- assegnazione info
EgtSetInfo( idMachGroup, "BARLEN", Beam.dTotalLength)
EgtSetInfo( idMachGroup, "MATERIAL", NEST.MATERIAL)
EgtSetInfo( idMachGroup, "AUTONEST", 1)
EgtSetInfo( idMachGroup, "PRODID", NEST.PRODID)
EgtSetInfo( idMachGroup, "PATTID", idMachGroup)
-- Spostamento pezzi verso la testa della barra e aggiunta duplo
local nIndex = 1
for j = #Beam.NestedParts, 1, -1 do
local Part = Beam.NestedParts[j]
local nInitialPosition = EgtGetInfo( Part.id, 'INITIALPOSITION', 'i')
-- spostamento verso la testa della barra
local dPosX = Part.dPosX - Beam.dResidualLength + dStartOffset
-- copia del pezzo (aggiunta duplo)
local idDuplo = EgtDuploNew( Part.id)
local Duplo = { id = idDuplo, idRaw = EgtGetRawPartFromPart( idDuplo)}
-- eventuale inversione
if EgtEndsWith( Part.sState, 'INV') then
BeamLib.InvertRawPart( Duplo, 2)
end
-- eventuale rotazione
if ( EgtStartsWith( Part.sState, '0010') and nInitialPosition == 1)
or ( EgtStartsWith( Part.sState, '1000') and nInitialPosition == 3) then
BeamLib.RotateRawPart( Duplo, 2)
end
-- assegnazione info
EgtSetInfo( idMachGroup, "PART" .. nIndex, idDuplo .. "," .. dPosX)
nIndex = nIndex + 1
end
end
-- creazione grezzi per ogni MachGroup (si chiama la BatchProcess in modalità creazione barra)
local nRawCounter = 0
local nTotalRaws = #RawInventory.ActiveBeams
-- variabili per BatchProcess
_G.BEAM = {}
BEAM.FILE = NEST.FILE
BEAM.MACHINE = NEST.MACHINE
BEAM.FLAG = 6 -- CREATE_PANEL
BEAM.BASEDIR = NEST.BASEDIR
-- per ogni MachGroup si chiama la BatchProcess pe creare il grezzo
local idMachGroup = EgtGetFirstMachGroup()
while idMachGroup do
local nNextMachGroup = EgtGetNextMachGroup( idMachGroup)
EgtSetCurrMachGroup( idMachGroup)
if EgtGetInfo( idMachGroup, "AUTONEST",'i') == 1 then
EgtRemoveInfo( idMachGroup, "AUTONEST")
EgtSetInfo( idMachGroup, "UPDATEUI", 1)
local bOk, sErr = pcall( dofile, BEAM.BASEDIR .. "\\BatchProcessNew.lua")
if not bOk then
EgtOutLog( 'Error in BatchProcessNew.lua call (' .. ( sErr or '') ..')')
end
nRawCounter = nRawCounter + 1
-- aggiorno interfaccia
EgtProcessEvents( 200 + ( nRawCounter / nTotalRaws * 100), 0)
end
idMachGroup = nNextMachGroup
end
EgtResetCurrMachGroup()
NEST.ERR = 0
-- calcolo bontà soluzione
RawInventory:PrintDiagnosticReport()