diff --git a/LuaLibs/BeamExec.lua b/LuaLibs/BeamExec.lua index 7c3dbec..a7d02ce 100644 --- a/LuaLibs/BeamExec.lua +++ b/LuaLibs/BeamExec.lua @@ -256,6 +256,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 @@ -320,6 +341,11 @@ function BeamExec.GetAvailableCombinations( PartInfo, bIsFlipRot) nCycles = 2 end + if bIsFlipRot and PartInfo.GeneralParameters.GEN_bGetAlternativesNesting2D then + BeamData.ROT90 = false + BeamData.ROT180 = false + end + -- verifico tutte le combinazioni che possono essere considerate for nInvertIndex = 1, nCycles do for nUnloadPos = 1, 4 do @@ -337,22 +363,31 @@ function BeamExec.GetAvailableCombinations( PartInfo, bIsFlipRot) Combination.bPartInCombiIsInverted = true end + -- counter numero totale di rotazioni della combinazione + Combination.nRotationCounter = 0 + -- se posizionamento iniziale attivo if string.sub( sBitIndexCombination, 1, 1) == '1' then CombinationList.Rotations[1] = 1 + Combination.nRotationCounter = Combination.nRotationCounter + 1 end -- se attiva rotazione 90 if string.sub( sBitIndexCombination, 2, 2) == '1' then CombinationList.Rotations[2] = 1 + Combination.nRotationCounter = Combination.nRotationCounter + 1 end -- se attiva rotazione 180 if string.sub( sBitIndexCombination, 3, 3) == '1' then CombinationList.Rotations[3] = 1 + Combination.nRotationCounter = Combination.nRotationCounter + 1 end -- se attiva rotazione 270 if string.sub( sBitIndexCombination, 4, 4) == '1' then CombinationList.Rotations[4] = 1 + Combination.nRotationCounter = Combination.nRotationCounter + 1 end + + Combination.nRotationCounter = Combination.nRotationCounter - 1 end end end @@ -361,188 +396,190 @@ function BeamExec.GetAvailableCombinations( PartInfo, bIsFlipRot) end ------------------------------------------------------------------------------------------------------------- --- *** funzioni posizionamento pezzi all'interno della barra *** +-- *** Funzioni posizionamento pezzi all'interno della barra*** ------------------------------------------------------------------------------------------------------------- -function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, bCreateMachGroup, bIsFlipRot) - -- gruppo per geometrie temporanee - local idTempGroup = BeamLib.GetTempGroup() +function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, bCreateMachGroup, bIsFlipRot ) + -- 1. Inizializzazione e Default + local idTempGroup = BeamLib.GetTempGroup( ) - -- default per nuove costanti qualora non definite - BeamData.OVM_BLADE_HBEAM = ( BeamData.OVM_BLADE_HBEAM or 11) - BeamData.OVM_CHAIN_HBEAM = ( BeamData.OVM_CHAIN_HBEAM or 8) + BeamData.OVM_BLADE_HBEAM = ( BeamData.OVM_BLADE_HBEAM or 11 ) + BeamData.OVM_CHAIN_HBEAM = ( BeamData.OVM_CHAIN_HBEAM or 8 ) - -- sovramateriale intermedio nullo se non definito - dOvmMid = ( dOvmMid or 0) + dOvmMid = ( dOvmMid or 5.4 ) + dOvmHead = ( dOvmHead or 0 ) + BeamExec.CalcMinUnloadableRaw( dRawW, dRawH ) - -- Determinazione minimo grezzo scaricabile - BeamExec.CalcMinUnloadableRaw( dRawW, dRawH) - - -- Creazione nuovo gruppo di lavoro (di default va creato) - if bCreateMachGroup == nil then - bCreateMachGroup = true - end - if bCreateMachGroup then - local sMgName = EgtGetMachGroupNewName( 'Mach_1') - local idNewMg = EgtAddMachGroup( sMgName) - if not idNewMg then - local sOut = 'Errore nella creazione del gruppo di lavoro ' .. sMgName - return false, sOut + -- 2. Gestione Gruppo di Lavoro + if ( bCreateMachGroup == nil ) then bCreateMachGroup = true end + if ( bCreateMachGroup ) then + local sMgName = EgtGetMachGroupNewName( 'Mach_1' ) + local idNewMg = EgtAddMachGroup( sMgName ) + if ( not idNewMg ) then + return false, 'Errore creazione gruppo di lavoro' end end - -- Impostazione della tavola - EgtSetTable( 'Tab') - - -- salvo nota con lunghezza grezzo - -- Recupero l'identificativo del gruppo di lavoro corrente - local nMGrpId = EgtGetCurrMachGroup() - -- Lunghezza della barra - local dBarLen = EgtGetInfo( nMGrpId, 'BARLEN', 'd') - if not dBarLen then - EgtSetInfo( nMGrpId, 'BARLEN', dRawL) + -- 3. Configurazione Tavola Macchina + EgtSetTable( 'Tab' ) + local nMGrpId = EgtGetCurrMachGroup( ) + if ( not EgtGetInfo( nMGrpId, 'BARLEN', 'd' ) ) then + EgtSetInfo( nMGrpId, 'BARLEN', dRawL ) end - -- Area tavola - local b3Tab = EgtGetTableArea() - -- Calcolo posizione estremo TR/BR della tavola rispetto a sua origine in BL - local dPosY = EgtIf( BeamData.CENTER_BEAM, ( b3Tab:getDimY() + dRawW * EgtIf( BeamData.RIGHT_LOAD, -1, 1)) / 2, EgtIf( BeamData.RIGHT_LOAD, 0, b3Tab:getDimY())) - BeamData.ptOriXR = Point3d( b3Tab:getDimX(), dPosY, 0) - BeamData.dPosXR = EgtIf( BeamData.RIGHT_LOAD, MCH_CR.BR, MCH_CR.TR) + local b3Tab = EgtGetTableArea( ) + local dPosY = EgtIf( BeamData.CENTER_BEAM, ( b3Tab:getDimY( ) + dRawW * EgtIf( BeamData.RIGHT_LOAD, -1, 1 ) ) / 2, EgtIf( BeamData.RIGHT_LOAD, 0, b3Tab:getDimY( ) ) ) + + BeamData.ptOriXR = Point3d( b3Tab:getDimX( ), dPosY, 0 ) + BeamData.dPosXR = EgtIf( BeamData.RIGHT_LOAD, MCH_CR.BR, MCH_CR.TR ) - -- Impostazione dell'attrezzaggio di default - EgtImportSetup() + EgtImportSetup( ) - -- Inserimento dei pezzi con il loro grezzo + -- 4. Ciclo di Inserimento Pezzi local nCnt = 0 - local dLen = dRawL - local idPrevRaw, dPrevDelta - local dDeltaS = dOvmHead - local dDeltaSMin = 0 - local dDeltaE = BeamData.OVM_MID + local dMaxX = 0 + local idPrevRaw = nil + local dNextStartOffset = dOvmHead -- Il primo pezzo applica il sormonto iniziale a destra (testa) + 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 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 - -- eventuale sovramateriale di testa - if i > 1 then - if PARTS[i].dPosX then - dDeltaS = max( PARTS[i].dPosX - ( dRawL - dLen), dDeltaSMin) - else - dDeltaS = max( dOvmMid - dDeltaE, 0) - end - end - -- dimensioni del grezzo - local dCrawLen = min( dPartLen + dDeltaS + dDeltaE, dLen) - local dDelta = dCrawLen - dPartLen - dDeltaS - -- creo e posiziono il grezzo - PARTS[i].idRaw = EgtAddRawPart( Point3d(0,0,0), dCrawLen, dRawW, dRawH, BeamData.RAWCOL) + local CurrentPart = PARTS[i] + local b3BoxExact = EgtGetBBoxGlob( CurrentPart.id or GDB_ID.NULL, GDB_BB.EXACT ) + + if ( b3BoxExact:isEmpty( ) or CurrentPart.b3PartOriginal:isEmpty( ) ) then break end - EgtMoveToCornerRawPart( PARTS[i].idRaw, BeamData.ptOriXR, BeamData.dPosXR) - EgtMoveRawPart( PARTS[i].idRaw, Vector3d( dLen - dRawL, 0, 0)) - -- assegno ordine in lavorazione + local dPartLen = CurrentPart.b3PartOriginal:getDimX( ) + local dPartWidth = CurrentPart.b3PartOriginal:getDimY( ) + local dPartHeight = CurrentPart.b3PartOriginal:getDimZ( ) + + -- Se il pezzo corrente non ha coordinata, si calcola da OvmMid + if ( not CurrentPart.dPosX) then + if ( i == 1) then + CurrentPart.dPosX = dOvmHead + else + CurrentPart.dPosX = PARTS[i - 1].dPosX + PARTS[i - 1].b3PartOriginal:getDimX( ) + dOvmMid + end + end + + -- Se il pezzo SUCCESSIVO non ha coordinata, si calcola da OvmMid + if ( i < #PARTS and not PARTS[i + 1].dPosX) then + PARTS[i + 1].dPosX = CurrentPart.dPosX + dPartLen + dOvmMid + end + + local dStartOffset = dNextStartOffset + local dEndOffset = ( i == #PARTS ) and 0 or dOvmMid + + -- Gap reale tra i pezzi (può essere negativo in caso di compenetrazione nesting obliqui) + if ( i < #PARTS ) then + local dTotalGap = PARTS[i + 1].dPosX - CurrentPart.dPosX - dPartLen + if ( dTotalGap > dOvmMid ) then + dEndOffset = dOvmMid + dNextStartOffset = dTotalGap - dOvmMid + else + -- Gap minore dello spessore lama (compenetrazione per nesting obliqui) + dEndOffset = dTotalGap + dNextStartOffset = 0 + end + end + + local dCurrentRawLen = dPartLen + dStartOffset + dEndOffset + local dDelta = dEndOffset + local dStartPos = CurrentPart.dPosX - dStartOffset + + 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 ( dStartPos + dCurrentRawLen <= dRawL + GEO.EPS_SMALL ) ) then + + -- 5. Creazione e Posizionamento del Contenitore RawPart + CurrentPart.idRaw = EgtAddRawPart( Point3d( 0, 0, 0 ), dCurrentRawLen, dRawW, dRawH, BeamData.RAWCOL ) + EgtMoveToCornerRawPart( CurrentPart.idRaw, BeamData.ptOriXR, BeamData.dPosXR ) + EgtMoveRawPart( CurrentPart.idRaw, Vector3d( -dStartPos, 0, 0 ) ) + + -- 6. Configurazione Geometrie Pezzo nCnt = nCnt + 1 - EgtSetInfo( PARTS[i].idRaw, 'ORD', nCnt) - -- creo o pulisco gruppo geometrie aggiuntive - if not BeamLib.CreateOrEmptyAddGroup( PARTS[i].id) then - local sOut = 'Error creating Additional Group in Part ' .. tostring( PARTS[i].id) - return false, sOut + EgtSetInfo( CurrentPart.idRaw, 'ORD', nCnt ) + + if ( not BeamLib.CreateOrEmptyAddGroup( CurrentPart.id ) ) then + return false, 'Error creating Additional Group in Part ' .. tostring( CurrentPart.id ) end - -- 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 idPrevRaw then - EgtSetInfo( idPrevRaw, 'BDST', dDeltaS + dPrevDelta) - end - end - if dDeltaE > 0.09 then - EgtSetInfo( PARTS[i].idRaw, 'TOVM', dDeltaE) - end - -- aggiungo faccia per taglio finale al pezzo - BeamLib.AddPartEndFace( PARTS[i].id, PARTS[i].b3PartOriginal) - -- inserisco il pezzo nel grezzo - EgtDeselectPartObjs( PARTS[i].id) - local ptPos = b3BoxExact:getMin() - PARTS[i].b3PartOriginal:getMin() + Vector3d( dDelta, ( dRawW - dPartWidth) / 2, ( dRawH - dPartHeight) / 2) - EgtAddPartToRawPart( PARTS[i].id, ptPos, PARTS[i].idRaw) - if abs( dPartWidth - dRawW) > 100 * GEO.EPS_SMALL then - -- rotazione attorno a centro geometria complessiva del pezzo - EgtRotatePartInRawPart( PARTS[i].id, X_AX(), 90) - -- correggo per eccentricità solido rispetto a geometria complessiva del pezzo - local vtEccOri = PARTS[i].b3PartOriginal:getCenter() - b3BoxExact:getCenter() - local vtEccRot = Vector3d( vtEccOri) - vtEccRot:rotate( X_AX(), 90) - EgtMovePartInRawPart( PARTS[i].id, ( vtEccOri - vtEccRot)) - end - -- aggiorno la lunghezza residua della barra - dLen = dLen - dCrawLen - -- aggiorno grezzo precedente - idPrevRaw = PARTS[i].idRaw - dPrevDelta = dDelta - PARTS[i].bIsLastPart = ( i == #PARTS) - PARTS[i].dDistanceToNextPiece = dDelta - PARTS[i].dRestLength = dLen - PARTS[i].b3Raw = EgtGetRawPartBBox( PARTS[i].idRaw) - PARTS[i].dLength = PARTS[i].b3Raw:getDimX() - PARTS[i].dWidth = PARTS[i].b3Raw:getDimY() - PARTS[i].dHeight = PARTS[i].b3Raw:getDimZ() - PARTS[i].bSquareSection = abs( PARTS[i].dWidth - PARTS[i].dHeight) < 100 * GEO.EPS_SMALL - PARTS[i].idBoxTm = EgtGetFirstInGroup( EgtGetFirstNameInGroup( PARTS[i].id, 'Box') or GDB_ID.NULL) - PARTS[i].b3Part = EgtGetBBoxGlob( PARTS[i].idBoxTm, GDB_BB.STANDARD) - 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].sBTLInfo = EgtGetInfo( PARTS[i].id, 'PROJ', 's') or nil + + BeamLib.AddPartStartFace( CurrentPart.id, CurrentPart.b3PartOriginal ) + BeamLib.AddPartEndFace( CurrentPart.id, CurrentPart.b3PartOriginal ) - PARTS[i].sAISetupConfig = EgtGetInfo( PARTS[i].id, 'AISETUP', 's') or - ( GENERAL_PARAMETERS.BTL[PARTS[i].sBTLInfo] and GENERAL_PARAMETERS.BTL[PARTS[i].sBTLInfo].sAISetupConfig) or -- i parametri BTL potrebbero non esistere - GENERAL_PARAMETERS.PROJECT.sAISetupConfig or nil + -- Inserimento con dDelta (lascia lo spazio vuoto a sinistra e spinge il pezzo a destra) + EgtDeselectPartObjs( CurrentPart.id ) + local ptPos = b3BoxExact:getMin( ) - CurrentPart.b3PartOriginal:getMin( ) + Vector3d( dDelta, ( dRawW - dPartWidth ) / 2, ( dRawH - dPartHeight ) / 2 ) + EgtAddPartToRawPart( CurrentPart.id, ptPos, CurrentPart.idRaw ) - -- si carica configurazione lavorazioni - TIMER:startElapsed('Json') - BeamExec.GetStrategiesFromJSONinBD( PARTS[i].sAISetupConfig) - PARTS[i].GeneralParameters = BeamLib.GetPieceGeneralParameters( PARTS[i], GENERAL_PARAMETERS_JSON) - TIMER:stopElapsed('Json') - PARTS[i].CombinationList = BeamExec.GetAvailableCombinations( PARTS[i], bIsFlipRot) - PARTS[i].idTempGroup = idTempGroup + -- Rotazione sezione se necessaria + if ( abs( dPartWidth - dRawW ) > 100 * GEO.EPS_SMALL ) then + EgtRotatePartInRawPart( CurrentPart.id, X_AX( ), 90 ) + local vtEccOri = CurrentPart.b3PartOriginal:getCenter( ) - b3BoxExact:getCenter( ) + local vtEccRot = Vector3d( vtEccOri ) + vtEccRot:rotate( X_AX( ), 90 ) + EgtMovePartInRawPart( CurrentPart.id, ( vtEccOri - vtEccRot ) ) + end + -- 7. Popolamento Metadati della Tabella CurrentPart + CurrentPart.bIsLastPart = ( i == #PARTS ) + CurrentPart.dDistanceToNextPiece = dEndOffset + CurrentPart.b3Raw = EgtGetRawPartBBox( CurrentPart.idRaw ) + CurrentPart.dLength = CurrentPart.b3Raw:getDimX( ) + CurrentPart.dWidth = CurrentPart.b3Raw:getDimY( ) + CurrentPart.dHeight = CurrentPart.b3Raw:getDimZ( ) + CurrentPart.bSquareSection = abs( CurrentPart.dWidth - CurrentPart.dHeight ) < 100 * GEO.EPS_SMALL + + CurrentPart.idBoxTm = EgtGetFirstInGroup( EgtGetFirstNameInGroup( CurrentPart.id, 'Box' ) or GDB_ID.NULL ) + CurrentPart.b3Part = EgtGetBBoxGlob( CurrentPart.idBoxTm, GDB_BB.STANDARD ) + CurrentPart.nIndexInParts = i + CurrentPart.SplittingPoints = BeamLib.GetPartSplittingPoints( CurrentPart ) + CurrentPart.NotClampableLength = { STD = { dHead = 0, dTail = 0 }, SIDE = { dHead = 0, dTail = 0 }, DOWN = { dHead = 0, dTail = 0 } } + CurrentPart.dHeadOverMaterial = dStartOffset + CurrentPart.sBTLInfo = EgtGetInfo( CurrentPart.id, 'PROJ', 's' ) or nil + CurrentPart.idTempGroup = idTempGroup + + -- Notifiche al Post-Processor basate sulla nuova scomposizione + if ( dStartOffset > 0.09 ) then EgtSetInfo( CurrentPart.idRaw, 'HOVM', dStartOffset ) end + if ( dEndOffset > 0.09 ) then EgtSetInfo( CurrentPart.idRaw, 'TOVM', dEndOffset ) end + if ( idPrevRaw ) then EgtSetInfo( idPrevRaw, 'BDST', dStartOffset ) end + + -- Caricamento Strategie JSON + CurrentPart.sAISetupConfig = EgtGetInfo( CurrentPart.id, 'AISETUP', 's' ) or + ( GENERAL_PARAMETERS.BTL[CurrentPart.sBTLInfo] and GENERAL_PARAMETERS.BTL[CurrentPart.sBTLInfo].sAISetupConfig ) or + GENERAL_PARAMETERS.PROJECT.sAISetupConfig or nil + + TIMER:startElapsed( 'Json' ) + BeamExec.GetStrategiesFromJSONinBD( CurrentPart.sAISetupConfig ) + CurrentPart.GeneralParameters = BeamLib.GetPieceGeneralParameters( CurrentPart, GENERAL_PARAMETERS_JSON ) + TIMER:stopElapsed( 'Json' ) + + CurrentPart.CombinationList = BeamExec.GetAvailableCombinations( CurrentPart, bIsFlipRot ) + + -- Avanzamento calcolato sulla coordinata reale di fine RawPart (estremità sinistra sulla barra) + dMaxX = max( dMaxX, dStartPos + dCurrentRawLen ) + CurrentPart.dRestLength = dRawL - dMaxX + idPrevRaw = CurrentPart.idRaw 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( dPartLen, 1 ) .. ') too big for remaining bar space' return false, sOut end - -- se rimasto troppo poco grezzo, esco - --if Len < BeamData.MinRaw then break end - DeltaS = 0 - end - if idPrevRaw then - EgtSetInfo( idPrevRaw, 'BDST', 10000) end - -- 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) - EgtMoveToCornerRawPart( idRaw, BeamData.ptOriXR, BeamData.dPosXR) - EgtMoveRawPart( idRaw, Vector3d( dLen - dRawL, 0, 0)) - -- assegno ordine in lavorazione + -- 8. Chiusura Barra e Gestione Avanzo (Rest Material) + if ( idPrevRaw ) then EgtSetInfo( idPrevRaw, 'BDST', 10000 ) end + + local dRemaining = dRawL - dMaxX + if ( dRemaining > 10 ) then + local idRawRest = EgtAddRawPart( Point3d( 0, 0, 0 ), dRemaining, dRawW, dRawH, BeamData.RAWCOL ) + EgtMoveToCornerRawPart( idRawRest, BeamData.ptOriXR, BeamData.dPosXR ) + EgtMoveRawPart( idRawRest, Vector3d( -dMaxX, 0, 0 ) ) nCnt = nCnt + 1 - EgtSetInfo( idRaw, 'ORD', nCnt) - -- aggiorno distanza dell'ultimo pezzo dall'eventuale grezzo scaricabile - if EgtGetRawPartBBox( idRaw):getDimX() < BeamData.dMinRaw then + EgtSetInfo( idRawRest, 'ORD', nCnt ) + + if ( EgtGetRawPartBBox( idRawRest ):getDimX( ) < BeamData.dMinRaw ) then PARTS[#PARTS].dDistanceToNextPiece = 10000 end else - PARTS[#PARTS].dDistanceToNextPiece = 10000 + if ( #PARTS > 0 ) then PARTS[#PARTS].dDistanceToNextPiece = 10000 end end return true @@ -785,10 +822,23 @@ local function AreDrillingsMirrored( Proc, ProcMirror, Part) end ------------------------------------------------------------------------------------------------------------- -local function GetFeatureInfoAndDependency( vProcSingleRot, Part) +local function GetFeatureInfoAndDependency( vProcSingleRot, Part, bIsFlipRot) + -- gruppo per geometrie temporanee + local idTempGroup = BeamLib.GetTempGroup() + + local HeadProcOriginal + local TailProcOriginal + local HeadProc + local TailProc + -- ciclo tutte le feature for i = 1, #vProcSingleRot do local Proc = vProcSingleRot[i] + if not HeadProcOriginal and Proc.Topology.sName == 'HeadCut' then + HeadProcOriginal = Proc + elseif not TailProcOriginal and Proc.Topology.sName == 'TailCut' then + TailProcOriginal = Proc + end -- se feature abilitata alla lavorazione if Proc.nFlg ~= 0 then -- controllo la feature con tutte le altre per recuperare le dipendenze @@ -796,6 +846,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 bIsFlipRot and Part.GeneralParameters.GEN_bGetAlternativesNesting2D and 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 + else + 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 + else + 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 @@ -804,6 +934,7 @@ local function GetFeatureInfoAndDependency( vProcSingleRot, Part) table.insert( Proc.SlaveProcIndexes, j) ProcB.nIndexMasterProc = i ProcB.nFlg = 0 + HeadProcOriginal = Proc end -- se entrambi tagli di coda, si tiene sempre il primo ( ma non quello aggiunto dall'automatismo) if ( ID.IsTailCut( Proc) and not EgtGetInfo( Proc.id, 'HEAD_ADD_CUT', 'i')) and ID.IsTailCut( ProcB) then @@ -813,6 +944,7 @@ local function GetFeatureInfoAndDependency( vProcSingleRot, Part) table.insert( Proc.SlaveProcIndexes, j) ProcB.nIndexMasterProc = i ProcB.nFlg = 0 + TailProcOriginal = Proc end -- verifico se feature tipo LapJoint è attraversata da almeno un foro if ( Proc.Topology.sFamily == 'Pocket' or Proc.Topology.sFamily == 'Tunnel' or Proc.Topology.sFamily == 'Groove' or ID.IsMortise( Proc)) and @@ -833,7 +965,55 @@ local function GetFeatureInfoAndDependency( vProcSingleRot, Part) end end end - return vProcSingleRot + + if not Part.GeneralParameters.GEN_bGetAlternativesNesting2D then + return vProcSingleRot + end + + -- si tiene via il riferimento alla Proc Head/Tail originale in caso si dovesse rimpiazzare + HeadProcOriginal.bIsOriginalHeadcut = true + TailProcOriginal.bIsOriginalTailcut = true + if not HeadProc then + HeadProc = HeadProcOriginal + else + HeadProc.HeadProcOriginal = HeadProcOriginal + end + if not TailProc then + TailProc = TailProcOriginal + else + TailProc.TailProcOriginal = TailProcOriginal + end + + 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, #PtSortedTail 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 ------------------------------------------------------------------------------------------------------------- @@ -988,6 +1168,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 @@ -1197,7 +1380,11 @@ local function CalculateMachinings( vProc, Part, nInitialRotation) local StrategyScriptName = Proc.ChosenStrategy.sStrategyId .. '\\' .. Proc.ChosenStrategy.sStrategyId local StrategyScript = require( StrategyScriptName) -- eseguo la strategia e si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati - _, _ = StrategyScript.Make( true, Proc, Part, Proc.ChosenStrategy) + local _, Result = StrategyScript.Make( true, Proc, Part, Proc.ChosenStrategy) + -- per i tagli di testa e coda, che non hanno girato nel CalculateStrategies, si devono settare i risultati + if ID.IsHeadCut( Proc) or ID.IsTailCut( Proc) then + Proc.ChosenStrategy.Result = Result + end -- se tutte le strategie disponibili non sono applicabili else local nOffsetIndex = EgtIf( Part.bPartInCombiIsInverted, 4, 0) @@ -1249,12 +1436,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 @@ -1262,11 +1453,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], bIsFlipRot) 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 @@ -1496,13 +1699,15 @@ local function GetCombinationListFromMatrix( ProcessingsOnPart, PartInfo, bRePro for nProc = 1, #ProcessingsOnPart.Rotation[1] do -- Si controlla sempre la rotazione 1 perchè la dipendenza di una feature da un'altra non dipende dalla rotazione -- se feature disattivata perchè eseguita da master a lei associata dichiaro comunque eseguita - if ProcessingsOnPart.Rotation[1][nProc].nFlg == 0 and ProcessingsOnPart.Rotation[1][nProc].nIndexMasterProc then - ProcessingsOnPart.Rotation[1][nProc].nIndexRotation = nUnloadPos - table.insert( SingleCombination.Rot0, ProcessingsOnPart.Rotation[1][nProc]) + local ProcOnFirstRotation = ProcessingsOnPart.Rotation[1][nProc] + if ProcOnFirstRotation.nFlg == 0 and ProcOnFirstRotation.nIndexMasterProc then + ProcOnFirstRotation.nIndexRotation = nUnloadPos + table.insert( SingleCombination.Rot0, ProcOnFirstRotation) SingleCombination.nComplete = SingleCombination.nComplete + 1 else local nOffsetIndex = EgtIf( SingleCombination.bPartInCombiIsInverted, 4, 0) - if not ID.IsHeadCut( ProcessingsOnPart.Rotation[1][nProc]) and not ID.IsTailCut( ProcessingsOnPart.Rotation[1][nProc]) then + if not ( ( ID.IsHeadCut( ProcOnFirstRotation) and ProcOnFirstRotation.bIsOriginalHeadcut) + or ( ID.IsTailCut( ProcOnFirstRotation) and ProcOnFirstRotation.bIsOriginalTailcut)) then -- ciclo sulle rotazioni local nNextRot = nUnloadPos local ResultsList = {} @@ -1528,34 +1733,40 @@ local function GetCombinationListFromMatrix( ProcessingsOnPart, PartInfo, bRePro nNextRot = EgtIf( nNextRot + 1 > 4, nNextRot + 1 - 4, nNextRot + 1) end - -- se la feature può essere lavorata in almeno una rotazione - if #ResultsList > 0 then - local Proc, Data = GetProcBestMachRotationFromList( ResultsList, PartInfo) - Proc.nIndexRotation = Data.nIndexRotation - -- inserisco la Proc nell'apposita lista - if Data.nIndexRotation == nUnloadPos then - table.insert( SingleCombination.Rot0, Proc) - elseif Data.nIndexRotation == nUnloadPos + 1 then - table.insert( SingleCombination.Rot90, Proc) - bRot90 = true - else - table.insert( SingleCombination.Rot180, Proc) - bRot180 = true - end - - SingleCombination.dTotalTimeToMachine = SingleCombination.dTotalTimeToMachine + Data.dTimeToMachine - SingleCombination.dTotalQuality = SingleCombination.dTotalQuality + Data.dQuality - SingleCombination.dTotalCompletionIndex = SingleCombination.dTotalCompletionIndex + Data.dCompletionIndex - SingleCombination.nComplete = SingleCombination.nComplete + EgtIf( Data.bComplete, 1, 0) - SingleCombination.nNotComplete = SingleCombination.nNotComplete + EgtIf( Data.bNotComplete, 1, 0) - SingleCombination.nNotExecute = SingleCombination.nNotExecute + EgtIf( Data.bNotApplicable, 1, 0) - SingleCombination.nIndexInCombinationList = i - SingleCombination.nIndexRotation = nUnloadPos + -- se la feature può essere lavorata in almeno una rotazione e non è un taglio di testa o coda + if ID.IsHeadCut( ProcessingsOnPart.Rotation[1+nOffsetIndex][nProc]) then + SingleCombination.nIndexHeadCutInVProc = nProc + elseif ID.IsTailCut( ProcessingsOnPart.Rotation[1+nOffsetIndex][nProc]) then + SingleCombination.nIndexTailCutInVProc = nProc else - ProcessingsOnPart.Rotation[nUnloadPos+nOffsetIndex][nProc].nIndexRotation = nUnloadPos - ProcessingsOnPart.Rotation[nUnloadPos+nOffsetIndex][nProc].nFlg = 0 - table.insert( SingleCombination.Rot0, ProcessingsOnPart.Rotation[nUnloadPos+nOffsetIndex][nProc]) - SingleCombination.nNotExecute = SingleCombination.nNotExecute + 1 + if #ResultsList > 0 then + local Proc, Data = GetProcBestMachRotationFromList( ResultsList, PartInfo) + Proc.nIndexRotation = Data.nIndexRotation + -- inserisco la Proc nell'apposita lista + if Data.nIndexRotation == nUnloadPos then + table.insert( SingleCombination.Rot0, Proc) + elseif Data.nIndexRotation == nUnloadPos + 1 then + table.insert( SingleCombination.Rot90, Proc) + bRot90 = true + else + table.insert( SingleCombination.Rot180, Proc) + bRot180 = true + end + + SingleCombination.dTotalTimeToMachine = SingleCombination.dTotalTimeToMachine + Data.dTimeToMachine + SingleCombination.dTotalQuality = SingleCombination.dTotalQuality + Data.dQuality + SingleCombination.dTotalCompletionIndex = SingleCombination.dTotalCompletionIndex + Data.dCompletionIndex + SingleCombination.nComplete = SingleCombination.nComplete + EgtIf( Data.bComplete, 1, 0) + SingleCombination.nNotComplete = SingleCombination.nNotComplete + EgtIf( Data.bNotComplete, 1, 0) + SingleCombination.nNotExecute = SingleCombination.nNotExecute + EgtIf( Data.bNotApplicable, 1, 0) + SingleCombination.nIndexInCombinationList = i + SingleCombination.nIndexRotation = nUnloadPos + else + ProcessingsOnPart.Rotation[nUnloadPos+nOffsetIndex][nProc].nIndexRotation = nUnloadPos + ProcessingsOnPart.Rotation[nUnloadPos+nOffsetIndex][nProc].nFlg = 0 + table.insert( SingleCombination.Rot0, ProcessingsOnPart.Rotation[nUnloadPos+nOffsetIndex][nProc]) + SingleCombination.nNotExecute = SingleCombination.nNotExecute + 1 + end end else if ID.IsHeadCut( ProcessingsOnPart.Rotation[1+nOffsetIndex][nProc]) then @@ -1629,7 +1840,7 @@ function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) -- ricerca strategia di lavorazione per ogni pezzo e applicazione lavorazioni for nPart = 1, #PARTS do local nCycles = 1 - local nMaxReProcessCycles = EgtClamp( PARTS[nPart].GeneralParameters.GEN_nMaxReProcessCycles, 1, 3) + local nMaxReProcessCycles = EgtClamp( PARTS[nPart].GeneralParameters.GEN_nMaxReProcessCycles, 1, 5) -- la parte di applicazione lavorazioni può essere lanciata più volte in caso della presenza di errori local bProcess = true @@ -1670,7 +1881,7 @@ function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) nPhase = nPhase} table.insert( DB_MACH_APPLIED, MachExtraInfo) else - BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, 0) + BeamLib.AddPhaseWithRawParts( PARTS, nPart, BeamData.ptOriXR, BeamData.dPosXR, 0) end -- si sposta il pezzo nella posizione originale, di quando è stata fatta la collect. In questo modo tutti i dati calcolati nella collect restano validi. -- Altrimenti bisognava ricalcolare tutto, aumentando tempo di calcolo. @@ -1828,7 +2039,7 @@ function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -2) - bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'DOWN') + bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS, PARTS[nPart], 'DOWN') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot bProcess = bProcess or bTryToReProcess end @@ -1837,7 +2048,7 @@ function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) if MatrixResult.bSomeFeatureSide then -- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima if MatrixResult.bSomeFeatureDown then - BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) + BeamLib.AddPhaseWithRawParts( PARTS, nPart, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) @@ -1856,14 +2067,14 @@ function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -1) - bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'SIDE') + bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS, PARTS[nPart], 'SIDE') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot bProcess = bProcess or bTryToReProcess end -- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima if MatrixResult.bSomeFeatureDown or MatrixResult.bSomeFeatureSide then - BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) + BeamLib.AddPhaseWithRawParts( PARTS, nPart, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) @@ -1882,7 +2093,7 @@ function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) BeamLib.RotateRawPart( PARTS[nPart], nInitialPosition - 1) -- aggiunta lavorazioni in ultima fase - _, _, _, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'STD') + _, _, _, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS, PARTS[nPart], 'STD') bProcess = bProcess or bTryToReProcess -- se bisogna riprocessare, si annulla tutto @@ -1922,7 +2133,7 @@ function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) -- ===== finiti i pezzi, si scarica il restante ===== local idRestPart = EgtGetNextRawPart( PARTS[#PARTS].idRaw) if idRestPart and EgtGetRawPartBBox( idRestPart):getDimX() >= BeamData.dMinRaw then - BeamLib.AddPhaseWithRawParts( idRestPart, BeamData.ptOriXR, BeamData.dPosXR, 0) + BeamLib.AddPhaseWithRawParts( PARTS, #PARTS + 1, BeamData.ptOriXR, BeamData.dPosXR, 0) local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'TYPE', 'REST') @@ -1967,8 +2178,8 @@ end function BeamExec.ProcessAlternatives( PARTS) -- inizializzazione variabili globali per interfaccia - BEAM.ALTERNATIVESNEST2D = '' - BEAM.ALTERNATIVES = '' + local Alternatives = {} + local AlternativesNest2D = {} -- ciclo sui pezzi local BestCombination = {} @@ -2198,7 +2409,7 @@ function BeamExec.ProcessAlternatives( PARTS) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -2) - bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'DOWN') + bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS, PARTS[nPart], 'DOWN') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot bProcess = bProcess or bTryToReProcess end @@ -2207,7 +2418,7 @@ function BeamExec.ProcessAlternatives( PARTS) if MatrixResult.bSomeFeatureSide then -- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima if MatrixResult.bSomeFeatureDown then - BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) + BeamLib.AddPhaseWithRawParts( PARTS, nPart, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) @@ -2226,14 +2437,14 @@ function BeamExec.ProcessAlternatives( PARTS) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -1) - bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'SIDE') + bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS, PARTS[nPart], 'SIDE') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot bProcess = bProcess or bTryToReProcess end -- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima if MatrixResult.bSomeFeatureDown or MatrixResult.bSomeFeatureSide then - BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) + BeamLib.AddPhaseWithRawParts( PARTS, nPart, BeamData.ptOriXR, BeamData.dPosXR, EgtIf( bSplitAlreadyExecuted, BeamData.RAW_OFFSET, 0)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) @@ -2252,7 +2463,7 @@ function BeamExec.ProcessAlternatives( PARTS) BeamLib.RotateRawPart( PARTS[nPart], nInitialPosition - 1) -- aggiunta lavorazioni in ultima fase - _, _, _, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'STD') + _, _, _, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS, PARTS[nPart], 'STD') bProcess = bProcess or bTryToReProcess -- se bisogna riprocessare, si annulla tutto @@ -2286,7 +2497,7 @@ function BeamExec.ProcessAlternatives( PARTS) -- ===== finiti i pezzi, si scarica il restante ===== local idRestPart = EgtGetNextRawPart( PARTS[#PARTS].idRaw) if idRestPart and EgtGetRawPartBBox( idRestPart):getDimX() >= BeamData.dMinRaw then - BeamLib.AddPhaseWithRawParts( idRestPart, BeamData.ptOriXR, BeamData.dPosXR, 0) + BeamLib.AddPhaseWithRawParts( PARTS, #PARTS + 1, BeamData.ptOriXR, BeamData.dPosXR, 0) local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'TYPE', 'REST') @@ -2297,23 +2508,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 @@ -2327,6 +2529,23 @@ function BeamExec.ProcessAlternatives( PARTS) end + -- passaggio info a interfaccia da scrivere sul pezzo + BEAM.INFONGEPART = {} + table.insert( BEAM.INFONGEPART, 'INITIALPOSITION=' .. PARTS[nPart].nInitialPosition) + 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) diff --git a/LuaLibs/BeamLib.lua b/LuaLibs/BeamLib.lua index de45e75..1ce20c2 100644 --- a/LuaLibs/BeamLib.lua +++ b/LuaLibs/BeamLib.lua @@ -161,17 +161,45 @@ function BeamLib.GetPartSplittingPoints( Part) end ------------------------------------------------------------------------------------------------------------- -function BeamLib.AddPhaseWithRawParts( idRaw, OriXR, PosXR, dDeltaSucc) +function BeamLib.AddPhaseWithRawParts( PARTS, nPartIndex, OriXR, PosXR, dDeltaSucc) local nPhase = EgtAddPhase() + local Part + local idRaw + -- se l'indice è oltre significa che è l'ultimo grezzo senza pezzi + if nPartIndex > #PARTS then + idRaw = EgtGetNextRawPart( PARTS[#PARTS].idRaw) + else + Part = PARTS[nPartIndex] + idRaw = Part.idRaw + end -- si aprono i limiti tavola per permettere rotazioni di pezzi più larghi della tavola EgtSetTableAreaOffset( 2000, 2000, 2000, 2000) local dRawMove = 0 + local bIsFirstRaw = true + local dPosXFirst = 0 while idRaw do + local dPosX + for i = 1, #PARTS do + local CurrentPart = PARTS[i] + if CurrentPart.idRaw == idRaw then + dPosX = CurrentPart.dPosX + if bIsFirstRaw then + dPosXFirst = dPosX + end + break + end + if i == #PARTS then + dPosX = PARTS[i].dPosX + PARTS[i].b3Raw:getDimX() + end + end + if bIsFirstRaw then + bIsFirstRaw = false + else + dRawMove = dDeltaSucc + dPosX - dPosXFirst + end EgtKeepRawPart( idRaw) EgtMoveToCornerRawPart( idRaw, OriXR, PosXR) EgtMoveRawPart( idRaw, Vector3d( - dRawMove, 0, 0)) - if dRawMove == 0 then dRawMove = dRawMove + dDeltaSucc end - dRawMove = dRawMove + EgtGetRawPartBBox( idRaw):getDimX() idRaw = EgtGetNextRawPart( idRaw) end -- salvo info nuova fase aggiunta @@ -395,6 +423,24 @@ function BeamLib.GetAddGroup( PartId) return AddGrpId, sMchGrp end +------------------------------------------------------------------------------------------------------------- +-- Funzione prossimo nome MachGroup libero (numero intero) +function BeamLib.GetNewMachGroupName() + local idMachGroup = EgtGetFirstMachGroup() + if not idMachGroup then return 1 end + local nMaxMachGroup = 0 + while idMachGroup do + local sMachGroupName = EgtGetMachGroupName( idMachGroup) + local nMachGroupName = tonumber( sMachGroupName) + if nMachGroupName > nMaxMachGroup then + nMaxMachGroup = nMachGroupName + end + idMachGroup = EgtGetNextMachGroup( idMachGroup) + end + + return nMaxMachGroup + 1 +end + ------------------------------------------------------------------------------------------------------------- -- restituisce le facce della parte interessate dalla feature Proc -- TODO da spostare in FeatureLib??? @@ -512,10 +558,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) @@ -831,6 +936,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 diff --git a/LuaLibs/FeatureLib.lua b/LuaLibs/FeatureLib.lua index 8ce6945..7054d5e 100644 --- a/LuaLibs/FeatureLib.lua +++ b/LuaLibs/FeatureLib.lua @@ -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 diff --git a/LuaLibs/MachiningLib.lua b/LuaLibs/MachiningLib.lua index b6ad0d5..da97f02 100644 --- a/LuaLibs/MachiningLib.lua +++ b/LuaLibs/MachiningLib.lua @@ -607,9 +607,6 @@ function MachiningLib.FindBlade( Proc, ToolSearchParameters) local ToolInfo = {} -- parametri obbligatori - if type( ToolSearchParameters.FaceToMachine) ~= 'table' then - error( 'FindBlade : missing face info') - end if type( ToolSearchParameters.bAllowTopHead) ~= 'boolean' then error( 'FindBlade : missing top head info') end @@ -675,12 +672,6 @@ function MachiningLib.FindBlade( Proc, ToolSearchParameters) if not bIsBladeOk then bIsToolCompatible = false end - - -- se si ha solo la faccia si può verificare se questa è orientata correttamente - elseif FaceToMachine then - if MachiningLib.IsFaceZOutOfRange( FaceToMachine.vtN, TOOLS[i]) then - bIsToolCompatible = false - end end end @@ -1184,7 +1175,7 @@ end ------------------------------------------------------------------------------------------------------------- -- funzione per aggiungere una nuova lavorazione -function MachiningLib.AddOperations( MACHININGS, Part, sRotation) +function MachiningLib.AddOperations( MACHININGS, PARTS, Part, sRotation) local nErr local sErr = '' local bAreAllMachiningApplyOk = true @@ -1452,7 +1443,7 @@ function MachiningLib.AddOperations( MACHININGS, Part, sRotation) bSplitExecuted = true MACHININGS.Info.bSplitExecuted = true - BeamLib.AddPhaseWithRawParts( Part.idRaw, BeamData.ptOriXR, BeamData.dPosXR, BeamData.RAW_OFFSET) + BeamLib.AddPhaseWithRawParts( PARTS, Part.nIndexInParts, BeamData.ptOriXR, BeamData.dPosXR, BeamData.RAW_OFFSET) -- se grezzo successivo senza pezzi e finale, va tolto local nNextRawId = EgtGetNextRawPart( Part.idRaw) if nNextRawId and EgtGetPartInRawPartCount( nNextRawId) == 0 and EgtGetRawPartBBox( nNextRawId):getDimX() < BeamData.dMinRaw then diff --git a/NestProcess.lua b/NestProcess.lua index aaace66..82a429f 100644 --- a/NestProcess.lua +++ b/NestProcess.lua @@ -1,139 +1,14 @@ --- BeamNestProcess.lua by Egaltech s.r.l. 2023/01/15 --- Gestione nesting automatico travi --- 2022/10/05 Piccole modifiche per far funzionare correttamente i compilati. --- 2022/10/06 Corretto bug che moltiplicava i pezzi se erano presenti più grezzi della stessa sezione. --- 2023/01/15 Piccole correzioni. +-- BeamNestProcess.lua by Egalware s.r.l. 2026/05/11 +-- Gestione nesting automatico travi anche oblique -- Intestazioni require( 'EgtBase') _ENV = EgtProtectGlobal() EgtEnableDebug( false) --- Per test ---NEST = {} ---NEST.FILE = 'c:\\TechnoEssetre7\\EgtData\\Prods\\0010\\Bar_10_1.btl' ---NEST.MACHINE = 'Essetre-90480019_MW' ---NEST.FLAG = 3 - -local sLog = ' +++ BeamNestProcess : ' .. NEST.FILE .. ', ' .. NEST.MACHINE .. ', ' .. LEN[1] -EgtOutLog( sLog) - --- flag per abilitare statistiche in log -local bLogStat = false - --- Cancello file di log specifico -local sLogFile = EgtChangePathExtension( NEST.FILE, '.txt') -EgtEraseFile( sLogFile) - --- Funzioni per scrittura su file di log specifico -local function WriteErrToLogFile( nErr, sMsg, nRot, nCutId, nTaskId) - local hFile = io.open( sLogFile, 'a') - hFile:write( 'ERR=' .. tostring( nErr) .. '\n') - hFile:write( sMsg .. '\n') - hFile:write( 'ROT=' .. tostring( nRot or 0) .. '\n') - hFile:write( 'CUTID=' .. tostring( nCutId or 0) .. '\n') - hFile:write( 'TASKID=' .. tostring( nTaskId or 0) .. '\n') - hFile:close() -end - -local function WriteTimeToLogFile( dTime) - local hFile = io.open( sLogFile, 'a') - hFile:write( 'TIME=' .. EgtNumToString( dTime) .. '\n') - hFile:close() -end - --- Funzione per gestire visualizzazione dopo errore -local function PostErrView( nErr, sMsg) - if nErr ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then - EgtSetView( SCE_VD.ISO_SW, false) - EgtZoom( SCE_ZM.ALL) - EgtOutBox( sMsg, 'BatchProcess (err=' .. tostring( nErr) .. ')', 'ERRORS') - end -end - --- Funzione per gestire visualizzazione dopo warning -local function PostWarnView( nWarn, sMsg) - if nWarn ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then - EgtSetView( SCE_VD.ISO_SW, false) - EgtZoom( SCE_ZM.ALL) - EgtOutBox( sMsg, 'BatchProcess (wrn=' .. tostring( nWarn) .. ')', 'WARNINGS') - end -end - --- Funzione per aggiornare dati ausiliari -local function UpdateAuxData( sAuxFile) - local bModif = false - -- Se definito LOAD90, aggiorno - local sLoad90 = EgtGetStringFromIni( 'AuxData', 'LOAD90', '', sAuxFile) - if sLoad90 ~= '' then - local BtlInfoId = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'BtlInfo') or GDB_ID.NULL - EgtSetInfo( BtlInfoId, 'LOAD90', sLoad90) - bModif = true - end - return bModif -end - -local function PartsToFill( Parts) - local nToFill = 0 - for i = 1, #Parts do - if Parts[i].Cnt > 0 then - nToFill = nToFill + Parts[i].Cnt - end - end - return nToFill -end - -local function ExecMaximumFilling( Raw, Parts) - -- Inizializzo maximum filler - EgtMaxFillerStart() - -- Inserisco i pezzi - for i = 1, #Parts do - EgtMaxFillerAddPart( i, Parts[i].Len, Parts[i].DispLen or Parts[i].Len, Parts[i].Cnt or 1) - end - -- Eseguo l'ottimizzazione - EgtMaxFillerCompute( Raw.LenToFill, Raw.StartGap, Raw.MidGap, Raw.EndGap, Raw.SortType) - -- Recupero i risultati - local nFilledParts, nDiffParts, dTotFillRatio = EgtMaxFillerGetResults() - local OneRes = {} - for i = 0, nDiffParts - 1 do - local nPartId, nCount = EgtMaxFillerGetOneResult( i) - table.insert( OneRes, { Id=nPartId, Count=nCount}) - end - --return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, Time=dTime, Data=OneRes} - return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, Data=OneRes} -end - --- Funzione per trovare nome MachGroup -local function NewMachGroupName() - local nMachGroupId = EgtGetFirstMachGroup() - if not nMachGroupId then return 1 end - local nMaxMachGroup = 0 - while nMachGroupId do - sMachGroupName = EgtGetMachGroupName(nMachGroupId) - local nMachGroupName = tonumber(sMachGroupName) - if nMachGroupName > nMaxMachGroup then - nMaxMachGroup = nMachGroupName - end - nMachGroupId = EgtGetNextMachGroup(nMachGroupId) - end - return nMaxMachGroup + 1 -end - -local function TotRawCount(Raws) - local nTotRaws = 0 - for RawIndex = 1, #Raws do - nTotRaws = nTotRaws + Raws[RawIndex].Count - end - return nTotRaws -end - -local function TotPartLen(Parts) - local nTotPartLen = 0 - for PartIndex = 1, #Parts do - nTotPartLen = nTotPartLen + ( Parts[PartIndex].Len * Parts[PartIndex].Cnt) - end - return nTotPartLen -end +-- Include +local BeamData = require( 'BeamDataNew') +local BeamLib = require( 'BeamLib') -- Imposto direttorio libreria specializzata per Travi EgtAddToPackagePath( NEST.BASEDIR .. '\\LuaLibs\\?.lua') @@ -143,7 +18,7 @@ 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 + NEST.MSG = 'Error : machine' .. sMachine .. 'note configured for Beam' WriteErrToLogFile( NEST.ERR, NEST.MSG) PostErrView( NEST.ERR, NEST.MSG) return @@ -153,325 +28,669 @@ end 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} +---------------------------------------------------------------------------------------------------------- +-- 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, + + -- Tolleranze + BEAM_SAFETY_BUFFER = 5 -- Buffer di sicurezza riempimento barra per evitare problemi nei solidi EgtCam5 +} + +---------------------------------------------------------------------------------------------------------- +-- 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 -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) + +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 --- 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 +-- 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 -end + local nUnnestedParts = #JobPool - nActualNestedParts --- 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 + for i = 1, nBeamsUsed do + local Beam = self.ActiveBeams[i] + dTotalStockLength = dTotalStockLength + Beam.dTotalLength --- 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]) + -- Classificazione del residuo rimasto in fondo alla barra + if Beam.dResidualLength >= CONFIG.MIN_USABLE_REMNANT then + dTotalUsableRemnants = dTotalUsableRemnants + Beam.dResidualLength else - table.insert( LongList, Parts[PartIndex]) + dTotalScrap = dTotalScrap + Beam.dResidualLength 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 + + -- Somma delle lunghezze dei pezzi reali inseriti + for j = 1, #Beam.NestedParts do + local Part = Beam.NestedParts[j] + dTotalPartLength = dTotalPartLength + Part.dLength 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 + -- 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(" NESTING RESULT ") + 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 m", dTotalPartLength / 1000)) + 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 m (Safe for Clamps)", dTotalUsableRemnants / 1000)) + EgtOutLog(string.format("Total Pure Scrap (Sfrido): %.2f m (Trash Zone)", dTotalScrap / 1000)) + EgtOutLog("==========================================================") +end + +---------------------------------------------------------------------------------------------------------- +-- PartTemplates (informazioni geometriche pezzi univoci) +-- JobPool (lista di tutti i singoli pezzi, multipli compresi, da nestare) +local PartTemplates = {} + +function PartTemplates:GetStateInfoFromPart( 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 + + -- info eventuali rotazioni / inversioni custom e forzature manuali + local bIsManualFlipRot = ( ( EgtGetInfo( id, 'MANUALROT', 'i') or 0) == 1) or ( ( EgtGetInfo( id, 'MANUALFLIP', 'i') or 0) == 1) + + if not bIsManualFlipRot then + + local States = { '1000', '0010', '1000_INV', '0010_INV' } + + for _, sState in ipairs(States) do + local OffsetXHead, vtNHead = self:GetStateInfoFromPart( id, 'ALT' .. sState .. '_H') + local OffsetXTail, vtNTail = self:GetStateInfoFromPart( 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 + + -- se nessuna alternativa stato fisso + if not next( self[id].States) then + + self[id].States['FIXED'] = {} + local State = self[id].States['FIXED'] + + State.Head = { + OffsetX = { 0, 0, 0, 0}, + vtN = Vector3d( 1, 0, 0), + vtNXabs = 1 + } + State.Tail = { + OffsetX = { 0, 0, 0, 0}, + vtN = Vector3d( -1, 0, 0), + vtNXabs = 1 + } + State.dMaxHeadRecess = 0 + State.dMaxTailRecess = 0 + 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 solo se ci sono già altri pezzi sulla barra + local dProjectedBlade = 0 + if #Beam.NestedParts > 0 then + dProjectedBlade = CONFIG.BLADE_THICKNESS / min( Beam.vtNXabs, State.Tail.vtNXabs) + end + dSafeOverlap = dSafeOverlap - dProjectedBlade + + -- lunghezza barra rimasta (se negativo non ci sta) + local dFutureResidualLength = Beam.dResidualLength + dSafeOverlap - dPartLength + if dFutureResidualLength < CONFIG.BEAM_SAFETY_BUFFER - 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 - -- 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]) + 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 - - -- 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 + + -- 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 - -- 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 + + -- 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 + -- Per velocizzare il calcolo, VirtualBeam si resetta invece di crearne una nuova ogni volta + 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 - -- 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 + + -- 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 -local CycleCount = 0 +-- creazione MachGroup e Duplo +for i = 1, #RawInventory.ActiveBeams do + local dStartOffset = NEST.STARTOFFSET or 0 + local Beam = RawInventory.ActiveBeams[i] -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 + -- creazione MachGroup + local sMachGroup = BeamLib.GetNewMachGroupName() + local idMachGroup = EgtAddMachGroup( sMachGroup) -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 + -- 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) --- 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) + -- 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) + + -- eventuale rotazione + if ( EgtStartsWith( Part.sState, '0010') and nInitialPosition == 1) + or ( EgtStartsWith( Part.sState, '1000') and nInitialPosition == 3) then + local idSurfTmBoxDuplo = EgtGetFirstNameInGroup( idDuplo, "Box") + local b3Duplo = EgtGetBBoxGlob( idSurfTmBoxDuplo, GDB_BB.STANDARD) + EgtRotate( idDuplo, b3Duplo:getCenter(), X_AX(), 180, GDB_RT.GLOB) + EgtSetInfo( idDuplo, 'ROT', 180) + end + + -- eventuale inversione + if EgtEndsWith( Part.sState, 'INV') then + local idSurfTmBoxDuplo = EgtGetFirstNameInGroup( idDuplo, "Box") + local b3Duplo = EgtGetBBoxGlob( idSurfTmBoxDuplo, GDB_BB.STANDARD) + EgtRotate( idDuplo, b3Duplo:getCenter(), Z_AX(), 180, GDB_RT.GLOB) + EgtSetInfo( idDuplo, 'FLIP', 180) + end + + -- assegnazione info + EgtSetInfo( idMachGroup, "PART" .. nIndex, idDuplo .. "," .. dPosX) + + nIndex = nIndex + 1 end end --- creo grezzi per ogni gruppo di lavorazione -local nRawCnt = 0 -local nRawTot = ResultList[BestResultIndex] +-- 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 -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) + +-- 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 - nRawCnt = nRawCnt + 1 + nRawCounter = nRawCounter + 1 -- aggiorno interfaccia - EgtProcessEvents( 200 + ( nRawCnt / nRawTot * 100), 0) + EgtProcessEvents( 200 + ( nRawCounter / nTotalRaws * 100), 0) end - nMachGroup = nNextMachGroup + idMachGroup = nNextMachGroup end EgtResetCurrMachGroup() - NEST.ERR = 0 -EgtOutLog( ' +++ BeamNestProcess completed') +-- calcolo bontà soluzione +RawInventory:PrintDiagnosticReport() \ No newline at end of file diff --git a/NestProcessOld.lua b/NestProcessOld.lua new file mode 100644 index 0000000..aaace66 --- /dev/null +++ b/NestProcessOld.lua @@ -0,0 +1,477 @@ +-- BeamNestProcess.lua by Egaltech s.r.l. 2023/01/15 +-- Gestione nesting automatico travi +-- 2022/10/05 Piccole modifiche per far funzionare correttamente i compilati. +-- 2022/10/06 Corretto bug che moltiplicava i pezzi se erano presenti più grezzi della stessa sezione. +-- 2023/01/15 Piccole correzioni. + +-- Intestazioni +require( 'EgtBase') +_ENV = EgtProtectGlobal() +EgtEnableDebug( false) + +-- Per test +--NEST = {} +--NEST.FILE = 'c:\\TechnoEssetre7\\EgtData\\Prods\\0010\\Bar_10_1.btl' +--NEST.MACHINE = 'Essetre-90480019_MW' +--NEST.FLAG = 3 + +local sLog = ' +++ BeamNestProcess : ' .. NEST.FILE .. ', ' .. NEST.MACHINE .. ', ' .. LEN[1] +EgtOutLog( sLog) + +-- flag per abilitare statistiche in log +local bLogStat = false + +-- Cancello file di log specifico +local sLogFile = EgtChangePathExtension( NEST.FILE, '.txt') +EgtEraseFile( sLogFile) + +-- Funzioni per scrittura su file di log specifico +local function WriteErrToLogFile( nErr, sMsg, nRot, nCutId, nTaskId) + local hFile = io.open( sLogFile, 'a') + hFile:write( 'ERR=' .. tostring( nErr) .. '\n') + hFile:write( sMsg .. '\n') + hFile:write( 'ROT=' .. tostring( nRot or 0) .. '\n') + hFile:write( 'CUTID=' .. tostring( nCutId or 0) .. '\n') + hFile:write( 'TASKID=' .. tostring( nTaskId or 0) .. '\n') + hFile:close() +end + +local function WriteTimeToLogFile( dTime) + local hFile = io.open( sLogFile, 'a') + hFile:write( 'TIME=' .. EgtNumToString( dTime) .. '\n') + hFile:close() +end + +-- Funzione per gestire visualizzazione dopo errore +local function PostErrView( nErr, sMsg) + if nErr ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then + EgtSetView( SCE_VD.ISO_SW, false) + EgtZoom( SCE_ZM.ALL) + EgtOutBox( sMsg, 'BatchProcess (err=' .. tostring( nErr) .. ')', 'ERRORS') + end +end + +-- Funzione per gestire visualizzazione dopo warning +local function PostWarnView( nWarn, sMsg) + if nWarn ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then + EgtSetView( SCE_VD.ISO_SW, false) + EgtZoom( SCE_ZM.ALL) + EgtOutBox( sMsg, 'BatchProcess (wrn=' .. tostring( nWarn) .. ')', 'WARNINGS') + end +end + +-- Funzione per aggiornare dati ausiliari +local function UpdateAuxData( sAuxFile) + local bModif = false + -- Se definito LOAD90, aggiorno + local sLoad90 = EgtGetStringFromIni( 'AuxData', 'LOAD90', '', sAuxFile) + if sLoad90 ~= '' then + local BtlInfoId = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'BtlInfo') or GDB_ID.NULL + EgtSetInfo( BtlInfoId, 'LOAD90', sLoad90) + bModif = true + end + return bModif +end + +local function PartsToFill( Parts) + local nToFill = 0 + for i = 1, #Parts do + if Parts[i].Cnt > 0 then + nToFill = nToFill + Parts[i].Cnt + end + end + return nToFill +end + +local function ExecMaximumFilling( Raw, Parts) + -- Inizializzo maximum filler + EgtMaxFillerStart() + -- Inserisco i pezzi + for i = 1, #Parts do + EgtMaxFillerAddPart( i, Parts[i].Len, Parts[i].DispLen or Parts[i].Len, Parts[i].Cnt or 1) + end + -- Eseguo l'ottimizzazione + EgtMaxFillerCompute( Raw.LenToFill, Raw.StartGap, Raw.MidGap, Raw.EndGap, Raw.SortType) + -- Recupero i risultati + local nFilledParts, nDiffParts, dTotFillRatio = EgtMaxFillerGetResults() + local OneRes = {} + for i = 0, nDiffParts - 1 do + local nPartId, nCount = EgtMaxFillerGetOneResult( i) + table.insert( OneRes, { Id=nPartId, Count=nCount}) + end + --return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, Time=dTime, Data=OneRes} + return { FilledParts=nFilledParts, DiffParts=nDiffParts, FillRatio=dTotFillRatio, Data=OneRes} +end + +-- Funzione per trovare nome MachGroup +local function NewMachGroupName() + local nMachGroupId = EgtGetFirstMachGroup() + if not nMachGroupId then return 1 end + local nMaxMachGroup = 0 + while nMachGroupId do + sMachGroupName = EgtGetMachGroupName(nMachGroupId) + local nMachGroupName = tonumber(sMachGroupName) + if nMachGroupName > nMaxMachGroup then + nMaxMachGroup = nMachGroupName + end + nMachGroupId = EgtGetNextMachGroup(nMachGroupId) + end + return nMaxMachGroup + 1 +end + +local function TotRawCount(Raws) + local nTotRaws = 0 + for RawIndex = 1, #Raws do + nTotRaws = nTotRaws + Raws[RawIndex].Count + end + return nTotRaws +end + +local function TotPartLen(Parts) + local nTotPartLen = 0 + for PartIndex = 1, #Parts do + nTotPartLen = nTotPartLen + ( Parts[PartIndex].Len * Parts[PartIndex].Cnt) + end + return nTotPartLen +end + +-- Imposto direttorio libreria specializzata per Travi +EgtAddToPackagePath( NEST.BASEDIR .. '\\LuaLibs\\?.lua') + +-- Imposto la macchina corrente e verifico sia abilitata per la lavorazione delle Travi +EgtSetCurrMachine( NEST.MACHINE) +local sMachDir = EgtGetCurrMachineDir() +if not EgtExistsFile( sMachDir .. '\\Beam\\BeamData.lua') then + NEST.ERR = 12 + NEST.MSG = 'Error not configured for beam machine : ' .. sMachine + WriteErrToLogFile( NEST.ERR, NEST.MSG) + PostErrView( NEST.ERR, NEST.MSG) + return +end + +-- Elimino direttori altre macchine e imposto direttorio macchina corrente per ricerca librerie +EgtRemoveBaseMachineDirFromPackagePath() +EgtAddToPackagePath( sMachDir .. '\\Beam\\?.lua') + +-- Inizializzo contatori errori e avvisi +local nErrCnt = 0 +local nWarnCnt = 0 + +-- Grezzi +local Raws = {} +-- creo tabella dei grezzi +for nIndex, nLen in pairs( LEN) do + Raws[tonumber(nIndex)] = {LenToFill = nLen, StartGap = NEST.STARTOFFSET, MidGap = NEST.OFFSET, EndGap = 0, SortType = -1} +end +for nIndex, nQty in pairs( QTY) do + Raws[tonumber(nIndex)].Count = nQty +end +-- cerco il grezzo con la lunghezza maggiore, epurata dello start gap +local maxRawLenToFillNoStartGap = 0 +for RawIndex = 1, #Raws do + if Raws[RawIndex].Count > 0 then + maxRawLenToFillNoStartGap = max( maxRawLenToFillNoStartGap, Raws[RawIndex].LenToFill - Raws[RawIndex].StartGap) + end +end + +-- Pezzi +local Parts = {} +-- ciclo su pezzi per aggiungerli al nesting +local dTotLen = 0 +for nPartId, nCount in pairs( PART) do + -- recupero lunghezza pezzo + local Len = EgtGetInfo( nPartId, "L", 'd') + local DispLen = EgtIf( Len <= 1000, 2000, 0) --EgtIf( Len <= 2000, max( 2000, 6000 - Len), 0) + -- aggiungo il pezzo solo se ci sta nel grezzo più lungo a disposizione + if Len < maxRawLenToFillNoStartGap then + for nCntIndex = 1 , nCount do + table.insert( Parts, {Id = nPartId, Len = Len, DispLen = DispLen, Cnt = 1}) + dTotLen = dTotLen + Len + end + end +end + +-- lunghezza totale pezzi +local dTotPartLen = TotPartLen( Parts) +-- calcolo media delle barre necessarie +local NeededRawsForType = {} +for RawIndex = 1, #Raws do + NeededRawsForType[RawIndex] = min( ceil( dTotPartLen / Raws[RawIndex].LenToFill), Raws[RawIndex].Count) +end +local RawQtySum = 0 +for NeededRawIndex = 1, #NeededRawsForType do + RawQtySum = RawQtySum + NeededRawsForType[NeededRawIndex] +end +local MediumRawQty = ceil( RawQtySum / #NeededRawsForType) +if MediumRawQty > 1 then + MediumRawQty = MediumRawQty - 1 +end + +-- lista dei risultati +local ResultList = {} +local BestResult = nil +local BestResultIndex = nil +-- riordino lista pezzi per lunghezza +table.sort( Parts, function( B1, B2) return B1.Len < B2.Len end) + +local function NestSolutionByIndex( Index) + + -- creo copia lista raw + local TempRaws = {} + for TempRawIndex = 1, #Raws do + table.insert(TempRaws, {LenToFill = Raws[TempRawIndex].LenToFill, StartGap = Raws[TempRawIndex].StartGap, MidGap = Raws[TempRawIndex].MidGap, EndGap = Raws[TempRawIndex].EndGap, SortType = Raws[TempRawIndex].SortType, Count = Raws[TempRawIndex].Count}) + end + + -- recupero pezzi corti + local ShortList = {} + local LongList = {} + + for PartIndex = 1, #Parts do + if PartIndex <= Index then + table.insert( ShortList, Parts[PartIndex]) + else + table.insert( LongList, Parts[PartIndex]) + end + Parts[PartIndex].Cnt = 1 + end + -- numero di pezzi piccoli per barra + local ShortCount = Index + local ShortForRaw = floor( ShortCount / MediumRawQty) + local ExtraShortForRaw = 0 + if MediumRawQty > 0 then + ExtraShortForRaw = fmod( ShortCount, MediumRawQty) + end + -- creo lista pezzi corti singoli + local SingleShortList = {} + for ShortIndex = 1, #ShortList do + for ShortCount = 1, ShortList[ShortIndex].Cnt do + table.insert( SingleShortList, {Id = ShortList[ShortIndex].Id, Len = ShortList[ShortIndex].Len, DispLen = ShortList[ShortIndex].DispLen, Cnt = 1}) + end + end + -- li divido per le barre previste + local RawsShortList = {} + local RawIndex = 0 + local ShortRawIndex = 0 + for ShortIndex = 1, #SingleShortList do + if ShortRawIndex > 0 then + table.insert( RawsShortList[RawIndex], SingleShortList[ShortIndex]) + ShortRawIndex = ShortRawIndex - 1 + else + table.insert( RawsShortList, {SingleShortList[ShortIndex]}) + RawIndex = RawIndex + 1 + ShortRawIndex = ShortForRaw + EgtIf( RawIndex <= ExtraShortForRaw, 1, 0) - 1 + end + end + + -- Ciclo fino ad esaurimento pezzi o barre + local dTotPartInRawLen = 0 + local nRawTot = 0 + local dRawTotLen = 0 + local dTime = 0 + local nCycle = 1 + local CurrResult = {} + while TotRawCount( TempRaws) > 0 and PartsToFill( Parts) > 0 do + + -- creo lista con pezzi lunghi e pezzi corti di questo Cycle + local PartsToNest = {} + for PartIndex = 1, #LongList do + table.insert( PartsToNest, LongList[PartIndex]) + end + for CycleIndex = 1, #RawsShortList do + if CycleIndex <= nCycle then + for PartIndex = 1, #RawsShortList[CycleIndex] do + table.insert( PartsToNest, RawsShortList[CycleIndex][PartIndex]) + end + end + end + + -- se non ci sono pezzi da nestare, esco + if PartsToFill( PartsToNest) <= 0 then + break + end + -- Eseguo ottimizzazione per ogni lunghezza di barra + local Results = {} + for RawIndex = 1, #TempRaws do + if TempRaws[RawIndex].Count > 0 then + Results[RawIndex] = ExecMaximumFilling( TempRaws[RawIndex], PartsToNest) + else + Results[RawIndex] = { FillRatio = 0.001, LenToFill = 1000, DiffParts = 0} + end + end + -- verifico quale e' quella con meno scarto + local nMinWasteRawIndex = GDB_ID.NULL + local dMinWaste = 100000 + for ResultIndex = 1, #Results do + if Results[ResultIndex] then + local dWaste = (1 - Results[ResultIndex].FillRatio) * TempRaws[ResultIndex].LenToFill + if Results[ResultIndex].DiffParts > 0 and dWaste < dMinWaste then + dMinWaste = dWaste + nMinWasteRawIndex = ResultIndex + end + end + end + -- verifico se ci sono pezzi + if nMinWasteRawIndex > 0 and Results[nMinWasteRawIndex] and Results[nMinWasteRawIndex].DiffParts > 0 then + -- riporto barra e pezzi nel risultato corrente + local CurrBar = { BarLen = TempRaws[nMinWasteRawIndex].LenToFill, Parts = {}} + local CurrX = TempRaws[nMinWasteRawIndex].StartGap + local nInfoIndex = 1 + for i = 1, Results[nMinWasteRawIndex].DiffParts do + local PartIndex = Results[nMinWasteRawIndex].Data[i].Id + local PartId = PartsToNest[PartIndex].Id + local dLen = PartsToNest[PartIndex].Len + for j = 1, Results[nMinWasteRawIndex].Data[i].Count do + -- creo pezzo copia + CurrPart = { Index = nInfoIndex, PartId = PartId, PosX = CurrX} + table.insert( CurrBar.Parts, CurrPart) + CurrX = CurrX + dLen + TempRaws[nMinWasteRawIndex].MidGap + nInfoIndex = nInfoIndex + 1 + end + end + table.insert( CurrResult, CurrBar) + dTotPartInRawLen = dTotPartInRawLen + ( Results[nMinWasteRawIndex].FillRatio * TempRaws[nMinWasteRawIndex].LenToFill) + nRawTot = nRawTot + 1 + dRawTotLen = dRawTotLen + TempRaws[nMinWasteRawIndex].LenToFill + -- Aggiorno per prossima iterazione + TempRaws[nMinWasteRawIndex].Count = TempRaws[nMinWasteRawIndex].Count - 1 + for i = 1, Results[nMinWasteRawIndex].DiffParts do + local PartId = Results[nMinWasteRawIndex].Data[i].Id + PartsToNest[PartId].Cnt = PartsToNest[PartId].Cnt - Results[nMinWasteRawIndex].Data[i].Count + end + else + -- se non sono riuscito ad inserire alcun pezzo esco dal ciclo perche' non ci sono pezzi inseribili + break + end + nCycle = nCycle + 1 + end + -- riporto risultato in lista + ResultList[Index] = dTotPartInRawLen + if not BestResult or not BestResultIndex or + ( dTotPartInRawLen > ResultList[BestResultIndex] + 0.02 or ( abs( dTotPartInRawLen - ResultList[BestResultIndex]) < 0.02 and dRawTotLen < BestResult.RawTotLen - 0.02)) then + BestResult = CurrResult + BestResult.RawTotLen = dRawTotLen + BestResultIndex = Index + end +end + +local CycleCount = 0 + +local MinTime = 10 + pow( 3, ceil( log10( #Parts)) - 1) +if bLogStat then EgtOutLog('MinTime: ' .. MinTime ) end +local MaxTime = 30 + pow( 7, ceil( log10( #Parts)) - 1) +if bLogStat then EgtOutLog('MaxTime: ' .. MaxTime ) end +local TargetRatio = 0.98 +local dTargetRatioLen = TargetRatio * dTotLen +if bLogStat then EgtOutLog('TargetRatioLen: ' .. dTargetRatioLen ) end +local CurrTime = 0 + +local function NestSolutionFromSP( StartingPoint, OscillationStep) + -- ciclo sulle possibilita' da un punto di origine con uno step fisso + local CurrResultIndex = StartingPoint + NestSolutionByIndex( StartingPoint) + if OscillationStep == 0 then return end + local CycleIndex = 1 + local nOutOfBoundary = 0 + while nOutOfBoundary ~= 3 do + CurrTime = EgtStopCounter() / 1000 + if bLogStat then EgtOutLog('CurrTime: ' .. CurrTime ) end + if bLogStat then EgtOutLog('BestRatio: ' .. dTotLen / BestResult.RawTotLen ) end + -- se e' passato il tempo massimo, o e' passato il tempo minimo, ha inserito tutti i pezzi e la percentuale di utilizzo del materiale e' maggiore della soglia + if CurrTime > MaxTime or ( CurrTime > MinTime and ResultList[BestResultIndex] > dTotLen - 0.1 and ( dTotLen / BestResult.RawTotLen ) >= TargetRatio) then + if bLogStat then EgtOutLog('Brake') end + break + end + local bCurrOutOfBoundary = false + if CurrResultIndex < 0 then + bCurrOutOfBoundary = true + if nOutOfBoundary == 2 then + nOutOfBoundary = 3 + else + nOutOfBoundary = 1 + end + end + if CurrResultIndex > #Parts then + bCurrOutOfBoundary = true + if nOutOfBoundary == 1 then + nOutOfBoundary = 3 + else + nOutOfBoundary = 2 + end + end + if not bCurrOutOfBoundary and not ResultList[CurrResultIndex] then + NestSolutionByIndex( CurrResultIndex) + if bLogStat then EgtOutLog('CurrResultIndex: ' .. CurrResultIndex ) end + if bLogStat then EgtOutLog('Result: ' .. ResultList[CurrResultIndex]) end + CycleCount = CycleCount + 1 + end + CurrResultIndex = StartingPoint + EgtIf( CycleIndex % 2 == 0, (CycleIndex / 2) * OscillationStep, -( ( CycleIndex + 1) / 2) * OscillationStep ) + CycleIndex = CycleIndex + 1 + end +end + +-- lancio calcolo +EgtStartCounter() +local StartingResult = floor( #Parts * 0.3) +if bLogStat then EgtOutLog('StartingResult: ' .. StartingResult ) end +--local Step = floor( #Parts / 10) * floor( log10( #Parts)) +local nDividendo = pow( 10, floor( log10( #Parts)) - 1) +nDividendo = EgtIf( nDividendo ~= 1, nDividendo, 10) +local Step = floor( #Parts / nDividendo) * floor( log10( #Parts)) +if bLogStat then EgtOutLog('Step: ' .. Step ) end +NestSolutionFromSP( StartingResult, Step) +if Step > 1 then + NestSolutionFromSP( StartingResult, 1) +end + +-- creo gruppi di lavorazione per risultato +for MachGroupIndex = 1, #BestResult do + local CurrMachGroup = BestResult[ MachGroupIndex] + -- creo gruppo di lavorazione + local MachGroupName = NewMachGroupName() + nMachGroup = EgtAddMachGroup( MachGroupName) + EgtSetInfo( nMachGroup, "BARLEN", CurrMachGroup.BarLen) + EgtSetInfo( nMachGroup, "MATERIAL", NEST.MATERIAL) + EgtSetInfo( nMachGroup, "AUTONEST", 1) + -- scrivo dati per variabili P di comunicazione con la macchina in gruppo di lavorazione + EgtSetInfo( nMachGroup, "PRODID", NEST.PRODID) + EgtSetInfo( nMachGroup, "PATTID", nMachGroup) + -- Disegno i pezzi + for i = 1, #CurrMachGroup.Parts do + local CurrPart = CurrMachGroup.Parts[ i] + -- creo pezzo copia + local nPartDuploId = EgtDuploNew( CurrPart.PartId) + EgtSetInfo( nMachGroup, "PART" .. CurrPart.Index, nPartDuploId .. "," .. CurrPart.PosX) + end +end + +-- creo grezzi per ogni gruppo di lavorazione +local nRawCnt = 0 +local nRawTot = ResultList[BestResultIndex] +_G.BEAM = {} +BEAM.FILE = NEST.FILE +BEAM.MACHINE = NEST.MACHINE +BEAM.FLAG = 6 -- CREATE_PANEL +BEAM.BASEDIR = NEST.BASEDIR +nMachGroup = EgtGetFirstMachGroup() +while nMachGroup do + local nNextMachGroup = EgtGetNextMachGroup( nMachGroup) + EgtSetCurrMachGroup( nMachGroup) + if EgtGetInfo( nMachGroup, "AUTONEST",'i') == 1 then + EgtRemoveInfo( nMachGroup, "AUTONEST") + EgtSetInfo( nMachGroup, "UPDATEUI", 1) + local bOk, sErr = pcall( dofile, BEAM.BASEDIR .. "\\BatchProcessNew.lua") + if not bOk then + EgtOutLog( 'Error in BatchProcessNew.lua call (' .. ( sErr or '') ..')') + end + nRawCnt = nRawCnt + 1 + -- aggiorno interfaccia + EgtProcessEvents( 200 + ( nRawCnt / nRawTot * 100), 0) + end + nMachGroup = nNextMachGroup +end + +EgtResetCurrMachGroup() + +NEST.ERR = 0 + +EgtOutLog( ' +++ BeamNestProcess completed') diff --git a/Strategies/GeneralParameters.json b/Strategies/GeneralParameters.json index 69eabef..dd2a2df 100644 --- a/Strategies/GeneralParameters.json +++ b/Strategies/GeneralParameters.json @@ -46,7 +46,7 @@ "sName": "GEN_bGetAlternativesNesting2D", "sNameNge": "GET_ALTERNATIVES_NEST2D", "sValue": "false", - "sDescriptionShort": "Enable material optimization function in nesting", + "sDescriptionShort": "Enable material optimization function in nesting (part rotation disabled)", "sDescriptionLong": "", "sType": "b", "sMessageId": " ", diff --git a/Strategies/Standard/STR0011/STR0011.lua b/Strategies/Standard/STR0011/STR0011.lua index 666be25..407bf2b 100644 --- a/Strategies/Standard/STR0011/STR0011.lua +++ b/Strategies/Standard/STR0011/STR0011.lua @@ -16,6 +16,27 @@ local Strategy = {} -- TODO Da fare completamente gestione foratura doppia con 2 teste +------------------------------------------------------------------------------------------------------------- +local function GetSCC( vtMachiningDirection) + -- TODO implementare SCC come per FacesBySaw + local nSCC = MCH_SCC.NONE + if vtMachiningDirection:getZ() < -0.9 then + nSCC = MCH_SCC.ADIR_ZM + elseif vtMachiningDirection:getZ() > 0.9 then + nSCC = MCH_SCC.ADIR_ZP + elseif vtMachiningDirection:getY() < -0.707 then + nSCC = MCH_SCC.ADIR_YM + elseif vtMachiningDirection:getY() > 0.707 then + nSCC = MCH_SCC.ADIR_YP + elseif vtMachiningDirection:getX() < -0.707 then + nSCC = MCH_SCC.ADIR_XM + elseif vtMachiningDirection:getX() > 0.707 then + nSCC = MCH_SCC.ADIR_XP + end + + return nSCC +end + ------------------------------------------------------------------------------------------------------------- local function GetDrillingStrategy( Proc, Part) local ToolSearchParameters = {} @@ -39,6 +60,7 @@ local function GetDrillingStrategy( Proc, Part) Drilling.nToolIndex = Drilling.ToolInfo.nToolIndex Drilling.vtToolDirection = Proc.FeatureInfo.vtDrillExtrusion Drilling.dStep = TOOLS[Drilling.nToolIndex].dStep + Drilling.nSCC = GetSCC( -Drilling.vtToolDirection) end -- TODO se utensile 2 che si torverà è il gemello da usare nelle lavorazioni in doppio, allora gestire di conseguenza l'applicazione delle lavorazioni in doppio @@ -61,6 +83,7 @@ local function GetDrillingStrategy( Proc, Part) Drilling2.bInvert = true Drilling2.vtToolDirection = -Proc.FeatureInfo.vtDrillExtrusion Drilling2.dStep = TOOLS[Drilling2.nToolIndex].dStep + Drilling2.nSCC = GetSCC( -Drilling2.vtToolDirection) end end diff --git a/Strategies/Standard/STR0013/STR0013.lua b/Strategies/Standard/STR0013/STR0013.lua index 00c55bf..5d67f1e 100644 --- a/Strategies/Standard/STR0013/STR0013.lua +++ b/Strategies/Standard/STR0013/STR0013.lua @@ -14,6 +14,27 @@ local FeatureLib = require( 'FeatureLib') local STR0013 = {} local Strategy = {} +------------------------------------------------------------------------------------------------------------- +local function GetSCC( vtMachiningDirection) + -- TODO implementare SCC come per FacesBySaw + local nSCC = MCH_SCC.NONE + if vtMachiningDirection:getZ() < -0.9 then + nSCC = MCH_SCC.ADIR_ZM + elseif vtMachiningDirection:getZ() > 0.9 then + nSCC = MCH_SCC.ADIR_ZP + elseif vtMachiningDirection:getY() < -0.707 then + nSCC = MCH_SCC.ADIR_YM + elseif vtMachiningDirection:getY() > 0.707 then + nSCC = MCH_SCC.ADIR_YP + elseif vtMachiningDirection:getX() < -0.707 then + nSCC = MCH_SCC.ADIR_XM + elseif vtMachiningDirection:getX() > 0.707 then + nSCC = MCH_SCC.ADIR_XP + end + + return nSCC +end + ------------------------------------------------------------------------------------------------------------- local function GetDrillingWithMillStrategy( Proc, Part) local ToolSearchParameters = {} @@ -172,6 +193,7 @@ function STR0013.Make( bAddMachining, Proc, Part, CustomParameters) MachiningToAdd = MachiningLib.InitMachiningParameters( MCH_MY.DRILLING) MachiningToAdd = BeamLib.MergeTables( MachiningToAdd, Strategy.Machinings[j]) MachiningToAdd.Steps.dStep = TOOLS[nIndexTool].dStep / 3 + MachiningToAdd.nSCC = GetSCC( -MachiningToAdd.vtToolDirection) -- se diametro foro più grande della fresa, ma non oltre il doppio del diametro, si fa contornatura a spirale elseif Proc.FeatureInfo.dDrillDiam < ( TOOLS[nIndexTool].dDiameter * 0.75) * 2 or Strategy.Parameters.bOnlyContouring then MachiningToAdd = MachiningLib.InitMachiningParameters( MCH_MY.MILLING) @@ -184,6 +206,7 @@ function STR0013.Make( bAddMachining, Proc, Part, CustomParameters) MachiningToAdd.LeadOut.dTangentDistance = 0.5 MachiningToAdd.LeadOut.dPerpDistance = 0.5 MachiningToAdd.LeadOut.dElevation = Proc.FeatureInfo.dDrillLen + MachiningToAdd.nSCC = GetSCC( -MachiningToAdd.vtToolDirection) -- se diametro foro più grande del doppio del diametro fresa, si fa svuotatura else MachiningToAdd = MachiningLib.InitMachiningParameters( MCH_MY.POCKETING) @@ -194,6 +217,7 @@ function STR0013.Make( bAddMachining, Proc, Part, CustomParameters) MachiningToAdd.LeadIn.nType = MCH_POCK_LI.HELIX MachiningToAdd.LeadIn.dTangentDistance = TOOLS[nIndexTool].dDiameter / 2 MachiningToAdd.LeadIn.dElevation = MachiningToAdd.Steps.dStep / 2 + MachiningToAdd.nSCC = GetSCC( -MachiningToAdd.vtToolDirection) end MachiningToAdd.nToolIndex = nIndexTool diff --git a/StrategyLibs/BLADETOWASTE.lua b/StrategyLibs/BLADETOWASTE.lua index 6b43936..ce94957 100644 --- a/StrategyLibs/BLADETOWASTE.lua +++ b/StrategyLibs/BLADETOWASTE.lua @@ -1025,7 +1025,7 @@ local function CutWithDicing( Proc, Part, OptionalParameters) -- scelta lama da sopra o da sotto local sChosenBladeType = '' if not nToolIndex then - nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, Face1) + nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part) end -- se non trovata lama la lavorazione non è fattibile