b8299df247
- in BeamLib aggiunta funzione ConvertBitIndexToRotationIndex per convertire da BitIndex a RotationIndex
2498 lines
129 KiB
Lua
2498 lines
129 KiB
Lua
-- 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
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function GetRotationName( nRotIndex, nInvertIndex)
|
|
local sRotation = ''
|
|
|
|
if nRotIndex == 1 then
|
|
sRotation = '0'
|
|
elseif nRotIndex == 2 then
|
|
sRotation = '90'
|
|
elseif nRotIndex == 3 then
|
|
sRotation = '180'
|
|
elseif nRotIndex == 4 then
|
|
sRotation = '270'
|
|
end
|
|
|
|
if nInvertIndex > 1 then
|
|
sRotation = sRotation .. 'INV'
|
|
end
|
|
|
|
return sRotation
|
|
end
|
|
|
|
-- TODO prevedere parametri per preferire carico del pezzo verticale oppure orizzontale?
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che controlla validità delle combinazioni proposte
|
|
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 dResidualLength = dRawL
|
|
local idPrevRaw, dPrevDelta
|
|
local dStartOffset = dOvmHead
|
|
local dEndOffset = 0 -- TODO cosa fare di BD.OVM_MID? usarlo solo se non è nesting obliquo?
|
|
for i = 1, #PARTS do
|
|
-- dati del pezzo
|
|
local b3BoxExact = EgtGetBBoxGlob( PARTS[i].id or GDB_ID.NULL, GDB_BB.EXACT)
|
|
if b3BoxExact:isEmpty() or PARTS[i].b3PartOriginal:isEmpty() then break end
|
|
EgtOutLog( 'PartSez=' .. EgtNumToString( b3BoxExact:getDimY(), 1) .. 'x' .. EgtNumToString( b3BoxExact:getDimZ(), 1), 3)
|
|
-- se sezione compatibile e lunghezza disponibile sufficiente
|
|
local dPartLength = PARTS[i].b3PartOriginal:getDimX()
|
|
local dPartWidth = PARTS[i].b3PartOriginal:getDimY()
|
|
local dPartHeight = PARTS[i].b3PartOriginal:getDimZ()
|
|
local dNextResidualLength = dResidualLength - EgtIf( i == 1, dStartOffset, 0) - dPartLength - dEndOffset
|
|
local bIsSectionOk = (( abs( dPartWidth - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartHeight - dRawH) < 100 * GEO.EPS_SMALL) or
|
|
( abs( dPartHeight - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartWidth - dRawH) < 100 * GEO.EPS_SMALL))
|
|
if bIsSectionOk and ( dNextResidualLength + dEndOffset >= 0) then
|
|
-- eventuale sovramateriale di testa
|
|
if i > 1 then
|
|
if PARTS[i].dPosX then
|
|
dStartOffset = PARTS[i].dPosX - ( dRawL - dResidualLength)
|
|
if dStartOffset < -GEO.EPS_SMALL then
|
|
dResidualLength = dResidualLength - dStartOffset
|
|
dStartOffset = 0
|
|
end
|
|
else
|
|
dStartOffset = max( dOvmMid - dEndOffset, 0)
|
|
end
|
|
end
|
|
-- dimensioni del grezzo
|
|
local dCurrentRawLength = min( dPartLength + dStartOffset + dEndOffset, dResidualLength)
|
|
local dDelta = dCurrentRawLength - dPartLength - dStartOffset
|
|
-- creo e posiziono il grezzo
|
|
PARTS[i].idRaw = EgtAddRawPart( Point3d(0,0,0), dCurrentRawLength, dRawW, dRawH, BeamData.RAWCOL)
|
|
|
|
EgtMoveToCornerRawPart( PARTS[i].idRaw, BeamData.ptOriXR, BeamData.dPosXR)
|
|
EgtMoveRawPart( PARTS[i].idRaw, Vector3d( dResidualLength - 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 dStartOffset > 0.09 then
|
|
EgtSetInfo( PARTS[i].idRaw, 'HOVM', dStartOffset)
|
|
if idPrevRaw then
|
|
EgtSetInfo( idPrevRaw, 'BDST', dStartOffset + dPrevDelta)
|
|
end
|
|
end
|
|
if dEndOffset > 0.09 then
|
|
EgtSetInfo( PARTS[i].idRaw, 'TOVM', dEndOffset)
|
|
end
|
|
-- aggiungo faccia per taglio finale al pezzo
|
|
BeamLib.AddPartEndFace( PARTS[i].id, PARTS[i].b3PartOriginal)
|
|
-- 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
|
|
dResidualLength = dResidualLength - dCurrentRawLength
|
|
-- aggiorno grezzo precedente
|
|
idPrevRaw = PARTS[i].idRaw
|
|
dPrevDelta = dDelta
|
|
PARTS[i].bIsLastPart = ( i == #PARTS)
|
|
PARTS[i].dDistanceToNextPiece = dDelta
|
|
PARTS[i].dRestLength = dResidualLength
|
|
PARTS[i].b3Raw = EgtGetRawPartBBox( PARTS[i].idRaw)
|
|
PARTS[i].dLength = PARTS[i].b3Raw:getDimX()
|
|
PARTS[i].dWidth = PARTS[i].b3Raw:getDimY()
|
|
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 = dStartOffset
|
|
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( dPartLength, 1) .. ') too big for raw part L(' .. EgtNumToString( dResidualLength - 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 dResidualLength > 10 then
|
|
local idRaw = EgtAddRawPart( Point3d(0,0,0), dResidualLength, dRawW, dRawH, BeamData.RAWCOL)
|
|
EgtMoveToCornerRawPart( idRaw, BeamData.ptOriXR, BeamData.dPosXR)
|
|
EgtMoveRawPart( idRaw, Vector3d( dResidualLength - 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)
|
|
-- gruppo per geometrie temporanee
|
|
local idTempGroup = BeamLib.GetTempGroup()
|
|
|
|
local HeadProc = {}
|
|
local TailProc = {}
|
|
|
|
-- ciclo tutte le feature
|
|
for i = 1, #vProcSingleRot do
|
|
local Proc = vProcSingleRot[i]
|
|
-- 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
|
|
|
|
local bAreBothTruncatingCuts =
|
|
( ID.IsCut( Proc) or ID.IsHeadCut( Proc) or ID.IsTailCut( Proc)) and ( ID.IsCut( ProcB) or ID.IsHeadCut( ProcB) or ID.IsTailCut( ProcB))
|
|
and ( FeatureLib.IsFeatureCuttingEntireSection( Proc.b3Box, Part) and FeatureLib.IsFeatureCuttingEntireSection( ProcB.b3Box, Part))
|
|
|
|
-- si trovano i veri tagli di testa e coda e si disattivano gli altri, se necessario
|
|
if bAreBothTruncatingCuts then
|
|
-- testa
|
|
if Proc.Faces[1].vtN:getX() > GEO.EPS_SMALL and ProcB.Faces[1].vtN:getX() > GEO.EPS_SMALL then
|
|
-- il primo taglio è più verso il centro della trave
|
|
if ( Proc.b3Box:getMin():getX() < ProcB.b3Box:getMin():getX() - 10 * GEO.EPS_SMALL) then
|
|
HeadProc = Proc
|
|
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
|
|
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
|
|
EgtMove( idProcCopy, - 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
|
|
EgtMove( idProcBCopy, 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
|
|
-- se i tagli non si intersecano, quello più esterno è da disattivare
|
|
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
|
|
if not Proc.SlaveProcIndexes then
|
|
Proc.SlaveProcIndexes = {}
|
|
end
|
|
table.insert( Proc.SlaveProcIndexes, j)
|
|
ProcB.nIndexMasterProc = i
|
|
ProcB.nFlg = 0
|
|
end
|
|
-- il secondo taglio è più verso il centro della trave
|
|
elseif Proc.b3Box:getMin():getX() >= ProcB.b3Box:getMin():getX() - 10 * GEO.EPS_SMALL then
|
|
HeadProc = ProcB
|
|
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
|
|
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
|
|
EgtMove( idProcBCopy, - 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
|
|
EgtMove( idProcCopy, 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
|
|
-- se i tagli non si intersecano, quello più esterno è da disattivare
|
|
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
|
|
if not ProcB.SlaveProcIndexes then
|
|
ProcB.SlaveProcIndexes = {}
|
|
end
|
|
table.insert( ProcB.SlaveProcIndexes, i)
|
|
Proc.nIndexMasterProc = j
|
|
Proc.nFlg = 0
|
|
end
|
|
end
|
|
-- coda
|
|
elseif Proc.Faces[1].vtN:getX() <= GEO.EPS_SMALL and ProcB.Faces[1].vtN:getX() <= GEO.EPS_SMALL then
|
|
-- il primo taglio è più verso il centro della trave
|
|
if Proc.b3Box:getMax():getX() > ProcB.b3Box:getMax():getX() + 10 * GEO.EPS_SMALL then
|
|
TailProc = Proc
|
|
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
|
|
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
|
|
EgtMove( idProcCopy, - 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
|
|
EgtMove( idProcBCopy, 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
|
|
-- se i tagli non si intersecano, quello più esterno è da disattivare
|
|
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
|
|
if not Proc.SlaveProcIndexes then
|
|
Proc.SlaveProcIndexes = {}
|
|
end
|
|
table.insert( Proc.SlaveProcIndexes, j)
|
|
ProcB.nIndexMasterProc = i
|
|
ProcB.nFlg = 0
|
|
end
|
|
-- il secondo taglio è più verso il centro della trave
|
|
elseif Proc.b3Box:getMax():getX() >= ProcB.b3Box:getMax():getX() - 10 * GEO.EPS_SMALL then
|
|
TailProc = ProcB
|
|
local idProcCopy = EgtCopyGlob( Proc.id, idTempGroup)
|
|
local idProcBCopy = EgtCopyGlob( ProcB.id, idTempGroup)
|
|
EgtMove( idProcBCopy, - 500 * GEO.EPS_SMALL * ProcB.Faces[1].vtN, GDB_RT.GLOB)
|
|
EgtMove( idProcCopy, 500 * GEO.EPS_SMALL * Proc.Faces[1].vtN, GDB_RT.GLOB)
|
|
-- se i tagli non si intersecano, quello più esterno è da disattivare
|
|
if not EgtTestSurfaceSurface( idProcCopy, idProcBCopy, GEO.EPS_SMALL) then
|
|
if not ProcB.SlaveProcIndexes then
|
|
ProcB.SlaveProcIndexes = {}
|
|
end
|
|
table.insert( ProcB.SlaveProcIndexes, i)
|
|
Proc.nIndexMasterProc = j
|
|
Proc.nFlg = 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- se entrambi tagli di testa, si tiene sempre il primo ( ma non quello aggiunto dall'automatismo)
|
|
if ( ID.IsHeadCut( Proc) and not EgtGetInfo( Proc.id, 'HEAD_ADD_CUT', 'i')) and ID.IsHeadCut( ProcB) then
|
|
if not Proc.SlaveProcIndexes then
|
|
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
|
|
|
|
HeadProc.Topology = {}
|
|
TailProc.Topology = {}
|
|
HeadProc.Topology.sFamily = 'HeadCut'
|
|
HeadProc.Topology.sName = 'HeadCut'
|
|
TailProc.Topology.sFamily = 'TailCut'
|
|
TailProc.Topology.sName = 'TailCut'
|
|
HeadProc.AvailableStrategies = GetStrategies( HeadProc, Part.sAISetupConfig)
|
|
TailProc.AvailableStrategies = GetStrategies( TailProc, Part.sAISetupConfig)
|
|
-- per nesting, si settano come info gli offset X degli estremi dei tagli
|
|
local HeadcutInfo = {}
|
|
local PtSortedHead = BeamLib.GetSortedVertices( HeadProc)
|
|
if PtSortedHead then
|
|
HeadcutInfo.OffsetX = {}
|
|
for i = 1, #PtSortedHead do
|
|
table.insert( HeadcutInfo.OffsetX, Part.b3Part:getMax():getX() - PtSortedHead[i]:getX())
|
|
end
|
|
end
|
|
local TailcutInfo = {}
|
|
local PtSortedTail = BeamLib.GetSortedVertices( TailProc)
|
|
if PtSortedTail then
|
|
TailcutInfo.OffsetX = {}
|
|
for i = 1, #PtSortedHead do
|
|
table.insert( TailcutInfo.OffsetX, Part.b3Part:getMin():getX() - PtSortedTail[i]:getX())
|
|
end
|
|
end
|
|
-- per nesting, si settano come info le normali delle facce di taglio
|
|
HeadcutInfo.vtN = HeadProc.Faces[1].vtN
|
|
TailcutInfo.vtN = TailProc.Faces[1].vtN
|
|
|
|
return vProcSingleRot, HeadcutInfo, TailcutInfo
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- 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
|
|
|
|
if not Proc.AvailableStrategies.dAllStrategiesTotalTime then
|
|
Proc.AvailableStrategies.dAllStrategiesTotalTime = 0
|
|
end
|
|
Proc.AvailableStrategies.dAllStrategiesTotalTime = Proc.AvailableStrategies.dAllStrategiesTotalTime + Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine
|
|
-- se scelta strategia in modalità base o standard, esco subito alla prima che trovo completa
|
|
if Part.GeneralParameters.GEN_sMachiningStrategy == 'FIRST_IN_LIST' and Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus == 'Complete' then
|
|
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)
|
|
PARTS[nPart].HeadcutInfo = {}
|
|
PARTS[nPart].TailcutInfo = {}
|
|
-- per ogni inversione
|
|
for nInvertIndex = 1, nCycles do
|
|
-- per ogni rotazione
|
|
for nRotIndex = 1, 4 do
|
|
local nOffsetIndex = EgtIf( nInvertIndex == 2, 4, 0)
|
|
-- le rotazioni sono 1,2,3,4 (0, 90, 180, 270) e 5,6,7,8 (le stesse invertite)
|
|
local nIndex = nRotIndex + nOffsetIndex
|
|
local HeadcutInfo, TailcutInfo
|
|
-- si calcolano le feature solo se la rotazione può essere presa in considerazione
|
|
if PARTS[nPart].CombinationList.Rotations[nRotIndex] == 1 then
|
|
-- recupero le feature di lavorazione della trave
|
|
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], HeadcutInfo, TailcutInfo = GetFeatureInfoAndDependency( vProcRot[nIndex], PARTS[nPart])
|
|
else
|
|
-- inserisco una tabella vuota
|
|
table.insert( vProcRot, {})
|
|
end
|
|
if HeadcutInfo then
|
|
PARTS[nPart].HeadcutInfo[nIndex] = {
|
|
OffsetX = HeadcutInfo.OffsetX,
|
|
vtN = HeadcutInfo.vtN
|
|
}
|
|
end
|
|
if TailcutInfo then
|
|
PARTS[nPart].TailcutInfo[nIndex] = {
|
|
OffsetX = TailcutInfo.OffsetX,
|
|
vtN = TailcutInfo.vtN
|
|
}
|
|
end
|
|
-- rotazione pezzo di 90° per volta
|
|
BeamLib.RotateRawPart( PARTS[nPart], 1)
|
|
-- aggiorno info pezzo
|
|
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
|
|
local Alternatives = {}
|
|
local AlternativesNest2D = {}
|
|
|
|
-- 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 sBitIndexCombinationWithInvert = BestCombination.sBitIndexCombination .. EgtIf( BestCombination.bPartInCombiIsInverted, '_INV', '')
|
|
if TotalCombiToTest[z].bIsNesting2D then
|
|
table.insert( AlternativesNest2D, sBitIndexCombinationWithInvert)
|
|
else
|
|
table.insert( Alternatives, sBitIndexCombinationWithInvert)
|
|
end
|
|
end
|
|
-- se ultima combinazione, si esce e non si riporta in posizione iniziale. Verrà infatti cancellata
|
|
if z == #TotalCombiToTest then break end
|
|
|
|
-- si riporta pezzo in posizione iniziale
|
|
-- 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
|
|
|
|
-- passaggio info a interfaccia da scrivere sul pezzo
|
|
BEAM.INFONGEPART = {}
|
|
for i = 1, #AlternativesNest2D do
|
|
local sRotation = BeamLib.ConvertBitIndexToRotationIndex( AlternativesNest2D[i])
|
|
if PARTS[nPart].HeadcutInfo then
|
|
local sOffsetX = table.concat( PARTS[nPart].HeadcutInfo[sRotation].OffsetX, ',')
|
|
local sVtN = ( tostring( PARTS[nPart].HeadcutInfo[sRotation].vtN)):gsub("^%(", ""):gsub("%)$", "")
|
|
table.insert( BEAM.INFONGEPART, 'ALT' .. AlternativesNest2D[i].. '_H' .. '=' .. sOffsetX .. ';' .. sVtN )
|
|
end
|
|
if PARTS[nPart].TailcutInfo then
|
|
local sOffsetX = table.concat( PARTS[nPart].TailcutInfo[sRotation].OffsetX, ',')
|
|
local sVtN = ( tostring( PARTS[nPart].TailcutInfo[sRotation].vtN)):gsub("^%(", ""):gsub("%)$", "")
|
|
table.insert( BEAM.INFONGEPART, 'ALT' .. AlternativesNest2D[i] .. '_T' .. '=' .. sOffsetX .. ';' .. sVtN)
|
|
end
|
|
end
|
|
|
|
-- si cancella eventuale mach group creato per le alternative
|
|
EgtRemoveMachGroup( nTempMachGroupName)
|
|
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
return BeamExec
|