c6ced91b14
- Aggiunta funzione dedicata alla creazione del gruppo ausiliario al MachGroup - Alla funzione BeamExec.GetCombinationListFromMatrix si può passare una lista con le sole combinazioni da provare - Nella BeamExec aggiunta funzione ProcessAlternatives solo per provare le alternative (simile alla ProcessFeatures, ma completamente slegata). La ProcessFeature decide il posizionamento iniziale, la ProcessAlternatives verifica solamente le alternative. - FlipRot adeguata a nuovo funzionamento - Altre piccole migliorie varie - TODO : 1) manca la parte per settare nelle info pezzo la rotazione iniziale (per vista in Aedifica) e le altre alternative (per ottimizzazione in nesting) 2) In FlipRot gestire interazione con Aedifica, dato che verrà lanciato direttamente dal programma (per ora funziona lanciato come la Process)
2282 lines
117 KiB
Lua
2282 lines
117 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
|
|
|
|
-- TODO prevedere parametri per preferire carico del pezzo verticale oppure orizzontale?
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che controlla validità delle combinazioni proposte
|
|
local function IsCombinationAvailable( sCombination, nUnloadPos, GeneralParameters, bIsFlipRot)
|
|
-- PREROTATION : flip-rot, si considerano tutte le posizioni possibili, a meno che non si voglia come da BTL
|
|
if bIsFlipRot and GeneralParameters.GEN_sPiecesLoadingPosition ~= 'BTL_POSITION' then
|
|
local nRotation90 = EgtIf( nUnloadPos + 1 > 4, nUnloadPos + 1 - 4, nUnloadPos + 1)
|
|
local nRotation180 = EgtIf( nUnloadPos + 2 > 4, nUnloadPos + 2 - 4, nUnloadPos + 2)
|
|
local nExtraRotation = EgtIf( nUnloadPos + 3 > 4, nUnloadPos + 3 - 4, nUnloadPos + 3)
|
|
|
|
-- se fase di unload disabilitata e soluzioni con terza rotazione sempre disabilitate
|
|
if string.sub( sCombination, nUnloadPos, nUnloadPos) ~= '1' or string.sub( sCombination, nExtraRotation, nExtraRotation) == '1' then
|
|
return false
|
|
elseif GeneralParameters.GEN_sPiecesLoadingPosition == 'STD_PRE_ROTATION' and ( nUnloadPos == 2 or nUnloadPos == 4) then
|
|
return false
|
|
else
|
|
if ( not BeamData.ROT90 or GeneralParameters.GEN_sPiecesLoadingPosition == 'STD_PRE_ROTATION') and string.sub( sCombination, nRotation90, nRotation90) == '1' then
|
|
return false
|
|
elseif not BeamData.ROT180 and string.sub( sCombination, nRotation180, nRotation180) == '1' then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
-- se invece si è nel caso standard di calcolo
|
|
else
|
|
-- NO ROTATION : solo posizione di partenza e non sono ammesse rotazioni
|
|
if GeneralParameters.GEN_sPieceRotation == 'NOT_ALLOWED' then
|
|
if sCombination == '1000' and nUnloadPos == 1 then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
-- STANDARD : posizione di scarico come posizionamento iniziale
|
|
else
|
|
local ExtraRotation = EgtIf( nUnloadPos + 3 > 4, nUnloadPos + 3 - 4, nUnloadPos + 3)
|
|
if nUnloadPos ~= 1 then
|
|
return false
|
|
elseif string.sub( sCombination, nUnloadPos, nUnloadPos) == '1' and string.sub( sCombination, ExtraRotation, ExtraRotation) == '0' then
|
|
if not BeamData.ROT90 and string.sub( sCombination, nUnloadPos+1, nUnloadPos+1) == '1' then
|
|
return false
|
|
elseif not BeamData.ROT180 and string.sub( sCombination, nUnloadPos+2, nUnloadPos+2) == '1' then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
function BeamExec.GetAvailableCombinations( PartInfo, bIsFlipRot)
|
|
local CombinationList = {}
|
|
CombinationList.Rotations = {0, 0, 0, 0} -- indice rotazione attiva, per calcolo collect feature
|
|
local nCycles = 1
|
|
|
|
-- se si sta calcolando il migliore posizionamento del pezzo, verifico se sono ammesse le combinazioni con pezzo invertito
|
|
if bIsFlipRot and PartInfo.GeneralParameters.GEN_bAllowPieceInversion then
|
|
nCycles = 2
|
|
end
|
|
|
|
-- verifico tutte le combinazioni che possono essere considerate
|
|
for nInvertIndex = 1, nCycles do
|
|
for nUnloadPos = 1, 4 do
|
|
for i = 1, BeamLib.BinaryToDecimal( 1111) do
|
|
local nBitIndexCombination = BeamLib.DecimalToBinary( i)
|
|
local sBitIndexCombination = BeamLib.CalculateStringBinaryFormat( nBitIndexCombination, 4)
|
|
-- si calcolano le combinazioni all'inizio, ottimizzando calcolo della collect solo nelle rotazioni che possono essere considerate
|
|
if IsCombinationAvailable( sBitIndexCombination, nUnloadPos, PartInfo.GeneralParameters, bIsFlipRot) then
|
|
local Combination = {}
|
|
Combination.sBitIndexCombination = sBitIndexCombination
|
|
Combination.nUnloadPos = nUnloadPos
|
|
table.insert( CombinationList, Combination)
|
|
|
|
if nInvertIndex == 2 then
|
|
Combination.bPartInCombiIsInverted = true
|
|
end
|
|
|
|
-- se posizionamento iniziale attivo
|
|
if string.sub( sBitIndexCombination, 1, 1) == '1' then
|
|
CombinationList.Rotations[1] = 1
|
|
end
|
|
-- se attiva rotazione 90
|
|
if string.sub( sBitIndexCombination, 2, 2) == '1' then
|
|
CombinationList.Rotations[2] = 1
|
|
end
|
|
-- se attiva rotazione 180
|
|
if string.sub( sBitIndexCombination, 3, 3) == '1' then
|
|
CombinationList.Rotations[3] = 1
|
|
end
|
|
-- se attiva rotazione 270
|
|
if string.sub( sBitIndexCombination, 4, 4) == '1' then
|
|
CombinationList.Rotations[4] = 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return CombinationList
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- *** funzioni posizionamento pezzi all'interno della barra ***
|
|
-------------------------------------------------------------------------------------------------------------
|
|
function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS, bCreateMachGroup, bIsFlipRot)
|
|
-- gruppo per geometrie temporanee
|
|
local idTempGroup = BeamLib.GetTempGroup()
|
|
|
|
-- default per nuove costanti qualora non definite
|
|
BeamData.OVM_BLADE_HBEAM = ( BeamData.OVM_BLADE_HBEAM or 11)
|
|
BeamData.OVM_CHAIN_HBEAM = ( BeamData.OVM_CHAIN_HBEAM or 8)
|
|
|
|
-- sovramateriale intermedio nullo se non definito
|
|
dOvmMid = ( dOvmMid or 0)
|
|
|
|
-- Determinazione minimo grezzo scaricabile
|
|
BeamExec.CalcMinUnloadableRaw( dRawW, dRawH)
|
|
|
|
-- Creazione nuovo gruppo di lavoro (di default va creato)
|
|
if bCreateMachGroup == nil then
|
|
bCreateMachGroup = true
|
|
end
|
|
if bCreateMachGroup then
|
|
local sMgName = EgtGetMachGroupNewName( 'Mach_1')
|
|
local idNewMg = EgtAddMachGroup( sMgName)
|
|
if not idNewMg then
|
|
local sOut = 'Errore nella creazione del gruppo di lavoro ' .. sMgName
|
|
return false, sOut
|
|
end
|
|
end
|
|
|
|
-- Impostazione della tavola
|
|
EgtSetTable( 'Tab')
|
|
|
|
-- salvo nota con lunghezza grezzo
|
|
-- Recupero l'identificativo del gruppo di lavoro corrente
|
|
local nMGrpId = EgtGetCurrMachGroup()
|
|
-- Lunghezza della barra
|
|
local dBarLen = EgtGetInfo( nMGrpId, 'BARLEN', 'd')
|
|
if not dBarLen then
|
|
EgtSetInfo( nMGrpId, 'BARLEN', dRawL)
|
|
end
|
|
|
|
-- Area tavola
|
|
local b3Tab = EgtGetTableArea()
|
|
-- Calcolo posizione estremo TR/BR della tavola rispetto a sua origine in BL
|
|
local dPosY = EgtIf( BeamData.CENTER_BEAM, ( b3Tab:getDimY() + dRawW * EgtIf( BeamData.RIGHT_LOAD, -1, 1)) / 2, EgtIf( BeamData.RIGHT_LOAD, 0, b3Tab:getDimY()))
|
|
BeamData.ptOriXR = Point3d( b3Tab:getDimX(), dPosY, 0)
|
|
BeamData.dPosXR = EgtIf( BeamData.RIGHT_LOAD, MCH_CR.BR, MCH_CR.TR)
|
|
|
|
-- Impostazione dell'attrezzaggio di default
|
|
EgtImportSetup()
|
|
|
|
-- Inserimento dei pezzi con il loro grezzo
|
|
local nCnt = 0
|
|
local dLen = dRawL
|
|
local idPrevRaw, dPrevDelta
|
|
local dDeltaS = dOvmHead
|
|
local dDeltaSMin = 0
|
|
local dDeltaE = BeamData.OVM_MID
|
|
for i = 1, #PARTS do
|
|
-- dati del pezzo
|
|
local b3BoxExact = EgtGetBBoxGlob( PARTS[i].id or GDB_ID.NULL, GDB_BB.EXACT)
|
|
if b3BoxExact:isEmpty() or PARTS[i].b3PartOriginal:isEmpty() then break end
|
|
EgtOutLog( 'PartSez=' .. EgtNumToString( b3BoxExact:getDimY(), 1) .. 'x' .. EgtNumToString( b3BoxExact:getDimZ(), 1), 3)
|
|
-- se sezione compatibile e lunghezza disponibile sufficiente
|
|
local dPartLen = PARTS[i].b3PartOriginal:getDimX()
|
|
local dPartWidth = PARTS[i].b3PartOriginal:getDimY()
|
|
local dPartHeight = PARTS[i].b3PartOriginal:getDimZ()
|
|
local dNextLen = dLen - EgtIf( i == 1, dDeltaS, 0) - dPartLen - dDeltaE
|
|
if (( abs( dPartWidth - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartHeight - dRawH) < 100 * GEO.EPS_SMALL) or
|
|
( abs( dPartHeight - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartWidth - dRawH) < 100 * GEO.EPS_SMALL)) and
|
|
dNextLen + dDeltaE >= 0 then
|
|
-- eventuale sovramateriale di testa
|
|
if i > 1 then
|
|
if PARTS[i].dPosX then
|
|
dDeltaS = max( PARTS[i].dPosX - ( dRawL - dLen), dDeltaSMin)
|
|
else
|
|
dDeltaS = max( dOvmMid - dDeltaE, 0)
|
|
end
|
|
end
|
|
-- dimensioni del grezzo
|
|
local dCrawLen = min( dPartLen + dDeltaS + dDeltaE, dLen)
|
|
local dDelta = dCrawLen - dPartLen - dDeltaS
|
|
-- creo e posiziono il grezzo
|
|
PARTS[i].idRaw = EgtAddRawPart( Point3d(0,0,0), dCrawLen, dRawW, dRawH, BeamData.RAWCOL)
|
|
|
|
EgtMoveToCornerRawPart( PARTS[i].idRaw, BeamData.ptOriXR, BeamData.dPosXR)
|
|
EgtMoveRawPart( PARTS[i].idRaw, Vector3d( dLen - dRawL, 0, 0))
|
|
-- assegno ordine in lavorazione
|
|
nCnt = nCnt + 1
|
|
EgtSetInfo( PARTS[i].idRaw, 'ORD', nCnt)
|
|
-- creo o pulisco gruppo geometrie aggiuntive
|
|
if not BeamLib.CreateOrEmptyAddGroup( PARTS[i].id) then
|
|
local sOut = 'Error creating Additional Group in Part ' .. tostring( PARTS[i].id)
|
|
return false, sOut
|
|
end
|
|
-- aggiungo faccia per taglio iniziale al pezzo
|
|
BeamLib.AddPartStartFace( PARTS[i].id, PARTS[i].b3PartOriginal)
|
|
-- se sovramateriale di testa, lo notifico
|
|
if dDeltaS > 0.09 then
|
|
EgtSetInfo( PARTS[i].idRaw, 'HOVM', dDeltaS)
|
|
if idPrevRaw then
|
|
EgtSetInfo( idPrevRaw, 'BDST', dDeltaS + dPrevDelta)
|
|
end
|
|
end
|
|
if dDeltaE > 0.09 then
|
|
EgtSetInfo( PARTS[i].idRaw, 'TOVM', dDeltaE)
|
|
end
|
|
-- aggiungo faccia per taglio finale al pezzo
|
|
BeamLib.AddPartEndFace( PARTS[i].id, PARTS[i].b3PartOriginal)
|
|
-- inserisco il pezzo nel grezzo
|
|
EgtDeselectPartObjs( PARTS[i].id)
|
|
local ptPos = b3BoxExact:getMin() - PARTS[i].b3PartOriginal:getMin() + Vector3d( dDelta, ( dRawW - dPartWidth) / 2, ( dRawH - dPartHeight) / 2)
|
|
EgtAddPartToRawPart( PARTS[i].id, ptPos, PARTS[i].idRaw)
|
|
if abs( dPartWidth - dRawW) > 100 * GEO.EPS_SMALL then
|
|
-- rotazione attorno a centro geometria complessiva del pezzo
|
|
EgtRotatePartInRawPart( PARTS[i].id, X_AX(), 90)
|
|
-- correggo per eccentricità solido rispetto a geometria complessiva del pezzo
|
|
local vtEccOri = PARTS[i].b3PartOriginal:getCenter() - b3BoxExact:getCenter()
|
|
local vtEccRot = Vector3d( vtEccOri)
|
|
vtEccRot:rotate( X_AX(), 90)
|
|
EgtMovePartInRawPart( PARTS[i].id, ( vtEccOri - vtEccRot))
|
|
end
|
|
-- aggiorno la lunghezza residua della barra
|
|
dLen = dLen - dCrawLen
|
|
-- aggiorno grezzo precedente
|
|
idPrevRaw = PARTS[i].idRaw
|
|
dPrevDelta = dDelta
|
|
PARTS[i].bIsLastPart = ( i == #PARTS)
|
|
PARTS[i].dDistanceToNextPiece = dDelta
|
|
PARTS[i].dRestLength = dLen
|
|
PARTS[i].b3Raw = EgtGetRawPartBBox( PARTS[i].idRaw)
|
|
PARTS[i].dLength = PARTS[i].b3Raw:getDimX()
|
|
PARTS[i].dWidth = PARTS[i].b3Raw:getDimY()
|
|
PARTS[i].dHeight = PARTS[i].b3Raw:getDimZ()
|
|
PARTS[i].bSquareSection = abs( PARTS[i].dWidth - PARTS[i].dHeight) < 100 * GEO.EPS_SMALL
|
|
PARTS[i].idBoxTm = EgtGetFirstInGroup( EgtGetFirstNameInGroup( PARTS[i].id, 'Box') or GDB_ID.NULL)
|
|
PARTS[i].b3Part = EgtGetBBoxGlob( PARTS[i].idBoxTm, GDB_BB.STANDARD)
|
|
PARTS[i].nIndexInParts = i
|
|
PARTS[i].SplittingPoints = BeamLib.GetPartSplittingPoints( PARTS[i])
|
|
PARTS[i].NotClampableLength = { STD = { dHead = 0, dTail = 0}, SIDE = { dHead = 0, dTail = 0}, DOWN = { dHead = 0, dTail = 0}}
|
|
PARTS[i].dHeadOverMaterial = dDeltaS
|
|
PARTS[i].sBTLInfo = EgtGetInfo( PARTS[i].id, 'PROJ', 's') or nil
|
|
|
|
PARTS[i].sAISetupConfig = EgtGetInfo( PARTS[i].id, 'AISETUP', 's') or
|
|
( GENERAL_PARAMETERS.BTL[PARTS[i].sBTLInfo] and GENERAL_PARAMETERS.BTL[PARTS[i].sBTLInfo].sAISetupConfig) or -- i parametri BTL potrebbero non esistere
|
|
GENERAL_PARAMETERS.PROJECT.sAISetupConfig or nil
|
|
|
|
-- si carica configurazione lavorazioni
|
|
TIMER:startElapsed('Json')
|
|
BeamExec.GetStrategiesFromJSONinBD( PARTS[i].sAISetupConfig)
|
|
PARTS[i].GeneralParameters = BeamLib.GetPieceGeneralParameters( PARTS[i], GENERAL_PARAMETERS_JSON)
|
|
TIMER:stopElapsed('Json')
|
|
PARTS[i].CombinationList = BeamExec.GetAvailableCombinations( PARTS[i], bIsFlipRot)
|
|
PARTS[i].idTempGroup = idTempGroup
|
|
|
|
else
|
|
local sOut = 'Error: part L(' .. EgtNumToString( dPartLen, 1) .. ') too big for raw part L(' .. EgtNumToString( dLen - 0.1, 1) .. ')'
|
|
return false, sOut
|
|
end
|
|
-- se rimasto troppo poco grezzo, esco
|
|
--if Len < BeamData.MinRaw then break end
|
|
DeltaS = 0
|
|
end
|
|
if idPrevRaw then
|
|
EgtSetInfo( idPrevRaw, 'BDST', 10000)
|
|
end
|
|
|
|
-- Se rimasto materiale aggiungo grezzo dell'avanzo
|
|
-- TODO valutare se ridurre la dLen minima perchè crea discrepanze tra lunghezza inserita e VMill
|
|
if dLen > 10 then
|
|
local idRaw = EgtAddRawPart( Point3d(0,0,0), dLen, dRawW, dRawH, BeamData.RAWCOL)
|
|
EgtMoveToCornerRawPart( idRaw, BeamData.ptOriXR, BeamData.dPosXR)
|
|
EgtMoveRawPart( idRaw, Vector3d( dLen - dRawL, 0, 0))
|
|
-- assegno ordine in lavorazione
|
|
nCnt = nCnt + 1
|
|
EgtSetInfo( idRaw, 'ORD', nCnt)
|
|
-- aggiorno distanza dell'ultimo pezzo dall'eventuale grezzo scaricabile
|
|
if EgtGetRawPartBBox( idRaw):getDimX() < BeamData.dMinRaw then
|
|
PARTS[#PARTS].dDistanceToNextPiece = 10000
|
|
end
|
|
else
|
|
PARTS[#PARTS].dDistanceToNextPiece = 10000
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
function BeamExec.CalcMinUnloadableRaw( dRawW, dRawH)
|
|
-- TODO convertire in GetMinUnloadableRaw che viene richiamata all'inizio delle funzioni che necessitano di dMinRaw
|
|
if BeamData.GetMinUnloadableRaw then
|
|
BeamData.dMinRaw = BeamData.GetMinUnloadableRaw( dRawW, dRawH)
|
|
else
|
|
local H_S = 200
|
|
local H_L = 400
|
|
-- Determinazione minimo grezzo scaricabile
|
|
if dRawH <= H_S then
|
|
BeamData.dMinRaw = BeamData.MINRAW_S
|
|
elseif dRawH <= H_L then
|
|
local Coeff = ( dRawH - H_S) / ( H_L - H_S)
|
|
BeamData.dMinRaw = ( 1 - Coeff) * BeamData.MINRAW_S + Coeff * BeamData.MINRAW_L
|
|
else
|
|
BeamData.dMinRaw = BeamData.MINRAW_L
|
|
end
|
|
end
|
|
-- lettura minimo pezzo pinzabile
|
|
BeamData.dMinClampRaw = BeamData.LEN_VERY_SHORT_PART or 400
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- *** Inserimento delle lavorazioni nelle travi ***
|
|
-------------------------------------------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function GetStrategies( Proc, sAISetupConfigName)
|
|
local AvailableStrategiesForProc = nil
|
|
-- se la lista STRATEGIES è stata letta da JSON (quindi non è vuota), ritorno le strategie possibili
|
|
if STRATEGIES and sAISetupConfigName and STRATEGIES[sAISetupConfigName] and #STRATEGIES[sAISetupConfigName].FEATURE > 0 then
|
|
AvailableStrategiesForProc = BeamLib.GetStrategiesFromList( Proc, STRATEGIES[sAISetupConfigName])
|
|
end
|
|
-- se non ho trovato strategie disponibili nel JSON, o se JSON non presente, lancio script che setta le strategie in modo statico, come definito con cliente
|
|
if not AvailableStrategiesForProc then
|
|
AvailableStrategiesForProc = BCS.GetStrategiesFromBasicCustomerStrategies( Proc)
|
|
end
|
|
return AvailableStrategiesForProc
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function GetFeatureForcedStrategy( Proc)
|
|
-- cerco nelle note se è stata forzata una strategia specifica
|
|
local sStrategyId = EgtGetInfo( Proc.id, 'STRATEGY', 's')
|
|
|
|
-- se è presente la strategia forzata
|
|
if sStrategyId then
|
|
-- si prepara tabella strategia. In questa fase si salva solo id strategia e il fatto che è stata forzata. I parametri forzati vengono letti all'esecuzioen della strategia
|
|
local StrategyToProc = {}
|
|
local ParamList = {}
|
|
ParamList.bForcedStrategy = true
|
|
ParamList.sStrategyId = sStrategyId
|
|
|
|
-- ritorno la lista strategia con parametri
|
|
table.insert( StrategyToProc, ParamList)
|
|
return StrategyToProc
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function CollectFeatures( Part, dRotIndex)
|
|
-- recupero le feature
|
|
local nProcCount = 0
|
|
local vProc = {}
|
|
local LayerId = {}
|
|
LayerId[1] = BeamLib.GetAddGroup( Part.id)
|
|
LayerId[2] = EgtGetFirstNameInGroup( Part.id or GDB_ID.NULL, 'Processings')
|
|
for nInd = 1, 2 do
|
|
local ProcId = EgtGetFirstInGroup( LayerId[nInd] or GDB_ID.NULL)
|
|
while ProcId do
|
|
local nEntType = EgtGetType( ProcId)
|
|
if nEntType == GDB_TY.SRF_MESH or nEntType == GDB_TY.EXT_TEXT or
|
|
nEntType == GDB_TY.CRV_LINE or nEntType == GDB_TY.CRV_ARC or nEntType == GDB_TY.CRV_BEZ or nEntType == GDB_TY.CRV_COMPO then
|
|
local nGrp = EgtGetInfo( ProcId, 'GRP', 'i')
|
|
local nPrc = EgtGetInfo( ProcId, 'PRC', 'i')
|
|
local nDo = EgtGetInfo( ProcId, 'DO', 'i') or 1
|
|
if nGrp and nPrc and nDo == 1 then
|
|
local Proc = {}
|
|
Proc.isVirtualProc = nInd == 1 -- feature non presente in lista originale BTL ma aggiunta da automatismo
|
|
Proc.idPart = Part.id
|
|
Proc.idRaw = Part.idRaw
|
|
Proc.nIndexPartInParts = Part.nIndexInParts
|
|
Proc.nCurrentRotation = dRotIndex
|
|
Proc.id = ProcId
|
|
-- id della feature btl ( se non presente info, si prende id dell'entità geometrica)
|
|
Proc.idFeature = EgtGetInfo( Proc.id, 'PRID', 's') or Proc.id
|
|
Proc.nGrp = nGrp
|
|
Proc.nPrc = nPrc
|
|
Proc.nFlg = 1
|
|
Proc.nFct = EgtSurfTmFacetCount( ProcId) or 0
|
|
Proc.idCut = EgtGetInfo( EgtGetParent( EgtGetParent( ProcId)), 'CUTID', 'i') or 0
|
|
Proc.idTask = EgtGetInfo( ProcId, 'TASKID', 'i') or 0
|
|
Proc.b3Box = EgtGetBBoxGlob( ProcId, GDB_BB.STANDARD)
|
|
EgtOutLog( '------Feature ' .. Proc.idFeature .. '------')
|
|
Proc.Topology = {}
|
|
-- se esiste la geometria
|
|
if Proc.b3Box and not Proc.b3Box:isEmpty() then
|
|
-- informazioni facce e topologia
|
|
Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part)
|
|
-- volume feature approssimato
|
|
Proc.dVolume = FeatureLib.GetFeatureVolume( Proc, Part)
|
|
-- calcolo topologia solo se necessario, altrimenti si sfruttano le informazioni della feature BTL
|
|
local bIsFeatureReadyForProcessing = false
|
|
if FeatureLib.NeedTopologyFeature( Proc, Part) then
|
|
Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc)
|
|
Proc.Faces = FaceData.GetFacesInfo( Proc, Part)
|
|
Proc.Topology = FeatureLib.ClassifyTopology( Proc, Part)
|
|
-- se topologia feature riconosciuta, oppure da non calcolare perchè il riconoscimento topologico è basato sulla feature stessa
|
|
if Proc.Topology.sName ~= 'NOT_IMPLEMENTED' then
|
|
Proc.MainFaces = FaceData.GetMainFaces( Proc, Part)
|
|
bIsFeatureReadyForProcessing = true
|
|
end
|
|
else
|
|
Proc = FeatureLib.GetAdditionalInfo( Proc, Part)
|
|
Proc.Topology = FeatureLib.GetTopologyFromFeature( Proc, Part)
|
|
bIsFeatureReadyForProcessing = true
|
|
end
|
|
-- se la feature è stata compresa
|
|
if bIsFeatureReadyForProcessing then
|
|
-- se la processing ha una strategia forzata, riporto tutto nella proc
|
|
local vForcedStrategy = GetFeatureForcedStrategy( Proc)
|
|
if vForcedStrategy then
|
|
Proc.AvailableStrategies = vForcedStrategy
|
|
-- altrimenti cerco tra le strategie disponibili
|
|
else
|
|
Proc.AvailableStrategies = GetStrategies( Proc, Part.sAISetupConfig)
|
|
end
|
|
-- se ci sono strategie disponibili, aggiungo a lista delle feature da lavorare
|
|
if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then
|
|
Proc.AvailableStrategies.dAllStrategiesTotalTime = 0
|
|
nProcCount = nProcCount + 1
|
|
Proc.nIndexInVProc = nProcCount
|
|
table.insert( vProc, Proc)
|
|
-- altrimenti errore (non ci sono strategie per lavorare la topologia riconosciuta)
|
|
else
|
|
Proc.nFlg = 0
|
|
nProcCount = nProcCount + 1
|
|
Proc.nIndexInVProc = nProcCount
|
|
table.insert( vProc, Proc)
|
|
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies')
|
|
end
|
|
-- calcolo riduzione lunghezza pinzabile testa/coda
|
|
Proc.NotClampableLength = FeatureLib.CalculateFeatureNotClampableLengths( Proc, Part)
|
|
-- si verifica se la feature, lavorata in questa fase, compromette lettura misura laser
|
|
Proc.bHindersLaserMeasure = FeatureLib.CalculateFeatureHindersLaserMeasure( Proc, Part)
|
|
-- altrimenti errore (serviva riconoscimento topologico, ma non è stato possibile farlo)
|
|
else
|
|
Proc.nFlg = 0
|
|
nProcCount = nProcCount + 1
|
|
Proc.nIndexInVProc = nProcCount
|
|
table.insert( vProc, Proc)
|
|
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies')
|
|
end
|
|
else
|
|
Proc.nFlg = 0
|
|
nProcCount = nProcCount + 1
|
|
Proc.nIndexInVProc = nProcCount
|
|
table.insert( vProc, Proc)
|
|
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' is empty (no geometry)')
|
|
end
|
|
end
|
|
end
|
|
ProcId = EgtGetNext( ProcId)
|
|
end
|
|
end
|
|
return vProc
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function AreDrillingsMirrored( Proc, ProcMirror, Part)
|
|
if Proc.id == ProcMirror.id then return false end
|
|
|
|
-- geometria ausiliaria foro principale
|
|
AuxId = EgtGetInfo( Proc.id, 'AUXID', 'i')
|
|
if AuxId then AuxId = AuxId + Proc.id end
|
|
if not AuxId or EgtGetType( AuxId ) ~= GDB_TY.CRV_ARC then return false end
|
|
-- geometria ausiliaria foro specchiato
|
|
local AuxIdMirror = EgtGetInfo( ProcMirror.id, 'AUXID', 'i')
|
|
if AuxIdMirror then AuxIdMirror = AuxIdMirror + ProcMirror.id end
|
|
if not AuxIdMirror or EgtGetType( AuxIdMirror ) ~= GDB_TY.CRV_ARC then return false end
|
|
-- dati del foro principale
|
|
local vtExtr = EgtCurveExtrusion( AuxId, GDB_RT.GLOB)
|
|
local ptBC = EgtGP( AuxId, GDB_RT.GLOB)
|
|
-- dati del foro specchiato
|
|
local vtExtrMirror = EgtCurveExtrusion( AuxIdMirror, GDB_RT.GLOB)
|
|
local ptBCMirror = EgtGP( AuxIdMirror, GDB_RT.GLOB)
|
|
|
|
-- direzione fori
|
|
local nDouble
|
|
if AreOppositeVectorApprox( vtExtr, vtExtrMirror) then
|
|
-- fori lungo Y
|
|
-- per macchine tipo PF il foro principale è sul lato back, per macchine tipo PF1250 è sul lato front
|
|
-- TODO eliminare i riferimenti a DOWN_HEAD e TWO_EQUAL_HEADS e sostituirli con check iniziale sulla disponibilità di teste?
|
|
if ( BeamData.TWO_EQUAL_HEADS and AreSameVectorApprox( vtExtr, Y_AX())) or
|
|
( BeamData.DOWN_HEAD and AreOppositeVectorApprox( vtExtr, Y_AX())) then
|
|
nDouble = 2
|
|
-- fori lungo Z
|
|
elseif BeamData.DOWN_HEAD and AreSameVectorApprox( vtExtr, Z_AX()) then
|
|
nDouble = 3
|
|
else
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
|
|
-- centri allineati, equidistanti dalla mezzeria trave, non troppo vicini
|
|
local vtDisplacement = ptBC - ptBCMirror
|
|
local ptCenRaw = Part.b3Raw:getCenter()
|
|
if nDouble == 2 then
|
|
local dYMinDistance = max( Proc.b3Box:getMin():getY(), ProcMirror.b3Box:getMin():getY()) - min( Proc.b3Box:getMax():getY(), ProcMirror.b3Box:getMax():getY())
|
|
if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getZ()) < 100 * GEO.EPS_SMALL and
|
|
( abs( ptBC:getY() - ptCenRaw:getY()) - abs( ptBCMirror:getY() - ptCenRaw:getY())) < 100 * GEO.EPS_SMALL and
|
|
dYMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then
|
|
return false
|
|
end
|
|
else
|
|
local dZMinDistance = max( Proc.b3Box:getMin():getZ(), ProcMirror.b3Box:getMin():getZ()) - min( Proc.b3Box:getMax():getZ(), ProcMirror.b3Box:getMax():getZ())
|
|
if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getY()) < 100 * GEO.EPS_SMALL and
|
|
( abs( ptBC:getZ() - ptCenRaw:getZ()) - abs( ptBCMirror:getZ() - ptCenRaw:getZ())) < 100 * GEO.EPS_SMALL and
|
|
dZMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- fori della stessa profondità
|
|
if abs( Proc.dLen - ProcMirror.dLen) > 10 * GEO.EPS_SMALL then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function GetFeatureInfoAndDependency( vProcSingleRot, Part)
|
|
-- ciclo tutte le feature
|
|
for i = 1, #vProcSingleRot do
|
|
local Proc = vProcSingleRot[i]
|
|
-- se feature abilitata alla lavorazione
|
|
if Proc.nFlg ~= 0 then
|
|
-- controllo la feature con tutte le altre per recuperare le dipendenze
|
|
for j = 1, #vProcSingleRot do
|
|
local ProcB = vProcSingleRot[j]
|
|
-- non si controlla la feature con se stessa o se feature disabilitata
|
|
if i ~= j and ProcB.nFlg ~= 0 then
|
|
-- se entrambi tagli di testa, si tiene sempre il primo ( ma non quello aggiunto dall'automatismo)
|
|
if ( ID.IsHeadCut( Proc) and not EgtGetInfo( Proc.id, 'HEAD_ADD_CUT', 'i')) and ID.IsHeadCut( ProcB) then
|
|
if not Proc.SlaveProcIndexes then
|
|
Proc.SlaveProcIndexes = {}
|
|
end
|
|
table.insert( Proc.SlaveProcIndexes, j)
|
|
ProcB.nIndexMasterProc = i
|
|
ProcB.nFlg = 0
|
|
end
|
|
-- se entrambi tagli di coda, si tiene sempre il primo ( ma non quello aggiunto dall'automatismo)
|
|
if ( ID.IsTailCut( Proc) and not EgtGetInfo( Proc.id, 'HEAD_ADD_CUT', 'i')) and ID.IsTailCut( ProcB) then
|
|
if not Proc.SlaveProcIndexes then
|
|
Proc.SlaveProcIndexes = {}
|
|
end
|
|
table.insert( Proc.SlaveProcIndexes, j)
|
|
ProcB.nIndexMasterProc = i
|
|
ProcB.nFlg = 0
|
|
end
|
|
-- verifico se feature tipo LapJoint è attraversata da almeno un foro
|
|
if ( Proc.Topology.sFamily == 'Pocket' or Proc.Topology.sFamily == 'Tunnel' or Proc.Topology.sFamily == 'Groove' or ID.IsMortise( Proc)) and
|
|
ID.IsDrill( ProcB) and Overlaps( Proc.b3Box, ProcB.b3Box) then
|
|
Proc.bPassedByHole = true
|
|
end
|
|
-- verifiche per specchiature
|
|
-- TODO eliminare i riferimenti a DOWN_HEAD e TWO_EQUAL_HEADS e sostituirli con check iniziale sulla disponibilità di teste?
|
|
if BeamData.DOWN_HEAD or BeamData.TWO_EQUAL_HEADS then
|
|
-- forature
|
|
if BeamData.DOUBLE_HEAD_DRILLING and ID.IsDrill( Proc) and ID.IsDrill( ProcB) and not Proc.Mirror then
|
|
if AreDrillingsMirrored( Proc, ProcB, Part) then
|
|
Proc.Mirror = ProcB
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return vProcSingleRot
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- caricamento librerie strategie (standard o speciali) se presenti nella Proc come strategie disponibili per questo Processing
|
|
local function RunStrategyLibraries( sStrategyId)
|
|
-- file script
|
|
local StrategyScriptName = sStrategyId .. '\\' .. sStrategyId
|
|
local StrategyScriptPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyScriptName .. '.lua'
|
|
local StrategyScriptPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyScriptName .. '.lua'
|
|
-- file config
|
|
local StrategyConfigName = sStrategyId .. '\\' .. sStrategyId .. '.json'
|
|
local StrategyConfigPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyConfigName
|
|
local StrategyConfigPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyConfigName
|
|
|
|
local StrategyLib = {}
|
|
if ( EgtExistsFile( StrategyConfigPathStandard) and EgtExistsFile( StrategyScriptPathStandard)) or
|
|
( EgtExistsFile( StrategyConfigPathSpecial) and EgtExistsFile( StrategyScriptPathSpecial)) then
|
|
-- caricamento script strategia come libreria
|
|
StrategyLib.Script = require( StrategyScriptName)
|
|
-- se config strategia non ancora caricato, importo il JSON
|
|
if not STRATEGIES_CONFIG[sStrategyId] then
|
|
if EgtExistsFile( StrategyConfigPathStandard) then
|
|
STRATEGIES_CONFIG[sStrategyId] = BeamLib.ImportFileJSON( StrategyConfigPathStandard)
|
|
else
|
|
STRATEGIES_CONFIG[sStrategyId] = BeamLib.ImportFileJSON( StrategyConfigPathSpecial)
|
|
end
|
|
end
|
|
StrategyLib.Config = STRATEGIES_CONFIG[sStrategyId]
|
|
return StrategyLib
|
|
else
|
|
return {}
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- ritorna l'indice della strategia migliore, tra le due passate
|
|
local function GetIndexBestStrategyFromComparison( AvailableStrategies, Part, nIndex1, nIndex2)
|
|
local dChosenIndex = 0
|
|
-- controllo indici
|
|
if nIndex1 == 0 and nIndex2 == 0 then
|
|
dChosenIndex = 0
|
|
elseif nIndex1 == nIndex2 then
|
|
dChosenIndex = nIndex1
|
|
elseif nIndex1 == 0 then
|
|
-- basta che sia applicabile
|
|
if AvailableStrategies[nIndex2].Result and ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' or AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed') then
|
|
dChosenIndex = nIndex2
|
|
end
|
|
elseif nIndex2 == 0 then
|
|
-- basta che sia applicabile
|
|
if AvailableStrategies[nIndex1].Result and ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' or AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed') then
|
|
dChosenIndex = nIndex1
|
|
end
|
|
elseif not AvailableStrategies[nIndex1].Result or not AvailableStrategies[nIndex2].Result then
|
|
dChosenIndex = 0
|
|
elseif ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Completed') or
|
|
( AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex2].Result.sStatus == 'Not-Applicable') then
|
|
dChosenIndex = nIndex1
|
|
elseif ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' and AvailableStrategies[nIndex1].Result.sStatus ~= 'Completed') or
|
|
( AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex1].Result.sStatus == 'Not-Applicable') then
|
|
dChosenIndex = nIndex2
|
|
else
|
|
-- se le due strategie hanno stesso stato e sono entrambe applicabili (quindi entrambe complete o entrambe non-complete)
|
|
if AvailableStrategies[nIndex1].Result.sStatus ~= 'Not-Applicable' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Not-Applicable' and
|
|
AvailableStrategies[nIndex1].Result.sStatus == AvailableStrategies[nIndex2].Result.sStatus then
|
|
|
|
-- si predilige strategia con rating composito più alto
|
|
if AvailableStrategies[nIndex1].Result.dCompositeRating > AvailableStrategies[nIndex2].Result.dCompositeRating then
|
|
dChosenIndex = nIndex1
|
|
elseif AvailableStrategies[nIndex2].Result.dCompositeRating > AvailableStrategies[nIndex1].Result.dCompositeRating then
|
|
dChosenIndex = nIndex2
|
|
-- altrimenti si prende la strategia con indice più basso
|
|
else
|
|
dChosenIndex = EgtIf( nIndex1 < nIndex2, nIndex1, nIndex2)
|
|
end
|
|
end
|
|
end
|
|
return dChosenIndex
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che trova la strategia migliore tra quelle disponibili
|
|
local function GetFeatureBestStrategy( Proc, Part)
|
|
if Proc.nFlg ~= 0 then
|
|
local nIndexBestStrategy = 0
|
|
-- controllo se ci sono strategie disponibili
|
|
if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then
|
|
-- ciclo tutte le strategie della feature
|
|
for nIndexCurrentStrategy = 1, #Proc.AvailableStrategies do
|
|
-- scelgo la migliore strategia tra le due
|
|
nIndexBestStrategy = GetIndexBestStrategyFromComparison( Proc.AvailableStrategies, Part, nIndexCurrentStrategy, nIndexBestStrategy)
|
|
-- salvo sulla proc la migliore strategia
|
|
end
|
|
if nIndexBestStrategy ~= 0 then
|
|
Proc.ChosenStrategy = BeamLib.TableCopyDeep( Proc.AvailableStrategies[nIndexBestStrategy])
|
|
Proc.nIndexBestStrategy = nIndexBestStrategy
|
|
end
|
|
end
|
|
end
|
|
|
|
return Proc
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che trova la strategia migliore tra quelle disponibili
|
|
local function GetBestStrategyFromProcList( vProcSingleRot, Part)
|
|
for i = 1, #vProcSingleRot do
|
|
-- processo tutte le feature attive
|
|
local Proc = vProcSingleRot[i]
|
|
Proc = GetFeatureBestStrategy( Proc, Part)
|
|
end
|
|
return vProcSingleRot
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che processa tutte le feature del pezzo
|
|
local function CalculateStrategies( vProcSingleRot, Part)
|
|
-- per ogni feature
|
|
for i = 1, #vProcSingleRot do
|
|
-- processo tutte le feature attive
|
|
local Proc = vProcSingleRot[i]
|
|
if Proc.nFlg ~= 0 then
|
|
-- controllo se ci sono strategie disponibili
|
|
if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then
|
|
-- se le strategie disponibili sono le basic, non si possono customizzare valori di default da interfaccia
|
|
-- si leggono allora eventuali parametri di default scritti come info sulla feature
|
|
if Proc.AvailableStrategies.bIsBasicStrategy then
|
|
-- si recuperano eventuali parametri custom
|
|
for j = 1, #Proc.AvailableStrategies do
|
|
-- essendo una strategia basic, la lista dei parametri custom dovrebbe essere sempre vuota. Si leggono ora
|
|
if not Proc.AvailableStrategies[j].ParameterList then
|
|
Proc.AvailableStrategies[j].ParameterList = BCS.GetParametersFromBasicCustomerStrategies( Proc, Proc.AvailableStrategies[j].sStrategyId)
|
|
end
|
|
end
|
|
-- si riprocessano le strategie dopo che sono stati letti i parametri
|
|
Proc.AvailableStrategies = BCS.UpdateStrategies( Proc.AvailableStrategies)
|
|
end
|
|
|
|
-- ciclo tutte le strategie della feature
|
|
for nIndexCurrentStrategy = 1, #Proc.AvailableStrategies do
|
|
-- eseguo file config con i parametri di default
|
|
local CurrentStrategy = {}
|
|
CurrentStrategy = RunStrategyLibraries( Proc.AvailableStrategies[nIndexCurrentStrategy].sStrategyId)
|
|
-- controllo che le librerie siano state effettivamente caricate
|
|
if CurrentStrategy.Config and CurrentStrategy.Script then
|
|
-- eseguo la strategia solo come calcolo fattibilità e voto. Non si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati
|
|
_, Proc.AvailableStrategies[nIndexCurrentStrategy].Result = CurrentStrategy.Script.Make( false, Proc, Part, Proc.AvailableStrategies[nIndexCurrentStrategy])
|
|
|
|
-- TODO da capire se dare un tempo molto alto oppure se dare errore perchè non deve mai capitare. Per ora si setta tempo alto
|
|
-- se tempo non calcolato, si setta un tempo molto alto, 99 minuti
|
|
if not Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine or Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine == 0 then
|
|
Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine = 99
|
|
end
|
|
|
|
Proc.AvailableStrategies.dAllStrategiesTotalTime = Proc.AvailableStrategies.dAllStrategiesTotalTime + Proc.AvailableStrategies[nIndexCurrentStrategy].Result.dTimeToMachine
|
|
-- se scelta strategia in modalità base o standard, esco subito alla prima che trovo completa
|
|
if Part.GeneralParameters.GEN_sMachiningStrategy == 'FIRST_IN_LIST' and Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus == 'Complete' then
|
|
break
|
|
end
|
|
|
|
-- se non trovo i file della strategia (Script e Config), scrivo che non è più disponibile
|
|
else
|
|
Proc.AvailableStrategies[nIndexCurrentStrategy].Result = {}
|
|
Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sInfo = 'Strategy not found'
|
|
Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus = 'Not-Applicable'
|
|
end
|
|
end
|
|
-- si calcola il composite rating delle strategie
|
|
Proc.AvailableStrategies = FeatureLib.CalculateStrategiesCompositeRating( Proc.AvailableStrategies, Part.GeneralParameters.GEN_sMachiningStrategy)
|
|
end
|
|
end
|
|
end
|
|
return vProcSingleRot
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- Ordina le feature in base a fase di lavorazione
|
|
-- 1) Head : ( intestatura)
|
|
-- 2) Standard : ( lavorazioni standard che non impattano sulla coda)
|
|
-- 3) AdvanceTail : ( lavorazioni che richiedono taglio di separazione, ma che devono essere fatte prima perchè il taglio di separazione farebbe cadere il pezzo)
|
|
-- 4) Split : ( taglio di separazione)
|
|
-- 5) Tail : ( lavorazioni di coda, fatte dopo il taglio di separazione)
|
|
local function OrderFeatures( vProc)
|
|
local vProcToSort = vProc
|
|
-- funzione di confronto. TRUE = B1 prima di B2. FALSE = B2 prima di B1
|
|
local function CompareFeatures( B1, B2)
|
|
-- se secondo disabilitato, va lasciato dopo
|
|
if B1.nFlg ~= 0 and B2.nFlg == 0 then
|
|
return true
|
|
elseif B2.nFlg ~= 0 and B1.nFlg == 0 then
|
|
return false
|
|
-- se entrambi disabilitati seguo l'Id
|
|
elseif B1.nFlg == 0 and B2.nFlg == 0 then
|
|
return ( B1.id < B2.id)
|
|
-- se in rotazioni diverse, si mette in ordine di rotazioni
|
|
elseif B1.nRot ~= B2.nRot then
|
|
return ( B1.nRot < B2.nRot)
|
|
-- se primo è taglio di testa, va prima degli altri casi
|
|
elseif B1.Head and ( B2.Standard or B2.AdvanceTail or B2.Split or B2.Tail) then
|
|
return true
|
|
-- se primo è taglio standard, va prima degli altri casi
|
|
elseif B1.Standard and ( B2.AdvanceTail or B2.Split or B2.Tail) then
|
|
return true
|
|
-- se primo è taglio di testa anticipata, va prima degli altri casi
|
|
elseif B1.AdvanceTail and ( B2.Split or B2.Tail) then
|
|
return true
|
|
-- se primo è taglio di separazione, va prima delle lavorazioni di coda
|
|
elseif B1.Split and B2.Tail then
|
|
return true
|
|
-- se da lavorare in stessa fase pezzo
|
|
elseif B1.Head == B2.Head or B1.Standard == B2.Standard or B1.AdvanceTail == B2.AdvanceTail or B1.Split == B2.Split or B1.Tail == B2.Tail then
|
|
-- confronto standard
|
|
if abs( B1.b3Box:getCenter():getX() - B2.b3Box:getCenter():getX()) > 0.4 * ( B1.b3Box:getDimX() + B2.b3Box:getDimX()) then
|
|
return B1.b3Box:getCenter():getX() > B2.b3Box:getCenter():getX()
|
|
elseif abs( B1.b3Box:getCenter():getY() - B2.b3Box:getCenter():getY()) > 0.2 * ( B1.b3Box:getDimY() + B2.b3Box:getDimY()) then
|
|
return B1.b3Box:getCenter():getY() > B2.b3Box:getCenter():getY()
|
|
elseif abs( B1.b3Box:getCenter():getZ() - B2.b3Box:getCenter():getZ()) > 0.1 * ( B1.b3Box:getDimZ() + B2.b3Box:getDimZ()) then
|
|
return B1.b3Box:getCenter():getZ() > B2.b3Box:getCenter():getZ()
|
|
end
|
|
-- altrimenti si inverte
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- test della funzione di ordinamento
|
|
if EgtGetDebugLevel() >= 3 then
|
|
EgtOutLog( ' CompareFeatures Test ')
|
|
local bCompTest = true
|
|
for i = 1, #vProcToSort do
|
|
for j = i + 1, #vProcToSort do
|
|
local bComp1 = CompareFeatures( vProcToSort[i], vProcToSort[j])
|
|
local bComp2 = CompareFeatures( vProcToSort[j], vProcToSort[i])
|
|
if bComp1 == bComp2 then
|
|
bCompTest = false
|
|
EgtOutLog( string.format( ' ProcId : %d vs %d --> ERROR', vProcToSort[i].id, vProcToSort[j].id))
|
|
end
|
|
end
|
|
end
|
|
if bCompTest then
|
|
EgtOutLog( ' ALL OK')
|
|
end
|
|
end
|
|
|
|
-- eseguo ordinamento
|
|
table.sort( vProcToSort, CompareFeatures)
|
|
|
|
return vProcToSort
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function AddFeatureResultToGlobalList( 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 ChosenStrategyTable = PROCESSINGS[Proc.nIndexPartInParts].Rotation[Proc.nIndexRotation][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][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
|
|
-- se non esiste una strategia scelta (non dovrebbe mai succedere) cancello da lista generale
|
|
PROCESSINGS[Proc.nIndexPartInParts].Rotation[Proc.nIndexRotation][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( Proc, { nRotation = nCurrRotation})
|
|
end
|
|
|
|
-- ripristino pezzo in posizione originale
|
|
if nCurrRotation ~= 1 then
|
|
BeamLib.RotateRawPart( Part, 1 - nCurrRotation)
|
|
-- aggiorno info pezzo
|
|
Part.b3Raw = EgtGetRawPartBBox( Part.idRaw)
|
|
Part.b3Part = EgtGetBBoxGlob( Part.idBoxTm, GDB_BB.STANDARD)
|
|
end
|
|
return MACHININGS
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function PrintFeatures( vProc, Part)
|
|
if vProc then
|
|
EgtOutLog( ' RawBox=' .. tostring( Part.b3Box))
|
|
for i = 1, #vProc do
|
|
local Proc = vProc[i]
|
|
local sOut = string.format( ' Id=%3d Grp=%1d Prc=%3d Flg=%2d Fct=%2d TopoName=%s',
|
|
Proc.id, Proc.nGrp, Proc.nPrc, Proc.nFlg, Proc.nFct, Proc.Topology.sName or '')
|
|
EgtOutLog( sOut)
|
|
end
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che recupera tutti i processing di tutti i pezzi in tutte le rotazioni
|
|
function BeamExec.GetProcessings( PARTS, bIsFlipRot)
|
|
|
|
-- si aprono i limiti tavola per permettere rotazioni di pezzi più larghi della tavola
|
|
EgtSetTableAreaOffset( 2000, 2000, 2000, 2000)
|
|
|
|
for nPart = 1, #PARTS do
|
|
if not PARTS[nPart].id and PARTS[nPart].b3Raw:getDimX() < BeamData.dMinRaw then break end
|
|
local vProcRot = {}
|
|
|
|
-- se è prerotazione, oltre al ciclo normale, si devono verificare anche invertiti
|
|
local bCalcInverted = bIsFlipRot and PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion
|
|
local nCycles = EgtIf( bCalcInverted, 2, 1)
|
|
-- per ogni inversione
|
|
for nInvertIndex = 1, nCycles do
|
|
-- per ogni rotazione
|
|
for nRotIndex = 1, 4 do
|
|
local nOffsetIndex = EgtIf( nInvertIndex == 2, 4, 0)
|
|
local nIndex = nRotIndex + nOffsetIndex
|
|
-- si calcolano le feature solo se la rotazione può essere presa in considerazione
|
|
if PARTS[nPart].CombinationList.Rotations[nRotIndex] == 1 then
|
|
-- recupero le feature di lavorazione della trave
|
|
table.insert( vProcRot, CollectFeatures( PARTS[nPart], nIndex))
|
|
|
|
-- recupero informazioni ausiliarie feature e dipendenze tra feature stesse
|
|
-- TODO le dipendenze cambiano in base alla rotazione del pezzo? probabilmente no
|
|
vProcRot[nIndex] = GetFeatureInfoAndDependency( vProcRot[nIndex], PARTS[nPart])
|
|
else
|
|
-- inserisco una tabella vuota
|
|
table.insert( vProcRot, {})
|
|
end
|
|
-- rotazione pezzo di 90° per volta
|
|
BeamLib.RotateRawPart( PARTS[nPart], 1)
|
|
-- aggiorno info pezzo
|
|
PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw)
|
|
PARTS[nPart].b3Part = EgtGetBBoxGlob( PARTS[nPart].idBoxTm, GDB_BB.STANDARD)
|
|
end
|
|
-- se si devono calcolare anche gli invertiti
|
|
if bCalcInverted then
|
|
-- inversione pezzo testa-coda
|
|
BeamLib.InvertRawPart( PARTS[nPart], 2)
|
|
-- aggiorno info pezzo
|
|
PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw)
|
|
PARTS[nPart].b3Part = EgtGetBBoxGlob( PARTS[nPart].idBoxTm, GDB_BB.STANDARD)
|
|
end
|
|
end
|
|
local Part = {}
|
|
Part.Rotation = vProcRot
|
|
-- recupero le feature di lavorazione della trave
|
|
table.insert( PROCESSINGS, Part)
|
|
end
|
|
|
|
return PROCESSINGS
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che decide la migliore tra le combinazioni di rotazione disponibili
|
|
local function GetBestCombination( ListToCompare, Part)
|
|
local nIndexBestCombination = 1
|
|
if #ListToCompare == 1 then
|
|
nIndexBestCombination = 1
|
|
else
|
|
for ListIndex = 2, #ListToCompare do
|
|
-- se rotazione non ammessa, si sceglie la migliore soluzione SENZA rotazioni
|
|
if Part.GeneralParameters.GEN_sPieceRotation == 'NOT_ALLOWED' then
|
|
-- scelgo soluzione senza rotazioni
|
|
if ListToCompare[ListIndex].nRotations == 0 then
|
|
-- scelgo soluzione con voto più alto
|
|
if ListToCompare[nIndexBestCombination].dTotalRating < 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 rotazioni indipendentemente dal voto
|
|
if 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 < 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 < dOtherTotalRating then
|
|
nIndexBestCombination = ListIndex
|
|
-- se stesso voto
|
|
elseif dBestTotalRating == dOtherTotalRating 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)
|
|
-- ciclo sui pezzi
|
|
local nTotErr = 0
|
|
local Stats = {}
|
|
local nOrd = 1
|
|
local nMaxReProcessCycles = EgtClamp( GENERAL_PARAMETERS.PROJECT.GEN_nMaxReProcessCycles, 1, 3)
|
|
local bTryToReProcess = false
|
|
|
|
-- ricerca strategia di lavorazione per ogni pezzo e applicazione lavorazioni
|
|
for nPart = 1, #PARTS do
|
|
local nCycles = 1
|
|
|
|
-- 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( 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
|
|
-- salvo sul PART la posizione di partenza che è stata scelta
|
|
PARTS[nPart].nInitialPosition = MatrixResult.nInitialPosition
|
|
|
|
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
|
|
nRotHeadCut = nRotHeadCut + nOffsetIndex
|
|
local nRotSplitCut = MatrixResult.nInitialPosition + MACHININGS.Info.nSplitCutRotation - 1
|
|
if nRotSplitCut > 4 then
|
|
nRotSplitCut = nRotSplitCut - 4
|
|
end
|
|
nRotSplitCut = nRotSplitCut + nOffsetIndex
|
|
-- setto nella Proc l'indice rotazione nella quale deve essere lavorata
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].nIndexRotation = nRotHeadCut
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].nIndexRotation = nRotSplitCut
|
|
|
|
-- si imposta flag rotazione per taglio di testa
|
|
if MACHININGS.Info.nHeadCutRotation == 2 then
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bSide = true
|
|
elseif MACHININGS.Info.nHeadCutRotation == 3 then
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bDown = true
|
|
else
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bStd = true
|
|
end
|
|
-- si imposta flag rotazione per taglio di coda
|
|
if MACHININGS.Info.nSplitCutRotation == 2 then
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bSide = true
|
|
elseif MACHININGS.Info.nSplitCutRotation == 3 then
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bDown = true
|
|
else
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bStd = true
|
|
end
|
|
|
|
local vProcHeadTail = {}
|
|
table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc])
|
|
table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotSplitCut][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 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
|
|
local 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 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)
|
|
-- ciclo sui pezzi
|
|
local nTotErr = 0
|
|
local Stats = {}
|
|
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_bTestAlternative or not PARTS[nPart].GeneralParameters.GEN_bGetAlternativesNesting2D then
|
|
return
|
|
end
|
|
|
|
-- si analizzano le alternative solo del primo pezzo (la FlipRot viene chiamata sempre con un solo pezzo)
|
|
local nCycles = 1
|
|
|
|
-- 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_bTestAlternative 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}})
|
|
if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then
|
|
table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = 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}})
|
|
if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then
|
|
table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = 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}})
|
|
if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then
|
|
table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = 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}})
|
|
if PARTS[nPart].GeneralParameters.GEN_bAllowPieceInversion then
|
|
table.insert( TotalCombiToTest, {{ sBitIndexCombination = sCombinationToCheck, bPartInCombiIsInverted = 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 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
|
|
|
|
-- 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
|
|
nRotHeadCut = nRotHeadCut + nOffsetIndex
|
|
local nRotSplitCut = MatrixResult.nInitialPosition + MACHININGS.Info.nSplitCutRotation - 1
|
|
if nRotSplitCut > 4 then
|
|
nRotSplitCut = nRotSplitCut - 4
|
|
end
|
|
nRotSplitCut = nRotSplitCut + nOffsetIndex
|
|
-- setto nella Proc l'indice rotazione nella quale deve essere lavorata
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].nIndexRotation = nRotHeadCut
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].nIndexRotation = nRotSplitCut
|
|
|
|
-- si imposta flag rotazione per taglio di testa
|
|
if MACHININGS.Info.nHeadCutRotation == 2 then
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bDown = nil
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bSide = true
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bStd = nil
|
|
elseif MACHININGS.Info.nHeadCutRotation == 3 then
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bDown = true
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bSide = nil
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bStd = nil
|
|
else
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bDown = nil
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bSide = nil
|
|
PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc].bStd = true
|
|
end
|
|
-- si imposta flag rotazione per taglio di coda
|
|
if MACHININGS.Info.nSplitCutRotation == 2 then
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bDown = nil
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bSide = true
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bStd = nil
|
|
elseif MACHININGS.Info.nSplitCutRotation == 3 then
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bDown = true
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bSide = nil
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bStd = nil
|
|
else
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bDown = nil
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bSide = nil
|
|
PROCESSINGS[nPart].Rotation[nRotSplitCut][MatrixResult.nIndexTailCutInVProc].bStd = true
|
|
end
|
|
|
|
local vProcHeadTail = {}
|
|
table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotHeadCut][MatrixResult.nIndexHeadCutInVProc])
|
|
table.insert( vProcHeadTail, PROCESSINGS[nPart].Rotation[nRotSplitCut][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
|
|
if bApplOk then
|
|
-- TODO scrivere info su pezzo!
|
|
end
|
|
|
|
-- se ultima combinazione, si esce e non si riporta in posizione inizale. Verrà infatti cancellata
|
|
if z == #TotalCombiToTest then break end
|
|
|
|
-- si riporta pezzo in posizione iniziale
|
|
-- se combinazione prevedeva inversione, si rigira il pezzo
|
|
if BestCombination.bPartInCombiIsInverted then
|
|
BeamLib.InvertRawPart( PARTS[nPart], -2)
|
|
end
|
|
-- si ribalta il pezzo in posizione iniziale
|
|
BeamLib.RotateRawPart( PARTS[nPart], 1 - BestCombination.nInitialPosition)
|
|
end
|
|
|
|
end
|
|
|
|
-- si cancella eventuale mach group creato per le alternative
|
|
EgtRemoveMachGroup( nTempMachGroupName)
|
|
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
return BeamExec
|