-- BeamExec.lua by Egalware s.r.l. 2024/04/02 -- Libreria esecuzione lavorazioni per Travi -- 2024/04/02 PRIMA VERSIONE CALCOLO LAVORAZIONI CON STRATEGIE -- Tabella per definizione modulo local BeamExec = {} -- Include require( 'EgtBase') -- Carico i dati globali local BeamData = require( 'BeamDataNew') -- carico librerie local BeamLib = require( 'BeamLib') local ID = require( 'Identity') local BCS = require( 'BasicCustomerStrategies') local FeatureLib = require( 'FeatureLib') local FaceData = require( 'FaceData') local MachiningLib = require( 'MachiningLib') local Logs = require( 'Logs') local TimeLib = require( 'TimeLib') EgtOutLog( ' BeamExec started', 1) EgtMdbSetGeneralParam( MCH_GP.MAXDEPTHSAFE, BeamData.COLL_SIC) EgtMdbSave() ------------------------------------------------------------------------------------------------------------- -- *** variabili globali *** ------------------------------------------------------------------------------------------------------------- -- TODO spostare in file separato TOOLS = {} -- tabella contenente tutti gli utensili STRATEGIES = {} -- tabella contenente le strategie disponibili per ogni feature STRATEGIES_CONFIG = {} -- tabella contenente i parametri di default delle strategie disponibili MACHININGS = {} -- tabella contenente le lavorazioni da applicare MACHININGS.Info = {} DB_MACH_APPLIED = {} -- tabella contenente informazioni delle lavorazioni create e aggiunte in lista PROCESSINGS = {} -- tabella contenente tutte le informazioni di ogni feature, processate per ogni rotazione RESULT = {} -- tabella contenente il risultato, feature per feature, dell'applicazione della strategia scelta e il resoconto dell'analisi fatta GENERAL_PARAMETERS = {} -- tabella contenente i parametri generali già letti e importati GENERAL_PARAMETERS_JSON = {} -- tabella contenente i parametri generali di default TIMER = TimeLib.new() -- tabella contenente l'oggetto timer che tiene traccia del tempo di esecuzione ------------------------------------------------------------------------------------------------------------- -- *** COSTANTI *** TODO -> DA SPOSTARE IN BEAMDATA??? ------------------------------------------------------------------------------------------------------------- TH_DIAMETER_HSK63 = 63 TH_LENGTH_HSK63 = 75 SIDEANGLE_DOVETAIL = 15 ------------------------------------------------------------------------------------------------------------- -- *** funzioni di base *** ------------------------------------------------------------------------------------------------------------- local function GetToolTypeNameFromToolTypeID( dToolTypeID) if dToolTypeID == MCH_TY.DRILL_STD then return 'DRILL_STD', 'DRILLBIT' elseif dToolTypeID == MCH_TY.DRILL_LONG then return 'DRILL_LONG', 'DRILLBIT' elseif dToolTypeID == MCH_TY.SAW_STD then return 'SAW_STD', 'SAWBLADE' elseif dToolTypeID == MCH_TY.SAW_FLAT then return 'SAW_FLAT', 'SAWBLADE' elseif dToolTypeID == MCH_TY.MILL_STD then return 'MILL_STD', 'MILL' elseif dToolTypeID == MCH_TY.MILL_NOTIP then return 'MILL_NOTIP', 'MILL' elseif dToolTypeID == MCH_TY.MORTISE_STD then return 'MORTISE_STD', 'MORTISE' else return nil end end ------------------------------------------------------------------------------------------------------------- local function IsToolOk( Tool) -- controllo che i dati necessari siano impostati if Tool.dMaxMaterial and Tool.dDiameter and Tool.dLength and Tool.sHead and Tool.sUUID then -- se DRILLBIT non ho altri dati if Tool.sFamily == 'DRILLBIT' then return true -- altrimenti controllo dati aggiuntivi altre famiglie di utensili elseif Tool.sFamily == 'MORTISE' then if Tool.dCornerRadius then return true end else if Tool.dThickness then return true end end end return false end ------------------------------------------------------------------------------------------------------------- function BeamExec.GetToolsFromDB() -- lista appoggio utensile local Tool = {} -- recupero tutti gli utensili : punte a forare, lame, frese e motoseghe Tool.sName = EgtTdbGetFirstTool( MCH_TF.DRILLBIT + MCH_TF.SAWBLADE + MCH_TF.MILL + MCH_TF.MORTISE) while Tool.sName ~= '' do -- imposto utensile come corrente per recuperarne i dati EgtTdbSetCurrTool( Tool.sName) -- verifico se utensile disponibile in attrezzaggio attuale e che abbia un tipo ben definito local bToolLoadedOnSetup, sToolTCPos = EgtFindToolInCurrSetup( Tool.sName) local nToolTypeId = EgtTdbGetCurrToolParam( MCH_TP.TYPE) local sToolType, sToolFamily = GetToolTypeNameFromToolTypeID( nToolTypeId) -- se verifica condizioni minime, recupero tutti gli altri dati if bToolLoadedOnSetup and sToolType then Tool.sTcPos = sToolTCPos Tool.sFamily = sToolFamily Tool.sType = sToolType Tool.nTypeId = nToolTypeId Tool.dMaxMaterial = EgtTdbGetCurrToolParam( MCH_TP.MAXMAT) Tool.dDiameter = EgtTdbGetCurrToolParam( MCH_TP.DIAM) Tool.dLength = EgtTdbGetCurrToolParam( MCH_TP.LEN) Tool.dTotalLength = EgtTdbGetCurrToolParam( MCH_TP.TOTLEN) Tool.dSpeed = EgtTdbGetCurrToolParam( MCH_TP.SPEED) Tool.bIsCCW = Tool.dSpeed < 0 Tool.Feeds = {} Tool.Feeds.dFeed = EgtTdbGetCurrToolParam( MCH_TP.FEED) Tool.Feeds.dStartFeed = EgtTdbGetCurrToolParam( MCH_TP.STARTFEED) Tool.Feeds.dEndFeed = EgtTdbGetCurrToolParam( MCH_TP.ENDFEED) Tool.Feeds.dTipFeed = EgtTdbGetCurrToolParam( MCH_TP.TIPFEED) Tool.sHead = EgtTdbGetCurrToolParam( MCH_TP.HEAD) Tool.nExit = EgtTdbGetCurrToolParam( MCH_TP.EXIT) Tool.SetupInfo = {} Tool.SetupInfo = BeamData.GetSetupInfo( Tool.sHead) if not Tool.SetupInfo.GetMinNzDownUp then Tool.SetupInfo.GetMinNzDownUp = BeamLib.GetMinNzDownUpDefault end if not Tool.SetupInfo.GetMinNz then Tool.SetupInfo.GetMinNz = BeamLib.GetMinNzDefault end if not Tool.SetupInfo.GetMaxNz then Tool.SetupInfo.GetMaxNz = BeamLib.GetMaxNzDefault end if not Tool.SetupInfo.dMaxMatDecrease then Tool.SetupInfo.dMaxMatDecrease = 0 end Tool.sUUID = EgtTdbGetCurrToolParam( MCH_TP.UUID) Tool.sUserNotes = EgtTdbGetCurrToolParam( MCH_TP.USERNOTES) Tool.dMaxDepth = EgtTdbGetCurrToolMaxDepth() or Tool.dMaxMaterial Tool.ToolHolder = {} Tool.ToolHolder.dDiameter = EgtTdbGetCurrToolThDiam() or TH_DIAMETER_HSK63 -- diametro standard HSK63 Tool.ToolHolder.dLength = EgtTdbGetCurrToolThLength() or TH_LENGTH_HSK63 -- lunghezza standard HSK63 -- parametri scritti nelle note Tool.nDouble = EgtGetValInNotes( Tool.sUserNotes, 'DOUBLE', 'd') Tool.bIsProfiledTool = not EgtTdbIsCurrToolStandardDraw() -- lettura parametri non comuni ( famiglia DRILLBIT non ha parametri specifici) if sToolFamily ~= 'DRILLBIT' then Tool.dThickness = EgtTdbGetCurrToolParam( MCH_TP.THICK) Tool.dLongitudinalOffset = EgtTdbGetCurrToolParam( MCH_TP.LONOFFSET) Tool.dRadialOffset = EgtTdbGetCurrToolParam( MCH_TP.RADOFFSET) -- recupero parametri propri delle frese if sToolFamily == 'MILL' then Tool.dStemDiameter = EgtTdbGetCurrToolParam( MCH_TP.STEMDIAM) or Tool.ToolHolder.dDiameter -- se non settato, considero diametro come ToolHolder Tool.dSideAngle = EgtTdbGetCurrToolParam( MCH_TP.SIDEANG) or 0 Tool.bIsVMill = Tool.dSideAngle > 40 -- verifico che parametri siano compatibili con una fresa a coda di rondine ( angolo di fianco standard Coda di rondine -> 15°) Tool.bIsDoveTail = Tool.sType == 'MILL_NOTIP' and abs( abs( Tool.dSideAngle) - SIDEANGLE_DOVETAIL) < 1 -- verifico che sia una fresa tipo T-Mill o BlockHaus Tool.dSideDepth = EgtGetValInNotes( Tool.sUserNotes, 'SIDEDEPTH', 'd') or 0 -- se non settato nell'utensile, dico che non ha massimo affondamento laterale Tool.bIsTMill = Tool.dSideDepth > 0 Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP', 'd') or ( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero un terzo del tagliente Tool.dSideStep = max( EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP', 'd') or floor( Tool.dDiameter / 3), 1) -- se non settato nell'utensile, considero un terzo del diametro, almeno 1mm Tool.bIsPen = abs( Tool.dSpeed) < 5 Tool.dPerformanceIndex = ( Tool.dDiameter * Tool.dMaxMaterial) / Tool.dLength -- recupero parametri propri delle lame elseif sToolFamily == 'SAWBLADE' then Tool.bIsUsedForLongCut = EgtGetValInNotes( Tool.sUserNotes, 'LONGCUT') == 1 or false -- false come valore di default Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP', 'd') or Tool.dThickness -- se non settato nell'utensile, considero lo spessore lama Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP', 'd') or Tool.dMaxMaterial -- se non settato nell'utensile, considero il massimo materiale Tool.dPerformanceIndex = 1 / ( Tool.dDiameter * Tool.dLength) -- recupero parametri propri delle motoseghe elseif sToolFamily == 'MORTISE' then Tool.dDistance = EgtTdbGetCurrToolParam( MCH_TP.DIST) or 90 -- 90mm dimensione standard aggregato catena Tool.bIsMortise = EgtGetValInNotes( Tool.sUserNotes, 'MORTISE') == 1 Tool.bIsChainSaw = not Tool.bIsMortise Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP', 'd') or floor( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero un terzo della lunghezza Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP', 'd') or ( Tool.dThickness - 1) -- se non settato nell'utensile, considero spessore catena meno 1mm di sicurezza Tool.dCornerRadius = EgtTdbGetCurrToolParam( MCH_TP.CORNRAD) Tool.dWidth = Tool.dDiameter Tool.dPerformanceIndex = 1 / Tool.dLength end -- drillbit else Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP', 'd') or ( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero un terzo del tagliente Tool.dSideStep = Tool.dDiameter -- si utilizza SOLO per i calcoli del MRR Tool.dPerformanceIndex = Tool.dDiameter / Tool.dLength end -- se tutti i dati necessari sono disponibili, inserisco utensile nella lista globale degli utensili disponibili if IsToolOk( Tool) then Tool.nIndex = #TOOLS + 1 table.insert( TOOLS, Tool) -- altrimenti scrivo nel log che l'utensile non è conforme else EgtOutLog( '*** ' .. Tool.sName .. ' : NOT-COMPLIANT ***', 1) end -- reset dati Tool = {} end -- recupero utensile successivo ( punte a forare, lame, frese e motoseghe) Tool.sName = EgtTdbGetNextTool( MCH_TF.DRILLBIT + MCH_TF.SAWBLADE + MCH_TF.MILL + MCH_TF.MORTISE) end end ------------------------------------------------------------------------------------------------------------- function BeamExec.GetGeneralParameters() local CompleteList = {} local sFile = BEAM.BASEDIR .. '\\Strategies\\GeneralParameters.json' -- se non esiste file JSON, esco subito if not EgtExistsFile( sFile) then return end GENERAL_PARAMETERS_JSON = BeamLib.ImportFileJSON( sFile) -- all'inizio si legge solo PROJECT e BTL. I parametri PIECE vengono letti direttamente dalle strategie GENERAL_PARAMETERS = BeamLib.LoadGeneralParametersInList( GENERAL_PARAMETERS_JSON) end ------------------------------------------------------------------------------------------------------------- function BeamExec.GetStrategiesFromJSONinBD( sAISetupConfigName) -- si legge il JSON contenente la configurazione da utilizzare solo se non è già stato importato if sAISetupConfigName and not STRATEGIES[sAISetupConfigName] then local sMachDir = EgtGetCurrMachineDir() local sFile = sMachDir .. '\\Beam\\AISetup\\' .. sAISetupConfigName .. '.json' -- se non esiste file JSON, annullo la lista contenente le strategie if not EgtExistsFile( sFile) then STRATEGIES[sAISetupConfigName] = nil return end local FeatureList = {} FeatureList = BeamLib.ImportFileJSON( sFile) -- metto la tabella letta nella lista globale STRATEGIES STRATEGIES[sAISetupConfigName] = {} STRATEGIES[sAISetupConfigName].GENERAL = FeatureList.GENERAL STRATEGIES[sAISetupConfigName].FEATURE = FeatureList.FEATURE end end -- TODO prevedere parametri per preferire carico del pezzo verticale oppure orizzontale? ------------------------------------------------------------------------------------------------------------- -- funzione che controlla validità delle combinazioni proposte local function IsCombinationAvailable( sCombination, nUnloadPos, GeneralParameters, bIsFlipRot) -- PREROTATION : flip-rot, si considerano tutte le posizioni possibili, a meno che non si voglia come da BTL if bIsFlipRot and GeneralParameters.GEN_sPiecesLoadingPosition ~= 'BTL_POSITION' then local nRotation90 = EgtIf( nUnloadPos + 1 > 4, nUnloadPos + 1 - 4, nUnloadPos + 1) local nRotation180 = EgtIf( nUnloadPos + 2 > 4, nUnloadPos + 2 - 4, nUnloadPos + 2) local nExtraRotation = EgtIf( nUnloadPos + 3 > 4, nUnloadPos + 3 - 4, nUnloadPos + 3) -- se fase di unload disabilitata e soluzioni con terza rotazione sempre disabilitate if string.sub( sCombination, nUnloadPos, nUnloadPos) ~= '1' or string.sub( sCombination, nExtraRotation, nExtraRotation) == '1' then return false elseif GeneralParameters.GEN_sPiecesLoadingPosition == 'STD_PRE_ROTATION' and ( nUnloadPos == 2 or nUnloadPos == 4) then return false else if ( not BeamData.ROT90 or GeneralParameters.GEN_sPiecesLoadingPosition == 'STD_PRE_ROTATION') and string.sub( sCombination, nRotation90, nRotation90) == '1' then return false elseif not BeamData.ROT180 and string.sub( sCombination, nRotation180, nRotation180) == '1' then return false else return true end end -- se invece si è nel caso standard di calcolo else -- NO ROTATION : solo posizione di partenza e non sono ammesse rotazioni if GeneralParameters.GEN_sPieceRotation == 'NOT_ALLOWED' then if sCombination == '1000' and nUnloadPos == 1 then return true else return false end -- STANDARD : posizione di scarico come posizionamento iniziale else local ExtraRotation = EgtIf( nUnloadPos + 3 > 4, nUnloadPos + 3 - 4, nUnloadPos + 3) if nUnloadPos ~= 1 then return false elseif string.sub( sCombination, nUnloadPos, nUnloadPos) == '1' and string.sub( sCombination, ExtraRotation, ExtraRotation) == '0' then if not BeamData.ROT90 and string.sub( sCombination, nUnloadPos+1, nUnloadPos+1) == '1' then return false elseif not BeamData.ROT180 and string.sub( sCombination, nUnloadPos+2, nUnloadPos+2) == '1' then return false else return true end else return false end end end end ------------------------------------------------------------------------------------------------------------- function BeamExec.GetAvailableCombinations( PartInfo, bIsFlipRot) local CombinationList = {} CombinationList.Rotations = {0, 0, 0, 0} -- indice rotazione attiva, per calcolo collect feature local nCycles = 1 -- se si sta calcolando il migliore posizionamento del pezzo, verifico se sono ammesse le combinazioni con pezzo invertito if bIsFlipRot and PartInfo.GeneralParameters.GEN_bAllowPieceInversion then nCycles = 2 end -- verifico tutte le combinazioni che possono essere considerate for nInvertIndex = 1, nCycles do for nUnloadPos = 1, 4 do for i = 1, BeamLib.BinaryToDecimal( 1111) do local nBitIndexCombination = BeamLib.DecimalToBinary( i) local sBitIndexCombination = BeamLib.CalculateStringBinaryFormat( nBitIndexCombination, 4) -- si calcolano le combinazioni all'inizio, ottimizzando calcolo della collect solo nelle rotazioni che possono essere considerate if IsCombinationAvailable( sBitIndexCombination, nUnloadPos, PartInfo.GeneralParameters, bIsFlipRot) then local Combination = {} Combination.sBitIndexCombination = sBitIndexCombination Combination.nUnloadPos = nUnloadPos table.insert( CombinationList, Combination) if nInvertIndex == 2 then Combination.bPartInCombiIsInverted = true end -- se posizionamento iniziale attivo if string.sub( sBitIndexCombination, 1, 1) == '1' then CombinationList.Rotations[1] = 1 end -- se attiva rotazione 90 if string.sub( sBitIndexCombination, 2, 2) == '1' then CombinationList.Rotations[2] = 1 end -- se attiva rotazione 180 if string.sub( sBitIndexCombination, 3, 3) == '1' then CombinationList.Rotations[3] = 1 end -- se attiva rotazione 270 if string.sub( sBitIndexCombination, 4, 4) == '1' then CombinationList.Rotations[4] = 1 end end end end end return CombinationList end ------------------------------------------------------------------------------------------------------------- -- *** 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() -- 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) -- sovramateriale intermedio nullo se non definito dOvmMid = ( dOvmMid or 0) -- 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 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) 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) -- Impostazione dell'attrezzaggio di default EgtImportSetup() -- Inserimento dei pezzi con il loro grezzo local nCnt = 0 local dLen = dRawL local idPrevRaw, dPrevDelta local dDeltaS = dOvmHead local dDeltaSMin = 0 local dDeltaE = BeamData.OVM_MID 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) EgtMoveToCornerRawPart( PARTS[i].idRaw, BeamData.ptOriXR, BeamData.dPosXR) EgtMoveRawPart( PARTS[i].idRaw, Vector3d( dLen - dRawL, 0, 0)) -- assegno ordine in lavorazione 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 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 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 -- 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 else local sOut = 'Error: part L(' .. EgtNumToString( dPartLen, 1) .. ') too big for raw part L(' .. EgtNumToString( dLen - 0.1, 1) .. ')' 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 nCnt = nCnt + 1 EgtSetInfo( idRaw, 'ORD', nCnt) -- aggiorno distanza dell'ultimo pezzo dall'eventuale grezzo scaricabile if EgtGetRawPartBBox( idRaw):getDimX() < BeamData.dMinRaw then PARTS[#PARTS].dDistanceToNextPiece = 10000 end else PARTS[#PARTS].dDistanceToNextPiece = 10000 end return true end ------------------------------------------------------------------------------------------------------------- function BeamExec.CalcMinUnloadableRaw( dRawW, dRawH) -- TODO convertire in GetMinUnloadableRaw che viene richiamata all'inizio delle funzioni che necessitano di dMinRaw if BeamData.GetMinUnloadableRaw then BeamData.dMinRaw = BeamData.GetMinUnloadableRaw( dRawW, dRawH) else local H_S = 200 local H_L = 400 -- Determinazione minimo grezzo scaricabile if dRawH <= H_S then BeamData.dMinRaw = BeamData.MINRAW_S elseif dRawH <= H_L then local Coeff = ( dRawH - H_S) / ( H_L - H_S) BeamData.dMinRaw = ( 1 - Coeff) * BeamData.MINRAW_S + Coeff * BeamData.MINRAW_L else BeamData.dMinRaw = BeamData.MINRAW_L end end -- lettura minimo pezzo pinzabile BeamData.dMinClampRaw = BeamData.LEN_VERY_SHORT_PART or 400 end ------------------------------------------------------------------------------------------------------------- -- *** Inserimento delle lavorazioni nelle travi *** ------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------- local function GetStrategies( Proc, sAISetupConfigName) local AvailableStrategiesForProc = nil -- se la lista STRATEGIES è stata letta da JSON (quindi non è vuota), ritorno le strategie possibili if STRATEGIES and sAISetupConfigName and STRATEGIES[sAISetupConfigName] and #STRATEGIES[sAISetupConfigName].FEATURE > 0 then AvailableStrategiesForProc = BeamLib.GetStrategiesFromList( Proc, STRATEGIES[sAISetupConfigName]) end -- se non ho trovato strategie disponibili nel JSON, o se JSON non presente, lancio script che setta le strategie in modo statico, come definito con cliente if not AvailableStrategiesForProc then AvailableStrategiesForProc = BCS.GetStrategiesFromBasicCustomerStrategies( Proc) end return AvailableStrategiesForProc end ------------------------------------------------------------------------------------------------------------- local function GetFeatureForcedStrategy( Proc) -- cerco nelle note se è stata forzata una strategia specifica local sStrategyId = EgtGetInfo( Proc.id, 'STRATEGY', 's') -- se è presente la strategia forzata if sStrategyId then -- si prepara tabella strategia. In questa fase si salva solo id strategia e il fatto che è stata forzata. I parametri forzati vengono letti all'esecuzioen della strategia local StrategyToProc = {} local ParamList = {} ParamList.bForcedStrategy = true ParamList.sStrategyId = sStrategyId -- ritorno la lista strategia con parametri table.insert( StrategyToProc, ParamList) return StrategyToProc end return nil end ------------------------------------------------------------------------------------------------------------- local function CollectFeatures( Part, dRotIndex) -- recupero le feature local nProcCount = 0 local vProc = {} local LayerId = {} LayerId[1] = BeamLib.GetAddGroup( Part.id) LayerId[2] = EgtGetFirstNameInGroup( Part.id or GDB_ID.NULL, 'Processings') for nInd = 1, 2 do local ProcId = EgtGetFirstInGroup( LayerId[nInd] or GDB_ID.NULL) while ProcId do local nEntType = EgtGetType( ProcId) if nEntType == GDB_TY.SRF_MESH or nEntType == GDB_TY.EXT_TEXT or nEntType == GDB_TY.CRV_LINE or nEntType == GDB_TY.CRV_ARC or nEntType == GDB_TY.CRV_BEZ or nEntType == GDB_TY.CRV_COMPO then local nGrp = EgtGetInfo( ProcId, 'GRP', 'i') local nPrc = EgtGetInfo( ProcId, 'PRC', 'i') local nDo = EgtGetInfo( ProcId, 'DO', 'i') or 1 if nGrp and nPrc and nDo == 1 then local Proc = {} Proc.isVirtualProc = nInd == 1 -- feature non presente in lista originale BTL ma aggiunta da automatismo Proc.idPart = Part.id Proc.idRaw = Part.idRaw Proc.nIndexPartInParts = Part.nIndexInParts Proc.nCurrentRotation = dRotIndex Proc.id = ProcId -- id della feature btl ( se non presente info, si prende id dell'entità geometrica) Proc.idFeature = EgtGetInfo( Proc.id, 'PRID', 's') or Proc.id Proc.nGrp = nGrp Proc.nPrc = nPrc Proc.nFlg = 1 Proc.nFct = EgtSurfTmFacetCount( ProcId) or 0 Proc.idCut = EgtGetInfo( EgtGetParent( EgtGetParent( ProcId)), 'CUTID', 'i') or 0 Proc.idTask = EgtGetInfo( ProcId, 'TASKID', 'i') or 0 Proc.b3Box = EgtGetBBoxGlob( ProcId, GDB_BB.STANDARD) EgtOutLog( '------Feature ' .. Proc.idFeature .. '------') Proc.Topology = {} -- se esiste la geometria if Proc.b3Box and not Proc.b3Box:isEmpty() then -- informazioni facce e topologia Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part) -- volume feature approssimato Proc.dVolume = FeatureLib.GetFeatureVolume( Proc, Part) -- se trimesh, numero di parti di cui è composta Proc.nParts = EgtSurfTmPartCount( Proc.id) or 1 -- calcolo topologia solo se necessario, altrimenti si sfruttano le informazioni della feature BTL local bIsFeatureReadyForProcessing = false if FeatureLib.NeedTopologyFeature( Proc, Part) then Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc) Proc.Faces = FaceData.GetFacesInfo( Proc, Part) Proc.Topology = FeatureLib.ClassifyTopology( Proc, Part) -- se topologia feature riconosciuta, oppure da non calcolare perchè il riconoscimento topologico è basato sulla feature stessa if Proc.Topology.sName ~= 'NOT_IMPLEMENTED' then Proc.MainFaces = FaceData.GetMainFaces( Proc, Part) bIsFeatureReadyForProcessing = true end else Proc = FeatureLib.GetAdditionalInfo( Proc, Part) Proc.Topology = FeatureLib.GetTopologyFromFeature( Proc, Part) bIsFeatureReadyForProcessing = true end -- se la feature è stata compresa if bIsFeatureReadyForProcessing then -- se la processing ha una strategia forzata, riporto tutto nella proc local vForcedStrategy = GetFeatureForcedStrategy( Proc) if vForcedStrategy then Proc.AvailableStrategies = vForcedStrategy -- altrimenti cerco tra le strategie disponibili else Proc.AvailableStrategies = GetStrategies( Proc, Part.sAISetupConfig) end -- se ci sono strategie disponibili, aggiungo a lista delle feature da lavorare if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then Proc.AvailableStrategies.dAllStrategiesTotalTime = 0 nProcCount = nProcCount + 1 Proc.nIndexInVProc = nProcCount table.insert( vProc, Proc) -- altrimenti errore (non ci sono strategie per lavorare la topologia riconosciuta) else Proc.nFlg = 0 nProcCount = nProcCount + 1 Proc.nIndexInVProc = nProcCount table.insert( vProc, Proc) EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies') end -- calcolo riduzione lunghezza pinzabile testa/coda Proc.NotClampableLength = FeatureLib.CalculateFeatureNotClampableLengths( Proc, Part) -- si verifica se la feature, lavorata in questa fase, compromette lettura misura laser Proc.bHindersLaserMeasure = FeatureLib.CalculateFeatureHindersLaserMeasure( Proc, Part) -- altrimenti errore (serviva riconoscimento topologico, ma non è stato possibile farlo) else Proc.nFlg = 0 nProcCount = nProcCount + 1 Proc.nIndexInVProc = nProcCount table.insert( vProc, Proc) EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies') end else Proc.nFlg = 0 nProcCount = nProcCount + 1 Proc.nIndexInVProc = nProcCount table.insert( vProc, Proc) EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' is empty (no geometry)') end end end ProcId = EgtGetNext( ProcId) end end return vProc end ------------------------------------------------------------------------------------------------------------- local function AreDrillingsMirrored( Proc, ProcMirror, Part) if Proc.id == ProcMirror.id then return false end -- geometria ausiliaria foro principale AuxId = EgtGetInfo( Proc.id, 'AUXID', 'i') if AuxId then AuxId = AuxId + Proc.id end if not AuxId or EgtGetType( AuxId ) ~= GDB_TY.CRV_ARC then return false end -- geometria ausiliaria foro specchiato local AuxIdMirror = EgtGetInfo( ProcMirror.id, 'AUXID', 'i') if AuxIdMirror then AuxIdMirror = AuxIdMirror + ProcMirror.id end if not AuxIdMirror or EgtGetType( AuxIdMirror ) ~= GDB_TY.CRV_ARC then return false end -- dati del foro principale local vtExtr = EgtCurveExtrusion( AuxId, GDB_RT.GLOB) local ptBC = EgtGP( AuxId, GDB_RT.GLOB) -- dati del foro specchiato local vtExtrMirror = EgtCurveExtrusion( AuxIdMirror, GDB_RT.GLOB) local ptBCMirror = EgtGP( AuxIdMirror, GDB_RT.GLOB) -- direzione fori local nDouble if AreOppositeVectorApprox( vtExtr, vtExtrMirror) then -- fori lungo Y -- per macchine tipo PF il foro principale è sul lato back, per macchine tipo PF1250 è sul lato front -- TODO eliminare i riferimenti a DOWN_HEAD e TWO_EQUAL_HEADS e sostituirli con check iniziale sulla disponibilità di teste? if ( BeamData.TWO_EQUAL_HEADS and AreSameVectorApprox( vtExtr, Y_AX())) or ( BeamData.DOWN_HEAD and AreOppositeVectorApprox( vtExtr, Y_AX())) then nDouble = 2 -- fori lungo Z elseif BeamData.DOWN_HEAD and AreSameVectorApprox( vtExtr, Z_AX()) then nDouble = 3 else return false end else return false end -- centri allineati, equidistanti dalla mezzeria trave, non troppo vicini local vtDisplacement = ptBC - ptBCMirror local ptCenRaw = Part.b3Raw:getCenter() if nDouble == 2 then local dYMinDistance = max( Proc.b3Box:getMin():getY(), ProcMirror.b3Box:getMin():getY()) - min( Proc.b3Box:getMax():getY(), ProcMirror.b3Box:getMax():getY()) if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getZ()) < 100 * GEO.EPS_SMALL and ( abs( ptBC:getY() - ptCenRaw:getY()) - abs( ptBCMirror:getY() - ptCenRaw:getY())) < 100 * GEO.EPS_SMALL and dYMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then return false end else local dZMinDistance = max( Proc.b3Box:getMin():getZ(), ProcMirror.b3Box:getMin():getZ()) - min( Proc.b3Box:getMax():getZ(), ProcMirror.b3Box:getMax():getZ()) if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getY()) < 100 * GEO.EPS_SMALL and ( abs( ptBC:getZ() - ptCenRaw:getZ()) - abs( ptBCMirror:getZ() - ptCenRaw:getZ())) < 100 * GEO.EPS_SMALL and dZMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then return false end end -- fori della stessa profondità if abs( Proc.dLen - ProcMirror.dLen) > 10 * GEO.EPS_SMALL then return false end return true end ------------------------------------------------------------------------------------------------------------- local function GetFeatureInfoAndDependency( vProcSingleRot, Part) -- ciclo tutte le feature for i = 1, #vProcSingleRot do local Proc = vProcSingleRot[i] -- se feature abilitata alla lavorazione if Proc.nFlg ~= 0 then -- controllo la feature con tutte le altre per recuperare le dipendenze for j = 1, #vProcSingleRot do local ProcB = vProcSingleRot[j] -- non si controlla la feature con se stessa o se feature disabilitata if i ~= j and ProcB.nFlg ~= 0 then -- 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 Proc.SlaveProcIndexes = {} end table.insert( Proc.SlaveProcIndexes, j) ProcB.nIndexMasterProc = i ProcB.nFlg = 0 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 if not Proc.SlaveProcIndexes then Proc.SlaveProcIndexes = {} end table.insert( Proc.SlaveProcIndexes, j) ProcB.nIndexMasterProc = i ProcB.nFlg = 0 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 ID.IsDrill( ProcB) and Overlaps( Proc.b3Box, ProcB.b3Box) then Proc.bPassedByHole = true end -- verifiche per specchiature -- TODO eliminare i riferimenti a DOWN_HEAD e TWO_EQUAL_HEADS e sostituirli con check iniziale sulla disponibilità di teste? if BeamData.DOWN_HEAD or BeamData.TWO_EQUAL_HEADS then -- forature if BeamData.DOUBLE_HEAD_DRILLING and ID.IsDrill( Proc) and ID.IsDrill( ProcB) and not Proc.Mirror then if AreDrillingsMirrored( Proc, ProcB, Part) then Proc.Mirror = ProcB end end end end end end end return vProcSingleRot end ------------------------------------------------------------------------------------------------------------- -- caricamento librerie strategie (standard o speciali) se presenti nella Proc come strategie disponibili per questo Processing local function RunStrategyLibraries( sStrategyId) -- file script local StrategyScriptName = sStrategyId .. '\\' .. sStrategyId local StrategyScriptPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyScriptName .. '.lua' local StrategyScriptPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyScriptName .. '.lua' -- file config local StrategyConfigName = sStrategyId .. '\\' .. sStrategyId .. '.json' local StrategyConfigPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyConfigName local StrategyConfigPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyConfigName local StrategyLib = {} if ( EgtExistsFile( StrategyConfigPathStandard) and EgtExistsFile( StrategyScriptPathStandard)) or ( EgtExistsFile( StrategyConfigPathSpecial) and EgtExistsFile( StrategyScriptPathSpecial)) then -- caricamento script strategia come libreria StrategyLib.Script = require( StrategyScriptName) -- se config strategia non ancora caricato, importo il JSON if not STRATEGIES_CONFIG[sStrategyId] then if EgtExistsFile( StrategyConfigPathStandard) then STRATEGIES_CONFIG[sStrategyId] = BeamLib.ImportFileJSON( StrategyConfigPathStandard) else STRATEGIES_CONFIG[sStrategyId] = BeamLib.ImportFileJSON( StrategyConfigPathSpecial) end end StrategyLib.Config = STRATEGIES_CONFIG[sStrategyId] return StrategyLib else return {} end end ------------------------------------------------------------------------------------------------------------- -- ritorna l'indice della strategia migliore, tra le due passate local function GetIndexBestStrategyFromComparison( AvailableStrategies, Part, nIndex1, nIndex2) local dChosenIndex = 0 -- controllo indici if nIndex1 == 0 and nIndex2 == 0 then dChosenIndex = 0 elseif nIndex1 == nIndex2 then dChosenIndex = nIndex1 elseif nIndex1 == 0 then -- basta che sia applicabile if AvailableStrategies[nIndex2].Result and ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' or AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed') then dChosenIndex = nIndex2 end elseif nIndex2 == 0 then -- basta che sia applicabile if AvailableStrategies[nIndex1].Result and ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' or AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed') then dChosenIndex = nIndex1 end elseif not AvailableStrategies[nIndex1].Result or not AvailableStrategies[nIndex2].Result then dChosenIndex = 0 elseif ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Completed') or ( AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex2].Result.sStatus == 'Not-Applicable') then dChosenIndex = nIndex1 elseif ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' and AvailableStrategies[nIndex1].Result.sStatus ~= 'Completed') or ( AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex1].Result.sStatus == 'Not-Applicable') then dChosenIndex = nIndex2 else -- se le due strategie hanno stesso stato e sono entrambe applicabili (quindi entrambe complete o entrambe non-complete) if AvailableStrategies[nIndex1].Result.sStatus ~= 'Not-Applicable' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Not-Applicable' and AvailableStrategies[nIndex1].Result.sStatus == AvailableStrategies[nIndex2].Result.sStatus then -- si predilige strategia con rating composito più alto if AvailableStrategies[nIndex1].Result.dCompositeRating > AvailableStrategies[nIndex2].Result.dCompositeRating then dChosenIndex = nIndex1 elseif AvailableStrategies[nIndex2].Result.dCompositeRating > AvailableStrategies[nIndex1].Result.dCompositeRating then dChosenIndex = nIndex2 -- altrimenti si prende la strategia con indice più basso else dChosenIndex = EgtIf( nIndex1 < nIndex2, nIndex1, nIndex2) end end end return dChosenIndex end ------------------------------------------------------------------------------------------------------------- -- funzione che trova la strategia migliore tra quelle disponibili local function GetFeatureBestStrategy( Proc, Part) if Proc.nFlg ~= 0 then local nIndexBestStrategy = 0 -- controllo se ci sono strategie disponibili if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then -- ciclo tutte le strategie della feature for nIndexCurrentStrategy = 1, #Proc.AvailableStrategies do -- scelgo la migliore strategia tra le due nIndexBestStrategy = GetIndexBestStrategyFromComparison( Proc.AvailableStrategies, Part, nIndexCurrentStrategy, nIndexBestStrategy) -- salvo sulla proc la migliore strategia end if nIndexBestStrategy ~= 0 then Proc.ChosenStrategy = BeamLib.TableCopyDeep( Proc.AvailableStrategies[nIndexBestStrategy]) Proc.nIndexBestStrategy = nIndexBestStrategy end end end return Proc end ------------------------------------------------------------------------------------------------------------- -- funzione che trova la strategia migliore tra quelle disponibili local function GetBestStrategyFromProcList( vProcSingleRot, Part) for i = 1, #vProcSingleRot do -- processo tutte le feature attive local Proc = vProcSingleRot[i] Proc = GetFeatureBestStrategy( Proc, Part) end return vProcSingleRot end ------------------------------------------------------------------------------------------------------------- -- funzione che processa tutte le feature del pezzo local function CalculateStrategies( vProcSingleRot, Part) -- per ogni feature for i = 1, #vProcSingleRot do -- processo tutte le feature attive local Proc = vProcSingleRot[i] if Proc.nFlg ~= 0 then -- controllo se ci sono strategie disponibili if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then -- se le strategie disponibili sono le basic, non si possono customizzare valori di default da interfaccia -- si leggono allora eventuali parametri di default scritti come info sulla feature if Proc.AvailableStrategies.bIsBasicStrategy then -- si recuperano eventuali parametri custom for j = 1, #Proc.AvailableStrategies do -- essendo una strategia basic, la lista dei parametri custom dovrebbe essere sempre vuota. Si leggono ora if not Proc.AvailableStrategies[j].ParameterList then Proc.AvailableStrategies[j].ParameterList = BCS.GetParametersFromBasicCustomerStrategies( Proc, Proc.AvailableStrategies[j].sStrategyId) end end -- si riprocessano le strategie dopo che sono stati letti i parametri Proc.AvailableStrategies = BCS.UpdateStrategies( Proc.AvailableStrategies) end -- ciclo tutte le strategie della feature for nIndexCurrentStrategy = 1, #Proc.AvailableStrategies do -- eseguo file config con i parametri di default local CurrentStrategy = {} CurrentStrategy = RunStrategyLibraries( Proc.AvailableStrategies[nIndexCurrentStrategy].sStrategyId) -- controllo che le librerie siano state effettivamente caricate if CurrentStrategy.Config and CurrentStrategy.Script then -- eseguo la strategia solo come calcolo fattibilità e voto. Non si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati _, Proc.AvailableStrategies[nIndexCurrentStrategy].Result = CurrentStrategy.Script.Make( false, Proc, Part, Proc.AvailableStrategies[nIndexCurrentStrategy]) -- TODO da capire se dare un tempo molto alto oppure se dare errore perchè non deve mai capitare. Per ora si setta tempo alto -- se tempo non calcolato, si setta un tempo molto alto, 99 minuti if not Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine or Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine == 0 then Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine = 99 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 break end -- se non trovo i file della strategia (Script e Config), scrivo che non è più disponibile else Proc.AvailableStrategies[nIndexCurrentStrategy].Result = {} Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sInfo = 'Strategy not found' Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus = 'Not-Applicable' end end -- si calcola il composite rating delle strategie Proc.AvailableStrategies = FeatureLib.CalculateStrategiesCompositeRating( Proc.AvailableStrategies, Part.GeneralParameters.GEN_sMachiningStrategy) end end end return vProcSingleRot end ------------------------------------------------------------------------------------------------------------- -- Ordina le feature in base a fase di lavorazione -- 1) Head : ( intestatura) -- 2) Standard : ( lavorazioni standard che non impattano sulla coda) -- 3) AdvanceTail : ( lavorazioni che richiedono taglio di separazione, ma che devono essere fatte prima perchè il taglio di separazione farebbe cadere il pezzo) -- 4) Split : ( taglio di separazione) -- 5) Tail : ( lavorazioni di coda, fatte dopo il taglio di separazione) local function OrderFeatures( vProc) local vProcToSort = vProc -- funzione di confronto. TRUE = B1 prima di B2. FALSE = B2 prima di B1 local function CompareFeatures( B1, B2) -- se secondo disabilitato, va lasciato dopo if B1.nFlg ~= 0 and B2.nFlg == 0 then return true elseif B2.nFlg ~= 0 and B1.nFlg == 0 then return false -- se entrambi disabilitati seguo l'Id elseif B1.nFlg == 0 and B2.nFlg == 0 then return ( B1.id < B2.id) -- se in rotazioni diverse, si mette in ordine di rotazioni elseif B1.nRot ~= B2.nRot then return ( B1.nRot < B2.nRot) -- se primo è taglio di testa, va prima degli altri casi elseif B1.Head and ( B2.Standard or B2.AdvanceTail or B2.Split or B2.Tail) then return true -- se primo è taglio standard, va prima degli altri casi elseif B1.Standard and ( B2.AdvanceTail or B2.Split or B2.Tail) then return true -- se primo è taglio di testa anticipata, va prima degli altri casi elseif B1.AdvanceTail and ( B2.Split or B2.Tail) then return true -- se primo è taglio di separazione, va prima delle lavorazioni di coda elseif B1.Split and B2.Tail then return true -- se da lavorare in stessa fase pezzo elseif B1.Head == B2.Head or B1.Standard == B2.Standard or B1.AdvanceTail == B2.AdvanceTail or B1.Split == B2.Split or B1.Tail == B2.Tail then -- confronto standard if abs( B1.b3Box:getCenter():getX() - B2.b3Box:getCenter():getX()) > 0.4 * ( B1.b3Box:getDimX() + B2.b3Box:getDimX()) then return B1.b3Box:getCenter():getX() > B2.b3Box:getCenter():getX() elseif abs( B1.b3Box:getCenter():getY() - B2.b3Box:getCenter():getY()) > 0.2 * ( B1.b3Box:getDimY() + B2.b3Box:getDimY()) then return B1.b3Box:getCenter():getY() > B2.b3Box:getCenter():getY() elseif abs( B1.b3Box:getCenter():getZ() - B2.b3Box:getCenter():getZ()) > 0.1 * ( B1.b3Box:getDimZ() + B2.b3Box:getDimZ()) then return B1.b3Box:getCenter():getZ() > B2.b3Box:getCenter():getZ() end -- altrimenti si inverte else return false end end -- test della funzione di ordinamento if EgtGetDebugLevel() >= 3 then EgtOutLog( ' CompareFeatures Test ') local bCompTest = true for i = 1, #vProcToSort do for j = i + 1, #vProcToSort do local bComp1 = CompareFeatures( vProcToSort[i], vProcToSort[j]) local bComp2 = CompareFeatures( vProcToSort[j], vProcToSort[i]) if bComp1 == bComp2 then bCompTest = false EgtOutLog( string.format( ' ProcId : %d vs %d --> ERROR', vProcToSort[i].id, vProcToSort[j].id)) end end end if bCompTest then EgtOutLog( ' ALL OK') end end -- eseguo ordinamento table.sort( vProcToSort, CompareFeatures) return vProcToSort end ------------------------------------------------------------------------------------------------------------- local function AddFeatureResultToGlobalList( Part, Proc, OptionalParameters) local sStrategyId local sStatus local dCompletionIndex local dCompositeRating local sInfo local sApplyInfo if not OptionalParameters then OptionalParameters = {} end local nRotation = OptionalParameters.nRotation or 1 if Proc.nIndexMasterProc then -- se è una feature non presente in elenco ed è saltata perchè lavorata da un'altra feature, non si fa passare dai RESULT. -- se non avesse una master associata, allora risulterà come messaggio su pezzo if Proc.isVirtualProc then return end local nOffsetIndex = EgtIf( Part.bPartInCombiIsInverted, 4, 0) local ChosenStrategyTable = PROCESSINGS[Proc.nIndexPartInParts].Rotation[Proc.nIndexRotation+nOffsetIndex][Proc.nIndexMasterProc].ChosenStrategy if ChosenStrategyTable then sStrategyId = ChosenStrategyTable.sStrategyId sStatus = ChosenStrategyTable.Result.sStatus dCompletionIndex = ChosenStrategyTable.Result.dCompletionIndex dCompositeRating = ChosenStrategyTable.Result.dCompositeRating local idFeature = PROCESSINGS[Proc.nIndexPartInParts].Rotation[Proc.nIndexRotation+nOffsetIndex][Proc.nIndexMasterProc].idFeature sApplyInfo = 'Skipped. Feature machined: ' .. EgtNumToString( idFeature) end elseif Proc.ChosenStrategy then sStrategyId = Proc.ChosenStrategy.sStrategyId sStatus = Proc.ChosenStrategy.Result.sStatus dCompletionIndex = Proc.ChosenStrategy.Result.dCompletionIndex dCompositeRating = Proc.ChosenStrategy.Result.dCompositeRating sInfo = Proc.ChosenStrategy.Result.sInfo end RESULT[#RESULT+1] = { sType = 'Feature', id = Proc.id, idFeature = Proc.idFeature, idTask = Proc.idTask, idCut = Proc.idCut, nFlg = Proc.nFlg, nRotation = nRotation, idPart = Proc.idPart, ChosenStrategy = { sStrategyName = sStrategyId, sStatus = sStatus, dCompletionIndex = dCompletionIndex, dCompositeRating = dCompositeRating, sInfo = sInfo, sApplyInfo = sApplyInfo } } -- scrivo le strategie disponibili solo se ne esistono if Proc.AvailableStrategies then RESULT[#RESULT].AvailableStrategies = BeamLib.TableCopyDeep( Proc.AvailableStrategies) end -- si segna nella Proc la sua posizione nel RESULT Proc.nIndexInResult = #RESULT return RESULT end ------------------------------------------------------------------------------------------------------------- function BeamExec.AddApplyResultToGlobalList( nErr, idCut, sMsg) RESULT[#RESULT+1] = { sType = 'Part', idCut = idCut, idTask = 0, nErr = nErr, sMsg = sMsg } end ------------------------------------------------------------------------------------------------------------- -- esegue le strategie migliori che ha precedentemente scelto local function CalculateMachinings( vProc, Part, nInitialRotation) local n0Rotation = nInitialRotation local n90Rotation = nInitialRotation + 1 if n90Rotation > 4 then n90Rotation = n90Rotation - 4 end local n180Rotation = nInitialRotation + 2 if n180Rotation > 4 then n180Rotation = n180Rotation - 4 end local nCurrRotation = 1 -- applico le strategie scelte for i = 1, #vProc do -- processo tutte le feature attive applicando le lavorazioni local Proc = vProc[i] -- se la feature deve essere processata if Proc.nFlg ~= 0 then -- si sistemano i pezzi per le rotazioni if Proc.bDown and nCurrRotation ~= n180Rotation then BeamLib.RotateRawPart( Part, n180Rotation - nCurrRotation) nCurrRotation = n180Rotation elseif Proc.bSide and nCurrRotation ~= n90Rotation then BeamLib.RotateRawPart( Part, n90Rotation - nCurrRotation) nCurrRotation = n90Rotation elseif Proc.bStd and nCurrRotation ~= n0Rotation then BeamLib.RotateRawPart( Part, n0Rotation - nCurrRotation) nCurrRotation = n0Rotation end -- aggiorno info pezzo Part.b3Raw = EgtGetRawPartBBox( Part.idRaw) Part.b3Part = EgtGetBBoxGlob( Part.idBoxTm, GDB_BB.STANDARD) -- si applicano le strategie if Proc.ChosenStrategy then -- carico file script strategia (non serve verificare presenza del file perchè già fatto durante scelta strategia) 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) -- se tutte le strategie disponibili non sono applicabili else local nOffsetIndex = EgtIf( Part.bPartInCombiIsInverted, 4, 0) -- se non esiste una strategia scelta (non dovrebbe mai succedere) cancello da lista generale PROCESSINGS[Proc.nIndexPartInParts].Rotation[Proc.nIndexRotation+nOffsetIndex][Proc.nIndexInVProc].ChosenStrategy = nil -- TODO dare messaggio che la feature non è stata eseguita nonostante la presenza di strategie disponibili end end -- scrivo risultato in tabella globale AddFeatureResultToGlobalList( Part, Proc, { nRotation = nCurrRotation}) end -- ripristino pezzo in posizione originale if nCurrRotation ~= 1 then BeamLib.RotateRawPart( Part, 1 - nCurrRotation) -- aggiorno info pezzo Part.b3Raw = EgtGetRawPartBBox( Part.idRaw) Part.b3Part = EgtGetBBoxGlob( Part.idBoxTm, GDB_BB.STANDARD) end return MACHININGS end ------------------------------------------------------------------------------------------------------------- local function PrintFeatures( vProc, Part) if vProc then EgtOutLog( ' RawBox=' .. tostring( Part.b3Box)) for i = 1, #vProc do local Proc = vProc[i] local sOut = string.format( ' Id=%3d Grp=%1d Prc=%3d Flg=%2d Fct=%2d TopoName=%s', Proc.id, Proc.nGrp, Proc.nPrc, Proc.nFlg, Proc.nFct, Proc.Topology.sName or '') EgtOutLog( sOut) end end end ------------------------------------------------------------------------------------------------------------- -- funzione che recupera tutti i processing di tutti i pezzi in tutte le rotazioni function BeamExec.GetProcessings( PARTS, bIsFlipRot) -- si aprono i limiti tavola per permettere rotazioni di pezzi più larghi della tavola EgtSetTableAreaOffset( 2000, 2000, 2000, 2000) for nPart = 1, #PARTS do if not PARTS[nPart].id and PARTS[nPart].b3Raw:getDimX() < BeamData.dMinRaw then break end local vProcRot = {} -- 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) -- per ogni inversione for nInvertIndex = 1, nCycles do -- per ogni rotazione for nRotIndex = 1, 4 do local nOffsetIndex = EgtIf( nInvertIndex == 2, 4, 0) local nIndex = nRotIndex + nOffsetIndex -- 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 table.insert( vProcRot, CollectFeatures( PARTS[nPart], nIndex)) -- 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]) else -- inserisco una tabella vuota table.insert( vProcRot, {}) end -- rotazione pezzo di 90° per volta BeamLib.RotateRawPart( PARTS[nPart], 1) -- aggiorno info pezzo PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw) PARTS[nPart].b3Part = EgtGetBBoxGlob( PARTS[nPart].idBoxTm, GDB_BB.STANDARD) end -- se si devono calcolare anche gli invertiti if bCalcInverted then -- inversione pezzo testa-coda BeamLib.InvertRawPart( PARTS[nPart], 2) -- aggiorno info pezzo PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw) PARTS[nPart].b3Part = EgtGetBBoxGlob( PARTS[nPart].idBoxTm, GDB_BB.STANDARD) end end local Part = {} Part.Rotation = vProcRot -- recupero le feature di lavorazione della trave table.insert( PROCESSINGS, Part) end return PROCESSINGS end ------------------------------------------------------------------------------------------------------------- -- funzione che decide la migliore tra le combinazioni di rotazione disponibili local function GetBestCombination( ListToCompare, Part) local nIndexBestCombination = 1 if #ListToCompare == 1 then nIndexBestCombination = 1 else for ListIndex = 2, #ListToCompare do -- se rotazione non ammessa, si sceglie la migliore soluzione SENZA rotazioni if Part.GeneralParameters.GEN_sPieceRotation == 'NOT_ALLOWED' then -- scelgo soluzione senza rotazioni if ListToCompare[ListIndex].nRotations == 0 then -- scelgo soluzione con voto più alto if ListToCompare[nIndexBestCombination].dTotalRating + 10 * GEO.EPS_SMALL < ListToCompare[ListIndex].dTotalRating then nIndexBestCombination = ListIndex end end -- se rotazioni ammesse, si sceglie in base a peso singola rotazione else local bBestComplete = ListToCompare[nIndexBestCombination].nNotComplete == 0 and ListToCompare[nIndexBestCombination].nNotExecute == 0 local bOtherComplete = ListToCompare[ListIndex].nNotComplete == 0 and ListToCompare[ListIndex].nNotExecute == 0 -- prediligo combinazione completa if bBestComplete and not bOtherComplete then ; -- la migliore resta la stessa elseif not bBestComplete and bOtherComplete then nIndexBestCombination = ListIndex -- altrimenti guardo il voto else -- se rotazione ha un grande impatto if Part.GeneralParameters.GEN_sPieceRotation == 'IF_NECESSARY' then -- scelgo soluzione con meno feature saltate if ListToCompare[ListIndex].nNotExecute < ListToCompare[nIndexBestCombination].nNotExecute then nIndexBestCombination = ListIndex -- scelgo soluzione con meno rotazioni indipendentemente dal voto elseif ListToCompare[nIndexBestCombination].nRotations > ListToCompare[ListIndex].nRotations then nIndexBestCombination = ListIndex -- se stesso numero di rotazioni elseif ListToCompare[nIndexBestCombination].nRotations == ListToCompare[ListIndex].nRotations then -- si sceglie soluzione con più feature complete if ListToCompare[ListIndex].nComplete > ListToCompare[nIndexBestCombination].nComplete then nIndexBestCombination = ListIndex elseif ListToCompare[ListIndex].nComplete == ListToCompare[nIndexBestCombination].nComplete then -- scelgo soluzione con voto più alto if ListToCompare[nIndexBestCombination].dTotalRating + 10 * GEO.EPS_SMALL < ListToCompare[ListIndex].dTotalRating then nIndexBestCombination = ListIndex end end end -- GEN_sPieceRotation = 'NO_CONSTRAINT' else -- si sceglie soluzione con più feature complete if ListToCompare[ListIndex].nComplete > ListToCompare[nIndexBestCombination].nComplete then nIndexBestCombination = ListIndex elseif ListToCompare[ListIndex].nComplete == ListToCompare[nIndexBestCombination].nComplete then local dBestTotalRating = ListToCompare[nIndexBestCombination].dTotalRating local dOtherTotalRating = ListToCompare[ListIndex].dTotalRating -- scelgo soluzione con voto più alto if dBestTotalRating + 10 * GEO.EPS_SMALL < dOtherTotalRating then nIndexBestCombination = ListIndex -- se stesso voto elseif abs( dBestTotalRating - dOtherTotalRating) < 10 * GEO.EPS_SMALL then -- scelgo soluzione con meno rotazioni if ListToCompare[nIndexBestCombination].nRotations > ListToCompare[ListIndex].nRotations then nIndexBestCombination = ListIndex -- se anche le rotazioni sono le stesse, prendo la soluzione con più lavorazioni alla fine elseif #ListToCompare[nIndexBestCombination].Rot0 < #ListToCompare[ListIndex].Rot0 then nIndexBestCombination = ListIndex end end end end end end end end ChosenCombination = ListToCompare[nIndexBestCombination] return ChosenCombination end ------------------------------------------------------------------------------------------------------------- -- funzione che decide le combinazioni di rotazione per lavorare la trave local function GetProcessingListFromCombination( BestCombination) local vProc = {} local ProcessingResult = {} ProcessingResult.bSomeFeatureDown = false ProcessingResult.bSomeFeatureSide = false ProcessingResult.nInitialPosition = BestCombination.nIndexRotation ProcessingResult.nIndexInCombinationList = BestCombination.nIndexInCombinationList ProcessingResult.nIndexHeadCutInVProc = BestCombination.nIndexHeadCutInVProc ProcessingResult.nIndexTailCutInVProc = BestCombination.nIndexTailCutInVProc -- aggiungo processing da fare in fase ribaltata if #BestCombination.Rot180 > 0 then ProcessingResult.bSomeFeatureDown = true for i = 1, #BestCombination.Rot180 do BestCombination.Rot180[i].bDown = true table.insert( vProc, BestCombination.Rot180[i]) end end -- aggiungo processing da fare in fase ruotata if #BestCombination.Rot90 > 0 then ProcessingResult.bSomeFeatureSide = true for i = 1, #BestCombination.Rot90 do BestCombination.Rot90[i].bSide = true table.insert( vProc, BestCombination.Rot90[i]) end end -- aggiungo processing da fare in ultima fase if #BestCombination.Rot0 > 0 then for i = 1, #BestCombination.Rot0 do BestCombination.Rot0[i].bStd = true table.insert( vProc, BestCombination.Rot0[i]) end end return vProc, ProcessingResult end ------------------------------------------------------------------------------------------------------------- -- funzione che ritorna la Proc nella rotazione scelta local function GetProcBestMachRotationFromList( ListToCompare, Part) local Data = {} local Proc = {} local nIndexChosenProcInRot = 1 -- se ci sono almeno 2 possibili soluzioni, scelgo la posizione migliore di lavorazione if #ListToCompare > 1 then -- formatto lista strategie disponibili come se le aspetta la funzione di compare local AvailableStrategiesInRot = { dAllStrategiesTotalTime = 0} for i = 1, #ListToCompare do table.insert( AvailableStrategiesInRot, ListToCompare[i][1].ChosenStrategy) AvailableStrategiesInRot.dAllStrategiesTotalTime = AvailableStrategiesInRot.dAllStrategiesTotalTime + ListToCompare[i][1].ChosenStrategy.Result.dTimeToMachine end AvailableStrategiesInRot = FeatureLib.CalculateStrategiesCompositeRating( AvailableStrategiesInRot, Part.GeneralParameters.GEN_sMachiningStrategy) for nIndexCurrentStrategy = 1, #AvailableStrategiesInRot do -- la scelta tra le differenti strategie tra le rotazioni utilizza gli stessi criteri della scelta strategie all'interno della feature stessa nIndexChosenProcInRot = GetIndexBestStrategyFromComparison( AvailableStrategiesInRot, Part, nIndexCurrentStrategy, nIndexChosenProcInRot) end -- altrimenti prendo la prima else nIndexChosenProcInRot = 1 end Proc = ListToCompare[nIndexChosenProcInRot][1] Data.nIndexRotation = ListToCompare[nIndexChosenProcInRot].nRotation Data.dTimeToMachine = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.dTimeToMachine Data.dQuality = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.dQuality Data.dCompletionIndex = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.dCompletionIndex Data.bComplete = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.sStatus == 'Completed' Data.bNotComplete = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.sStatus == 'Not-Completed' Data.bNotApplicable = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.sStatus == 'Not-Applicable' return Proc, Data end ------------------------------------------------------------------------------------------------------------- -- funzione che calcola le combinazioni di rotazione per lavorare la trave e sceglie la migliore local function GetCombinationListFromMatrix( ProcessingsOnPart, PartInfo, bReProcessEmptyChosenStrategy, BitCombinationListToReprocess) local CombinationsList = { dAllCombinationsTotalTime = 0} -- calcolo per tutte le combinazioni disponibili precedentemente verificate for i = 1, #PartInfo.CombinationList do local bToProcess = false -- controllo se debbano essere ricalcolate solo alcune soluzioni if BitCombinationListToReprocess and #BitCombinationListToReprocess > 0 then for c = 1, #BitCombinationListToReprocess do if PartInfo.CombinationList[i].sBitIndexCombination == BitCombinationListToReprocess[c].sBitIndexCombination and PartInfo.CombinationList[i].bPartInCombiIsInverted == BitCombinationListToReprocess[c].bPartInCombiIsInverted then bToProcess = true end end else bToProcess = true end -- si riprocessano tutte a meno che non sia indicata una specifica da riprovare if bToProcess then local bRot90, bRot180 local SingleCombination = {} local nUnloadPos = PartInfo.CombinationList[i].nUnloadPos SingleCombination.nRotations = 0 SingleCombination.dTotalTimeToMachine = 0 SingleCombination.dTotalQuality = 0 SingleCombination.dTotalCompletionIndex = 0 SingleCombination.nComplete = 0 SingleCombination.nNotComplete = 0 SingleCombination.nNotExecute = 0 SingleCombination.sBitIndexCombination = PartInfo.CombinationList[i].sBitIndexCombination -- TODO se pezzo invertito bisogna considerare le rotazioni nell'array dalla 5 alla 8 SingleCombination.bPartInCombiIsInverted = PartInfo.CombinationList[i].bPartInCombiIsInverted SingleCombination.nUnloadPos = nUnloadPos -- creo liste dei proc suddivisi per rotazione SingleCombination.Rot0 = {} SingleCombination.Rot90 = {} SingleCombination.Rot180 = {} -- ciclo su tutte le feature, ad eccezione dei tagli testa/coda che dipendono dal risultato delle altre -- tagli testa e coda vengono aggiunti sempre alla fine 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]) 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 -- ciclo sulle rotazioni local nNextRot = nUnloadPos local ResultsList = {} for nRotation = 1, 3 do -- se rotazione abilitata da combinazione if string.sub( PartInfo.CombinationList[i].sBitIndexCombination, nNextRot, nNextRot) == '1' then local CurrProc = ProcessingsOnPart.Rotation[nNextRot+nOffsetIndex][nProc] -- se è ultima rotazione oppure se feature non impatta su misura laser, allora è valida e può essere effettivamente considerata if nNextRot == nUnloadPos or not( CurrProc.bHindersLaserMeasure) then -- se non è settata la ChosenStrtegy, provo a cercare comunque tra quelle disponibili if not CurrProc.ChosenStrategy and bReProcessEmptyChosenStrategy then CurrProc = GetFeatureBestStrategy( CurrProc, PartInfo) end -- controllo se è stata scelta una strategia if CurrProc.ChosenStrategy then local Proc = {} Proc.nRotation = nNextRot table.insert( Proc, CurrProc) table.insert( ResultsList, Proc) end end end 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 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 else if ID.IsHeadCut( ProcessingsOnPart.Rotation[1+nOffsetIndex][nProc]) then SingleCombination.nIndexHeadCutInVProc = nProc elseif ID.IsTailCut( ProcessingsOnPart.Rotation[1+nOffsetIndex][nProc]) then SingleCombination.nIndexTailCutInVProc = nProc end end end end -- aggiungo rotazioni SingleCombination.nRotations = EgtIf( bRot90, 1, 0) + EgtIf( bRot180, 1, 0) -- aggiungo tempo totale considerando le rotazioni -- TODO per il momento la rotazione impiega 1 minuto. Serve configurare? SingleCombination.dTotalTimeToMachine = SingleCombination.dTotalTimeToMachine + ( SingleCombination.nRotations * 1) -- aggiungo la combinazione all'elenco delle combinazioni disponibili table.insert( CombinationsList, SingleCombination) -- aggiorno tempo totale di tutte le combinazioni CombinationsList.dAllCombinationsTotalTime = CombinationsList.dAllCombinationsTotalTime + SingleCombination.dTotalTimeToMachine end end -- si calsola il total rating CombinationsList = FeatureLib.CalculateCombinationsCompositeRating( CombinationsList, PartInfo.GeneralParameters.GEN_sMachiningStrategy) return CombinationsList end ------------------------------------------------------------------------------------------------------------- function BeamExec.GetCombinationMatrix( PARTS, bIsFlipRot) -- ricerca strategia di lavorazione per ogni pezzo e applicazione lavorazioni for nPart = 1, #PARTS do local nCycles = EgtIf( bIsFlipRot and PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion, 2, 1) -- per ogni inversione for nInvertIndex = 1, nCycles do -- calcolo della migliore strategia per ogni rotazione del pezzo for nRotIndex = 1, 4 do local nOffsetIndex = EgtIf( nInvertIndex == 2, 4, 0) local nIndex = nRotIndex + nOffsetIndex -- calcola le strategie applicabili PROCESSINGS[nPart].Rotation[nIndex] = CalculateStrategies( PROCESSINGS[nPart].Rotation[nIndex], PARTS[nPart]) -- tra le calcolate, sceglie la migliore PROCESSINGS[nPart].Rotation[nIndex] = GetBestStrategyFromProcList( PROCESSINGS[nPart].Rotation[nIndex], PARTS[nPart]) -- rotazione pezzo di 90° per volta BeamLib.RotateRawPart( PARTS[nPart], 1) -- aggiorno info pezzo PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw) PARTS[nPart].b3Part = EgtGetBBoxGlob( PARTS[nPart].idBoxTm, GDB_BB.STANDARD) end -- se bisogna invertire if bIsFlipRot and PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then -- inversione del pezzo testa-coda BeamLib.InvertRawPart( PARTS[nPart], 2) -- aggiorno info pezzo PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw) PARTS[nPart].b3Part = EgtGetBBoxGlob( PARTS[nPart].idBoxTm, GDB_BB.STANDARD) end end end end ------------------------------------------------------------------------------------------------------------- function BeamExec.ProcessMachinings( PARTS, bIsFlipRot) -- ciclo sui pezzi local nTotErr = 0 local Stats = {} local nOrd = 1 local bTryToReProcess = false -- 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) -- la parte di applicazione lavorazioni può essere lanciata più volte in caso della presenza di errori local bProcess = true while bProcess do -- si resetta la variabile per riprocessare, sarà la AddOperation a decidere se serve riprocessare oppure no bProcess = false -- scrittura nel log del risultato della scelta della strategia migliore tra quelle disponibili if EgtGetDebugLevel() >= 3 then Logs.WriteFeaturesLog( PROCESSINGS[nPart], PARTS[nPart], nCycles) end -- si ricavano tutte le combinazioni possibili local CombinationListFromMatrix = GetCombinationListFromMatrix( PROCESSINGS[nPart], PARTS[nPart], nCycles > 1) -- ci deve essere almeno una combinazione, altrimenti errore if #CombinationListFromMatrix < 1 then error( 'UNEXPECTED ERROR: NO combinations available') end -- scelta della migliore combinazione local BestCombination = GetBestCombination( CombinationListFromMatrix, PARTS[nPart]) PARTS[nPart].ChosenCombination = BestCombination.sBitIndexCombination PARTS[nPart].bPartInCombiIsInverted = BestCombination.bPartInCombiIsInverted -- scrittura nel log delle combinazioni possibili if EgtGetDebugLevel() >= 3 then Logs.WriteCombinationLog( CombinationListFromMatrix, BestCombination) end -- compilazione della vProc finale contenente le feature da lavorare nella giusta rotazione local vProc, MatrixResult = GetProcessingListFromCombination( BestCombination) -- si mette subito il pezzo nella fase if nOrd == 1 then EgtSetCurrPhase( 1) local nPhase = EgtGetCurrPhase() -- salvo info nuova fase aggiunta local MachExtraInfo = { sType = 'DISP', nPhase = nPhase} table.insert( DB_MACH_APPLIED, MachExtraInfo) else BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, 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. local vtRawOffsetPos = PARTS[nPart].b3Raw:getMin() - EgtGetRawPartBBox( PARTS[nPart].idRaw):getMin() local nRawId = PARTS[nPart].idRaw while nRawId and abs( vtRawOffsetPos:len()) > 0 do EgtKeepRawPart( nRawId) EgtMoveRawPart( nRawId, vtRawOffsetPos) nRawId = EgtGetNextRawPart( nRawId) end -- se combinazione prevede inversione, si gira il pezzo if PARTS[nPart].bPartInCombiIsInverted then BeamLib.InvertRawPart( PARTS[nPart], 2) end -- debug if EgtGetDebugLevel() >= 1 then PrintFeatures( vProc, PARTS[nPart]) end EgtOutLog( ' *** AddMachinings ***', 1) -- se la posizione iniziale non è calcolata, significa che non ci sono feature da eseguire. Solo taglio testa/coda if not MatrixResult.nInitialPosition then MatrixResult.nInitialPosition = 1 MACHININGS.Info.nHeadCutRotation = 1 MACHININGS.Info.nSplitCutRotation = 1 -- anche se non ci sono feature da eseguire, bisogna comunque scrivrere i risultati for nProc = 1, #vProc do AddFeatureResultToGlobalList( PARTS[nPart], vProc[nProc]) end -- altrimenti si fanno tutti i calcoli else -- TODO serve ancora ordinare le feature con nuovo metodo di calcolo ottimizzazione lavorazioni? -- ordinamento di base delle feature vProc = OrderFeatures( vProc) -- esegue le strategie migliori che ha precedentemente scelto e salva le lavorazioni nella lista globale MACHININGS = CalculateMachinings( vProc, PARTS[nPart], MatrixResult.nInitialPosition) -- se non sono sono settate le rotazioni di lavorazione di testa e coda, significa che le feature da eseguire non sono state eseguite per qualche problema. Si forza rotazione 1 if not MACHININGS.Info.nHeadCutRotation or not MACHININGS.Info.nSplitCutRotation then MACHININGS.Info.nHeadCutRotation = 1 MACHININGS.Info.nSplitCutRotation = 1 end end local nOffsetIndex = EgtIf( PARTS[nPart].bPartInCombiIsInverted, 4, 0) -- aggiunge tagli testa e coda in fasi opportune local nRotHeadCut = MatrixResult.nInitialPosition + MACHININGS.Info.nHeadCutRotation - 1 if nRotHeadCut > 4 then nRotHeadCut = nRotHeadCut - 4 end local nRotSplitCut = MatrixResult.nInitialPosition + MACHININGS.Info.nSplitCutRotation - 1 if nRotSplitCut > 4 then nRotSplitCut = nRotSplitCut - 4 end -- setto nella Proc l'indice rotazione nella quale deve essere lavorata PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].nIndexRotation = nRotHeadCut PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].nIndexRotation = nRotSplitCut -- si imposta flag rotazione per taglio di testa if MACHININGS.Info.nHeadCutRotation == 2 then PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bSide = true elseif MACHININGS.Info.nHeadCutRotation == 3 then PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bDown = true else PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bStd = true end -- si imposta flag rotazione per taglio di coda if MACHININGS.Info.nSplitCutRotation == 2 then PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bSide = true elseif MACHININGS.Info.nSplitCutRotation == 3 then PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bDown = true else PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bStd = true end local vProcHeadTail = {} table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc]) table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc]) MACHININGS = CalculateMachinings( vProcHeadTail, PARTS[nPart], MatrixResult.nInitialPosition) -- si preparano le lavorazioni assegnandole al proprio stage MACHININGS = MachiningLib.PrepareMachiningsForSorting( PARTS[nPart]) -- TODO queste funzioni andrebbero rimosse e utilizzato algoritmo di sorting dedicato -- #### #### #### #### #### #### #### #### #### #### -- ordinamento lavorazioni per stage (N.B.: potrebbe compromettere ordine lavorazioni della feature, che non può essere cambiato) MACHININGS = BeamLib.StableSort( MACHININGS, MachiningLib.CompareMachinings) -- dopo il sorting bisogna riverificare che ordine delle lavorazioni della feature non sia compromesso MACHININGS = MachiningLib.FinalizeSorting() -- #### #### #### #### #### #### #### #### #### #### -- finiti i calcoli di applicazione delle lavorazioni, si riporta il pezzo nello zero della fase nRawId = PARTS[nPart].idRaw while nRawId and abs( vtRawOffsetPos:len()) > 0 do EgtKeepRawPart( nRawId) EgtMoveRawPart( nRawId, -vtRawOffsetPos) nRawId = EgtGetNextRawPart( nRawId) end local nInitialPosition = MatrixResult.nInitialPosition -- PREROTAZIONE PEZZO if MatrixResult.nInitialPosition ~= 1 or PARTS[nPart].bPartInCombiIsInverted then -- si esce dalle lavorazioni e si torna in disegna local nCurrMachGroup = EgtGetCurrMachGroup() EgtResetCurrMachGroup() -- salvo situazione precedente su lista BEAM ( scrittura variabili globali per interfaccia) if bIsFlipRot then BEAM.PREROTATE90 = MatrixResult.nInitialPosition - 1 BEAM.PREINVERT = EgtIf( PARTS[nPart].bPartInCombiIsInverted, 1, 0) end -- se c'è stata inversione, si inverte il pezzo anche in disegna if PARTS[nPart].bPartInCombiIsInverted then local ptInv = PARTS[nPart].b3PartOriginal:getMin() + Vector3d( PARTS[nPart].b3PartOriginal:getDimX() / 2, PARTS[nPart].b3PartOriginal:getDimY() / 2, 0) EgtRotate( PARTS[nPart].id, ptInv, Z_AX(), 180, GDB_RT.GLOB) PARTS[nPart].bIsInverted = true end -- se c'è una prerotazione, si inverte il pezzo if MatrixResult.nInitialPosition ~= 1 then local ptRot = PARTS[nPart].b3PartOriginal:getMin() + Vector3d( 0, PARTS[nPart].b3PartOriginal:getDimY() / 2, PARTS[nPart].b3PartOriginal:getDimZ() / 2) local nRotationDeg = 90 * ( MatrixResult.nInitialPosition - 1) EgtRotate( PARTS[nPart].id, ptRot, X_AX(), nRotationDeg, GDB_RT.GLOB) end -- si riattiva il MachGroup, i pezzi son rimasti dove erano EgtSetCurrMachGroup( nCurrMachGroup) end -- salvo sul PART la posizione di partenza che è stata scelta PARTS[nPart].nInitialPosition = MatrixResult.nInitialPosition local bAreAllMachiningApplyOk local sErr local bSplitAlreadyExecuted = false local bSplitExecutedOnRot = false local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'TYPE', 'START') EgtSetInfo( idDisp, 'ORD', nOrd) EgtOutLog( ' *** Phase=' .. tostring( nPhase) .. ' Raw=' .. tostring( PARTS[nPart].idRaw) .. ' Part=' .. tostring( PARTS[nPart].id) .. ' ***', 1) -- creazione effettiva delle lavorazioni MACHININGS.Info = {} local nCurrPosition = 1 -- se c'è almeno una lavorazione in posizionamento con trave ribaltata if MatrixResult.bSomeFeatureDown then local nRotation = EgtIf( nInitialPosition + 2 > 4, nInitialPosition + 2 - 4, nInitialPosition + 2) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -2) bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'DOWN') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot bProcess = bProcess or bTryToReProcess end -- se c'è almeno una lavorazione in posizionamento con trave ruotata 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)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) -- se c'è già stata separazione if bSplitAlreadyExecuted then EgtSetInfo( idDisp, 'TYPE', 'MID2') else EgtSetInfo( idDisp, 'TYPE', 'MID') end -- se combinazione prevede inversione, si gira il pezzo if PARTS[nPart].bPartInCombiIsInverted and not PARTS[nPart].bIsInverted then BeamLib.InvertRawPart( PARTS[nPart], 2) end end local nRotation = EgtIf( nInitialPosition + 1 > 4, nInitialPosition + 1 - 4, nInitialPosition + 1) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -1) bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, 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)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) -- se c'è già stata separazione if bSplitAlreadyExecuted then EgtSetInfo( idDisp, 'TYPE', 'END2') else EgtSetInfo( idDisp, 'TYPE', 'MID') end -- se combinazione prevede inversione, si gira il pezzo if PARTS[nPart].bPartInCombiIsInverted and not PARTS[nPart].bIsInverted then BeamLib.InvertRawPart( PARTS[nPart], 2) end end BeamLib.RotateRawPart( PARTS[nPart], nInitialPosition - 1) -- aggiunta lavorazioni in ultima fase _, _, _, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'STD') bProcess = bProcess or bTryToReProcess -- se bisogna riprocessare, si annulla tutto nCycles = nCycles + 1 if bProcess and nCycles <= nMaxReProcessCycles then -- azzero liste MACHININGS = {} DB_MACH_APPLIED = {} MACHININGS.Info = {} RESULT = {} EgtRemoveAllOperations() -- se combinazione prevedeva inversione, si rigira il pezzo if PARTS[nPart].bPartInCombiIsInverted then BeamLib.InvertRawPart( PARTS[nPart], -2) end -- si ribalta il pezzo in posizione iniziale BeamLib.RotateRawPart( PARTS[nPart], 1 - nInitialPosition) else bProcess = false end end -- ottimizzazione con algoritmo ShortestPath TIMER:startElapsed( 'Sorting') MachiningLib.ShortestPathSorting() TIMER:stopElapsed( 'Sorting') EgtOutLog( ' *** End AddMachinings ***', 1) -- azzero lavorazioni per pezzo successivo MACHININGS = {} DB_MACH_APPLIED = {} MACHININGS.Info = {} -- indice pezzo successivo nOrd = nOrd + 1 end -- ===== 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) local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'TYPE', 'REST') EgtSetInfo( idDisp, 'ORD', nOrd) end -- Aggiornamento finale di tutto EgtSetCurrPhase( 1) local bApplOk, sApplErrors, sApplWarns = EgtApplyAllMachinings() -- TODO a cosa serve questo ricalcolo? Con nuovo sistema si può eliminare? -- eventuale ricalcolo per macchine tipo PF (ma tengo warning del primo calcolo) if EgtExistsInfo( EgtGetCurrMachGroup(), 'RECALC') then EgtOutLog( ' **** RECALC ****') bApplOk, sApplErrors, _ = EgtApplyAllMachinings() EgtRemoveInfo( EgtGetCurrMachGroup(), 'RECALC') end -- TODO qui restituire errori in modo migliore, come per feature -- TODO in caso di lavorazioni di coda saltate, l'informazione va restituita anche sulle singole feature if not bApplOk then nTotErr = nTotErr + 1 BeamExec.AddApplyResultToGlobalList( 1, 0, sApplErrors) elseif sApplWarns and #sApplWarns > 0 then local vLine = EgtSplitString( sApplWarns, '\r\n') for i = 1, #vLine do local nPos = vLine[i]:find( '(WRN', 1, true) if nPos then local sData = vLine[i]:sub( nPos + 1, -2) local vVal = EgtSplitString( sData, ',') local nWarn = EgtGetVal( vVal[1] or '', 'WRN', 'i') local nCutId = EgtGetVal( vVal[2] or '', 'CUTID', 'i') if nWarn and nCutId then BeamExec.AddApplyResultToGlobalList( -nWarn, nCutId, vLine[i]) end end end end return ( nTotErr == 0), RESULT end ------------------------------------------------------------------------------------------------------------- function BeamExec.ProcessAlternatives( PARTS) -- inizializzazione variabili globali per interfaccia BEAM.ALTERNATIVESNEST2D = '' BEAM.ALTERNATIVES = '' -- ciclo sui pezzi local BestCombination = {} local nPart = 1 local nOrd = 1 local nMaxReProcessCycles = EgtClamp( PARTS[nPart].GeneralParameters.GEN_nMaxReProcessCycles, 1, 3) local bTryToReProcess = false -- se non serve trovare altre soluzioni, si esce subito if not ( PARTS[nPart].GeneralParameters.GEN_sPiecesLoadingPosition == 'FULL_PRE_ROTATION' and not PARTS[nPart].bSquareSection) and not PARTS[nPart].GeneralParameters.GEN_bGetAlternativesNesting2D then return end -- si copia il MachGroup originale local nOriginalMachGroup = EgtGetCurrMachGroup() local sMachGroupName = EgtGetMachGroupName( nOriginalMachGroup) local sTempMachGroupName = sMachGroupName .. '_A' local nTempMachGroupName = EgtCopyMachGroup( sMachGroupName, sTempMachGroupName) EgtSetCurrMachGroup( nTempMachGroupName) EgtRemoveAllOperations() BeamLib.CreateAddGroup( PARTS[nPart].id, sTempMachGroupName) -- si aggiorna il raw dopo averlo copiato PARTS[nPart].idRaw = EgtGetFirstRawPart() -- si riporta pezzo in posizione iniziale -- se combinazione prevedeva inversione, si rigira il pezzo if PARTS[nPart].bPartInCombiIsInverted then BeamLib.InvertRawPart( PARTS[nPart], -2) end -- si ribalta il pezzo in posizione iniziale BeamLib.RotateRawPart( PARTS[nPart], 1 - PARTS[nPart].nInitialPosition) local TotalCombiToTest = {} -- se serve calcolare soluzione alternativa ruotata di 90° if PARTS[nPart].GeneralParameters.GEN_sPiecesLoadingPosition == 'FULL_PRE_ROTATION' and not PARTS[nPart].bSquareSection then local CombinationListToCheck = {} for i = 1, #PARTS[nPart].CombinationList do local nUnloadPos = PARTS[nPart].nInitialPosition local nOtherSimilarUnloadPos = EgtIf( nUnloadPos + 2 > 4, nUnloadPos + 2 - 4, nUnloadPos + 2) -- restano abilitate soluzioni ruotate di 90° o 270° rispetto alla migliore soluzione trovata nella prima analisi if PARTS[nPart].CombinationList[i].nUnloadPos ~= nUnloadPos and PARTS[nPart].CombinationList[i].nUnloadPos ~= nOtherSimilarUnloadPos then table.insert( CombinationListToCheck, { sBitIndexCombination = PARTS[nPart].CombinationList[i].sBitIndexCombination, bPartInCombiIsInverted = PARTS[nPart].CombinationList[i].bPartInCombiIsInverted}) end end -- se c'è almeno una combinazione da testare if #CombinationListToCheck > 0 then table.insert( TotalCombiToTest, CombinationListToCheck) end end -- se serve calcolare posizione per ottimizzazione tagli in nesting (le soluzioni non possono avere ribaltamenti) if PARTS[nPart].GeneralParameters.GEN_bGetAlternativesNesting2D then -- POSIZIONE 0 (e invertito) local sCombinationToCheck = BeamLib.StringReplaceChar( '0000', PARTS[nPart].nInitialPosition, "1") table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck}, bIsNesting2D = true}) if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = true}, bIsNesting2D = true}) end -- POSIZIONE 180 (e invertito) if PARTS[nPart].GeneralParameters.GEN_sPiecesLoadingPosition == 'STD_PRE_ROTATION' then local nOtherPosition = EgtIf( PARTS[nPart].nInitialPosition + 2 > 4, PARTS[nPart].nInitialPosition + 2 - 4, PARTS[nPart].nInitialPosition + 2) sCombinationToCheck = BeamLib.StringReplaceChar( '0000', nOtherPosition, "1") table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck}, bIsNesting2D = true}) if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = true}, bIsNesting2D = true}) end end -- POSIZIONE 90/270 (e invertito) if PARTS[nPart].GeneralParameters.GEN_sPiecesLoadingPosition == 'FULL_PRE_ROTATION' then local nOtherPosition = EgtIf( PARTS[nPart].nInitialPosition + 1 > 4, PARTS[nPart].nInitialPosition + 1 - 4, PARTS[nPart].nInitialPosition + 1) sCombinationToCheck = BeamLib.StringReplaceChar( '0000', nOtherPosition, "1") table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck}, bIsNesting2D = true}) if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = true}, bIsNesting2D = true}) end nOtherPosition = EgtIf( PARTS[nPart].nInitialPosition + 3 > 4, PARTS[nPart].nInitialPosition + 3 - 4, PARTS[nPart].nInitialPosition + 3) sCombinationToCheck = BeamLib.StringReplaceChar( '0000', nOtherPosition, "1") table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck}, bIsNesting2D = true}) if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = true}, bIsNesting2D = true}) end end end -- fino a che ci sono soluzioni da testare for z = 1, #TotalCombiToTest do -- si svuota il machgroup e si resettano le variabili EgtRemoveAllOperations() MACHININGS = {} MACHININGS.Info = {} nOrd = 1 local nCycles = 1 local bCombinationFound = true -- la parte di applicazione lavorazioni può essere lanciata più volte in caso della presenza di errori local bProcess = true while bProcess do bProcess = false -- si ricavano tutte le combinazioni possibili local CombinationListFromMatrix = GetCombinationListFromMatrix( PROCESSINGS[nPart], PARTS[nPart], nCycles > 1, TotalCombiToTest[z]) -- se ci sono soluzioni possibili if #CombinationListFromMatrix > 0 then BestCombination = GetBestCombination( CombinationListFromMatrix, PARTS[nPart]) -- se la soluzione alternativa migliore è completa, allora la verifico, altrimenti si tiene la migliore in assoluto if BestCombination.nNotComplete == 0 and BestCombination.nNotExecute == 0 then PARTS[nPart].bPartInCombiIsInverted = BestCombination.bPartInCombiIsInverted -- compilazione della vProc finale contenente le feature da lavorare nella giusta rotazione local vProc, MatrixResult = GetProcessingListFromCombination( BestCombination) -- si mette subito il pezzo nella fase EgtSetCurrPhase( 1) -- 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. local vtRawOffsetPos = PARTS[nPart].b3Raw:getMin() - EgtGetRawPartBBox( PARTS[nPart].idRaw):getMin() local nRawId = PARTS[nPart].idRaw while nRawId and abs( vtRawOffsetPos:len()) > 0 do EgtKeepRawPart( nRawId) EgtMoveRawPart( nRawId, vtRawOffsetPos) nRawId = EgtGetNextRawPart( nRawId) end if BestCombination.bPartInCombiIsInverted then BeamLib.InvertRawPart( PARTS[nPart], 2) end -- se la posizione iniziale non è calcolata, significa che non ci sono feature da eseguire. Solo taglio testa/coda if not MatrixResult.nInitialPosition then MatrixResult.nInitialPosition = 1 MACHININGS.Info.nHeadCutRotation = 1 MACHININGS.Info.nSplitCutRotation = 1 -- altrimenti si fanno tutti i calcoli else -- TODO serve ancora ordinare le feature con nuovo metodo di calcolo ottimizzazione lavorazioni? -- ordinamento di base delle feature vProc = OrderFeatures( vProc) -- esegue le strategie migliori che ha precedentemente scelto e salva le lavorazioni nella lista globale MACHININGS = CalculateMachinings( vProc, PARTS[nPart], MatrixResult.nInitialPosition) -- se non sono sono settate le rotazioni di lavorazione di testa e coda, significa che le feature da eseguire non sono state eseguite per qualche problema. Si forza rotazione 1 if not MACHININGS.Info.nHeadCutRotation or not MACHININGS.Info.nSplitCutRotation then MACHININGS.Info.nHeadCutRotation = 1 MACHININGS.Info.nSplitCutRotation = 1 end end local nOffsetIndex = EgtIf( BestCombination.bPartInCombiIsInverted, 4, 0) -- aggiunge tagli testa e coda in fasi opportune local nRotHeadCut = MatrixResult.nInitialPosition + MACHININGS.Info.nHeadCutRotation - 1 if nRotHeadCut > 4 then nRotHeadCut = nRotHeadCut - 4 end local nRotSplitCut = MatrixResult.nInitialPosition + MACHININGS.Info.nSplitCutRotation - 1 if nRotSplitCut > 4 then nRotSplitCut = nRotSplitCut - 4 end -- setto nella Proc l'indice rotazione nella quale deve essere lavorata PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].nIndexRotation = nRotHeadCut PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].nIndexRotation = nRotSplitCut -- si imposta flag rotazione per taglio di testa if MACHININGS.Info.nHeadCutRotation == 2 then PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bDown = nil PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bSide = true PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bStd = nil elseif MACHININGS.Info.nHeadCutRotation == 3 then PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bDown = true PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bSide = nil PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bStd = nil else PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bDown = nil PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bSide = nil PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc].bStd = true end -- si imposta flag rotazione per taglio di coda if MACHININGS.Info.nSplitCutRotation == 2 then PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bDown = nil PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bSide = true PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bStd = nil elseif MACHININGS.Info.nSplitCutRotation == 3 then PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bDown = true PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bSide = nil PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bStd = nil else PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bDown = nil PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bSide = nil PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc].bStd = true end local vProcHeadTail = {} table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotHeadCut+nOffsetIndex][MatrixResult.nIndexHeadCutInVProc]) table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotSplitCut+nOffsetIndex][MatrixResult.nIndexTailCutInVProc]) MACHININGS = CalculateMachinings( vProcHeadTail, PARTS[nPart], MatrixResult.nInitialPosition) -- finiti i calcoli di applicazione delle lavorazioni, si riporta il pezzo nello zero della fase nRawId = PARTS[nPart].idRaw while nRawId and abs( vtRawOffsetPos:len()) > 0 do EgtKeepRawPart( nRawId) EgtMoveRawPart( nRawId, -vtRawOffsetPos) nRawId = EgtGetNextRawPart( nRawId) end local bAreAllMachiningApplyOk local sErr local bSplitAlreadyExecuted = false local bSplitExecutedOnRot = false local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'TYPE', 'START') EgtSetInfo( idDisp, 'ORD', nOrd) -- creazione effettiva delle lavorazioni MACHININGS.Info = {} local nCurrPosition = 1 local nInitialPosition = MatrixResult.nInitialPosition BestCombination.nInitialPosition = MatrixResult.nInitialPosition -- se c'è almeno una lavorazione in posizionamento con trave ribaltata if MatrixResult.bSomeFeatureDown then local nRotation = EgtIf( nInitialPosition + 2 > 4, nInitialPosition + 2 - 4, nInitialPosition + 2) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -2) bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'DOWN') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot bProcess = bProcess or bTryToReProcess end -- se c'è almeno una lavorazione in posizionamento con trave ruotata 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)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) -- se c'è già stata separazione if bSplitAlreadyExecuted then EgtSetInfo( idDisp, 'TYPE', 'MID2') else EgtSetInfo( idDisp, 'TYPE', 'MID') end -- se combinazione prevede inversione, si gira il pezzo if BestCombination.bPartInCombiIsInverted and not PARTS[nPart].bIsInverted then BeamLib.InvertRawPart( PARTS[nPart], 2) end end local nRotation = EgtIf( nInitialPosition + 1 > 4, nInitialPosition + 1 - 4, nInitialPosition + 1) BeamLib.RotateRawPart( PARTS[nPart], nRotation - nCurrPosition) nCurrPosition = nRotation EgtSetInfo( idDisp, 'ROT', -1) bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, 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)) nPhase = EgtGetCurrPhase() idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'ORD', nOrd) -- se c'è già stata separazione if bSplitAlreadyExecuted then EgtSetInfo( idDisp, 'TYPE', 'END2') else EgtSetInfo( idDisp, 'TYPE', 'MID') end -- se combinazione prevede inversione, si gira il pezzo if BestCombination.bPartInCombiIsInverted and not PARTS[nPart].bIsInverted then BeamLib.InvertRawPart( PARTS[nPart], 2) end end BeamLib.RotateRawPart( PARTS[nPart], nInitialPosition - 1) -- aggiunta lavorazioni in ultima fase _, _, _, bTryToReProcess = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'STD') bProcess = bProcess or bTryToReProcess -- se bisogna riprocessare, si annulla tutto nCycles = nCycles + 1 if bProcess and nCycles <= nMaxReProcessCycles then -- azzero liste MACHININGS = {} MACHININGS.Info = {} RESULT = {} EgtRemoveAllOperations() -- si riporta pezzo in posizione iniziale -- se combinazione prevedeva inversione, si rigira il pezzo if BestCombination.bPartInCombiIsInverted then BeamLib.InvertRawPart( PARTS[nPart], -2) end -- si ribalta il pezzo in posizione iniziale BeamLib.RotateRawPart( PARTS[nPart], 1 - nInitialPosition) else bProcess = false end else bCombinationFound = false end else bCombinationFound = false end end nOrd = nOrd + 1 -- se ho trovato almeno una combinazione possibile if bCombinationFound then -- ===== 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) local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'TYPE', 'REST') EgtSetInfo( idDisp, 'ORD', nOrd) end -- Aggiornamento finale di tutto EgtSetCurrPhase( 1) 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, ', ') 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 if z == #TotalCombiToTest then break end -- si riporta pezzo in posizione iniziale -- se combinazione prevedeva inversione, si rigira il pezzo if BestCombination.bPartInCombiIsInverted then BeamLib.InvertRawPart( PARTS[nPart], -2) end -- si ribalta il pezzo in posizione iniziale BeamLib.RotateRawPart( PARTS[nPart], 1 - BestCombination.nInitialPosition) end end -- si cancella eventuale mach group creato per le alternative EgtRemoveMachGroup( nTempMachGroupName) end ------------------------------------------------------------------------------------------------------------- return BeamExec