-- 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( 'BeamData') -- 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') EgtOutLog( ' BeamExec started', 1) EgtMdbSetGeneralParam( MCH_GP.MAXDEPTHSAFE, BeamData.COLL_SIC) EgtMdbSave() ------------------------------------------------------------------------------------------------------------- -- *** variabili globali *** ------------------------------------------------------------------------------------------------------------- TOOLS = {} -- tabella contenente tutti gli utensili STRATEGIES = nil -- tabella contenente le strategie disponibili per ogni feature MACHININGS = {} -- tabella contenente le lavorazioni da applicare ------------------------------------------------------------------------------------------------------------- -- *** 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.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.SetupInfo = {} Tool.SetupInfo = BeamData.GetSetupInfo( Tool.sHead) 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 -- verifico che parametri siano compatibili con una fresa a coda di rondine ( angolo di fianco standard Coda di rondine -> 15°) Tool.bIsDoveTail = Tool.Type == '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 metà del tagliente Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP', 'd') or floor( Tool.dDiameter / 3) -- se non settato nell'utensile, considero metà del diametro Tool.bIsPen = abs( Tool.dSpeed) < 5 -- 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 un quarto del diametro -- 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 end end -- se tutti i dati necessari sono disponibili, inserisco utensile nella lista globale degli utensili disponibili if IsToolOk( Tool) then 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 ------------------------------------------------------------------------------------------------------------- local function ImportFileJSON( sFileToImport) local JSON = require( 'JSON') local function readAll( sFileToImport) local f = io.open( sFileToImport, "rb") local content = f:read( "*all") f:close() return content end local content = readAll( sFileToImport) return JSON:decode( content) end ------------------------------------------------------------------------------------------------------------- function BeamExec.GetStrategiesFromJSONinBD() -- si legge il JSON contenente la configurazione da utilizzare (nome del file salvato nel BeamData) local sMachDir = EgtGetCurrMachineDir() if BeamData.STRATEGIES_CONFIG_FILE then local sFile = sMachDir .. '\\Beam\\' .. BeamData.STRATEGIES_CONFIG_FILE -- se non esiste file JSON, annullo la lista contenente le strategie if not EgtExistsFile( sFile) then STRATEGIES = nil return end local FeaturesList = {} FeaturesList = ImportFileJSON( sFile) -- metto la tabella letta nella lista globale STRATEGIES STRATEGIES = {} STRATEGIES.Features = FeaturesList end end ------------------------------------------------------------------------------------------------------------- -- funzione che controlla validità delle combinazioni proposte local function IsCombinationAvailable( sCombination, nUnloadPos, bSquareSection) -- se non utilizzo BEAMWALL, forzo comportamento BASIC if not BEAM.BeamWall or not BEAM.Rotation then BEAM.Rotation = {} BEAM.Rotation.Basic = true end -------------------------------------------------------------------------- -- TODO scelta combinazione forzato DA RIMUOVERE!! Serve modifica al BEAM. BEAM.BeamWall = true BEAM.Rotation = {} BEAM.Rotation.bBasic = true BEAM.Rotation.bNoRotation = false BEAM.Rotation.bAdvanced = false -------------------------------------------------------------------------- -- BASIC : posizione di scarico come posizionamento iniziale if not BEAM.BeamWall or BEAM.Rotation.bBasic then local ExtraRotation = 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 -- NO ROTATION : solo posizione di partenza elseif BEAM.Rotation.bNoRotation then if sCombination == '1000' and nUnloadPos == 1 then return true else return false end -- ADVANCED : come BASIC ma ammesse anche le prerotazioni (posizione di scarico può essere diversa da posizione iniziale) elseif BEAM.Rotation.bAdvanced 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) if not bSquareSection and ( nUnloadPos == 2 or nUnloadPos == 4) then return false else if string.sub( sCombination, nUnloadPos, nUnloadPos) ~= '1' or string.sub( sCombination, nExtraRotation, nExtraRotation) == '1' then return false else if not BeamData.ROT90 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 end end end ------------------------------------------------------------------------------------------------------------- local function GetAvailableCombinations( PartInfo) local CombinationList = {} CombinationList.Rotations = {0, 0, 0, 0} -- indice rotazione attiva, per calcolo collect feature -- verifico tutte le combinazioni che possono essere considerate 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.bSquareSection) then local Combination = {} Combination.sBitIndexCombination = sBitIndexCombination Combination.nUnloadPos = nUnloadPos table.insert( CombinationList, Combination) -- 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 return CombinationList end ------------------------------------------------------------------------------------------------------------- -- *** funzioni posizionamento pezzi all'interno della barra *** ------------------------------------------------------------------------------------------------------------- function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS) -- 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 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 -- 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 b3Part = EgtGetBBoxGlob( PARTS[i].id or GDB_ID.NULL, GDB_BB.EXACT) if b3Part:isEmpty() or PARTS[i].b3Box:isEmpty() then break end EgtOutLog( 'PartSez=' .. EgtNumToString( b3Part:getDimY(), 1) .. 'x' .. EgtNumToString( b3Part:getDimZ(), 1), 3) -- se sezione compatibile e lunghezza disponibile sufficiente local dPartLen = PARTS[i].b3Box:getDimX() local dPartWidth = PARTS[i].b3Box:getDimY() local dPartHeight = PARTS[i].b3Box: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].b3Box) -- 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].b3Box) -- inserisco il pezzo nel grezzo EgtDeselectPartObjs( PARTS[i].id) local ptPos = b3Part:getMin() - PARTS[i].b3Box: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].b3Box:getCenter() - b3Part: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].b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[i].id, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD) PARTS[i].nIndexInParts = i PARTS[i].CombinationList = GetAvailableCombinations( PARTS[i]) 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 end ------------------------------------------------------------------------------------------------------------- -- *** Inserimento delle lavorazioni nelle travi *** ------------------------------------------------------------------------------------------------------------- local function GetStrategiesFromGlobalList( Proc) -- cerco tra le feature for i = 1, #STRATEGIES.Features do -- se trovo la feature if Proc.nPrc == STRATEGIES.Features[i].nPrc and Proc.nGrp == STRATEGIES.Features[i].nGrp then -- cerco tra le topologie for j = 1, #STRATEGIES.Features[i].Topologies do -- se trovo la topologia if Proc.Topology.sName == STRATEGIES.Features[i].Topologies[j].sName then -- ritorno le strategie disponibili per la feature che sto analizzando return STRATEGIES.Features[i].Topologies[j].Strategies end end end end return nil end ------------------------------------------------------------------------------------------------------------- local function GetStrategies( Proc) local AvailableStrategiesForProc = nil -- se la lista STRATEGIES è stata letta da JSON (quindi non è vuota), ritorno le strategie possibili if STRATEGIES and #STRATEGIES.Features > 0 then AvailableStrategiesForProc = GetStrategiesFromGlobalList( Proc) 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 -- eseguo file config con i parametri di default local StrategyData = require( sStrategyId .. '\\' .. sStrategyId .. 'Config') -- se ID strategia non esiste oppure ID letto in NGE è differente da quello letto nella strategia, esco subito if not StrategyData or StrategyData.sStrategyId ~= sStrategyId then return nil end -- salvo che questa strategia è stata forzata, e che quindi ho già letto il file config con i parametri di default -- quando si calcolerà la lavorazione non servirà leggere/riverificare i parametri di default StrategyData.bForcedStrategy = true -- cerco e aggiorno i parametri come sono settati nel processing for i = 1, #StrategyData.Parameters do local sParameterToRead = StrategyData.sStrategyId .. '_' .. StrategyData.Parameters[i].sNameNge local sForcedParameterForProc = EgtGetInfo( Proc.id, sParameterToRead, 's') -- se ho trovato il valore, lo sovrascrivo al default if sForcedParameterForProc then StrategyData.Parameters[i].sValue = sForcedParameterForProc end end -- ritorno la lista strategia con parametri local StrategyToProc = {} table.insert( StrategyToProc, StrategyData) return StrategyToProc end return nil end ------------------------------------------------------------------------------------------------------------- local function CollectFeatures( Part) -- recupero le feature 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.idPart = Part.id Proc.idRaw = Part.idRaw Proc.nIndexPartInParts = Part.nIndexInParts 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 .. '------') -- se esiste la geometria if Proc.b3Box and not Proc.b3Box:isEmpty() then -- TODO fare una funzione per recuperare i dati delle feature che non passano dal calcolo della topologia? -- se foro calcolo altri dati if ID.IsDrilling( Proc) then -- assegno diametro e facce di ingresso e uscita (dati tabelle sempre per riferimento) Proc.dDiam, Proc.dLen, Proc.nFcs, Proc.nFce = FeatureLib.GetDrillingData( Proc) end -- informazioni facce e topologia Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part) -- calcolo topologia solo se necessario, altrimenti si sfruttano le informazioni della feature BTL Proc.Topology = {} if FeatureLib.NeedTopologyFeature( Proc) then Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc) Proc.Faces = FaceData.GetFacesInfo( Proc, Part) Proc.Topology = FeatureLib.ClassifyTopology( Proc, Part) else Proc.Topology.sFamily = 'FEATURE' Proc.Topology.sName = 'FEATURE' end -- 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) -- 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) end -- se ci sono strategie disponibili, aggiungo a lista delle feature da lavorare if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then table.insert( vProc, Proc) -- altrimenti errore (non ci sono strategie per lavorare la topologia riconosciuta) else EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies') end -- altrimenti errore (serviva riconoscimento topologico, ma non è stato possibile farlo) else EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies') end else Proc.nFlg = 0 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 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] -- controllo la feature con tutte le altre per recuperare le dipendenze for j = 1, #vProcSingleRot do -- non si controlla la feature con se stessa if i ~= j then local ProcB = vProcSingleRot[j] -- 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.IsDrilling( ProcB) and Overlaps( Proc.b3Box, ProcB.b3Box) then Proc.bPassedByHole = true end -- verifiche per specchiature if BeamData.DOWN_HEAD or BeamData.TWO_EQUAL_HEADS then -- forature if BeamData.DOUBLE_HEAD_DRILLING and ID.IsDrilling( Proc) and ID.IsDrilling( ProcB) and not Proc.Mirror then if AreDrillingsMirrored( Proc, ProcB, Part) then Proc.Mirror = ProcB end end end end end end return vProcSingleRot end ------------------------------------------------------------------------------------------------------------- -- TODO contemplare anche strategie speciali local function RunStrategyLibraries( sStrategyId) local StrategyConfigName = sStrategyId .. '\\' .. sStrategyId .. 'Config' local StrategyScriptName = sStrategyId .. '\\' .. sStrategyId local StrategyConfigPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyConfigName .. '.lua' local StrategyScriptPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyScriptName .. '.lua' local StrategyConfigPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyConfigName .. '.lua' local StrategyScriptPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyScriptName .. '.lua' local StrategyLib = {} if ( EgtExistsFile( StrategyConfigPathStandard) and EgtExistsFile( StrategyScriptPathStandard)) or ( EgtExistsFile( StrategyConfigPathSpecial) and EgtExistsFile( StrategyScriptPathSpecial)) then StrategyLib.Config = require( StrategyConfigName) StrategyLib.Script = require( StrategyScriptName) return StrategyLib else return {} end end ------------------------------------------------------------------------------------------------------------- -- ritorna l'indice della strategia migliore, tra le due passate local function GetIndexBestStrategyFromComparison( AvailableStrategies, 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 local dCompositeRatingStrategy1 = AvailableStrategies[nIndex1].Result.dCompositeRating local dCompositeRatingStrategy2 = AvailableStrategies[nIndex2].Result.dCompositeRating -- si predilige strategia con rating composito più alto if dCompositeRatingStrategy1 > dCompositeRatingStrategy2 then dChosenIndex = nIndex1 elseif dCompositeRatingStrategy2 > dCompositeRatingStrategy1 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 GetBestStrategy( vProcSingleRot) for i = 1, #vProcSingleRot do -- processo tutte le feature attive local Proc = vProcSingleRot[i] 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, nIndexCurrentStrategy, nIndexBestStrategy) -- salvo sulla proc la migliore strategia end if nIndexBestStrategy ~= 0 then Proc.ChosenStrategy = Proc.AvailableStrategies[nIndexBestStrategy] Proc.nIndexBestStrategy = nIndexBestStrategy end end end 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 -- 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].Parameters) Proc.AvailableStrategies[nIndexCurrentStrategy].Result = FeatureLib.CalculateCompositeRating( Proc.AvailableStrategies[nIndexCurrentStrategy].Result) -- se scelta strategia in modalità base o standard, esco subito alla prima che trovo completa -- TODO serve paraemtro da Beam&Wall ( oppure da confirgurazione) !!!!!!!! if BEAM.GetFirstCompletedStrategy and Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus == 'Complete' then break end -- se non trovo i file della strategia, 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 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 -- 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 ------------------------------------------------------------------------------------------------------------- -- esegue le strategie migliori che ha precedentemente scelto local function CalculateMachinings( vProc, Part) local bAreAllApplyOk = true -- applico le strategie scelte for i = 1, #vProc do -- processo tutte le feature attive applicando le lavorazioni local Proc = vProc[i] if Proc.nFlg ~= 0 and 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 bAreAllApplyOk, _ = StrategyScript.Make( true, Proc, Part, Proc.ChosenStrategy.Parameters) end 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 ------------------------------------------------------------------------------------------------------------- function BeamExec.GetProcessings( PROCESSINGS, PARTS) -- recupero tutti i processing di tutti i pezzi in tutte le rotazioni -- TODO calcolo tempi da rimuovere o lasciare solo per debug -- if EgtGetDebugLevel() >= 3 then -- EgtStartCounter() -- end for nPart = 1, #PARTS do if not PARTS[nPart].id and PARTS[nPart].b3Raw:getDimX() < BeamData.dMinRaw then break end local vProcRot = {} for dRotIndex = 1, 4 do -- si calcolano le feature solo se la rotazione può essere presa in considerazione if PARTS[nPart].CombinationList.Rotations[dRotIndex] == 1 then -- recupero le feature di lavorazione della trave table.insert( vProcRot, CollectFeatures( PARTS[nPart])) -- recupero informazioni ausiliarie feature e dipendenze tra feature stesse -- TODO le dipendenze cambiano in base alla rotazione del pezzo? probabilmente no vProcRot[dRotIndex] = GetFeatureInfoAndDependency( vProcRot[dRotIndex], PARTS[nPart]) -- calcola le strategie applicabili ( presenti nella tabella vProcRot[dRotIndex].AvailableStrategies) vProcRot[dRotIndex] = CalculateStrategies( vProcRot[dRotIndex], PARTS[nPart]) else -- inserisco una tabella vuota table.insert( vProcRot, {}) end -- ruoto il grezzo per calcolare la fattibilità delle lavorazioni nella prossima rotazione -- vettore movimento grezzi per rotazione di 90deg ogni step local dDeltaYZ = PARTS[nPart].b3Raw:getDimY() - PARTS[nPart].b3Raw:getDimZ() local vtMove = Vector3d( 0, dDeltaYZ / 2 * EgtIf( BeamData.RIGHT_LOAD, -1, 1), dDeltaYZ / 2) local bPreMove = ( dDeltaYZ < 0) -- ruoto le travi della fase corrente if bPreMove then EgtMoveRawPart( PARTS[nPart].idRaw, vtMove) end EgtRotateRawPart( PARTS[nPart].idRaw, X_AX(), EgtIf( BeamData.RIGHT_LOAD, -90, 90)) if not bPreMove then EgtMoveRawPart( PARTS[nPart].idRaw, vtMove) end -- aggiorno info pezzo PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw) PARTS[nPart].b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[nPart].id, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD) end local Part = {} Part.Rotation = vProcRot -- recupero le feature di lavorazione della trave table.insert( PROCESSINGS, Part) end -- TODO calcolo tempi da rimuovere o lasciare solo per debug -- if EgtGetDebugLevel() >= 3 then -- local timeGetProcessings = EgtStopCounter() -- timeGetProcessings = timeGetProcessings + 0 -- EgtOutBox( timeGetProcessings, 'GetProcessings calculation time') -- EgtStartCounter() -- end return PROCESSINGS end ------------------------------------------------------------------------------------------------------------- -- funzione che decide la migliore tra le combinazioni di rotazione disponibili local function GetBestCombination( ListToCompare) -- TODO da completare local nIndexBestCombination = 1 if #ListToCompare == 1 then nIndexBestCombination = 1 else for ListIndex = 2, #ListToCompare do 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 stesso voto if ListToCompare[nIndexBestCombination].dTotalRating == ListToCompare[ListIndex].dTotalRating then -- TODO il voto dovrebbe essere considerato già pesando le rotazioni, con un coefficiente o un peso fisso aggiuntivo -- 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 -- se voto diverso else -- scelgo soluzione con voto più alto if ListToCompare[nIndexBestCombination].dTotalRating < ListToCompare[ListIndex].dTotalRating then nIndexBestCombination = ListIndex 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 bSomeFeatureDown = false local bSomeFeatureSide = false local nInitialPosition = BestCombination.nIndexRotation -- aggiungo processing da fare in fase ribaltata if #BestCombination.Rot180 > 0 then 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 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 table.insert( vProc, BestCombination.Rot0[i]) end end return vProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide end ------------------------------------------------------------------------------------------------------------- -- funzione che ritorna la Proc nella rotazione scelta local function GetProcBestMachRotationFromList( ListToCompare) 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 = {} for i = 1, #ListToCompare do table.insert( AvailableStrategiesInRot, ListToCompare[i][1].ChosenStrategy) end 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, nIndexCurrentStrategy, nIndexChosenProcInRot) end -- altrimenti prendo la prima else nIndexChosenProcInRot = 1 end Proc = ListToCompare[nIndexChosenProcInRot][1] Data.nIndexRotation = ListToCompare[nIndexChosenProcInRot].nRotation Data.dCompositeRating = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.dCompositeRating 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 GetBestResultFromCombinationsMatrix( ProcessingsOnPart, PartInfo) local BestCombination = {} local CombinationsList = {} -- scrittura nel log della matrice delle rotazioni if EgtGetDebugLevel() >= 3 then Logs.WriteMatrixLog( ProcessingsOnPart, PartInfo) end -- per ogni posizione di scarico for nUnloadPos = 1, 4 do -- calcolo per tutte le combinazioni disponibili precedentemente verificate for i = 1, #PartInfo.CombinationList do -- controllo che la combinazione abbia ultima fase come quella calcolata in fase di definizione combinazioni if PartInfo.CombinationList[i].nUnloadPos == nUnloadPos then local bRot90, bRot180 local SingleCombination = {} SingleCombination.nRotations = 0 SingleCombination.dTotalRating = 0 SingleCombination.nComplete = 0 SingleCombination.nNotComplete = 0 SingleCombination.nNotExecute = 0 SingleCombination.sBitIndexCombination = PartInfo.CombinationList[i].sBitIndexCombination SingleCombination.nUnloadPos = nUnloadPos -- creo liste dei proc suddivisi per rotazione SingleCombination.Rot0 = {} SingleCombination.Rot90 = {} SingleCombination.Rot180 = {} -- ciclo su tutte le feature for nProc = 1, #ProcessingsOnPart.Rotation[1] do -- 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 -- controllo se è stata scelta una strategia if ProcessingsOnPart.Rotation[nNextRot][nProc].ChosenStrategy then local Proc = {} Proc.nRotation = nNextRot table.insert( Proc, ProcessingsOnPart.Rotation[nNextRot][nProc]) table.insert( ResultsList, Proc) 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) -- 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.dTotalRating = SingleCombination.dTotalRating + Data.dCompositeRating 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.nIndexRotation = nUnloadPos else SingleCombination.nNotExecute = SingleCombination.nNotExecute + 1 end end -- aggiungo rotazioni SingleCombination.nRotations = EgtIf( bRot90, 1, 0) + EgtIf( bRot180, 1, 0) -- aggiungo la combinazione all'elenco delle combinazioni disponibili table.insert( CombinationsList, SingleCombination) end end end -- ci deve essere almeno una combinazione, altrimenti errore if #CombinationsList < 1 then error( 'UNEXPECTED ERROR: NO combinations available') end BestCombination = GetBestCombination( CombinationsList) -- scrittura nel log delle combinazioni possibili if EgtGetDebugLevel() >= 3 then Logs.WriteCombinationLog( CombinationsList, BestCombination) end local vFinalProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide = GetProcessingListFromCombination( BestCombination) return vFinalProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide end ------------------------------------------------------------------------------------------------------------- function BeamExec.ProcessMachinings( PROCESSINGS, PARTS) -- ciclo sui pezzi local nTotErr = 0 local Stats = {} local nOrd = 1 local ProcessCompleted = false local nMaxLoops = 5 -- TODO da rimuovere o lasciare solo per debug --if EgtGetDebugLevel() >= 3 then -- EgtStartCounter() --end -- TODO da rimuovere o lasciare solo per debug --if EgtGetDebugLevel() >= 3 then -- local timeCollect = EgtStopCounter() -- timeCollect = timeCollect + 0 -- EgtOutBox( timeCollect, 'Collect calculation time') -- EgtStartCounter() --end -- TODO da rimuovere o lasciare solo per debug --if EgtGetDebugLevel() >= 3 then -- local timeMachining = EgtStopCounter() -- timeMachining = timeMachining + 0 -- EgtOutBox( timeMachining, 'Machining calculation time') --end local nCurrLoop = 0 -- ciclo fino a che tutte le applicazioni delle lavorazioni sono corrette while not ProcessCompleted do -- aumento numero loop nel quale siamo nCurrLoop = nCurrLoop + 1 -- ricerca strategia di lavorazione per ogni pezzo e applicazione lavorazioni for nPart = 1, #PARTS do -- calcolo della migliore strategia per ogni rotazione del pezzo for dRotIndex = 1, 4 do PROCESSINGS[nPart].Rotation[dRotIndex] = GetBestStrategy( PROCESSINGS[nPart].Rotation[dRotIndex]) end -- scrittura nel log del risultato della scelta della strategia migliore tra quelle disponibili if EgtGetDebugLevel() >= 3 then Logs.WriteFeaturesLog( PROCESSINGS[nPart], PARTS[nPart]) end -- si calcola la combinazione di lavorazione migliore local vProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide = GetBestResultFromCombinationsMatrix( PROCESSINGS[nPart], PARTS[nPart]) -- salvo sul PART la posizione di partenza che è stata scelta PARTS[nPart].nInitialPosition = nInitialPosition -- debug if EgtGetDebugLevel() >= 1 then PrintFeatures( vProc, PARTS[nPart]) end EgtOutLog( ' *** AddMachinings ***', 1) -- ordino le features vProc = OrderFeatures( vProc) -- esegue le strategie migliori che ha precedentemente scelto e salva le lavorazioni nella lista globale MACHININGS = CalculateMachinings( vProc, PARTS[nPart]) -- TODO riordinare lavorazioni ottimizzando cambio utensile/spezzone ecc..., mantenendo dipendenze definite prima -- ordino le lavorazioni -- MACHININGS = OrderMachining( MACHININGS, PARTS[nPart]) -- aggiungo la fase, se non è la prima if nOrd == 1 then EgtSetCurrPhase( 1) else BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, 0) end local bAreAllMachiningApplyOk local sErr local bSplitAlreadyExecuted = false local bSplitExecutedOnRot = false local nPhase = EgtGetCurrPhase() local nDispId = EgtGetPhaseDisposition( nPhase) EgtSetInfo( nDispId, 'TYPE', 'START') EgtSetInfo( nDispId, 'ORD', nOrd) EgtOutLog( ' *** Phase=' .. tostring( nPhase) .. ' Raw=' .. tostring( PARTS[nPart].idRaw) .. ' Part=' .. tostring( PARTS[nPart].id) .. ' ***', 1) -- creazione effettiva delle lavorazioni -- se c'è almeno una lavorazione in posizionamento con trave ribaltata if bSomeFeatureDown then local nRotation = EgtIf( nInitialPosition + 2 > 4, nInitialPosition + 2 - 4, nInitialPosition + 2) BeamLib.RotatePart( PARTS[nPart], nRotation) EgtSetInfo( nDispId, 'ROT', -2) bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'DOWN') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot end -- se c'è almeno una lavorazione in posizionamento con trave ribaltata if bSomeFeatureSide then -- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima if bSomeFeatureDown then BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, 0) nPhase = EgtGetCurrPhase() nDispId = EgtGetPhaseDisposition( nPhase) EgtSetInfo( nDispId, 'ORD', nOrd) -- se c'è già stata seoparazione if bSplitAlreadyExecuted then EgtSetInfo( nDispId, 'TYPE', 'MID2') else EgtSetInfo( nDispId, 'TYPE', 'MID') end end local nRotation = EgtIf( nInitialPosition + 1 > 4, nInitialPosition + 1 - 4, nInitialPosition + 1) BeamLib.RotatePart( PARTS[nPart], nRotation) EgtSetInfo( nDispId, 'ROT', -1) bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'SIDE') bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot end -- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima if bSomeFeatureDown or bSomeFeatureSide then BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, 0) nPhase = EgtGetCurrPhase() nDispId = EgtGetPhaseDisposition( nPhase) EgtSetInfo( nDispId, 'ORD', nOrd) -- se c'è già stata seoparazione if bSplitAlreadyExecuted then EgtSetInfo( nDispId, 'TYPE', 'END2') else EgtSetInfo( nDispId, 'TYPE', 'MID') end end BeamLib.RotatePart( PARTS[nPart], nInitialPosition) -- aggiunta lavorazioni in ultima fase MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'STD') EgtOutLog( ' *** End AddMachinings ***', 1) -- azzero lavorazioni per pezzo successivo MACHININGS = {} -- 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 nDispId = EgtGetPhaseDisposition( nPhase) EgtSetInfo( nDispId, 'TYPE', 'REST') EgtSetInfo( nDispId, '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 if not bApplOk then nTotErr = nTotErr + 1 table.insert( Stats, {nErr = 1, sMsg=sApplErrors, nRot=0, idCut=0, idTask=0}) 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 table.insert( Stats, { nErr=-nWarn, sMsg=vLine[i], nRot=0, idCut=nCutId, idTask=0}) end end end end -- TODO per il momento non si cicla mai una seconda volta. Controllare anche parametro modalità scelta strategia -- Gestione da fare completamente. La strategia che ha generato una lavorazione con errore deve essere annullata -- si fa un reset del MACH e poi si ritorna a inizio ciclo e si rifà tutto -- se i cicli superano il massimo si esce (nell'ultimo ciclo possibile si potrebbe escludere la feature e basta, anche se avesse altre strategie da provare) if true or nCurrLoop > nMaxLoops then ProcessCompleted = true end end return ( nTotErr == 0), Stats end ------------------------------------------------------------------------------------------------------------- return BeamExec