cd1f892a6c
- STR0002 adeguata a nuovo standard - MachiningLib.AddNewMachining salva lavorazione in lista MACHINING - MachiningLib.AddOperations inseriwsce effettivamente lavorazione
1024 lines
48 KiB
Lua
1024 lines
48 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( 'BeamData')
|
|
|
|
-- carico librerie
|
|
local BeamLib = require( 'BeamLib')
|
|
local ID = require( 'Identity')
|
|
local BCS = require( 'BasicCustomerStrategies')
|
|
local FeatureData = require( 'FeatureData')
|
|
local FaceData = require( 'FaceData')
|
|
local MachiningLib = require( 'MachiningLib')
|
|
|
|
|
|
EgtOutLog( ' BeamExec started', 1)
|
|
EgtMdbSetGeneralParam( MCH_GP.MAXDEPTHSAFE, BeamData.COLL_SIC)
|
|
EgtMdbSave()
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- *** variabili globali ***
|
|
-------------------------------------------------------------------------------------------------------------
|
|
TOOLS = nil
|
|
STRATEGIES = nil
|
|
MACHININGS = nil
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- *** 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()
|
|
-- creo lista globale utensili disponibili
|
|
TOOLS = {}
|
|
-- 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
|
|
-- TODO Tool.AName -> da rimuovere perchè non serve. Per il momento lo manteniamo solo perchè è più facile vedere e interpretare la lista utensili nella watch di zerobrane
|
|
-- Nell'utilizzo, si legge sempre il Tool.Name
|
|
Tool.AName = Tool.sName
|
|
Tool.sTcPos = sToolTCPos
|
|
Tool.sFamily = sToolFamily
|
|
Tool.sType = sToolType
|
|
Tool.nTypeId = nToolTypeId
|
|
Tool.dMaxMaterial = EgtTdbGetCurrToolParam( MCH_TP.MAXMAT)
|
|
Tool.dDiameter = EgtTdbGetCurrToolParam( MCH_TP.DIAM)
|
|
Tool.dLength = EgtTdbGetCurrToolParam( MCH_TP.LEN)
|
|
Tool.dSpeed = EgtTdbGetCurrToolParam( MCH_TP.SPEED)
|
|
Tool.bIsCCW = Tool.dSpeed < 0
|
|
Tool.Feeds = {}
|
|
Tool.Feeds.dFeed = EgtTdbGetCurrToolParam( MCH_TP.FEED)
|
|
Tool.Feeds.dStartFeed = EgtTdbGetCurrToolParam( MCH_TP.STARTFEED)
|
|
Tool.Feeds.dEndFeed = EgtTdbGetCurrToolParam( MCH_TP.ENDFEED)
|
|
Tool.Feeds.dTipFeed = EgtTdbGetCurrToolParam( MCH_TP.TIPFEED)
|
|
-- TODO serve funzione in BeamData che data la posizione dell'utensile e della testa, capisca il montaggio (testa sopra - testa sotto - aggregato - ecc...)
|
|
Tool.sHead = EgtTdbGetCurrToolParam( MCH_TP.HEAD)
|
|
Tool.SetupInfo = {}
|
|
Tool.SetupInfo = BeamData.GetSetupInfo( Tool.sHead)
|
|
Tool.sUUID = EgtTdbGetCurrToolParam( MCH_TP.UUID)
|
|
Tool.sUserNotes = EgtTdbGetCurrToolParam( MCH_TP.USERNOTES)
|
|
Tool.dMaxDepth = EgtTdbGetCurrToolMaxDepth() or Tool.dMaxMaterial
|
|
Tool.ToolHolder = {}
|
|
Tool.ToolHolder.dDiameter = EgtTdbGetCurrToolThDiam() or TH_DIAMETER_HSK63 -- diametro standard HSK63
|
|
Tool.ToolHolder.dLength = EgtTdbGetCurrToolThLength() or TH_LENGTH_HSK63 -- lunghezza standard HSK63
|
|
-- parametri scritti nelle note
|
|
Tool.nDouble = EgtGetValInNotes( Tool.sUserNotes, 'DOUBLE')
|
|
|
|
-- lettura parametri non comuni ( famiglia DRILLBIT non ha parametri specifici)
|
|
if sToolFamily ~= 'DRILLBIT' then
|
|
Tool.dThickness = EgtTdbGetCurrToolParam( MCH_TP.THICK)
|
|
Tool.dLongitudinalOffset = EgtTdbGetCurrToolParam( MCH_TP.LONOFFSET)
|
|
Tool.dRadialOffset = EgtTdbGetCurrToolParam( MCH_TP.RADOFFSET)
|
|
-- recupero parametri propri delle frese
|
|
if sToolFamily == 'MILL' then
|
|
Tool.dStemDiameter = EgtTdbGetCurrToolParam( MCH_TP.STEMDIAM) or Tool.ToolHolder.dDiameter -- se non settato, considero diametro come ToolHolder
|
|
Tool.dSideAngle = EgtTdbGetCurrToolParam( MCH_TP.SIDEANG) or 0
|
|
-- verifico che parametri siano compatibili con una fresa a coda di rondine ( angolo di fianco standard Coda di rondine -> 15°)
|
|
Tool.bIsDoveTail = Tool.Type == 'MILL_NOTIP' and abs( abs( Tool.dSideAngle) - SIDEANGLE_DOVETAIL) < 1
|
|
-- verifico che sia una fresa tipo T-Mill o BlockHaus
|
|
Tool.dSideDepth = EgtGetValInNotes( Tool.sUserNotes, 'SIDEDEPTH') 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') or ( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero metà del tagliente
|
|
Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP') or floor( Tool.dDiameter / 3) -- se non settato nell'utensile, considero metà del diametro
|
|
Tool.bIsPen = abs( Tool.dSpeed) < 5
|
|
-- recupero parametri propri delle lame
|
|
elseif sToolFamily == 'SAWBLADE' then
|
|
Tool.bIsUsedForLongCut = EgtGetValInNotes( Tool.sUserNotes, 'LONGCUT') == 1 or false -- false coem valore di default
|
|
Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP') or Tool.dThickness -- se non settato nell'utensile, considero lo spessore lama
|
|
Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP') or Tool.dMaxMaterial -- se non settato nell'utensile, considero un quarto del diametro
|
|
-- recupero parametri propri delle motoseghe
|
|
elseif sToolFamily == 'MORTISE' then
|
|
Tool.dDistance = EgtTdbGetCurrToolParam( MCH_TP.DIST) or 90 -- 90mm dimensione standard aggregato catena
|
|
Tool.bIsMortise = EgtGetValInNotes( Tool.sUserNotes, 'MORTISE') == 1
|
|
Tool.bIsChainSaw = not Tool.bIsMortise
|
|
Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP') or floor( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero un terzo della lunghezza
|
|
Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP') or ( Tool.dThickness - 1) -- se non settato nell'utensile, considero spessore catena meno 1mm di sicurezza
|
|
Tool.dCornerRadius = EgtTdbGetCurrToolParam( MCH_TP.CORNRAD)
|
|
Tool.dWidth = Tool.dDiameter
|
|
end
|
|
end
|
|
|
|
-- se tutti i dati necessari sono disponibili, inserisco utensile nella lista globale degli utensili disponibili
|
|
if IsToolOk( Tool) then
|
|
table.insert( TOOLS, Tool)
|
|
-- altrimenti scrivo nel log che l'utensile non è conforme
|
|
else
|
|
EgtOutLog( '*** ' .. Tool.sName .. ' : NOT-COMPLIANT ***', 1)
|
|
end
|
|
|
|
-- reset dati
|
|
Tool = {}
|
|
end
|
|
|
|
-- recupero utensile successivo ( punte a forare, lame, frese e motoseghe)
|
|
Tool.sName = EgtTdbGetNextTool( MCH_TF.DRILLBIT + MCH_TF.SAWBLADE + MCH_TF.MILL + MCH_TF.MORTISE)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function ImportFileJSON( sFileToImport)
|
|
local JSON = require( 'JSON')
|
|
|
|
local function readAll( sFileToImport)
|
|
local f = io.open( sFileToImport, "rb")
|
|
local content = f:read( "*all")
|
|
f:close()
|
|
return content
|
|
end
|
|
|
|
local content = readAll( sFileToImport)
|
|
return JSON:decode( content)
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
function BeamExec.GetStrategiesFromJSONinBD()
|
|
-- si legge il JSON contenente la configurazione da utilizzare (nome del file salvato nel BeamData)
|
|
local sMachDir = EgtGetCurrMachineDir()
|
|
if BeamData.STRATEGIES_CONFIG_FILE then
|
|
local sFile = sMachDir .. '\\Beam\\' .. BeamData.STRATEGIES_CONFIG_FILE
|
|
-- se non esiste file JSON, annullo la lista contenente le strategie
|
|
if not EgtExistsFile( sFile) then
|
|
STRATEGIES = nil
|
|
return
|
|
end
|
|
|
|
local FeaturesList = {}
|
|
FeaturesList = ImportFileJSON( sFile)
|
|
|
|
-- metto la tabella letta nella lista globale STRATEGIES
|
|
STRATEGIES = {}
|
|
STRATEGIES.Features = FeaturesList
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- *** funzioni posizionamento pezzi all'interno della barra ***
|
|
-------------------------------------------------------------------------------------------------------------
|
|
function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, vBeam, bMachGroupOk)
|
|
|
|
-- 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
|
|
if not bMachGroupOk 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, #vBeam do
|
|
-- assegno identificativo pezzo
|
|
local idPz = vBeam[i].id
|
|
-- dati del pezzo
|
|
local b3Part = EgtGetBBoxGlob( idPz or GDB_ID.NULL, GDB_BB.EXACT)
|
|
local b3Solid = vBeam[i].b3Box
|
|
if b3Part:isEmpty() or b3Solid:isEmpty() then break end
|
|
EgtOutLog( 'PartSez=' .. EgtNumToString( b3Part:getDimY(), 1) .. 'x' .. EgtNumToString( b3Part:getDimZ(), 1), 3)
|
|
-- se sezione compatibile e lunghezza disponibile sufficiente
|
|
local dPartLen = b3Solid:getDimX()
|
|
local dPartWidth = b3Solid:getDimY()
|
|
local dPartHeight = b3Solid:getDimZ()
|
|
local dNextLen = dLen - dDeltaS - 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 vBeam[i].dPosX then
|
|
dDeltaS = max( vBeam[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
|
|
local idRaw = EgtAddRawPart( Point3d(0,0,0), dCrawLen, 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)
|
|
-- creo o pulisco gruppo geometrie aggiuntive
|
|
if not BeamLib.CreateOrEmptyAddGroup( idPz) then
|
|
local sOut = 'Error creating Additional Group in Part ' .. tostring( idPz)
|
|
return false, sOut
|
|
end
|
|
-- aggiungo faccia per taglio iniziale al pezzo
|
|
BeamLib.AddPartStartFace( idPz, b3Solid)
|
|
-- se sovramateriale di testa, lo notifico
|
|
if dDeltaS > 0.09 then
|
|
EgtSetInfo( idRaw, 'HOVM', dDeltaS)
|
|
if idPrevRaw then
|
|
EgtSetInfo( idPrevRaw, 'BDST', dDeltaS + dPrevDelta)
|
|
end
|
|
end
|
|
if dDeltaE > 0.09 then
|
|
EgtSetInfo( idRaw, 'TOVM', dDeltaE)
|
|
end
|
|
-- aggiungo faccia per taglio finale al pezzo
|
|
BeamLib.AddPartEndFace( idPz, b3Solid)
|
|
-- inserisco il pezzo nel grezzo
|
|
EgtDeselectPartObjs( idPz)
|
|
local ptPos = b3Part:getMin() - b3Solid:getMin() + Vector3d( dDelta, ( dRawW - dPartWidth) / 2, ( dRawH - dPartHeight) / 2)
|
|
EgtAddPartToRawPart( idPz, ptPos, idRaw)
|
|
if abs( dPartWidth - dRawW) > 100 * GEO.EPS_SMALL then
|
|
-- rotazione attorno a centro geometria complessiva del pezzo
|
|
EgtRotatePartInRawPart( idPz, X_AX(), 90)
|
|
-- correggo per eccentricità solido rispetto a geometria complessiva del pezzo
|
|
local vtEccOri = b3Solid:getCenter() - b3Part:getCenter()
|
|
local vtEccRot = Vector3d( vtEccOri)
|
|
vtEccRot:rotate( X_AX(), 90)
|
|
EgtMovePartInRawPart( idPz, ( vtEccOri - vtEccRot))
|
|
end
|
|
-- aggiorno la lunghezza residua della barra
|
|
dLen = dLen - dCrawLen
|
|
-- aggiorno grezzo precedente
|
|
idPrevRaw = idRaw
|
|
dPrevDelta = dDelta
|
|
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
|
|
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)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
function BeamExec.CalcMinUnloadableRaw( dRawW, dRawH)
|
|
if BeamData.GetMinUnloadableRaw then
|
|
BeamData.dMinRaw = BeamData.GetMinUnloadableRaw( dRawW, dRawH)
|
|
else
|
|
local H_S = 200
|
|
local H_L = 400
|
|
-- Determinazione minimo grezzo scaricabile
|
|
if dRawH <= H_S then
|
|
BeamData.dMinRaw = BeamData.MINRAW_S
|
|
elseif dRawH <= H_L then
|
|
local Coeff = ( dRawH - H_S) / ( H_L - H_S)
|
|
BeamData.dMinRaw = ( 1 - Coeff) * BeamData.MINRAW_S + Coeff * BeamData.MINRAW_L
|
|
else
|
|
BeamData.dMinRaw = BeamData.MINRAW_L
|
|
end
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- *** Inserimento delle lavorazioni nelle travi ***
|
|
-------------------------------------------------------------------------------------------------------------
|
|
|
|
local function GetStrategiesFromGlobalList( Proc)
|
|
-- cerco tra le feature
|
|
for i = 1, #STRATEGIES.Features do
|
|
-- se trovo la feature
|
|
if Proc.nPrc == STRATEGIES.Features[i].nPrc and Proc.nGrp == STRATEGIES.Features[i].nGrp then
|
|
-- cerco tra le topologie
|
|
for j = 1, #STRATEGIES.Features[i].Topologies do
|
|
-- se trovo la topologia
|
|
if Proc.Topology.sName == STRATEGIES.Features[i].Topologies[j].sName then
|
|
-- ritorno le strategie disponibili per la feature che sto analizzando
|
|
return STRATEGIES.Features[i].Topologies[j].Strategies
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function GetStrategies( Proc)
|
|
local AvailableStrategiesForProc = nil
|
|
-- se la lista STRATEGIES è stata letta da JSON (quindi non è vuota), ritorno le strategie possibili
|
|
if STRATEGIES and #STRATEGIES.Features > 0 then
|
|
AvailableStrategiesForProc = GetStrategiesFromGlobalList( Proc)
|
|
end
|
|
-- se non ho trovato strategie disponibili nel JSON, o se JSON non presente, lancio script che setta le strategie in modo statico, come definito con cliente
|
|
if not AvailableStrategiesForProc then
|
|
AvailableStrategiesForProc = BCS.GetStrategiesFromBasicCustomerStrategies( Proc)
|
|
end
|
|
return AvailableStrategiesForProc
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function GetFeatureForcedStrategy( Proc)
|
|
-- cerco nelle note se è stata forzata una strategia specifica
|
|
local sStrategyId = EgtGetInfo( Proc.id, 'STRATEGY', 's')
|
|
|
|
-- se è presente la strategia forzata
|
|
if sStrategyId then
|
|
-- eseguo file config con i parametri di default
|
|
local StrategyData = require( sStrategyId .. '\\' .. sStrategyId .. 'Config')
|
|
|
|
-- se ID strategia non esiste oppure ID letto in NGE è differente da quello letto nella strategia, esco subito
|
|
if not StrategyData or StrategyData.sStrategyId ~= sStrategyId then
|
|
return nil
|
|
end
|
|
|
|
-- salvo che questa strategia è stata forzata, e che quindi ho già letto il file config con i parametri di default
|
|
-- quando si calcolerà la lavorazione non servirà leggere/riverificare i parametri di default
|
|
StrategyData.bForcedStrategy = true
|
|
|
|
-- cerco e aggiorno i parametri come sono settati nel processing
|
|
for i = 1, #StrategyData.Parameters do
|
|
local sParameterToRead = StrategyData.sStrategyId .. '_' .. StrategyData.Parameters[i].sName
|
|
ForcedParameterForProc = EgtGetInfo( Proc.id, sParameterToRead, 's')
|
|
-- se ho trovato il valore, lo sovrascrivo al default
|
|
if ForcedParameterForProc then
|
|
StrategyData.Parameters[i].sValue = ForcedParameterForProc
|
|
end
|
|
end
|
|
|
|
-- ritorno la lista strategia con parametri
|
|
local StrategyToProc = {}
|
|
table.insert( StrategyToProc, StrategyData)
|
|
return StrategyToProc
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function CollectFeatures( Part)
|
|
-- recupero le feature
|
|
local vProc = {}
|
|
local LayerId = {}
|
|
LayerId[1] = BeamLib.GetAddGroup( Part.idPart)
|
|
LayerId[2] = EgtGetFirstNameInGroup( Part.idPart or GDB_ID.NULL, 'Processings')
|
|
for nInd = 1, 2 do
|
|
local ProcId = EgtGetFirstInGroup( LayerId[nInd] or GDB_ID.NULL)
|
|
while ProcId do
|
|
local nEntType = EgtGetType( ProcId)
|
|
if nEntType == GDB_TY.SRF_MESH or nEntType == GDB_TY.EXT_TEXT or
|
|
nEntType == GDB_TY.CRV_LINE or nEntType == GDB_TY.CRV_ARC or nEntType == GDB_TY.CRV_BEZ or nEntType == GDB_TY.CRV_COMPO then
|
|
local nGrp = EgtGetInfo( ProcId, 'GRP', 'i')
|
|
local nPrc = EgtGetInfo( ProcId, 'PRC', 'i')
|
|
local nDo = EgtGetInfo( ProcId, 'DO', 'i') or 1
|
|
if nGrp and nPrc and nDo == 1 then
|
|
local Proc = {}
|
|
Proc.idPart = Part.idPart
|
|
Proc.id = ProcId
|
|
-- id della feature btl ( se non presente info, si prende id dell'entità geometrica)
|
|
Proc.idFeature = EgtGetInfo( Proc.id, 'PRID', 's') or Proc.id
|
|
Proc.nGrp = nGrp
|
|
Proc.nPrc = nPrc
|
|
Proc.nFlg = 1
|
|
Proc.nFct = EgtSurfTmFacetCount( ProcId) or 0
|
|
Proc.idCut = EgtGetInfo( EgtGetParent( EgtGetParent( ProcId)), 'CUTID', 'i') or 0
|
|
Proc.idTask = EgtGetInfo( ProcId, 'TASKID', 'i') or 0
|
|
Proc.b3Box = EgtGetBBoxGlob( ProcId, GDB_BB.STANDARD)
|
|
EgtOutLog( '------Feature ' .. Proc.idFeature .. '------')
|
|
-- se esiste la geometria
|
|
if Proc.b3Box and not Proc.b3Box:isEmpty() then
|
|
-- TODO fare una funzione per recuperare i dati delle feature che non passano dal calcolo della topologia?
|
|
-- se foro calcolo altri dati
|
|
if ID.IsDrilling( Proc) then
|
|
-- assegno diametro e facce di ingresso e uscita (dati tabelle sempre per riferimento)
|
|
Proc.dDiam, Proc.dLen, Proc.nFcs, Proc.nFce = FeatureData.GetDrillingData( Proc)
|
|
end
|
|
-- informazioni facce e topologia
|
|
Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part)
|
|
-- calcolo topologia solo se necessario, altrimenti si sfruttano le informazioni della feature BTL
|
|
Proc.Topology = {}
|
|
if FeatureData.NeedTopologyFeature( Proc) then
|
|
Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc)
|
|
Proc.Faces = FaceData.GetFacesInfo( Proc, Part)
|
|
Proc.Topology = FeatureData.ClassifyTopology( Proc, Part)
|
|
else
|
|
Proc.Topology.sFamily = 'FEATURE'
|
|
Proc.Topology.sName = 'FEATURE'
|
|
end
|
|
-- se topologia feature riconosciuta, oppure da non calcolare perchè il riconoscimento topologico è basato sulla feature stessa
|
|
if Proc.Topology.sName ~= 'NOT_IMPLEMENTED' then
|
|
Proc.MainFaces = FaceData.GetMainFaces( Proc, Part)
|
|
-- se la processing ha una strategia forzata, riporto tutto nella proc
|
|
local vForcedStrategy = GetFeatureForcedStrategy( Proc)
|
|
if vForcedStrategy then
|
|
Proc.AvailableStrategies = vForcedStrategy
|
|
-- altrimenti cerco tra le strategie disponibili
|
|
else
|
|
Proc.AvailableStrategies = GetStrategies( Proc)
|
|
end
|
|
-- se ci sono strategie disponibili, aggiungo a lista delle feature da lavorare
|
|
if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then
|
|
table.insert( vProc, Proc)
|
|
-- altrimenti errore (non ci sono strategie per lavorare la topologia riconosciuta)
|
|
else
|
|
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies')
|
|
end
|
|
-- altrimenti errore (serviva riconoscimento topologico, ma non è stato possibile farlo)
|
|
else
|
|
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies')
|
|
end
|
|
else
|
|
Proc.nFlg = 0
|
|
table.insert( vProc, Proc)
|
|
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' is empty (no geometry)')
|
|
end
|
|
end
|
|
end
|
|
ProcId = EgtGetNext( ProcId)
|
|
end
|
|
end
|
|
return vProc
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function AreDrillingsMirrored( Proc, ProcMirror, Part)
|
|
if Proc.id == ProcMirror.id then return false end
|
|
|
|
-- geometria ausiliaria foro principale
|
|
AuxId = EgtGetInfo( Proc.id, 'AUXID', 'i')
|
|
if AuxId then AuxId = AuxId + Proc.id end
|
|
if not AuxId or EgtGetType( AuxId ) ~= GDB_TY.CRV_ARC then return false end
|
|
-- geometria ausiliaria foro specchiato
|
|
local AuxIdMirror = EgtGetInfo( ProcMirror.id, 'AUXID', 'i')
|
|
if AuxIdMirror then AuxIdMirror = AuxIdMirror + ProcMirror.id end
|
|
if not AuxIdMirror or EgtGetType( AuxIdMirror ) ~= GDB_TY.CRV_ARC then return false end
|
|
-- dati del foro principale
|
|
local vtExtr = EgtCurveExtrusion( AuxId, GDB_RT.GLOB)
|
|
local ptBC = EgtGP( AuxId, GDB_RT.GLOB)
|
|
-- dati del foro specchiato
|
|
local vtExtrMirror = EgtCurveExtrusion( AuxIdMirror, GDB_RT.GLOB)
|
|
local ptBCMirror = EgtGP( AuxIdMirror, GDB_RT.GLOB)
|
|
|
|
-- direzione fori
|
|
local nDouble
|
|
if AreOppositeVectorApprox( vtExtr, vtExtrMirror) then
|
|
-- fori lungo Y
|
|
-- per macchine tipo PF il foro principale è sul lato back, per macchine tipo PF1250 è sul lato front
|
|
if ( BeamData.TWO_EQUAL_HEADS and AreSameVectorApprox( vtExtr, Y_AX())) or
|
|
( BeamData.DOWN_HEAD and AreOppositeVectorApprox( vtExtr, Y_AX())) then
|
|
nDouble = 2
|
|
-- fori lungo Z
|
|
elseif BeamData.DOWN_HEAD and AreSameVectorApprox( vtExtr, Z_AX()) then
|
|
nDouble = 3
|
|
else
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
|
|
-- centri allineati, equidistanti dalla mezzeria trave, non troppo vicini
|
|
local vtDisplacement = ptBC - ptBCMirror
|
|
local ptCenRaw = Part.b3Raw:getCenter()
|
|
if nDouble == 2 then
|
|
local dYMinDistance = max( Proc.b3Box:getMin():getY(), ProcMirror.b3Box:getMin():getY()) - min( Proc.b3Box:getMax():getY(), ProcMirror.b3Box:getMax():getY())
|
|
if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getZ()) < 100 * GEO.EPS_SMALL and
|
|
( abs( ptBC:getY() - ptCenRaw:getY()) - abs( ptBCMirror:getY() - ptCenRaw:getY())) < 100 * GEO.EPS_SMALL and
|
|
dYMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then
|
|
return false
|
|
end
|
|
else
|
|
local dZMinDistance = max( Proc.b3Box:getMin():getZ(), ProcMirror.b3Box:getMin():getZ()) - min( Proc.b3Box:getMax():getZ(), ProcMirror.b3Box:getMax():getZ())
|
|
if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getY()) < 100 * GEO.EPS_SMALL and
|
|
( abs( ptBC:getZ() - ptCenRaw:getZ()) - abs( ptBCMirror:getZ() - ptCenRaw:getZ())) < 100 * GEO.EPS_SMALL and
|
|
dZMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- fori della stessa profondità
|
|
if abs( Proc.dLen - ProcMirror.dLen) > 10 * GEO.EPS_SMALL then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function GetFeatureInfoAndDependency( vProcSingleRot, Part)
|
|
-- ciclo tutte le feature
|
|
for i = 1, #vProcSingleRot do
|
|
local Proc = vProcSingleRot[i]
|
|
-- controllo la feature con tutte le altre per recuperare le dipendenze
|
|
for j = 1, #vProcSingleRot do
|
|
-- non si controlla la feature con se stessa
|
|
if i ~= j then
|
|
local ProcB = vProcSingleRot[j]
|
|
-- verifico se feature tipo LapJoint è attraversata da almeno un foro
|
|
if ( Proc.Topology.sFamily == 'Pocket' or Proc.Topology.sFamily == 'Tunnel' or Proc.Topology.sFamily == 'Groove' or ID.IsMortise( Proc)) and
|
|
ID.IsDrilling( ProcB) and Overlaps( Proc.b3Box, ProcB.b3Box) then
|
|
Proc.bPassedByHole = true
|
|
end
|
|
-- verifiche per specchiature
|
|
if BeamData.DOWN_HEAD or BeamData.TWO_EQUAL_HEADS then
|
|
-- forature
|
|
if BeamData.DOUBLE_HEAD_DRILLING and ID.IsDrilling( Proc) and ID.IsDrilling( ProcB) and not Proc.Mirror then
|
|
if AreDrillingsMirrored( Proc, ProcB, Part) then
|
|
Proc.Mirror = ProcB
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return vProcSingleRot
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function RunStrategyLibraries( sStrategyId)
|
|
local StrategyConfigName = sStrategyId .. '\\' .. sStrategyId .. 'Config'
|
|
local StrategyConfigPath = BEAM.BASEDIR .. '\\Strategies\\' .. StrategyConfigName .. '.lua'
|
|
local StrategyScriptName = sStrategyId .. '\\' .. sStrategyId
|
|
local StrategyScriptPath = BEAM.BASEDIR .. '\\Strategies\\' .. StrategyScriptName .. '.lua'
|
|
local StrategyLib = {}
|
|
if EgtExistsFile( StrategyConfigPath) and EgtExistsFile( StrategyScriptPath) then
|
|
StrategyLib.Config = require( StrategyConfigName)
|
|
-- eseguo file script strategia
|
|
StrategyLib.Script = require( StrategyScriptName)
|
|
return StrategyLib
|
|
else
|
|
return {}
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- ritorna l'indice della strategia migliore, tra le due passate
|
|
local function GetIndexBestStrategyFromComparison( AvailableStrategies, nIndex1, nIndex2)
|
|
local dChosenIndex = 0
|
|
-- controllo indici
|
|
if nIndex1 == 0 and nIndex2 == 0 then
|
|
dChosenIndex = 0
|
|
elseif nIndex1 == 0 then
|
|
-- basta che sia applicabile
|
|
if AvailableStrategies[nIndex2].Result and ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' or AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed') then
|
|
dChosenIndex = nIndex2
|
|
end
|
|
elseif nIndex2 == 0 then
|
|
-- basta che sia applicabile
|
|
if AvailableStrategies[nIndex1].Result and ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' or AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed') then
|
|
dChosenIndex = nIndex1
|
|
end
|
|
elseif not AvailableStrategies[nIndex1].Result or not AvailableStrategies[nIndex2].Result then
|
|
dChosenIndex = 0
|
|
elseif ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Completed') or
|
|
( AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex2].Result.sStatus == 'Not-Applicable') then
|
|
dChosenIndex = nIndex1
|
|
elseif ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' and AvailableStrategies[nIndex1].Result.sStatus ~= 'Completed') or
|
|
( AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex1].Result.sStatus == 'Not-Applicable') then
|
|
dChosenIndex = nIndex2
|
|
else
|
|
-- se le due strategie hanno stesso stato e sono entrambe applicabili (quindi entrambe complete o entrambe non-complete)
|
|
if AvailableStrategies[nIndex1].Result.sStatus ~= 'Not-Applicable' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Not-Applicable' and
|
|
AvailableStrategies[nIndex1].Result.sStatus == AvailableStrategies[nIndex2].Result.sStatus then
|
|
local dCompositeRatingStrategy1 = AvailableStrategies[nIndex1].Result.nQuality * AvailableStrategies[nIndex1].Result.nCompletionIndex * AvailableStrategies[nIndex1].Result.dMRR
|
|
local dCompositeRatingStrategy2 = AvailableStrategies[nIndex2].Result.nQuality * AvailableStrategies[nIndex2].Result.nCompletionIndex * AvailableStrategies[nIndex2].Result.dMRR
|
|
-- si predilige strategia con rating composito più alto
|
|
if dCompositeRatingStrategy1 > dCompositeRatingStrategy2 then
|
|
dChosenIndex = nIndex1
|
|
elseif dCompositeRatingStrategy2 > dCompositeRatingStrategy1 then
|
|
dChosenIndex = nIndex2
|
|
-- altrimenti si prende la strategia con indice più basso
|
|
else
|
|
dChosenIndex = EgtIf( nIndex1 < nIndex2, nIndex1, nIndex2)
|
|
end
|
|
end
|
|
end
|
|
return dChosenIndex
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- funzione che processa tutte le feature sul pezzo e trova la miglior strategia di lavorazione tra quelle disponibili
|
|
local function GetBestStrategy( 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
|
|
local nIndexBestStrategy = 0
|
|
-- 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
|
|
local bStrategyOk
|
|
-- eseguo la strategia solo come calcolo fattibilità e voto. Non si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati
|
|
_, Proc.AvailableStrategies[nIndexCurrentStrategy].Result = CurrentStrategy.Script.Make( false, Proc, Part, Proc.AvailableStrategies[nIndexCurrentStrategy].Parameters)
|
|
-- scelgo la migliore strategia tra le due
|
|
nIndexBestStrategy = GetIndexBestStrategyFromComparison( Proc.AvailableStrategies, nIndexCurrentStrategy, nIndexBestStrategy)
|
|
|
|
-- se scelta strategia standard, esco subito alla prima che trovo completa
|
|
-- TODO serve paraemtro da Beam&Wall ( oppure da confirgurazione) !!!!!!!!
|
|
if BEAM.GetFirstCompletedStrategy and nIndexBestStrategy > 0 then
|
|
if Proc.AvailableStrategies[nIndexBestStrategy].Result.sStatus == 'Complete' then
|
|
break
|
|
end
|
|
end
|
|
|
|
-- se non trovo i file della strategia, scrivo che non è più disponibile
|
|
else
|
|
Proc.AvailableStrategies[nIndexCurrentStrategy].Result = {}
|
|
Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sInfo = 'Strategy not found'
|
|
end
|
|
end
|
|
-- salvo sulla proc la migliore strategia
|
|
if nIndexBestStrategy ~= 0 then
|
|
Proc.ChosenStrategy = Proc.AvailableStrategies[nIndexBestStrategy]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return vProcSingleRot
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- Ordina le feature in base a fase di lavorazione
|
|
-- 1) Head : ( intestatura)
|
|
-- 2) Standard : ( lavorazioni standard che non impattano sulal 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 : ( lavorazionio di coda, fatte dopo il taglio di separazione)
|
|
local function OrderFeatures( vProc)
|
|
local vProcToSort = vProc
|
|
-- funzione di confronto. TRUE = B1 prima di B2. FALSE = B2 prima di B1
|
|
local function CompareFeatures( B1, B2)
|
|
-- se secondo disabilitato, va lasciato dopo
|
|
if B1.nFlg ~= 0 and B2.nFlg == 0 then
|
|
return true
|
|
-- se entrambi disabilitati seguo l'Id
|
|
elseif B1.nFlg == 0 and B2.nFlg == 0 then
|
|
return ( B1.id < B2.id)
|
|
-- se in rotazioni diverse, si mette in ordine di rotazioni
|
|
elseif B1.nRot ~= B2.nRot then
|
|
return ( B1.nRot < B2.nRot)
|
|
-- se primo è taglio di testa, va prima degli altri casi
|
|
elseif B1.Head and ( B2.Standard or B2.AdvanceTail or B2.Split or B2.Tail) then
|
|
return true
|
|
-- se primo è taglio standard, va prima degli altri casi
|
|
elseif B1.Standard and ( B2.AdvanceTail or B2.Split or B2.Tail) then
|
|
return true
|
|
-- se primo è taglio di testa anticipata, va prima degli altri casi
|
|
elseif B1.AdvanceTail and ( B2.Split or B2.Tail) then
|
|
return true
|
|
-- se primo è taglio di separazione, va prima delle lavorazioni di coda
|
|
elseif B1.Split and B2.Tail then
|
|
return true
|
|
-- se da lavorare in stessa fase pezzo
|
|
elseif B1.Head == B2.Head or B1.Standard == B2.Standard or B1.AdvanceTail == B2.AdvanceTail or B1.Split == B2.Split or B1.Tail == B2.Tail then
|
|
-- confronto standard
|
|
if abs( B1.b3Box:getCenter():getX() - B2.b3Box:getCenter():getX()) > 0.4 * ( B1.b3Box:getDimX() + B2.b3Box:getDimX()) then
|
|
return B1.b3Box:getCenter():getX() > B2.b3Box:getCenter():getX()
|
|
elseif abs( B1.b3Box:getCenter():getY() - B2.b3Box:getCenter():getY()) > 0.2 * ( B1.b3Box:getDimY() + B2.b3Box:getDimY()) then
|
|
return B1.b3Box:getCenter():getY() > B2.b3Box:getCenter():getY()
|
|
elseif abs( B1.b3Box:getCenter():getZ() - B2.b3Box:getCenter():getZ()) > 0.1 * ( B1.b3Box:getDimZ() + B2.b3Box:getDimZ()) then
|
|
return B1.b3Box:getCenter():getZ() > B2.b3Box:getCenter():getZ()
|
|
end
|
|
-- altrimenti si inverte
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- test della funzione di ordinamento
|
|
if EgtGetDebugLevel() >= 3 then
|
|
EgtOutLog( ' CompareFeatures Test ')
|
|
local bCompTest = true
|
|
for i = 1, #vProcToSort do
|
|
for j = i + 1, #vProcToSort do
|
|
local bComp1 = CompareFeatures( vProcToSort[i], vProcToSort[j])
|
|
local bComp2 = CompareFeatures( vProcToSort[j], vProcToSort[i])
|
|
if bComp1 == bComp2 then
|
|
bCompTest = false
|
|
EgtOutLog( string.format( ' ProcId : %d vs %d --> ERROR', vProcToSort[i].id, vProcToSort[j].id))
|
|
end
|
|
end
|
|
end
|
|
if bCompTest then
|
|
EgtOutLog( ' ALL OK')
|
|
end
|
|
end
|
|
|
|
-- eseguo ordinamento
|
|
table.sort( vProcToSort, CompareFeatures)
|
|
|
|
return vProcToSort
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
-- esegue le strategie migliori che ha precedentemente scelto
|
|
local function CalculateMachinings( vProc, Part)
|
|
local bAreAllApplyOk = true
|
|
MACHININGS = {}
|
|
-- applico le strategie scelte
|
|
for i = 1, #vProc do
|
|
-- processo tutte le feature attive applicando le lavorazioni
|
|
local Proc = vProc[i]
|
|
if Proc.nFlg ~= 0 and Proc.ChosenStrategy then
|
|
-- carico file script strategia (non serve verificare presenza del file perchè già fatto durante scelta strategia)
|
|
local StrategyScriptName = Proc.ChosenStrategy.sStrategyId .. '\\' .. Proc.ChosenStrategy.sStrategyId
|
|
local StrategyScript = require( StrategyScriptName)
|
|
-- eseguo la strategia e si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati
|
|
bAreAllApplyOk, _ = StrategyScript.Make( true, Proc, Part, Proc.ChosenStrategy.Parameters)
|
|
end
|
|
end
|
|
return vProc
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function PrintFeatures( vProc, Part)
|
|
EgtOutLog( ' RawBox=' .. tostring( Part.RawBox))
|
|
for i = 1, #vProc do
|
|
local Proc = vProc[i]
|
|
local sOut = string.format( ' Id=%3d Grp=%1d Prc=%3d TC=%2d/%d Flg=%2d Down=%s Side=%s Head=%s Tail=%s Fcse=%1d,%1d Diam=%.2f Fct=%2d Box=%s TopoName=%s',
|
|
Proc.id, Proc.nGrp, Proc.nPrc, Proc.idTask, Proc.idCut,
|
|
Proc.nFlg, EgtIf( Proc.bDown, 'T', 'F'), EgtIf( Proc.bSide, 'T', 'F'),
|
|
EgtIf( Proc.bHead, 'T', 'F'), EgtIf( Proc.bTail, 'T', EgtIf( Proc.bAdvTail, 'A', 'F')),
|
|
Proc.nFcs, Proc.nFce, Proc.dDiam, Proc.nFct, tostring( Proc.b3Box), Proc.Topology.sName or '')
|
|
-- info speciali per Block Haus Half Lap
|
|
if Proc.nPrc == 37 then
|
|
local sSpec = string.format( ' N=%s Hd=%s', tostring( Proc.vtN or V_NULL()), EgtIf( Proc.bHeadDir, 'T', 'F'))
|
|
sOut = sOut .. sSpec
|
|
end
|
|
EgtOutLog( sOut)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
function BeamExec.ProcessFeatures()
|
|
-- ciclo sui pezzi
|
|
local nTotErr = 0
|
|
local Stats = {}
|
|
local nOrd = 1
|
|
local Part = {}
|
|
|
|
Part.idRaw = EgtGetFirstRawPart()
|
|
while Part.idRaw do
|
|
-- verifico che il grezzo contenga pezzi oppure sia abbastanza lungo da essere scaricato coi carrelli
|
|
Part.idPart = EgtGetFirstPartInRawPart( Part.idRaw)
|
|
Part.b3Raw = EgtGetRawPartBBox( Part.idRaw)
|
|
Part.b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( Part.idPart, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD)
|
|
|
|
if not Part.idPart and Part.b3Raw:getDimX() < BeamData.dMinRaw then break end
|
|
|
|
-- per ogni rotazione, calcolo come lavorare le feature per decidere posizionamento iniziale e in che rotazione verranno lavorate le singole feature
|
|
local vProcRot = {}
|
|
-- lista contenente le feature da eseguire
|
|
local vProc = {}
|
|
|
|
|
|
-- TODO da rimuovere o lasciare solo per debug
|
|
if EgtGetDebugLevel() >= 3 then
|
|
EgtStartCounter()
|
|
end
|
|
|
|
|
|
-- TODO Il numero di rotazioni da calcolare deve dipendere dalle impostazionei del cliente. Per adesso si calcolano tutte e 4, ma può essere ottimizzato
|
|
for dRotIndex = 1, 4 do
|
|
|
|
-- recupero le feature di lavorazione della trave
|
|
table.insert( vProcRot, CollectFeatures( Part))
|
|
|
|
-- recupero informazioni ausiliarie feature e dipendenze tra feature stesse
|
|
-- TODO le dipendenze cambiano in base alla rotazione del pezzo? probabilmente no
|
|
vProcRot[dRotIndex] = GetFeatureInfoAndDependency( vProcRot[dRotIndex], Part)
|
|
|
|
-- sceglie la strategia migliore tra quelle disponibili ( presenti nella tabella vProcRot[dRotIndex].AvailableStrategies)
|
|
vProcRot[dRotIndex] = GetBestStrategy( vProcRot[dRotIndex], Part)
|
|
|
|
-- ruoto il grezzo per calcolare la fattibilità delle lavorazioni nella prossima rotazione
|
|
-- vettore movimento grezzi per rotazione di 90deg ogni step
|
|
local dDeltaYZ = Part.b3Raw:getDimY() - Part.b3Raw:getDimZ()
|
|
local vtMove = Vector3d( 0, dDeltaYZ / 2 * EgtIf( BeamData.RIGHT_LOAD, -1, 1), dDeltaYZ / 2)
|
|
local bPreMove = ( dDeltaYZ < 0)
|
|
-- ruoto le travi della fase corrente
|
|
if bPreMove then
|
|
EgtMoveRawPart( Part.idRaw, vtMove)
|
|
end
|
|
EgtRotateRawPart( Part.idRaw, X_AX(), EgtIf( BeamData.RIGHT_LOAD, -90, 90))
|
|
if not bPreMove then
|
|
EgtMoveRawPart( Part.idRaw, vtMove)
|
|
end
|
|
-- aggiorno info pezzo
|
|
Part.b3Raw = EgtGetRawPartBBox( Part.idRaw)
|
|
Part.b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( Part.idPart, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD)
|
|
end
|
|
|
|
|
|
-- TODO da rimuovere o lasciare solo per debug
|
|
if EgtGetDebugLevel() >= 3 then
|
|
local timeCollect = EgtStopCounter()
|
|
timeCollect = timeCollect + 0
|
|
EgtOutBox( timeCollect, 'Collect calculation time')
|
|
EgtStartCounter()
|
|
end
|
|
|
|
|
|
-- TODO decidere come lavorare ogni feature in base alla matrice delle rotazioni
|
|
-- la matrice delle rotazioni deve già salvare sulla proc la strategia da utilizzare
|
|
-- Conviene salvarsi tutti i dati fino alla lavorazione e il ciclo di applicazione va ad applicare senza calcolare?
|
|
|
|
-- aggiungo la fase, se non è la prima
|
|
if nOrd == 1 then
|
|
EgtSetCurrPhase( 1)
|
|
else
|
|
BeamLib.AddPhaseWithRawParts( Part, BeamData.ptOriXR, BeamData.dPosXR, 0)
|
|
end
|
|
local nPhase = EgtGetCurrPhase()
|
|
local nDispId = EgtGetPhaseDisposition( nPhase)
|
|
EgtSetInfo( nDispId, 'TYPE', EgtIf( Part.idPart, 'START', 'REST'))
|
|
EgtSetInfo( nDispId, 'ORD', nOrd)
|
|
EgtOutLog( ' *** Phase=' .. tostring( nPhase) .. ' Raw=' .. tostring( Part.idRaw) .. ' Part=' .. tostring( Part.idPart) .. ' ***', 1)
|
|
|
|
-- debug
|
|
if EgtGetDebugLevel() >= 1 then
|
|
PrintFeatures( vProc, Part)
|
|
end
|
|
EgtOutLog( ' *** AddMachinings ***', 1)
|
|
|
|
-- TODO PROVVISORIO in attesa di scelta lavorazione in fase opportuna
|
|
vProc = vProcRot[1]
|
|
|
|
-- ordino le features
|
|
vProc = OrderFeatures( vProc)
|
|
|
|
-- TODO da fare
|
|
MACHININGS = {}
|
|
-- esegue le strategie migliori che ha precedentemente scelto e salva le lavorazioni nella lista globale
|
|
CalculateMachinings( vProc, Part)
|
|
|
|
-- TODO riordinare lavorazioni ottimizzando cambio utensile/spezzone ecc..., mantenendo dipendenze definite prima
|
|
-- ordino le lavorazioni
|
|
-- OrderMachining( vProc, Part)
|
|
|
|
-- aggiunge effettivamente le lavorazioni
|
|
MachiningLib.AddOperations( vProc, Part)
|
|
|
|
|
|
-- TODO da rimuovere o lasciare solo per debug
|
|
if EgtGetDebugLevel() >= 3 then
|
|
local timeMachining = EgtStopCounter()
|
|
timeMachining = timeMachining + 0
|
|
EgtOutBox( timeMachining, 'Machining calculation time')
|
|
end
|
|
|
|
|
|
EgtOutLog( ' *** End AddMachinings ***', 1)
|
|
-- passo al grezzo successivo
|
|
nOrd = nOrd + 1
|
|
Part.idRaw = EgtGetNextRawPart( Part.idRaw)
|
|
end
|
|
|
|
-- Aggiornamento finale di tutto
|
|
EgtSetCurrPhase( 1)
|
|
local bApplOk, sApplErrors, sApplWarns = EgtApplyAllMachinings()
|
|
-- eventuale ricalcolo per macchine tipo PF (ma tengo warning del primo calcolo)
|
|
if EgtExistsInfo( EgtGetCurrMachGroup(), 'RECALC') then
|
|
EgtOutLog( ' **** RECALC ****')
|
|
bApplOk, sApplErrors, _ = EgtApplyAllMachinings()
|
|
EgtRemoveInfo( EgtGetCurrMachGroup(), 'RECALC')
|
|
end
|
|
if not bApplOk then
|
|
nTotErr = nTotErr + 1
|
|
table.insert( Stats, {nErr = 1, sMsg=sApplErrors, nRot=0, idCut=0, idTask=0})
|
|
elseif sApplWarns and #sApplWarns > 0 then
|
|
local vLine = EgtSplitString( sApplWarns, '\r\n')
|
|
for i = 1, #vLine do
|
|
local nPos = vLine[i]:find( '(WRN', 1, true)
|
|
if nPos then
|
|
local sData = vLine[i]:sub( nPos + 1, -2)
|
|
local vVal = EgtSplitString( sData, ',')
|
|
local nWarn = EgtGetVal( vVal[1] or '', 'WRN', 'i')
|
|
local nCutId = EgtGetVal( vVal[2] or '', 'CUTID', 'i')
|
|
if nWarn and nCutId then
|
|
table.insert( Stats, { nErr=-nWarn, sMsg=vLine[i], nRot=0, idCut=nCutId, idTask=0})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return ( nTotErr == 0), Stats
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
return BeamExec
|