12 Commits

Author SHA1 Message Date
luca.mazzoleni b8299df247 - in BeamExec.ProcessAlternatives si passano correttamente le info a interfaccia da scrivere sul pezzo
- in BeamLib aggiunta funzione ConvertBitIndexToRotationIndex per convertire da BitIndex a RotationIndex
2026-05-13 18:42:01 +02:00
luca.mazzoleni 4a99f2bdf6 - in BeamExec.GetProcessings per HeadcutInfo e TailcutInfo si usano gli indici di rotazione canonici (1,2,3,4 per std e 5,6,7,8 per invertiti) 2026-05-13 16:28:04 +02:00
luca.mazzoleni 1e86180723 - in BeamExec si scrivono HeadcutInfo e TailcutInfo nel PARTS che serviranno per nesting; da completare output alternative 2026-05-13 16:00:20 +02:00
luca.mazzoleni f6d6043c0e - piccole modifiche per test nesting 2026-05-13 12:40:45 +02:00
luca.mazzoleni b048e2ebe2 - in BeamLib.GetSortedVertices piccola correzione 2026-05-13 11:58:28 +02:00
luca.mazzoleni fc47bca0f1 - in NestProcess prime modifiche per nesting obliquo (da completare)
- in BeamExec test BEAM.INFONGEPART per scrittura note pezzo in nge tramite Aedifica
- in BeamLib aggiunta funzione RotateTableFromIndex per reindicizzare una tabella passata
2026-05-13 11:47:25 +02:00
luca.mazzoleni 0274096f57 - in BeamExec.GetFeatureInfoAndDependency si salvano le info necessarie per nesting (offset X dei vertici dei tagli rispetto al box, normali delle facce) 2026-05-12 11:59:26 +02:00
luca.mazzoleni 983609397e - in BeamLib funzione GetSurfTmSortedVertices diventa GetSortedVertices e si passa la Proc direttamente
- in BeamLib aggiunta funzione GetAdjacentIndices per la ricerca degli indici precedente e successivo con circular indexing
2026-05-12 10:45:26 +02:00
luca.mazzoleni 05a8d23f6a - in BeamExec.GetFeatureInfoAndDependency si calcolano i punti ai vertici dei tagli di testa e coda
- in BeamLib aggiunta funzione GetSurfTmSortedVertices per restituire i punti ai vertici già ordinati; da correggere perchè i vertici non arrivano ordinati dalla funzione EgtSurfTmGetAllVertInFacet
2026-05-12 09:06:43 +02:00
luca.mazzoleni 40580cdc69 - NestProcess attuale rinominata Old per test nesting obliqui, con NestProcess ripulita 2026-05-11 12:42:34 +02:00
luca.mazzoleni f6b2477f2b - in BeamExec.GetFeatureInfoAndDependency correzione 2026-05-08 12:29:46 +02:00
luca.mazzoleni 69db74e30e - in BeamExec.ProcessBeams modifiche per accettare grezzi compenetranti
- in BeamExec.GetFeatureInfoAndDependency si scelgono taglio di testa e coda anche obliqui, quelli più verso il centro della trave. Gli altri tagli si disattivano
- da completare
2026-05-08 12:00:58 +02:00
5 changed files with 858 additions and 500 deletions
+208 -42
View File
@@ -251,6 +251,27 @@ function BeamExec.GetStrategiesFromJSONinBD( sAISetupConfigName)
end
end
-------------------------------------------------------------------------------------------------------------
local function GetRotationName( nRotIndex, nInvertIndex)
local sRotation = ''
if nRotIndex == 1 then
sRotation = '0'
elseif nRotIndex == 2 then
sRotation = '90'
elseif nRotIndex == 3 then
sRotation = '180'
elseif nRotIndex == 4 then
sRotation = '270'
end
if nInvertIndex > 1 then
sRotation = sRotation .. 'INV'
end
return sRotation
end
-- TODO prevedere parametri per preferire carico del pezzo verticale oppure orizzontale?
-------------------------------------------------------------------------------------------------------------
-- funzione che controlla validità delle combinazioni proposte
@@ -409,40 +430,43 @@ function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, b
-- Inserimento dei pezzi con il loro grezzo
local nCnt = 0
local dLen = dRawL
local dResidualLength = dRawL
local idPrevRaw, dPrevDelta
local dDeltaS = dOvmHead
local dDeltaSMin = 0
local dDeltaE = BeamData.OVM_MID
local dStartOffset = dOvmHead
local dEndOffset = 0 -- TODO cosa fare di BD.OVM_MID? usarlo solo se non è nesting obliquo?
for i = 1, #PARTS do
-- dati del pezzo
local b3BoxExact = EgtGetBBoxGlob( PARTS[i].id or GDB_ID.NULL, GDB_BB.EXACT)
if b3BoxExact:isEmpty() or PARTS[i].b3PartOriginal:isEmpty() then break end
EgtOutLog( 'PartSez=' .. EgtNumToString( b3BoxExact:getDimY(), 1) .. 'x' .. EgtNumToString( b3BoxExact:getDimZ(), 1), 3)
-- se sezione compatibile e lunghezza disponibile sufficiente
local dPartLen = PARTS[i].b3PartOriginal:getDimX()
local dPartLength = PARTS[i].b3PartOriginal:getDimX()
local dPartWidth = PARTS[i].b3PartOriginal:getDimY()
local dPartHeight = PARTS[i].b3PartOriginal:getDimZ()
local dNextLen = dLen - EgtIf( i == 1, dDeltaS, 0) - dPartLen - dDeltaE
if (( abs( dPartWidth - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartHeight - dRawH) < 100 * GEO.EPS_SMALL) or
( abs( dPartHeight - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartWidth - dRawH) < 100 * GEO.EPS_SMALL)) and
dNextLen + dDeltaE >= 0 then
local dNextResidualLength = dResidualLength - EgtIf( i == 1, dStartOffset, 0) - dPartLength - dEndOffset
local bIsSectionOk = (( abs( dPartWidth - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartHeight - dRawH) < 100 * GEO.EPS_SMALL) or
( abs( dPartHeight - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartWidth - dRawH) < 100 * GEO.EPS_SMALL))
if bIsSectionOk and ( dNextResidualLength + dEndOffset >= 0) then
-- eventuale sovramateriale di testa
if i > 1 then
if PARTS[i].dPosX then
dDeltaS = max( PARTS[i].dPosX - ( dRawL - dLen), dDeltaSMin)
dStartOffset = PARTS[i].dPosX - ( dRawL - dResidualLength)
if dStartOffset < -GEO.EPS_SMALL then
dResidualLength = dResidualLength - dStartOffset
dStartOffset = 0
end
else
dDeltaS = max( dOvmMid - dDeltaE, 0)
dStartOffset = max( dOvmMid - dEndOffset, 0)
end
end
-- dimensioni del grezzo
local dCrawLen = min( dPartLen + dDeltaS + dDeltaE, dLen)
local dDelta = dCrawLen - dPartLen - dDeltaS
local dCurrentRawLength = min( dPartLength + dStartOffset + dEndOffset, dResidualLength)
local dDelta = dCurrentRawLength - dPartLength - dStartOffset
-- creo e posiziono il grezzo
PARTS[i].idRaw = EgtAddRawPart( Point3d(0,0,0), dCrawLen, dRawW, dRawH, BeamData.RAWCOL)
PARTS[i].idRaw = EgtAddRawPart( Point3d(0,0,0), dCurrentRawLength, dRawW, dRawH, BeamData.RAWCOL)
EgtMoveToCornerRawPart( PARTS[i].idRaw, BeamData.ptOriXR, BeamData.dPosXR)
EgtMoveRawPart( PARTS[i].idRaw, Vector3d( dLen - dRawL, 0, 0))
EgtMoveRawPart( PARTS[i].idRaw, Vector3d( dResidualLength - dRawL, 0, 0))
-- assegno ordine in lavorazione
nCnt = nCnt + 1
EgtSetInfo( PARTS[i].idRaw, 'ORD', nCnt)
@@ -454,14 +478,14 @@ function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, b
-- aggiungo faccia per taglio iniziale al pezzo
BeamLib.AddPartStartFace( PARTS[i].id, PARTS[i].b3PartOriginal)
-- se sovramateriale di testa, lo notifico
if dDeltaS > 0.09 then
EgtSetInfo( PARTS[i].idRaw, 'HOVM', dDeltaS)
if dStartOffset > 0.09 then
EgtSetInfo( PARTS[i].idRaw, 'HOVM', dStartOffset)
if idPrevRaw then
EgtSetInfo( idPrevRaw, 'BDST', dDeltaS + dPrevDelta)
EgtSetInfo( idPrevRaw, 'BDST', dStartOffset + dPrevDelta)
end
end
if dDeltaE > 0.09 then
EgtSetInfo( PARTS[i].idRaw, 'TOVM', dDeltaE)
if dEndOffset > 0.09 then
EgtSetInfo( PARTS[i].idRaw, 'TOVM', dEndOffset)
end
-- aggiungo faccia per taglio finale al pezzo
BeamLib.AddPartEndFace( PARTS[i].id, PARTS[i].b3PartOriginal)
@@ -479,13 +503,13 @@ function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, b
EgtMovePartInRawPart( PARTS[i].id, ( vtEccOri - vtEccRot))
end
-- aggiorno la lunghezza residua della barra
dLen = dLen - dCrawLen
dResidualLength = dResidualLength - dCurrentRawLength
-- aggiorno grezzo precedente
idPrevRaw = PARTS[i].idRaw
dPrevDelta = dDelta
PARTS[i].bIsLastPart = ( i == #PARTS)
PARTS[i].dDistanceToNextPiece = dDelta
PARTS[i].dRestLength = dLen
PARTS[i].dRestLength = dResidualLength
PARTS[i].b3Raw = EgtGetRawPartBBox( PARTS[i].idRaw)
PARTS[i].dLength = PARTS[i].b3Raw:getDimX()
PARTS[i].dWidth = PARTS[i].b3Raw:getDimY()
@@ -496,7 +520,7 @@ function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, b
PARTS[i].nIndexInParts = i
PARTS[i].SplittingPoints = BeamLib.GetPartSplittingPoints( PARTS[i])
PARTS[i].NotClampableLength = { STD = { dHead = 0, dTail = 0}, SIDE = { dHead = 0, dTail = 0}, DOWN = { dHead = 0, dTail = 0}}
PARTS[i].dHeadOverMaterial = dDeltaS
PARTS[i].dHeadOverMaterial = dStartOffset
PARTS[i].sBTLInfo = EgtGetInfo( PARTS[i].id, 'PROJ', 's') or nil
PARTS[i].sAISetupConfig = EgtGetInfo( PARTS[i].id, 'AISETUP', 's') or
@@ -512,7 +536,7 @@ function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, b
PARTS[i].idTempGroup = idTempGroup
else
local sOut = 'Error: part L(' .. EgtNumToString( dPartLen, 1) .. ') too big for raw part L(' .. EgtNumToString( dLen - 0.1, 1) .. ')'
local sOut = 'Error: part L(' .. EgtNumToString( dPartLength, 1) .. ') too big for raw part L(' .. EgtNumToString( dResidualLength - 0.1, 1) .. ')'
return false, sOut
end
-- se rimasto troppo poco grezzo, esco
@@ -525,10 +549,10 @@ function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, b
-- Se rimasto materiale aggiungo grezzo dell'avanzo
-- TODO valutare se ridurre la dLen minima perchè crea discrepanze tra lunghezza inserita e VMill
if dLen > 10 then
local idRaw = EgtAddRawPart( Point3d(0,0,0), dLen, dRawW, dRawH, BeamData.RAWCOL)
if dResidualLength > 10 then
local idRaw = EgtAddRawPart( Point3d(0,0,0), dResidualLength, dRawW, dRawH, BeamData.RAWCOL)
EgtMoveToCornerRawPart( idRaw, BeamData.ptOriXR, BeamData.dPosXR)
EgtMoveRawPart( idRaw, Vector3d( dLen - dRawL, 0, 0))
EgtMoveRawPart( idRaw, Vector3d( dResidualLength - dRawL, 0, 0))
-- assegno ordine in lavorazione
nCnt = nCnt + 1
EgtSetInfo( idRaw, 'ORD', nCnt)
@@ -781,6 +805,12 @@ end
-------------------------------------------------------------------------------------------------------------
local function GetFeatureInfoAndDependency( vProcSingleRot, Part)
-- gruppo per geometrie temporanee
local idTempGroup = BeamLib.GetTempGroup()
local HeadProc = {}
local TailProc = {}
-- ciclo tutte le feature
for i = 1, #vProcSingleRot do
local Proc = vProcSingleRot[i]
@@ -791,6 +821,86 @@ local function GetFeatureInfoAndDependency( vProcSingleRot, Part)
local ProcB = vProcSingleRot[j]
-- non si controlla la feature con se stessa o se feature disabilitata
if i ~= j and ProcB.nFlg ~= 0 then
local bAreBothTruncatingCuts =
( ID.IsCut( Proc) or ID.IsHeadCut( Proc) or ID.IsTailCut( Proc)) and ( ID.IsCut( ProcB) or ID.IsHeadCut( ProcB) or ID.IsTailCut( ProcB))
and ( FeatureLib.IsFeatureCuttingEntireSection( Proc.b3Box, Part) and FeatureLib.IsFeatureCuttingEntireSection( ProcB.b3Box, Part))
-- si trovano i veri tagli di testa e coda e si disattivano gli altri, se necessario
if bAreBothTruncatingCuts then
-- testa
if Proc.Faces[1].vtN:getX() > GEO.EPS_SMALL and ProcB.Faces[1].vtN:getX() > GEO.EPS_SMALL then
-- il primo taglio è più verso il centro della trave
if ( Proc.b3Box:getMin():getX() < ProcB.b3Box:getMin():getX() - 10 * GEO.EPS_SMALL) then
HeadProc = Proc
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
EgtMove( idProcCopy, - 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
EgtMove( idProcBCopy, 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
-- se i tagli non si intersecano, quello più esterno è da disattivare
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
if not Proc.SlaveProcIndexes then
Proc.SlaveProcIndexes = {}
end
table.insert( Proc.SlaveProcIndexes, j)
ProcB.nIndexMasterProc = i
ProcB.nFlg = 0
end
-- il secondo taglio è più verso il centro della trave
elseif Proc.b3Box:getMin():getX() >= ProcB.b3Box:getMin():getX() - 10 * GEO.EPS_SMALL then
HeadProc = ProcB
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
EgtMove( idProcBCopy, - 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
EgtMove( idProcCopy, 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
-- se i tagli non si intersecano, quello più esterno è da disattivare
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
if not ProcB.SlaveProcIndexes then
ProcB.SlaveProcIndexes = {}
end
table.insert( ProcB.SlaveProcIndexes, i)
Proc.nIndexMasterProc = j
Proc.nFlg = 0
end
end
-- coda
elseif Proc.Faces[1].vtN:getX() <= GEO.EPS_SMALL and ProcB.Faces[1].vtN:getX() <= GEO.EPS_SMALL then
-- il primo taglio è più verso il centro della trave
if Proc.b3Box:getMax():getX() > ProcB.b3Box:getMax():getX() + 10 * GEO.EPS_SMALL then
TailProc = Proc
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
EgtMove( idProcCopy, - 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
EgtMove( idProcBCopy, 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
-- se i tagli non si intersecano, quello più esterno è da disattivare
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
if not Proc.SlaveProcIndexes then
Proc.SlaveProcIndexes = {}
end
table.insert( Proc.SlaveProcIndexes, j)
ProcB.nIndexMasterProc = i
ProcB.nFlg = 0
end
-- il secondo taglio è più verso il centro della trave
elseif Proc.b3Box:getMax():getX() >= ProcB.b3Box:getMax():getX() - 10 * GEO.EPS_SMALL then
TailProc = ProcB
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
EgtMove( idProcBCopy, - 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
EgtMove( idProcCopy, 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
-- se i tagli non si intersecano, quello più esterno è da disattivare
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
if not ProcB.SlaveProcIndexes then
ProcB.SlaveProcIndexes = {}
end
table.insert( ProcB.SlaveProcIndexes, i)
Proc.nIndexMasterProc = j
Proc.nFlg = 0
end
end
end
end
-- se entrambi tagli di testa, si tiene sempre il primo ( ma non quello aggiunto dall'automatismo)
if ( ID.IsHeadCut( Proc) and not EgtGetInfo( Proc.id, 'HEAD_ADD_CUT', 'i')) and ID.IsHeadCut( ProcB) then
if not Proc.SlaveProcIndexes then
@@ -828,7 +938,37 @@ local function GetFeatureInfoAndDependency( vProcSingleRot, Part)
end
end
end
return vProcSingleRot
HeadProc.Topology = {}
TailProc.Topology = {}
HeadProc.Topology.sFamily = 'HeadCut'
HeadProc.Topology.sName = 'HeadCut'
TailProc.Topology.sFamily = 'TailCut'
TailProc.Topology.sName = 'TailCut'
HeadProc.AvailableStrategies = GetStrategies( HeadProc, Part.sAISetupConfig)
TailProc.AvailableStrategies = GetStrategies( TailProc, Part.sAISetupConfig)
-- per nesting, si settano come info gli offset X degli estremi dei tagli
local HeadcutInfo = {}
local PtSortedHead = BeamLib.GetSortedVertices( HeadProc)
if PtSortedHead then
HeadcutInfo.OffsetX = {}
for i = 1, #PtSortedHead do
table.insert( HeadcutInfo.OffsetX, Part.b3Part:getMax():getX() - PtSortedHead[i]:getX())
end
end
local TailcutInfo = {}
local PtSortedTail = BeamLib.GetSortedVertices( TailProc)
if PtSortedTail then
TailcutInfo.OffsetX = {}
for i = 1, #PtSortedHead do
table.insert( TailcutInfo.OffsetX, Part.b3Part:getMin():getX() - PtSortedTail[i]:getX())
end
end
-- per nesting, si settano come info le normali delle facce di taglio
HeadcutInfo.vtN = HeadProc.Faces[1].vtN
TailcutInfo.vtN = TailProc.Faces[1].vtN
return vProcSingleRot, HeadcutInfo, TailcutInfo
end
-------------------------------------------------------------------------------------------------------------
@@ -983,6 +1123,9 @@ local function CalculateStrategies( vProcSingleRot, Part)
Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine = 99
end
if not Proc.AvailableStrategies.dAllStrategiesTotalTime then
Proc.AvailableStrategies.dAllStrategiesTotalTime = 0
end
Proc.AvailableStrategies.dAllStrategiesTotalTime = Proc.AvailableStrategies.dAllStrategiesTotalTime + Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine
-- se scelta strategia in modalità base o standard, esco subito alla prima che trovo completa
if Part.GeneralParameters.GEN_sMachiningStrategy == 'FIRST_IN_LIST' and Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus == 'Complete' then
@@ -1244,12 +1387,16 @@ function BeamExec.GetProcessings( PARTS, bIsFlipRot)
-- se è prerotazione, oltre al ciclo normale, si devono verificare anche invertiti
local bCalcInverted = bIsFlipRot and PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion
local nCycles = EgtIf( bCalcInverted, 2, 1)
PARTS[nPart].HeadcutInfo = {}
PARTS[nPart].TailcutInfo = {}
-- per ogni inversione
for nInvertIndex = 1, nCycles do
-- per ogni rotazione
for nRotIndex = 1, 4 do
local nOffsetIndex = EgtIf( nInvertIndex == 2, 4, 0)
-- le rotazioni sono 1,2,3,4 (0, 90, 180, 270) e 5,6,7,8 (le stesse invertite)
local nIndex = nRotIndex + nOffsetIndex
local HeadcutInfo, TailcutInfo
-- si calcolano le feature solo se la rotazione può essere presa in considerazione
if PARTS[nPart].CombinationList.Rotations[nRotIndex] == 1 then
-- recupero le feature di lavorazione della trave
@@ -1257,11 +1404,23 @@ function BeamExec.GetProcessings( PARTS, bIsFlipRot)
-- recupero informazioni ausiliarie feature e dipendenze tra feature stesse
-- TODO le dipendenze cambiano in base alla rotazione del pezzo? probabilmente no
vProcRot[nIndex] = GetFeatureInfoAndDependency( vProcRot[nIndex], PARTS[nPart])
vProcRot[nIndex], HeadcutInfo, TailcutInfo = GetFeatureInfoAndDependency( vProcRot[nIndex], PARTS[nPart])
else
-- inserisco una tabella vuota
table.insert( vProcRot, {})
end
if HeadcutInfo then
PARTS[nPart].HeadcutInfo[nIndex] = {
OffsetX = HeadcutInfo.OffsetX,
vtN = HeadcutInfo.vtN
}
end
if TailcutInfo then
PARTS[nPart].TailcutInfo[nIndex] = {
OffsetX = TailcutInfo.OffsetX,
vtN = TailcutInfo.vtN
}
end
-- rotazione pezzo di 90° per volta
BeamLib.RotateRawPart( PARTS[nPart], 1)
-- aggiorno info pezzo
@@ -1962,8 +2121,8 @@ end
function BeamExec.ProcessAlternatives( PARTS)
-- inizializzazione variabili globali per interfaccia
BEAM.ALTERNATIVESNEST2D = ''
BEAM.ALTERNATIVES = ''
local Alternatives = {}
local AlternativesNest2D = {}
-- ciclo sui pezzi
local BestCombination = {}
@@ -2292,23 +2451,14 @@ function BeamExec.ProcessAlternatives( PARTS)
local bApplOk, _, _ = EgtApplyAllMachinings()
-- se non ci sono errori, soluzione alternativa valida: scrittura variabili globali per interfaccia
if bApplOk then
local Alternatives = {}
local sBitIndexCombinationWithInvert = BestCombination.sBitIndexCombination .. EgtIf( BestCombination.bPartInCombiIsInverted, '_INV', '')
if TotalCombiToTest[z].bIsNesting2D then
if BEAM.ALTERNATIVESNEST2D and BEAM.ALTERNATIVESNEST2D ~= "" then
table.insert( Alternatives, BEAM.ALTERNATIVESNEST2D)
end
table.insert( Alternatives, sBitIndexCombinationWithInvert)
BEAM.ALTERNATIVESNEST2D = table.concat( Alternatives, ', ')
table.insert( AlternativesNest2D, sBitIndexCombinationWithInvert)
else
if BEAM.ALTERNATIVES and BEAM.ALTERNATIVES ~= "" then
table.insert( Alternatives, BEAM.ALTERNATIVES)
end
table.insert( Alternatives, sBitIndexCombinationWithInvert)
BEAM.ALTERNATIVES = table.concat( Alternatives, ', ')
end
end
-- se ultima combinazione, si esce e non si riporta in posizione inizale. Verrà infatti cancellata
-- se ultima combinazione, si esce e non si riporta in posizione iniziale. Verrà infatti cancellata
if z == #TotalCombiToTest then break end
-- si riporta pezzo in posizione iniziale
@@ -2322,6 +2472,22 @@ function BeamExec.ProcessAlternatives( PARTS)
end
-- passaggio info a interfaccia da scrivere sul pezzo
BEAM.INFONGEPART = {}
for i = 1, #AlternativesNest2D do
local sRotation = BeamLib.ConvertBitIndexToRotationIndex( AlternativesNest2D[i])
if PARTS[nPart].HeadcutInfo then
local sOffsetX = table.concat( PARTS[nPart].HeadcutInfo[sRotation].OffsetX, ',')
local sVtN = ( tostring( PARTS[nPart].HeadcutInfo[sRotation].vtN)):gsub("^%(", ""):gsub("%)$", "")
table.insert( BEAM.INFONGEPART, 'ALT' .. AlternativesNest2D[i].. '_H' .. '=' .. sOffsetX .. ';' .. sVtN )
end
if PARTS[nPart].TailcutInfo then
local sOffsetX = table.concat( PARTS[nPart].TailcutInfo[sRotation].OffsetX, ',')
local sVtN = ( tostring( PARTS[nPart].TailcutInfo[sRotation].vtN)):gsub("^%(", ""):gsub("%)$", "")
table.insert( BEAM.INFONGEPART, 'ALT' .. AlternativesNest2D[i] .. '_T' .. '=' .. sOffsetX .. ';' .. sVtN)
end
end
-- si cancella eventuale mach group creato per le alternative
EgtRemoveMachGroup( nTempMachGroupName)
+97 -1
View File
@@ -512,10 +512,69 @@ function BeamLib.GetDirectionFromSCC( nSCC)
elseif nSCC == MCH_SCC.ADIR_ZM then
vtSCC = -Z_AX()
end
return vtSCC
end
-------------------------------------------------------------------------------------------------------------
-- Restituisce una tabella con i punti ai vertici della faccia, in globale
-- ordinati partendo da quello ai valori minimi degli assi e i successivi secondo rotazione destrorsa X+;
-- solo per Proc a 1 faccia
function BeamLib.GetSortedVertices( Proc)
local PtVerticesSorted = {}
-- se più di una faccia si esce subito
if Proc.nFct > 1 then
return
end
local PtVertices = {}
local nFirstIndex
local dMinYZ = GEO.INFINITO
for i = 1, #Proc.Faces[1].Edges do
local Edge = Proc.Faces[1].Edges[i]
table.insert( PtVertices, Edge.ptStart)
if ( Edge.ptStart:getY() + Edge.ptStart:getZ() < dMinYZ) then
nFirstIndex = i
dMinYZ = Edge.ptStart:getY() + Edge.ptStart:getZ()
end
end
table.insert( PtVerticesSorted, PtVertices[nFirstIndex])
local nCurrentIndex = nFirstIndex
-- faccia che guarda verso X+, ordine ok
if Proc.Faces[1].vtN:getX() > GEO.EPS_SMALL then
for _ = 1, #PtVertices - 1 do
_, nCurrentIndex = BeamLib.GetAdjacentIndices( nCurrentIndex, #PtVertices)
table.insert( PtVerticesSorted, PtVertices[nCurrentIndex])
end
-- faccia che guarda verso X-, ordine da invertire
else
for _ = 1, #PtVertices - 1 do
nCurrentIndex = BeamLib.GetAdjacentIndices( nCurrentIndex, #PtVertices)
table.insert( PtVerticesSorted, PtVertices[nCurrentIndex])
end
end
return PtVerticesSorted
end
-------------------------------------------------------------------------------------------------------------
-- restituisce il precedente e prossimo indice 1-based, tenendo conto del massimo indice
function BeamLib.GetAdjacentIndices( nCurrentIndex, nMaxIndex)
local nPreviousIndex, nNextIndex
if ( nCurrentIndex < 1) or ( nCurrentIndex > nMaxIndex) then
return
end
-- circular indexing 1-based
nPreviousIndex = ((nCurrentIndex - 2 + nMaxIndex) % nMaxIndex) + 1
nNextIndex = (nCurrentIndex % nMaxIndex) + 1
return nPreviousIndex, nNextIndex
end
-------------------------------------------------------------------------------------------------------------
-- Funzione per determinare se la faccia ha lati molto corti (trascurabili) ed è quindi approssimabile ad una 3 facce
function BeamLib.Is3EdgesApprox( Proc, idFace, nAddGrpId)
@@ -830,6 +889,43 @@ function BeamLib.CalculateStringBinaryFormat( dNumber, CharNumber)
return NumberString
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.ConvertBitIndexToRotationIndex( sBitIndexCombination)
local nRotationIndex
if sBitIndexCombination == '1000' then
return 1
elseif sBitIndexCombination == '0100' then
return 2
elseif sBitIndexCombination == '0010' then
return 3
elseif sBitIndexCombination == '0001' then
return 4
elseif sBitIndexCombination == '1000_INV' then
return 5
elseif sBitIndexCombination == '0100_INV' then
return 6
elseif sBitIndexCombination == '0010_INV' then
return 7
elseif sBitIndexCombination == '0001_INV' then
return 8
end
return nRotationIndex
end
-------------------------------------------------------------------------------------------------------------
-- reindicizza una tabella passata ripartendo dall'indice nStartIndex e mantenendo l'ordine
function BeamLib.RotateTableFromIndex( Table, nStartIndex)
local RotatedTable = {}
for i = 1, #Table do
RotatedTable[#RotatedTable + 1] = Table[((RotatedTable + i - 2) % #Table) + 1]
end
return RotatedTable
end
-------------------------------------------------------------------------------------------------------------
--- copia una tabella lua in modo ricorsivo, ossia mantiene indipendenti anche tutte le sottotabelle
--- ATTENZIONE: in caso di modifiche vanno gestiti anche i tipi custom; sarebbe meglio metterla nel LuaLibs
+2 -2
View File
@@ -43,7 +43,7 @@ end
-------------------------------------------------------------------------------------------------------------
-- restituisce vero se la feature con box b3Proc taglia l'intera sezione della barra
local function IsFeatureCuttingEntireSection( b3Proc, Part)
function FeatureLib.IsFeatureCuttingEntireSection( b3Proc, Part)
return ( b3Proc:getDimY() > ( Part.b3Part:getDimY() - 500 * GEO.EPS_SMALL) and b3Proc:getDimZ() > ( Part.b3Part:getDimZ() - 500 * GEO.EPS_SMALL))
end
@@ -198,7 +198,7 @@ function FeatureLib.ClassifyTopology( Proc, Part)
if not Proc.AffectedFaces then Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part) end
local bIsFeatureCuttingEntireSection = IsFeatureCuttingEntireSection( Proc.b3Box, Part)
local bIsFeatureCuttingEntireSection = FeatureLib.IsFeatureCuttingEntireSection( Proc.b3Box, Part)
local bIsFeatureCuttingEntireLength = IsFeatureCuttingEntireLength( Proc.b3Box, Part)
local bIsAnyDimensionLongAsPart = IsAnyDimensionLongAsPart( Proc, Part)
local vAdj = Proc.AdjacencyMatrix
+74 -455
View File
@@ -1,477 +1,96 @@
-- 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)
EgtEnableDebug( true)
-- Per test
--NEST = {}
--NEST.FILE = 'c:\\TechnoEssetre7\\EgtData\\Prods\\0010\\Bar_10_1.btl'
--NEST.MACHINE = 'Essetre-90480019_MW'
--NEST.FLAG = 3
-- Include
local BeamLib = require( 'BeamLib')
local sLog = ' +++ BeamNestProcess : ' .. NEST.FILE .. ', ' .. NEST.MACHINE .. ', ' .. LEN[1]
EgtOutLog( sLog)
----------------------------------------------------------------------------------------------------------
-- stati che definiscono rotazione / inversione della parte
local STATE = {
STD = 1, -- Standard
ROT180 = 2, -- Rotazione 180deg attorno a X+
INV = 3, -- Inversione (rotazione 180deg attorno a Z+)
INV_ROT180 = 4 -- Inversione + rotazione
}
-- flag per abilitare statistiche in log
local bLogStat = false
----------------------------------------------------------------------------------------------------------
-- inventario grezzi
local RawInventory = {
Stock = {},
ActiveBeams = {}
}
-- 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
function RawInventory:BuildStock()
if #LEN ~= #QTY then
error( 'NestProcess: invalid stock data')
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
for i = 1, #LEN do
self.Stock[#self.Stock + 1] = {
Length = LEN[i],
Count = QTY[i]
}
end
return RawInventory
end
local CycleCount = 0
function RawInventory:AddActiveBeam()
-- TODO da fare
end
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
----------------------------------------------------------------------------------------------------------
-- PartTemplates (informazioni geometriche pezzi univoci) e JobPool (lista di tutti i singoli pezzi, multipli compresi, da nestare)
local PartTemplates = {}
local JobPool = {}
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
function PartTemplates:AddPart( id)
PartTemplates[id] = {}
PartTemplates[id].dLength = EgtGetInfo( id, 'L', 'd')
-- TODO qua gli stati abilitati dovranno arrivare dalle alternatives
local VerticesHead = EgtSplitString( EgtGetInfo( id, 'HEADOFFSETX', 'd'))
local vtNHead = Vector3d( EgtSplitString( EgtGetInfo( id, 'HEADVTN', 'd')))
local VerticesTail = EgtSplitString( EgtGetInfo( id, 'TAILOFFSETX', 'd'))
local vtNTail = Vector3d( EgtSplitString( EgtGetInfo( id, 'TAILVTN', 'd')))
PartTemplates[id].States = {
[STATE.STD] = {
Head = { Vertices = VerticesHead, vtN = vtNHead},
Tail = { Vertices = VerticesTail, vtN = vtNTail}
},
[STATE.ROT180] = {
Head = { Vertices = BeamLib.RotateTableFromIndex( VerticesHead, 3), vtN = vtNHead},
Tail = { Vertices = BeamLib.RotateTableFromIndex( VerticesTail, 3), vtN = vtNHead}
},
[STATE.INV] = {
Head = { Vertices = VerticesTail, vtN = vtNTail},
Tail = { Vertices = VerticesHead, vtN = vtNHead}
},
[STATE.INV_ROT180] = {
Head = { Vertices = BeamLib.RotateTableFromIndex( VerticesTail, 3), vtN = vtNHead},
Tail = { Vertices = BeamLib.RotateTableFromIndex( VerticesHead, 3), vtN = vtNHead}
}
}
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)
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)
return PartTemplates, JobPool
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')
RawInventory:BuildStock()
BuildPartTemplatesAndJobPool()
+477
View File
@@ -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')