Files
databeamnew/LuaLibs/BeamExec.lua
T

1461 lines
69 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 FeatureLib = require( 'FeatureLib')
local FaceData = require( 'FaceData')
local MachiningLib = require( 'MachiningLib')
local Logs = require( 'Logs')
EgtOutLog( ' BeamExec started', 1)
EgtMdbSetGeneralParam( MCH_GP.MAXDEPTHSAFE, BeamData.COLL_SIC)
EgtMdbSave()
-------------------------------------------------------------------------------------------------------------
-- *** variabili globali ***
-------------------------------------------------------------------------------------------------------------
TOOLS = {} -- tabella contenente tutti gli utensili
STRATEGIES = nil -- tabella contenente le strategie disponibili per ogni feature
MACHININGS = {} -- tabella contenente le lavorazioni da applicare
-------------------------------------------------------------------------------------------------------------
-- *** COSTANTI *** TODO -> DA SPOSTARE IN BEAMDATA???
-------------------------------------------------------------------------------------------------------------
TH_DIAMETER_HSK63 = 63
TH_LENGTH_HSK63 = 75
SIDEANGLE_DOVETAIL = 15
-------------------------------------------------------------------------------------------------------------
-- *** funzioni di base ***
-------------------------------------------------------------------------------------------------------------
local function GetToolTypeNameFromToolTypeID( dToolTypeID)
if dToolTypeID == MCH_TY.DRILL_STD then
return 'DRILL_STD', 'DRILLBIT'
elseif dToolTypeID == MCH_TY.DRILL_LONG then
return 'DRILL_LONG', 'DRILLBIT'
elseif dToolTypeID == MCH_TY.SAW_STD then
return 'SAW_STD', 'SAWBLADE'
elseif dToolTypeID == MCH_TY.SAW_FLAT then
return 'SAW_FLAT', 'SAWBLADE'
elseif dToolTypeID == MCH_TY.MILL_STD then
return 'MILL_STD', 'MILL'
elseif dToolTypeID == MCH_TY.MILL_NOTIP then
return 'MILL_NOTIP', 'MILL'
elseif dToolTypeID == MCH_TY.MORTISE_STD then
return 'MORTISE_STD', 'MORTISE'
else
return nil
end
end
-------------------------------------------------------------------------------------------------------------
local function IsToolOk( Tool)
-- controllo che i dati necessari siano impostati
if Tool.dMaxMaterial and Tool.dDiameter and Tool.dLength and Tool.sHead and Tool.sUUID then
-- se DRILLBIT non ho altri dati
if Tool.sFamily == 'DRILLBIT' then
return true
-- altrimenti controllo dati aggiuntivi altre famiglie di utensili
elseif Tool.sFamily == 'MORTISE' then
if Tool.dCornerRadius then
return true
end
else
if Tool.dThickness then
return true
end
end
end
return false
end
-------------------------------------------------------------------------------------------------------------
function BeamExec.GetToolsFromDB()
-- lista appoggio utensile
local Tool = {}
-- recupero tutti gli utensili : punte a forare, lame, frese e motoseghe
Tool.sName = EgtTdbGetFirstTool( MCH_TF.DRILLBIT + MCH_TF.SAWBLADE + MCH_TF.MILL + MCH_TF.MORTISE)
while Tool.sName ~= '' do
-- imposto utensile come corrente per recuperarne i dati
EgtTdbSetCurrTool( Tool.sName)
-- verifico se utensile disponibile in attrezzaggio attuale e che abbia un tipo ben definito
local bToolLoadedOnSetup, sToolTCPos = EgtFindToolInCurrSetup( Tool.sName)
local nToolTypeId = EgtTdbGetCurrToolParam( MCH_TP.TYPE)
local sToolType, sToolFamily = GetToolTypeNameFromToolTypeID( nToolTypeId)
-- se verifica condizioni minime, recupero tutti gli altri dati
if bToolLoadedOnSetup and sToolType then
Tool.sTcPos = sToolTCPos
Tool.sFamily = sToolFamily
Tool.sType = sToolType
Tool.nTypeId = nToolTypeId
Tool.dMaxMaterial = EgtTdbGetCurrToolParam( MCH_TP.MAXMAT)
Tool.dDiameter = EgtTdbGetCurrToolParam( MCH_TP.DIAM)
Tool.dLength = EgtTdbGetCurrToolParam( MCH_TP.LEN)
Tool.dSpeed = EgtTdbGetCurrToolParam( MCH_TP.SPEED)
Tool.bIsCCW = Tool.dSpeed < 0
Tool.Feeds = {}
Tool.Feeds.dFeed = EgtTdbGetCurrToolParam( MCH_TP.FEED)
Tool.Feeds.dStartFeed = EgtTdbGetCurrToolParam( MCH_TP.STARTFEED)
Tool.Feeds.dEndFeed = EgtTdbGetCurrToolParam( MCH_TP.ENDFEED)
Tool.Feeds.dTipFeed = EgtTdbGetCurrToolParam( MCH_TP.TIPFEED)
Tool.sHead = EgtTdbGetCurrToolParam( MCH_TP.HEAD)
Tool.SetupInfo = {}
Tool.SetupInfo = BeamData.GetSetupInfo( Tool.sHead)
Tool.sUUID = EgtTdbGetCurrToolParam( MCH_TP.UUID)
Tool.sUserNotes = EgtTdbGetCurrToolParam( MCH_TP.USERNOTES)
Tool.dMaxDepth = EgtTdbGetCurrToolMaxDepth() or Tool.dMaxMaterial
Tool.ToolHolder = {}
Tool.ToolHolder.dDiameter = EgtTdbGetCurrToolThDiam() or TH_DIAMETER_HSK63 -- diametro standard HSK63
Tool.ToolHolder.dLength = EgtTdbGetCurrToolThLength() or TH_LENGTH_HSK63 -- lunghezza standard HSK63
-- parametri scritti nelle note
Tool.nDouble = EgtGetValInNotes( Tool.sUserNotes, 'DOUBLE', 'd')
Tool.bIsProfiledTool = not EgtTdbIsCurrToolStandardDraw()
-- lettura parametri non comuni ( famiglia DRILLBIT non ha parametri specifici)
if sToolFamily ~= 'DRILLBIT' then
Tool.dThickness = EgtTdbGetCurrToolParam( MCH_TP.THICK)
Tool.dLongitudinalOffset = EgtTdbGetCurrToolParam( MCH_TP.LONOFFSET)
Tool.dRadialOffset = EgtTdbGetCurrToolParam( MCH_TP.RADOFFSET)
-- recupero parametri propri delle frese
if sToolFamily == 'MILL' then
Tool.dStemDiameter = EgtTdbGetCurrToolParam( MCH_TP.STEMDIAM) or Tool.ToolHolder.dDiameter -- se non settato, considero diametro come ToolHolder
Tool.dSideAngle = EgtTdbGetCurrToolParam( MCH_TP.SIDEANG) or 0
-- verifico che parametri siano compatibili con una fresa a coda di rondine ( angolo di fianco standard Coda di rondine -> 15°)
Tool.bIsDoveTail = Tool.Type == 'MILL_NOTIP' and abs( abs( Tool.dSideAngle) - SIDEANGLE_DOVETAIL) < 1
-- verifico che sia una fresa tipo T-Mill o BlockHaus
Tool.dSideDepth = EgtGetValInNotes( Tool.sUserNotes, 'SIDEDEPTH', 'd') or 0 -- se non settato nell'utensile, dico che non ha massimo affondamento laterale
Tool.bIsTMill = Tool.dSideDepth > 0
Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP', 'd') or ( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero metà del tagliente
Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP', 'd') or floor( Tool.dDiameter / 3) -- se non settato nell'utensile, considero metà del diametro
Tool.bIsPen = abs( Tool.dSpeed) < 5
-- recupero parametri propri delle lame
elseif sToolFamily == 'SAWBLADE' then
Tool.bIsUsedForLongCut = EgtGetValInNotes( Tool.sUserNotes, 'LONGCUT') == 1 or false -- false come valore di default
Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP', 'd') or Tool.dThickness -- se non settato nell'utensile, considero lo spessore lama
Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP', 'd') or Tool.dMaxMaterial -- se non settato nell'utensile, considero un quarto del diametro
-- recupero parametri propri delle motoseghe
elseif sToolFamily == 'MORTISE' then
Tool.dDistance = EgtTdbGetCurrToolParam( MCH_TP.DIST) or 90 -- 90mm dimensione standard aggregato catena
Tool.bIsMortise = EgtGetValInNotes( Tool.sUserNotes, 'MORTISE') == 1
Tool.bIsChainSaw = not Tool.bIsMortise
Tool.dStep = EgtGetValInNotes( Tool.sUserNotes, 'STEP', 'd') or floor( Tool.dMaxMaterial / 3) -- se non settato nell'utensile, considero un terzo della lunghezza
Tool.dSideStep = EgtGetValInNotes( Tool.sUserNotes, 'SIDESTEP', 'd') or ( Tool.dThickness - 1) -- se non settato nell'utensile, considero spessore catena meno 1mm di sicurezza
Tool.dCornerRadius = EgtTdbGetCurrToolParam( MCH_TP.CORNRAD)
Tool.dWidth = Tool.dDiameter
end
end
-- se tutti i dati necessari sono disponibili, inserisco utensile nella lista globale degli utensili disponibili
if IsToolOk( Tool) then
table.insert( TOOLS, Tool)
-- altrimenti scrivo nel log che l'utensile non è conforme
else
EgtOutLog( '*** ' .. Tool.sName .. ' : NOT-COMPLIANT ***', 1)
end
-- reset dati
Tool = {}
end
-- recupero utensile successivo ( punte a forare, lame, frese e motoseghe)
Tool.sName = EgtTdbGetNextTool( MCH_TF.DRILLBIT + MCH_TF.SAWBLADE + MCH_TF.MILL + MCH_TF.MORTISE)
end
end
-------------------------------------------------------------------------------------------------------------
local function ImportFileJSON( sFileToImport)
local JSON = require( 'JSON')
local function readAll( sFileToImport)
local f = io.open( sFileToImport, "rb")
local content = f:read( "*all")
f:close()
return content
end
local content = readAll( sFileToImport)
return JSON:decode( content)
end
-------------------------------------------------------------------------------------------------------------
function BeamExec.GetStrategiesFromJSONinBD()
-- si legge il JSON contenente la configurazione da utilizzare (nome del file salvato nel BeamData)
local sMachDir = EgtGetCurrMachineDir()
if BeamData.STRATEGIES_CONFIG_FILE then
local sFile = sMachDir .. '\\Beam\\' .. BeamData.STRATEGIES_CONFIG_FILE
-- se non esiste file JSON, annullo la lista contenente le strategie
if not EgtExistsFile( sFile) then
STRATEGIES = nil
return
end
local FeaturesList = {}
FeaturesList = ImportFileJSON( sFile)
-- metto la tabella letta nella lista globale STRATEGIES
STRATEGIES = {}
STRATEGIES.Features = FeaturesList
end
end
-------------------------------------------------------------------------------------------------------------
-- funzione che controlla validità delle combinazioni proposte
local function IsCombinationAvailable( sCombination, nUnloadPos, bSquareSection)
-- se non utilizzo BEAMWALL, forzo comportamento BASIC
if not BEAM.BeamWall or not BEAM.Rotation then
BEAM.Rotation = {}
BEAM.Rotation.Basic = true
end
--------------------------------------------------------------------------
-- TODO scelta combinazione forzato DA RIMUOVERE!! Serve modifica al BEAM.
BEAM.BeamWall = true
BEAM.Rotation = {}
BEAM.Rotation.bBasic = true
BEAM.Rotation.bNoRotation = false
BEAM.Rotation.bAdvanced = false
--------------------------------------------------------------------------
-- BASIC : posizione di scarico come posizionamento iniziale
if not BEAM.BeamWall or BEAM.Rotation.bBasic then
local ExtraRotation = nUnloadPos + 3
if nUnloadPos ~= 1 then
return false
elseif string.sub( sCombination, nUnloadPos, nUnloadPos) == '1' and string.sub( sCombination, ExtraRotation, ExtraRotation) == '0' then
if not BeamData.ROT90 and string.sub( sCombination, nUnloadPos+1, nUnloadPos+1) == '1' then
return false
elseif not BeamData.ROT180 and string.sub( sCombination, nUnloadPos+2, nUnloadPos+2) == '1' then
return false
else
return true
end
else
return false
end
-- NO ROTATION : solo posizione di partenza
elseif BEAM.Rotation.bNoRotation then
if sCombination == '1000' and nUnloadPos == 1 then
return true
else
return false
end
-- ADVANCED : come BASIC ma ammesse anche le prerotazioni (posizione di scarico può essere diversa da posizione iniziale)
elseif BEAM.Rotation.bAdvanced then
local nRotation90 = EgtIf( nUnloadPos + 1 > 4, nUnloadPos + 1 - 4, nUnloadPos + 1)
local nRotation180 = EgtIf( nUnloadPos + 2 > 4, nUnloadPos + 2 - 4, nUnloadPos + 2)
local nExtraRotation = EgtIf( nUnloadPos + 3 > 4, nUnloadPos + 3 - 4, nUnloadPos + 3)
if not bSquareSection and ( nUnloadPos == 2 or nUnloadPos == 4) then
return false
else
if string.sub( sCombination, nUnloadPos, nUnloadPos) ~= '1' or string.sub( sCombination, nExtraRotation, nExtraRotation) == '1' then
return false
else
if not BeamData.ROT90 and string.sub( sCombination, nRotation90, nRotation90) == '1' then
return false
elseif not BeamData.ROT180 and string.sub( sCombination, nRotation180, nRotation180) == '1' then
return false
else
return true
end
end
end
end
end
-------------------------------------------------------------------------------------------------------------
local function GetAvailableCombinations( PartInfo)
local CombinationList = {}
CombinationList.Rotations = {0, 0, 0, 0} -- indice rotazione attiva, per calcolo collect feature
-- verifico tutte le combinazioni che possono essere considerate
for nUnloadPos = 1, 4 do
for i = 1, BeamLib.BinaryToDecimal( 1111) do
local nBitIndexCombination = BeamLib.DecimalToBinary( i)
local sBitIndexCombination = BeamLib.CalculateStringBinaryFormat( nBitIndexCombination, 4)
-- si calcolano le combinazioni all'inizio, ottimizzando calcolo della collect solo nelle rotazioni che possono essere considerate
if IsCombinationAvailable( sBitIndexCombination, nUnloadPos, PartInfo.bSquareSection) then
local Combination = {}
Combination.sBitIndexCombination = sBitIndexCombination
Combination.nUnloadPos = nUnloadPos
table.insert( CombinationList, Combination)
-- se posizionamento iniziale attivo
if string.sub( sBitIndexCombination, 1, 1) == '1' then
CombinationList.Rotations[1] = 1
end
-- se attiva rotazione 90
if string.sub( sBitIndexCombination, 2, 2) == '1' then
CombinationList.Rotations[2] = 1
end
-- se attiva rotazione 180
if string.sub( sBitIndexCombination, 3, 3) == '1' then
CombinationList.Rotations[3] = 1
end
-- se attiva rotazione 270
if string.sub( sBitIndexCombination, 4, 4) == '1' then
CombinationList.Rotations[4] = 1
end
end
end
end
return CombinationList
end
-------------------------------------------------------------------------------------------------------------
-- *** funzioni posizionamento pezzi all'interno della barra ***
-------------------------------------------------------------------------------------------------------------
function BeamExec.ProcessBeams( dRawW, dRawH, dRawL, dOvmHead, dOvmMid, PARTS)
-- default per nuove costanti qualora non definite
BeamData.OVM_BLADE_HBEAM = ( BeamData.OVM_BLADE_HBEAM or 11)
BeamData.OVM_CHAIN_HBEAM = ( BeamData.OVM_CHAIN_HBEAM or 8)
-- sovramateriale intermedio nullo se non definito
dOvmMid = ( dOvmMid or 0)
-- Determinazione minimo grezzo scaricabile
BeamExec.CalcMinUnloadableRaw( dRawW, dRawH)
-- Creazione nuovo gruppo di lavoro
local sMgName = EgtGetMachGroupNewName( 'Mach_1')
local idNewMg = EgtAddMachGroup( sMgName)
if not idNewMg then
local sOut = 'Errore nella creazione del gruppo di lavoro ' .. sMgName
return false, sOut
end
-- Impostazione della tavola
EgtSetTable( 'Tab')
-- salvo nota con lunghezza grezzo
-- Recupero l'identificativo del gruppo di lavoro corrente
local nMGrpId = EgtGetCurrMachGroup()
-- Lunghezza della barra
local dBarLen = EgtGetInfo( nMGrpId, 'BARLEN', 'd')
if not dBarLen then
EgtSetInfo( nMGrpId, 'BARLEN', dRawL)
end
-- Area tavola
local b3Tab = EgtGetTableArea()
-- Calcolo posizione estremo TR/BR della tavola rispetto a sua origine in BL
local dPosY = EgtIf( BeamData.CENTER_BEAM, ( b3Tab:getDimY() + dRawW * EgtIf( BeamData.RIGHT_LOAD, -1, 1)) / 2, EgtIf( BeamData.RIGHT_LOAD, 0, b3Tab:getDimY()))
BeamData.ptOriXR = Point3d( b3Tab:getDimX(), dPosY, 0)
BeamData.dPosXR = EgtIf( BeamData.RIGHT_LOAD, MCH_CR.BR, MCH_CR.TR)
-- Impostazione dell'attrezzaggio di default
EgtImportSetup()
-- Inserimento dei pezzi con il loro grezzo
local nCnt = 0
local dLen = dRawL
local idPrevRaw, dPrevDelta
local dDeltaS = dOvmHead
local dDeltaSMin = 0
local dDeltaE = BeamData.OVM_MID
for i = 1, #PARTS do
-- dati del pezzo
local b3Part = EgtGetBBoxGlob( PARTS[i].id or GDB_ID.NULL, GDB_BB.EXACT)
if b3Part:isEmpty() or PARTS[i].b3Box:isEmpty() then break end
EgtOutLog( 'PartSez=' .. EgtNumToString( b3Part:getDimY(), 1) .. 'x' .. EgtNumToString( b3Part:getDimZ(), 1), 3)
-- se sezione compatibile e lunghezza disponibile sufficiente
local dPartLen = PARTS[i].b3Box:getDimX()
local dPartWidth = PARTS[i].b3Box:getDimY()
local dPartHeight = PARTS[i].b3Box:getDimZ()
local dNextLen = dLen - EgtIf( i == 1, dDeltaS, 0) - dPartLen - dDeltaE
if (( abs( dPartWidth - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartHeight - dRawH) < 100 * GEO.EPS_SMALL) or
( abs( dPartHeight - dRawW) < 100 * GEO.EPS_SMALL and abs( dPartWidth - dRawH) < 100 * GEO.EPS_SMALL)) and
dNextLen + dDeltaE >= 0 then
-- eventuale sovramateriale di testa
if i > 1 then
if PARTS[i].dPosX then
dDeltaS = max( PARTS[i].dPosX - ( dRawL - dLen), dDeltaSMin)
else
dDeltaS = max( dOvmMid - dDeltaE, 0)
end
end
-- dimensioni del grezzo
local dCrawLen = min( dPartLen + dDeltaS + dDeltaE, dLen)
local dDelta = dCrawLen - dPartLen - dDeltaS
-- creo e posiziono il grezzo
PARTS[i].idRaw = EgtAddRawPart( Point3d(0,0,0), dCrawLen, dRawW, dRawH, BeamData.RAWCOL)
EgtMoveToCornerRawPart( PARTS[i].idRaw, BeamData.ptOriXR, BeamData.dPosXR)
EgtMoveRawPart( PARTS[i].idRaw, Vector3d( dLen - dRawL, 0, 0))
-- assegno ordine in lavorazione
nCnt = nCnt + 1
EgtSetInfo( PARTS[i].idRaw, 'ORD', nCnt)
-- creo o pulisco gruppo geometrie aggiuntive
if not BeamLib.CreateOrEmptyAddGroup( PARTS[i].id) then
local sOut = 'Error creating Additional Group in Part ' .. tostring( PARTS[i].id)
return false, sOut
end
-- aggiungo faccia per taglio iniziale al pezzo
BeamLib.AddPartStartFace( PARTS[i].id, PARTS[i].b3Box)
-- se sovramateriale di testa, lo notifico
if dDeltaS > 0.09 then
EgtSetInfo( PARTS[i].idRaw, 'HOVM', dDeltaS)
if idPrevRaw then
EgtSetInfo( idPrevRaw, 'BDST', dDeltaS + dPrevDelta)
end
end
if dDeltaE > 0.09 then
EgtSetInfo( PARTS[i].idRaw, 'TOVM', dDeltaE)
end
-- aggiungo faccia per taglio finale al pezzo
BeamLib.AddPartEndFace( PARTS[i].id, PARTS[i].b3Box)
-- inserisco il pezzo nel grezzo
EgtDeselectPartObjs( PARTS[i].id)
local ptPos = b3Part:getMin() - PARTS[i].b3Box:getMin() + Vector3d( dDelta, ( dRawW - dPartWidth) / 2, ( dRawH - dPartHeight) / 2)
EgtAddPartToRawPart( PARTS[i].id, ptPos, PARTS[i].idRaw)
if abs( dPartWidth - dRawW) > 100 * GEO.EPS_SMALL then
-- rotazione attorno a centro geometria complessiva del pezzo
EgtRotatePartInRawPart( PARTS[i].id, X_AX(), 90)
-- correggo per eccentricità solido rispetto a geometria complessiva del pezzo
local vtEccOri = PARTS[i].b3Box:getCenter() - b3Part:getCenter()
local vtEccRot = Vector3d( vtEccOri)
vtEccRot:rotate( X_AX(), 90)
EgtMovePartInRawPart( PARTS[i].id, ( vtEccOri - vtEccRot))
end
-- aggiorno la lunghezza residua della barra
dLen = dLen - dCrawLen
-- aggiorno grezzo precedente
idPrevRaw = PARTS[i].idRaw
dPrevDelta = dDelta
PARTS[i].bIsLastPart = ( i == #PARTS)
PARTS[i].dDistanceToNextPiece = dDelta
PARTS[i].dRestLength = dLen
PARTS[i].b3Raw = EgtGetRawPartBBox( PARTS[i].idRaw)
PARTS[i].dLength = PARTS[i].b3Raw:getDimX()
PARTS[i].dWidth = PARTS[i].b3Raw:getDimY()
PARTS[i].dHeight = PARTS[i].b3Raw:getDimZ()
PARTS[i].bSquareSection = abs( PARTS[i].dWidth - PARTS[i].dHeight) < 100 * GEO.EPS_SMALL
PARTS[i].b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[i].id, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD)
PARTS[i].nIndexInParts = i
PARTS[i].CombinationList = GetAvailableCombinations( PARTS[i])
else
local sOut = 'Error: part L(' .. EgtNumToString( dPartLen, 1) .. ') too big for raw part L(' .. EgtNumToString( dLen - 0.1, 1) .. ')'
return false, sOut
end
-- se rimasto troppo poco grezzo, esco
--if Len < BeamData.MinRaw then break end
DeltaS = 0
end
if idPrevRaw then
EgtSetInfo( idPrevRaw, 'BDST', 10000)
end
-- Se rimasto materiale aggiungo grezzo dell'avanzo
-- TODO valutare se ridurre la dLen minima perchè crea discrepanze tra lunghezza inserita e VMill
if dLen > 10 then
local idRaw = EgtAddRawPart( Point3d(0,0,0), dLen, dRawW, dRawH, BeamData.RAWCOL)
EgtMoveToCornerRawPart( idRaw, BeamData.ptOriXR, BeamData.dPosXR)
EgtMoveRawPart( idRaw, Vector3d( dLen - dRawL, 0, 0))
-- assegno ordine in lavorazione
nCnt = nCnt + 1
EgtSetInfo( idRaw, 'ORD', nCnt)
-- aggiorno distanza dell'ultimo pezzo dall'eventuale grezzo scaricabile
if EgtGetRawPartBBox( idRaw):getDimX() < BeamData.dMinRaw then
PARTS[#PARTS].dDistanceToNextPiece = 10000
end
else
PARTS[#PARTS].dDistanceToNextPiece = 10000
end
return true
end
-------------------------------------------------------------------------------------------------------------
function BeamExec.CalcMinUnloadableRaw( dRawW, dRawH)
-- TODO convertire in GetMinUnloadableRaw che viene richiamata all'inizio delle funzioni che necessitano di dMinRaw
if BeamData.GetMinUnloadableRaw then
BeamData.dMinRaw = BeamData.GetMinUnloadableRaw( dRawW, dRawH)
else
local H_S = 200
local H_L = 400
-- Determinazione minimo grezzo scaricabile
if dRawH <= H_S then
BeamData.dMinRaw = BeamData.MINRAW_S
elseif dRawH <= H_L then
local Coeff = ( dRawH - H_S) / ( H_L - H_S)
BeamData.dMinRaw = ( 1 - Coeff) * BeamData.MINRAW_S + Coeff * BeamData.MINRAW_L
else
BeamData.dMinRaw = BeamData.MINRAW_L
end
end
end
-------------------------------------------------------------------------------------------------------------
-- *** Inserimento delle lavorazioni nelle travi ***
-------------------------------------------------------------------------------------------------------------
local function GetStrategiesFromGlobalList( Proc)
-- cerco tra le feature
for i = 1, #STRATEGIES.Features do
-- se trovo la feature
if Proc.nPrc == STRATEGIES.Features[i].nPrc and Proc.nGrp == STRATEGIES.Features[i].nGrp then
-- cerco tra le topologie
for j = 1, #STRATEGIES.Features[i].Topologies do
-- se trovo la topologia
if Proc.Topology.sName == STRATEGIES.Features[i].Topologies[j].sName then
-- ritorno le strategie disponibili per la feature che sto analizzando
return STRATEGIES.Features[i].Topologies[j].Strategies
end
end
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------
local function GetStrategies( Proc)
local AvailableStrategiesForProc = nil
-- se la lista STRATEGIES è stata letta da JSON (quindi non è vuota), ritorno le strategie possibili
if STRATEGIES and #STRATEGIES.Features > 0 then
AvailableStrategiesForProc = GetStrategiesFromGlobalList( Proc)
end
-- se non ho trovato strategie disponibili nel JSON, o se JSON non presente, lancio script che setta le strategie in modo statico, come definito con cliente
if not AvailableStrategiesForProc then
AvailableStrategiesForProc = BCS.GetStrategiesFromBasicCustomerStrategies( Proc)
end
return AvailableStrategiesForProc
end
-------------------------------------------------------------------------------------------------------------
local function GetFeatureForcedStrategy( Proc)
-- cerco nelle note se è stata forzata una strategia specifica
local sStrategyId = EgtGetInfo( Proc.id, 'STRATEGY', 's')
-- se è presente la strategia forzata
if sStrategyId then
-- eseguo file config con i parametri di default
local StrategyData = require( sStrategyId .. '\\' .. sStrategyId .. 'Config')
-- se ID strategia non esiste oppure ID letto in NGE è differente da quello letto nella strategia, esco subito
if not StrategyData or StrategyData.sStrategyId ~= sStrategyId then
return nil
end
-- salvo che questa strategia è stata forzata, e che quindi ho già letto il file config con i parametri di default
-- quando si calcolerà la lavorazione non servirà leggere/riverificare i parametri di default
StrategyData.bForcedStrategy = true
-- cerco e aggiorno i parametri come sono settati nel processing
for i = 1, #StrategyData.Parameters do
local sParameterToRead = StrategyData.sStrategyId .. '_' .. StrategyData.Parameters[i].sNameNge
local sForcedParameterForProc = EgtGetInfo( Proc.id, sParameterToRead, 's')
-- se ho trovato il valore, lo sovrascrivo al default
if sForcedParameterForProc then
StrategyData.Parameters[i].sValue = sForcedParameterForProc
end
end
-- ritorno la lista strategia con parametri
local StrategyToProc = {}
table.insert( StrategyToProc, StrategyData)
return StrategyToProc
end
return nil
end
-------------------------------------------------------------------------------------------------------------
local function CollectFeatures( Part)
-- recupero le feature
local vProc = {}
local LayerId = {}
LayerId[1] = BeamLib.GetAddGroup( Part.id)
LayerId[2] = EgtGetFirstNameInGroup( Part.id or GDB_ID.NULL, 'Processings')
for nInd = 1, 2 do
local ProcId = EgtGetFirstInGroup( LayerId[nInd] or GDB_ID.NULL)
while ProcId do
local nEntType = EgtGetType( ProcId)
if nEntType == GDB_TY.SRF_MESH or nEntType == GDB_TY.EXT_TEXT or
nEntType == GDB_TY.CRV_LINE or nEntType == GDB_TY.CRV_ARC or nEntType == GDB_TY.CRV_BEZ or nEntType == GDB_TY.CRV_COMPO then
local nGrp = EgtGetInfo( ProcId, 'GRP', 'i')
local nPrc = EgtGetInfo( ProcId, 'PRC', 'i')
local nDo = EgtGetInfo( ProcId, 'DO', 'i') or 1
if nGrp and nPrc and nDo == 1 then
local Proc = {}
Proc.idPart = Part.id
Proc.idRaw = Part.idRaw
Proc.nIndexPartInParts = Part.nIndexInParts
Proc.id = ProcId
-- id della feature btl ( se non presente info, si prende id dell'entità geometrica)
Proc.idFeature = EgtGetInfo( Proc.id, 'PRID', 's') or Proc.id
Proc.nGrp = nGrp
Proc.nPrc = nPrc
Proc.nFlg = 1
Proc.nFct = EgtSurfTmFacetCount( ProcId) or 0
Proc.idCut = EgtGetInfo( EgtGetParent( EgtGetParent( ProcId)), 'CUTID', 'i') or 0
Proc.idTask = EgtGetInfo( ProcId, 'TASKID', 'i') or 0
Proc.b3Box = EgtGetBBoxGlob( ProcId, GDB_BB.STANDARD)
EgtOutLog( '------Feature ' .. Proc.idFeature .. '------')
-- se esiste la geometria
if Proc.b3Box and not Proc.b3Box:isEmpty() then
-- TODO fare una funzione per recuperare i dati delle feature che non passano dal calcolo della topologia?
-- se foro calcolo altri dati
if ID.IsDrilling( Proc) then
-- assegno diametro e facce di ingresso e uscita (dati tabelle sempre per riferimento)
Proc.dDiam, Proc.dLen, Proc.nFcs, Proc.nFce = FeatureLib.GetDrillingData( Proc)
end
-- informazioni facce e topologia
Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part)
-- calcolo topologia solo se necessario, altrimenti si sfruttano le informazioni della feature BTL
Proc.Topology = {}
if FeatureLib.NeedTopologyFeature( Proc) then
Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc)
Proc.Faces = FaceData.GetFacesInfo( Proc, Part)
Proc.Topology = FeatureLib.ClassifyTopology( Proc, Part)
else
Proc.Topology.sFamily = 'FEATURE'
Proc.Topology.sName = 'FEATURE'
end
-- se topologia feature riconosciuta, oppure da non calcolare perchè il riconoscimento topologico è basato sulla feature stessa
if Proc.Topology.sName ~= 'NOT_IMPLEMENTED' then
Proc.MainFaces = FaceData.GetMainFaces( Proc, Part)
-- se la processing ha una strategia forzata, riporto tutto nella proc
local vForcedStrategy = GetFeatureForcedStrategy( Proc)
if vForcedStrategy then
Proc.AvailableStrategies = vForcedStrategy
-- altrimenti cerco tra le strategie disponibili
else
Proc.AvailableStrategies = GetStrategies( Proc)
end
-- se ci sono strategie disponibili, aggiungo a lista delle feature da lavorare
if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then
table.insert( vProc, Proc)
-- altrimenti errore (non ci sono strategie per lavorare la topologia riconosciuta)
else
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies')
end
-- altrimenti errore (serviva riconoscimento topologico, ma non è stato possibile farlo)
else
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' : NO available strategies')
end
else
Proc.nFlg = 0
table.insert( vProc, Proc)
EgtOutLog( ' Feature ' .. tostring( Proc.idFeature) .. ' is empty (no geometry)')
end
end
end
ProcId = EgtGetNext( ProcId)
end
end
return vProc
end
-------------------------------------------------------------------------------------------------------------
local function AreDrillingsMirrored( Proc, ProcMirror, Part)
if Proc.id == ProcMirror.id then return false end
-- geometria ausiliaria foro principale
AuxId = EgtGetInfo( Proc.id, 'AUXID', 'i')
if AuxId then AuxId = AuxId + Proc.id end
if not AuxId or EgtGetType( AuxId ) ~= GDB_TY.CRV_ARC then return false end
-- geometria ausiliaria foro specchiato
local AuxIdMirror = EgtGetInfo( ProcMirror.id, 'AUXID', 'i')
if AuxIdMirror then AuxIdMirror = AuxIdMirror + ProcMirror.id end
if not AuxIdMirror or EgtGetType( AuxIdMirror ) ~= GDB_TY.CRV_ARC then return false end
-- dati del foro principale
local vtExtr = EgtCurveExtrusion( AuxId, GDB_RT.GLOB)
local ptBC = EgtGP( AuxId, GDB_RT.GLOB)
-- dati del foro specchiato
local vtExtrMirror = EgtCurveExtrusion( AuxIdMirror, GDB_RT.GLOB)
local ptBCMirror = EgtGP( AuxIdMirror, GDB_RT.GLOB)
-- direzione fori
local nDouble
if AreOppositeVectorApprox( vtExtr, vtExtrMirror) then
-- fori lungo Y
-- per macchine tipo PF il foro principale è sul lato back, per macchine tipo PF1250 è sul lato front
if ( BeamData.TWO_EQUAL_HEADS and AreSameVectorApprox( vtExtr, Y_AX())) or
( BeamData.DOWN_HEAD and AreOppositeVectorApprox( vtExtr, Y_AX())) then
nDouble = 2
-- fori lungo Z
elseif BeamData.DOWN_HEAD and AreSameVectorApprox( vtExtr, Z_AX()) then
nDouble = 3
else
return false
end
else
return false
end
-- centri allineati, equidistanti dalla mezzeria trave, non troppo vicini
local vtDisplacement = ptBC - ptBCMirror
local ptCenRaw = Part.b3Raw:getCenter()
if nDouble == 2 then
local dYMinDistance = max( Proc.b3Box:getMin():getY(), ProcMirror.b3Box:getMin():getY()) - min( Proc.b3Box:getMax():getY(), ProcMirror.b3Box:getMax():getY())
if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getZ()) < 100 * GEO.EPS_SMALL and
( abs( ptBC:getY() - ptCenRaw:getY()) - abs( ptBCMirror:getY() - ptCenRaw:getY())) < 100 * GEO.EPS_SMALL and
dYMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then
return false
end
else
local dZMinDistance = max( Proc.b3Box:getMin():getZ(), ProcMirror.b3Box:getMin():getZ()) - min( Proc.b3Box:getMax():getZ(), ProcMirror.b3Box:getMax():getZ())
if not ( abs( vtDisplacement:getX()) < 100 * GEO.EPS_SMALL and abs( vtDisplacement:getY()) < 100 * GEO.EPS_SMALL and
( abs( ptBC:getZ() - ptCenRaw:getZ()) - abs( ptBCMirror:getZ() - ptCenRaw:getZ())) < 100 * GEO.EPS_SMALL and
dZMinDistance > MIRROR_DRILLINGS_MIN_DISTANCE + 10 * GEO.EPS_SMALL) then
return false
end
end
-- fori della stessa profondità
if abs( Proc.dLen - ProcMirror.dLen) > 10 * GEO.EPS_SMALL then
return false
end
return true
end
-------------------------------------------------------------------------------------------------------------
local function GetFeatureInfoAndDependency( vProcSingleRot, Part)
-- ciclo tutte le feature
for i = 1, #vProcSingleRot do
local Proc = vProcSingleRot[i]
-- controllo la feature con tutte le altre per recuperare le dipendenze
for j = 1, #vProcSingleRot do
-- non si controlla la feature con se stessa
if i ~= j then
local ProcB = vProcSingleRot[j]
-- verifico se feature tipo LapJoint è attraversata da almeno un foro
if ( Proc.Topology.sFamily == 'Pocket' or Proc.Topology.sFamily == 'Tunnel' or Proc.Topology.sFamily == 'Groove' or ID.IsMortise( Proc)) and
ID.IsDrilling( ProcB) and Overlaps( Proc.b3Box, ProcB.b3Box) then
Proc.bPassedByHole = true
end
-- verifiche per specchiature
if BeamData.DOWN_HEAD or BeamData.TWO_EQUAL_HEADS then
-- forature
if BeamData.DOUBLE_HEAD_DRILLING and ID.IsDrilling( Proc) and ID.IsDrilling( ProcB) and not Proc.Mirror then
if AreDrillingsMirrored( Proc, ProcB, Part) then
Proc.Mirror = ProcB
end
end
end
end
end
end
return vProcSingleRot
end
-------------------------------------------------------------------------------------------------------------
-- TODO contemplare anche strategie speciali
local function RunStrategyLibraries( sStrategyId)
local StrategyConfigName = sStrategyId .. '\\' .. sStrategyId .. 'Config'
local StrategyScriptName = sStrategyId .. '\\' .. sStrategyId
local StrategyConfigPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyConfigName .. '.lua'
local StrategyScriptPathStandard = BEAM.BASEDIR .. '\\Strategies\\Standard\\' .. StrategyScriptName .. '.lua'
local StrategyConfigPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyConfigName .. '.lua'
local StrategyScriptPathSpecial = BEAM.BASEDIR .. '\\Strategies\\Special\\' .. StrategyScriptName .. '.lua'
local StrategyLib = {}
if ( EgtExistsFile( StrategyConfigPathStandard) and EgtExistsFile( StrategyScriptPathStandard)) or
( EgtExistsFile( StrategyConfigPathSpecial) and EgtExistsFile( StrategyScriptPathSpecial)) then
StrategyLib.Config = require( StrategyConfigName)
StrategyLib.Script = require( StrategyScriptName)
return StrategyLib
else
return {}
end
end
-------------------------------------------------------------------------------------------------------------
-- ritorna l'indice della strategia migliore, tra le due passate
local function GetIndexBestStrategyFromComparison( AvailableStrategies, nIndex1, nIndex2)
local dChosenIndex = 0
-- controllo indici
if nIndex1 == 0 and nIndex2 == 0 then
dChosenIndex = 0
elseif nIndex1 == nIndex2 then
dChosenIndex = nIndex1
elseif nIndex1 == 0 then
-- basta che sia applicabile
if AvailableStrategies[nIndex2].Result and ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' or AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed') then
dChosenIndex = nIndex2
end
elseif nIndex2 == 0 then
-- basta che sia applicabile
if AvailableStrategies[nIndex1].Result and ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' or AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed') then
dChosenIndex = nIndex1
end
elseif not AvailableStrategies[nIndex1].Result or not AvailableStrategies[nIndex2].Result then
dChosenIndex = 0
elseif ( AvailableStrategies[nIndex1].Result.sStatus == 'Completed' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Completed') or
( AvailableStrategies[nIndex1].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex2].Result.sStatus == 'Not-Applicable') then
dChosenIndex = nIndex1
elseif ( AvailableStrategies[nIndex2].Result.sStatus == 'Completed' and AvailableStrategies[nIndex1].Result.sStatus ~= 'Completed') or
( AvailableStrategies[nIndex2].Result.sStatus == 'Not-Completed' and AvailableStrategies[nIndex1].Result.sStatus == 'Not-Applicable') then
dChosenIndex = nIndex2
else
-- se le due strategie hanno stesso stato e sono entrambe applicabili (quindi entrambe complete o entrambe non-complete)
if AvailableStrategies[nIndex1].Result.sStatus ~= 'Not-Applicable' and AvailableStrategies[nIndex2].Result.sStatus ~= 'Not-Applicable' and
AvailableStrategies[nIndex1].Result.sStatus == AvailableStrategies[nIndex2].Result.sStatus then
local dCompositeRatingStrategy1 = AvailableStrategies[nIndex1].Result.dCompositeRating
local dCompositeRatingStrategy2 = AvailableStrategies[nIndex2].Result.dCompositeRating
-- si predilige strategia con rating composito più alto
if dCompositeRatingStrategy1 > dCompositeRatingStrategy2 then
dChosenIndex = nIndex1
elseif dCompositeRatingStrategy2 > dCompositeRatingStrategy1 then
dChosenIndex = nIndex2
-- altrimenti si prende la strategia con indice più basso
else
dChosenIndex = EgtIf( nIndex1 < nIndex2, nIndex1, nIndex2)
end
end
end
return dChosenIndex
end
-------------------------------------------------------------------------------------------------------------
-- funzione che trova la strategia migliore tra quelle disponibili
local function GetBestStrategy( vProcSingleRot)
for i = 1, #vProcSingleRot do
-- processo tutte le feature attive
local Proc = vProcSingleRot[i]
if Proc.nFlg ~= 0 then
local nIndexBestStrategy = 0
-- controllo se ci sono strategie disponibili
if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then
-- ciclo tutte le strategie della feature
for nIndexCurrentStrategy = 1, #Proc.AvailableStrategies do
-- scelgo la migliore strategia tra le due
nIndexBestStrategy = GetIndexBestStrategyFromComparison( Proc.AvailableStrategies, nIndexCurrentStrategy, nIndexBestStrategy)
-- salvo sulla proc la migliore strategia
end
if nIndexBestStrategy ~= 0 then
Proc.ChosenStrategy = Proc.AvailableStrategies[nIndexBestStrategy]
Proc.nIndexBestStrategy = nIndexBestStrategy
end
end
end
end
return vProcSingleRot
end
-------------------------------------------------------------------------------------------------------------
-- funzione che processa tutte le feature del pezzo
local function CalculateStrategies( vProcSingleRot, Part)
-- per ogni feature
for i = 1, #vProcSingleRot do
-- processo tutte le feature attive
local Proc = vProcSingleRot[i]
if Proc.nFlg ~= 0 then
-- controllo se ci sono strategie disponibili
if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then
-- ciclo tutte le strategie della feature
for nIndexCurrentStrategy = 1, #Proc.AvailableStrategies do
-- eseguo file config con i parametri di default
local CurrentStrategy = {}
CurrentStrategy = RunStrategyLibraries( Proc.AvailableStrategies[nIndexCurrentStrategy].sStrategyId)
-- controllo che le librerie siano state effettivamente caricate
if CurrentStrategy.Config and CurrentStrategy.Script then
-- eseguo la strategia solo come calcolo fattibilità e voto. Non si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati
_, Proc.AvailableStrategies[nIndexCurrentStrategy].Result = CurrentStrategy.Script.Make( false, Proc, Part, Proc.AvailableStrategies[nIndexCurrentStrategy].Parameters)
Proc.AvailableStrategies[nIndexCurrentStrategy].Result = FeatureLib.CalculateCompositeRating( Proc.AvailableStrategies[nIndexCurrentStrategy].Result)
-- se scelta strategia in modalità base o standard, esco subito alla prima che trovo completa
-- TODO serve paraemtro da Beam&Wall ( oppure da confirgurazione) !!!!!!!!
if BEAM.GetFirstCompletedStrategy and Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus == 'Complete' then
break
end
-- se non trovo i file della strategia, scrivo che non è più disponibile
else
Proc.AvailableStrategies[nIndexCurrentStrategy].Result = {}
Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sInfo = 'Strategy not found'
Proc.AvailableStrategies[nIndexCurrentStrategy].Result.sStatus = 'Not-Applicable'
end
end
end
end
end
return vProcSingleRot
end
-------------------------------------------------------------------------------------------------------------
-- Ordina le feature in base a fase di lavorazione
-- 1) Head : ( intestatura)
-- 2) Standard : ( lavorazioni standard che non impattano sulla coda)
-- 3) AdvanceTail : ( lavorazioni che richiedono taglio di separazione, ma che devono essere fatte prima perchè il taglio di separazione farebbe cadere il pezzo)
-- 4) Split : ( taglio di separazione)
-- 5) Tail : ( lavorazioni di coda, fatte dopo il taglio di separazione)
local function OrderFeatures( vProc)
local vProcToSort = vProc
-- funzione di confronto. TRUE = B1 prima di B2. FALSE = B2 prima di B1
local function CompareFeatures( B1, B2)
-- se secondo disabilitato, va lasciato dopo
if B1.nFlg ~= 0 and B2.nFlg == 0 then
return true
-- se entrambi disabilitati seguo l'Id
elseif B1.nFlg == 0 and B2.nFlg == 0 then
return ( B1.id < B2.id)
-- se in rotazioni diverse, si mette in ordine di rotazioni
elseif B1.nRot ~= B2.nRot then
return ( B1.nRot < B2.nRot)
-- se primo è taglio di testa, va prima degli altri casi
elseif B1.Head and ( B2.Standard or B2.AdvanceTail or B2.Split or B2.Tail) then
return true
-- se primo è taglio standard, va prima degli altri casi
elseif B1.Standard and ( B2.AdvanceTail or B2.Split or B2.Tail) then
return true
-- se primo è taglio di testa anticipata, va prima degli altri casi
elseif B1.AdvanceTail and ( B2.Split or B2.Tail) then
return true
-- se primo è taglio di separazione, va prima delle lavorazioni di coda
elseif B1.Split and B2.Tail then
return true
-- se da lavorare in stessa fase pezzo
elseif B1.Head == B2.Head or B1.Standard == B2.Standard or B1.AdvanceTail == B2.AdvanceTail or B1.Split == B2.Split or B1.Tail == B2.Tail then
-- confronto standard
if abs( B1.b3Box:getCenter():getX() - B2.b3Box:getCenter():getX()) > 0.4 * ( B1.b3Box:getDimX() + B2.b3Box:getDimX()) then
return B1.b3Box:getCenter():getX() > B2.b3Box:getCenter():getX()
elseif abs( B1.b3Box:getCenter():getY() - B2.b3Box:getCenter():getY()) > 0.2 * ( B1.b3Box:getDimY() + B2.b3Box:getDimY()) then
return B1.b3Box:getCenter():getY() > B2.b3Box:getCenter():getY()
elseif abs( B1.b3Box:getCenter():getZ() - B2.b3Box:getCenter():getZ()) > 0.1 * ( B1.b3Box:getDimZ() + B2.b3Box:getDimZ()) then
return B1.b3Box:getCenter():getZ() > B2.b3Box:getCenter():getZ()
end
-- altrimenti si inverte
else
return false
end
end
-- test della funzione di ordinamento
if EgtGetDebugLevel() >= 3 then
EgtOutLog( ' CompareFeatures Test ')
local bCompTest = true
for i = 1, #vProcToSort do
for j = i + 1, #vProcToSort do
local bComp1 = CompareFeatures( vProcToSort[i], vProcToSort[j])
local bComp2 = CompareFeatures( vProcToSort[j], vProcToSort[i])
if bComp1 == bComp2 then
bCompTest = false
EgtOutLog( string.format( ' ProcId : %d vs %d --> ERROR', vProcToSort[i].id, vProcToSort[j].id))
end
end
end
if bCompTest then
EgtOutLog( ' ALL OK')
end
end
-- eseguo ordinamento
table.sort( vProcToSort, CompareFeatures)
return vProcToSort
end
-------------------------------------------------------------------------------------------------------------
-- esegue le strategie migliori che ha precedentemente scelto
local function CalculateMachinings( vProc, Part)
local bAreAllApplyOk = true
-- applico le strategie scelte
for i = 1, #vProc do
-- processo tutte le feature attive applicando le lavorazioni
local Proc = vProc[i]
if Proc.nFlg ~= 0 and Proc.ChosenStrategy then
-- carico file script strategia (non serve verificare presenza del file perchè già fatto durante scelta strategia)
local StrategyScriptName = Proc.ChosenStrategy.sStrategyId .. '\\' .. Proc.ChosenStrategy.sStrategyId
local StrategyScript = require( StrategyScriptName)
-- eseguo la strategia e si applicano le lavorazioni. Si passa la Proc e i parametri personalizzati
bAreAllApplyOk, _ = StrategyScript.Make( true, Proc, Part, Proc.ChosenStrategy.Parameters)
end
end
return MACHININGS
end
-------------------------------------------------------------------------------------------------------------
local function PrintFeatures( vProc, Part)
if vProc then
EgtOutLog( ' RawBox=' .. tostring( Part.b3Box))
for i = 1, #vProc do
local Proc = vProc[i]
local sOut = string.format( ' Id=%3d Grp=%1d Prc=%3d Flg=%2d Fct=%2d TopoName=%s',
Proc.id, Proc.nGrp, Proc.nPrc, Proc.nFlg, Proc.nFct, Proc.Topology.sName or '')
EgtOutLog( sOut)
end
end
end
-------------------------------------------------------------------------------------------------------------
function BeamExec.GetProcessings( PROCESSINGS, PARTS)
-- recupero tutti i processing di tutti i pezzi in tutte le rotazioni
-- TODO calcolo tempi da rimuovere o lasciare solo per debug
-- if EgtGetDebugLevel() >= 3 then
-- EgtStartCounter()
-- end
for nPart = 1, #PARTS do
if not PARTS[nPart].id and PARTS[nPart].b3Raw:getDimX() < BeamData.dMinRaw then break end
local vProcRot = {}
for dRotIndex = 1, 4 do
-- si calcolano le feature solo se la rotazione può essere presa in considerazione
if PARTS[nPart].CombinationList.Rotations[dRotIndex] == 1 then
-- recupero le feature di lavorazione della trave
table.insert( vProcRot, CollectFeatures( PARTS[nPart]))
-- recupero informazioni ausiliarie feature e dipendenze tra feature stesse
-- TODO le dipendenze cambiano in base alla rotazione del pezzo? probabilmente no
vProcRot[dRotIndex] = GetFeatureInfoAndDependency( vProcRot[dRotIndex], PARTS[nPart])
-- calcola le strategie applicabili ( presenti nella tabella vProcRot[dRotIndex].AvailableStrategies)
vProcRot[dRotIndex] = CalculateStrategies( vProcRot[dRotIndex], PARTS[nPart])
else
-- inserisco una tabella vuota
table.insert( vProcRot, {})
end
-- ruoto il grezzo per calcolare la fattibilità delle lavorazioni nella prossima rotazione
-- vettore movimento grezzi per rotazione di 90deg ogni step
local dDeltaYZ = PARTS[nPart].b3Raw:getDimY() - PARTS[nPart].b3Raw:getDimZ()
local vtMove = Vector3d( 0, dDeltaYZ / 2 * EgtIf( BeamData.RIGHT_LOAD, -1, 1), dDeltaYZ / 2)
local bPreMove = ( dDeltaYZ < 0)
-- ruoto le travi della fase corrente
if bPreMove then
EgtMoveRawPart( PARTS[nPart].idRaw, vtMove)
end
EgtRotateRawPart( PARTS[nPart].idRaw, X_AX(), EgtIf( BeamData.RIGHT_LOAD, -90, 90))
if not bPreMove then
EgtMoveRawPart( PARTS[nPart].idRaw, vtMove)
end
-- aggiorno info pezzo
PARTS[nPart].b3Raw = EgtGetRawPartBBox( PARTS[nPart].idRaw)
PARTS[nPart].b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PARTS[nPart].id, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD)
end
local Part = {}
Part.Rotation = vProcRot
-- recupero le feature di lavorazione della trave
table.insert( PROCESSINGS, Part)
end
-- TODO calcolo tempi da rimuovere o lasciare solo per debug
-- if EgtGetDebugLevel() >= 3 then
-- local timeGetProcessings = EgtStopCounter()
-- timeGetProcessings = timeGetProcessings + 0
-- EgtOutBox( timeGetProcessings, 'GetProcessings calculation time')
-- EgtStartCounter()
-- end
return PROCESSINGS
end
-------------------------------------------------------------------------------------------------------------
-- funzione che decide la migliore tra le combinazioni di rotazione disponibili
local function GetBestCombination( ListToCompare)
-- TODO da completare
local nIndexBestCombination = 1
if #ListToCompare == 1 then
nIndexBestCombination = 1
else
for ListIndex = 2, #ListToCompare do
local bBestComplete = ListToCompare[nIndexBestCombination].nNotComplete == 0 and ListToCompare[nIndexBestCombination].nNotExecute == 0
local bOtherComplete = ListToCompare[ListIndex].nNotComplete == 0 and ListToCompare[ListIndex].nNotExecute == 0
-- prediligo combinazione completa
if bBestComplete and not bOtherComplete then
; -- la migliore resta la stessa
elseif not bBestComplete and bOtherComplete then
nIndexBestCombination = ListIndex
-- altrimenti guardo il voto
else
-- se stesso voto
if ListToCompare[nIndexBestCombination].dTotalRating == ListToCompare[ListIndex].dTotalRating then
-- TODO il voto dovrebbe essere considerato già pesando le rotazioni, con un coefficiente o un peso fisso aggiuntivo
-- scelgo soluzione con meno rotazioni
if ListToCompare[nIndexBestCombination].nRotations > ListToCompare[ListIndex].nRotations then
nIndexBestCombination = ListIndex
-- se anche le rotazioni sono le stesse, prendo la soluzione con più lavorazioni alla fine
elseif #ListToCompare[nIndexBestCombination].Rot0 < #ListToCompare[ListIndex].Rot0 then
nIndexBestCombination = ListIndex
end
-- se voto diverso
else
-- scelgo soluzione con voto più alto
if ListToCompare[nIndexBestCombination].dTotalRating < ListToCompare[ListIndex].dTotalRating then
nIndexBestCombination = ListIndex
end
end
end
end
end
ChosenCombination = ListToCompare[nIndexBestCombination]
return ChosenCombination
end
-------------------------------------------------------------------------------------------------------------
-- funzione che decide le combinazioni di rotazione per lavorare la trave
local function GetProcessingListFromCombination( BestCombination)
local vProc = {}
local bSomeFeatureDown = false
local bSomeFeatureSide = false
local nInitialPosition = BestCombination.nIndexRotation
-- aggiungo processing da fare in fase ribaltata
if #BestCombination.Rot180 > 0 then
bSomeFeatureDown = true
for i = 1, #BestCombination.Rot180 do
BestCombination.Rot180[i].bDown = true
table.insert( vProc, BestCombination.Rot180[i])
end
end
-- aggiungo processing da fare in fase ruotata
if #BestCombination.Rot90 > 0 then
bSomeFeatureSide = true
for i = 1, #BestCombination.Rot90 do
BestCombination.Rot90[i].bSide = true
table.insert( vProc, BestCombination.Rot90[i])
end
end
-- aggiungo processing da fare in ultima fase
if #BestCombination.Rot0 > 0 then
for i = 1, #BestCombination.Rot0 do
table.insert( vProc, BestCombination.Rot0[i])
end
end
return vProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide
end
-------------------------------------------------------------------------------------------------------------
-- funzione che ritorna la Proc nella rotazione scelta
local function GetProcBestMachRotationFromList( ListToCompare)
local Data = {}
local Proc = {}
local nIndexChosenProcInRot = 1
-- se ci sono almeno 2 possibili soluzioni, scelgo la posizione migliore di lavorazione
if #ListToCompare > 1 then
-- formatto lista strategie disponibili come se le aspetta la funzione di compare
local AvailableStrategiesInRot = {}
for i = 1, #ListToCompare do
table.insert( AvailableStrategiesInRot, ListToCompare[i][1].ChosenStrategy)
end
for nIndexCurrentStrategy = 1, #AvailableStrategiesInRot do
-- la scelta tra le differenti strategie tra le rotazioni utilizza gli stessi criteri della scelta strategie all'interno della feature stessa
nIndexChosenProcInRot = GetIndexBestStrategyFromComparison( AvailableStrategiesInRot, nIndexCurrentStrategy, nIndexChosenProcInRot)
end
-- altrimenti prendo la prima
else
nIndexChosenProcInRot = 1
end
Proc = ListToCompare[nIndexChosenProcInRot][1]
Data.nIndexRotation = ListToCompare[nIndexChosenProcInRot].nRotation
Data.dCompositeRating = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.dCompositeRating
Data.bComplete = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.sStatus == 'Completed'
Data.bNotComplete = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.sStatus == 'Not-Completed'
Data.bNotApplicable = ListToCompare[nIndexChosenProcInRot][1].ChosenStrategy.Result.sStatus == 'Not-Applicable'
return Proc, Data
end
-------------------------------------------------------------------------------------------------------------
-- funzione che calcola le combinazioni di rotazione per lavorare la trave e sceglie la migliore
local function GetBestResultFromCombinationsMatrix( ProcessingsOnPart, PartInfo)
local BestCombination = {}
local CombinationsList = {}
-- scrittura nel log della matrice delle rotazioni
if EgtGetDebugLevel() >= 3 then
Logs.WriteMatrixLog( ProcessingsOnPart, PartInfo)
end
-- per ogni posizione di scarico
for nUnloadPos = 1, 4 do
-- calcolo per tutte le combinazioni disponibili precedentemente verificate
for i = 1, #PartInfo.CombinationList do
-- controllo che la combinazione abbia ultima fase come quella calcolata in fase di definizione combinazioni
if PartInfo.CombinationList[i].nUnloadPos == nUnloadPos then
local bRot90, bRot180
local SingleCombination = {}
SingleCombination.nRotations = 0
SingleCombination.dTotalRating = 0
SingleCombination.nComplete = 0
SingleCombination.nNotComplete = 0
SingleCombination.nNotExecute = 0
SingleCombination.sBitIndexCombination = PartInfo.CombinationList[i].sBitIndexCombination
SingleCombination.nUnloadPos = nUnloadPos
-- creo liste dei proc suddivisi per rotazione
SingleCombination.Rot0 = {}
SingleCombination.Rot90 = {}
SingleCombination.Rot180 = {}
-- ciclo su tutte le feature
for nProc = 1, #ProcessingsOnPart.Rotation[1] do
-- ciclo sulle rotazioni
local nNextRot = nUnloadPos
local ResultsList = {}
for nRotation = 1, 3 do
-- se rotazione abilitata da combinazione
if string.sub( PartInfo.CombinationList[i].sBitIndexCombination, nNextRot, nNextRot) == '1' then
-- controllo se è stata scelta una strategia
if ProcessingsOnPart.Rotation[nNextRot][nProc].ChosenStrategy then
local Proc = {}
Proc.nRotation = nNextRot
table.insert( Proc, ProcessingsOnPart.Rotation[nNextRot][nProc])
table.insert( ResultsList, Proc)
end
end
nNextRot = EgtIf( nNextRot + 1 > 4, nNextRot + 1 - 4, nNextRot + 1)
end
-- se la feature può essere lavorata in almeno una rotazione
if #ResultsList > 0 then
local Proc, Data = GetProcBestMachRotationFromList( ResultsList)
-- inserisco la Proc nell'apposita lista
if Data.nIndexRotation == nUnloadPos then
table.insert( SingleCombination.Rot0, Proc)
elseif Data.nIndexRotation == nUnloadPos + 1 then
table.insert( SingleCombination.Rot90, Proc)
bRot90 = true
else
table.insert( SingleCombination.Rot180, Proc)
bRot180 = true
end
SingleCombination.dTotalRating = SingleCombination.dTotalRating + Data.dCompositeRating
SingleCombination.nComplete = SingleCombination.nComplete + EgtIf( Data.bComplete, 1, 0)
SingleCombination.nNotComplete = SingleCombination.nNotComplete + EgtIf( Data.bNotComplete, 1, 0)
SingleCombination.nNotExecute = SingleCombination.nNotExecute + EgtIf( Data.bNotApplicable, 1, 0)
SingleCombination.nIndexRotation = nUnloadPos
else
SingleCombination.nNotExecute = SingleCombination.nNotExecute + 1
end
end
-- aggiungo rotazioni
SingleCombination.nRotations = EgtIf( bRot90, 1, 0) + EgtIf( bRot180, 1, 0)
-- aggiungo la combinazione all'elenco delle combinazioni disponibili
table.insert( CombinationsList, SingleCombination)
end
end
end
-- ci deve essere almeno una combinazione, altrimenti errore
if #CombinationsList < 1 then
error( 'UNEXPECTED ERROR: NO combinations available')
end
BestCombination = GetBestCombination( CombinationsList)
-- scrittura nel log delle combinazioni possibili
if EgtGetDebugLevel() >= 3 then
Logs.WriteCombinationLog( CombinationsList, BestCombination)
end
local vFinalProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide = GetProcessingListFromCombination( BestCombination)
return vFinalProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide
end
-------------------------------------------------------------------------------------------------------------
function BeamExec.ProcessMachinings( PROCESSINGS, PARTS)
-- ciclo sui pezzi
local nTotErr = 0
local Stats = {}
local nOrd = 1
local ProcessCompleted = false
local nMaxLoops = 5
-- TODO da rimuovere o lasciare solo per debug
--if EgtGetDebugLevel() >= 3 then
-- EgtStartCounter()
--end
-- TODO da rimuovere o lasciare solo per debug
--if EgtGetDebugLevel() >= 3 then
-- local timeCollect = EgtStopCounter()
-- timeCollect = timeCollect + 0
-- EgtOutBox( timeCollect, 'Collect calculation time')
-- EgtStartCounter()
--end
-- TODO da rimuovere o lasciare solo per debug
--if EgtGetDebugLevel() >= 3 then
-- local timeMachining = EgtStopCounter()
-- timeMachining = timeMachining + 0
-- EgtOutBox( timeMachining, 'Machining calculation time')
--end
local nCurrLoop = 0
-- ciclo fino a che tutte le applicazioni delle lavorazioni sono corrette
while not ProcessCompleted do
-- aumento numero loop nel quale siamo
nCurrLoop = nCurrLoop + 1
-- ricerca strategia di lavorazione per ogni pezzo e applicazione lavorazioni
for nPart = 1, #PARTS do
-- calcolo della migliore strategia per ogni rotazione del pezzo
for dRotIndex = 1, 4 do
PROCESSINGS[nPart].Rotation[dRotIndex] = GetBestStrategy( PROCESSINGS[nPart].Rotation[dRotIndex])
end
-- scrittura nel log del risultato della scelta della strategia migliore tra quelle disponibili
if EgtGetDebugLevel() >= 3 then
Logs.WriteFeaturesLog( PROCESSINGS[nPart], PARTS[nPart])
end
-- si calcola la combinazione di lavorazione migliore
local vProc, nInitialPosition, bSomeFeatureDown, bSomeFeatureSide = GetBestResultFromCombinationsMatrix( PROCESSINGS[nPart], PARTS[nPart])
-- salvo sul PART la posizione di partenza che è stata scelta
PARTS[nPart].nInitialPosition = nInitialPosition
-- debug
if EgtGetDebugLevel() >= 1 then
PrintFeatures( vProc, PARTS[nPart])
end
EgtOutLog( ' *** AddMachinings ***', 1)
-- ordino le features
vProc = OrderFeatures( vProc)
-- esegue le strategie migliori che ha precedentemente scelto e salva le lavorazioni nella lista globale
MACHININGS = CalculateMachinings( vProc, PARTS[nPart])
-- TODO riordinare lavorazioni ottimizzando cambio utensile/spezzone ecc..., mantenendo dipendenze definite prima
-- ordino le lavorazioni
-- MACHININGS = OrderMachining( MACHININGS, PARTS[nPart])
-- aggiungo la fase, se non è la prima
if nOrd == 1 then
EgtSetCurrPhase( 1)
else
BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, 0)
end
local bAreAllMachiningApplyOk
local sErr
local bSplitAlreadyExecuted = false
local bSplitExecutedOnRot = false
local nPhase = EgtGetCurrPhase()
local nDispId = EgtGetPhaseDisposition( nPhase)
EgtSetInfo( nDispId, 'TYPE', 'START')
EgtSetInfo( nDispId, 'ORD', nOrd)
EgtOutLog( ' *** Phase=' .. tostring( nPhase) .. ' Raw=' .. tostring( PARTS[nPart].idRaw) .. ' Part=' .. tostring( PARTS[nPart].id) .. ' ***', 1)
-- creazione effettiva delle lavorazioni
-- se c'è almeno una lavorazione in posizionamento con trave ribaltata
if bSomeFeatureDown then
local nRotation = EgtIf( nInitialPosition + 2 > 4, nInitialPosition + 2 - 4, nInitialPosition + 2)
BeamLib.RotatePart( PARTS[nPart], nRotation)
EgtSetInfo( nDispId, 'ROT', -2)
bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'DOWN')
bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot
end
-- se c'è almeno una lavorazione in posizionamento con trave ribaltata
if bSomeFeatureSide then
-- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima
if bSomeFeatureDown then
BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, 0)
nPhase = EgtGetCurrPhase()
nDispId = EgtGetPhaseDisposition( nPhase)
EgtSetInfo( nDispId, 'ORD', nOrd)
-- se c'è già stata seoparazione
if bSplitAlreadyExecuted then
EgtSetInfo( nDispId, 'TYPE', 'MID2')
else
EgtSetInfo( nDispId, 'TYPE', 'MID')
end
end
local nRotation = EgtIf( nInitialPosition + 1 > 4, nInitialPosition + 1 - 4, nInitialPosition + 1)
BeamLib.RotatePart( PARTS[nPart], nRotation)
EgtSetInfo( nDispId, 'ROT', -1)
bAreAllMachiningApplyOk, sErr, bSplitExecutedOnRot = MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'SIDE')
bSplitAlreadyExecuted = bSplitAlreadyExecuted or bSplitExecutedOnRot
end
-- se ci sono state lavorazioni in rotazione precedente devo creare altra fase. Altrimenti già creata da prima
if bSomeFeatureDown or bSomeFeatureSide then
BeamLib.AddPhaseWithRawParts( PARTS[nPart].idRaw, BeamData.ptOriXR, BeamData.dPosXR, 0)
nPhase = EgtGetCurrPhase()
nDispId = EgtGetPhaseDisposition( nPhase)
EgtSetInfo( nDispId, 'ORD', nOrd)
-- se c'è già stata seoparazione
if bSplitAlreadyExecuted then
EgtSetInfo( nDispId, 'TYPE', 'END2')
else
EgtSetInfo( nDispId, 'TYPE', 'MID')
end
end
BeamLib.RotatePart( PARTS[nPart], nInitialPosition)
-- aggiunta lavorazioni in ultima fase
MachiningLib.AddOperations( MACHININGS, PARTS[nPart], 'STD')
EgtOutLog( ' *** End AddMachinings ***', 1)
-- azzero lavorazioni per pezzo successivo
MACHININGS = {}
-- indice pezzo successivo
nOrd = nOrd + 1
end
-- ===== finiti i pezzi, si scarica il restante =====
local idRestPart = EgtGetNextRawPart( PARTS[#PARTS].idRaw)
if idRestPart and EgtGetRawPartBBox( idRestPart):getDimX() >= BeamData.dMinRaw then
BeamLib.AddPhaseWithRawParts( idRestPart, BeamData.ptOriXR, BeamData.dPosXR, 0)
local nPhase = EgtGetCurrPhase()
local nDispId = EgtGetPhaseDisposition( nPhase)
EgtSetInfo( nDispId, 'TYPE', 'REST')
EgtSetInfo( nDispId, 'ORD', nOrd)
end
-- Aggiornamento finale di tutto
EgtSetCurrPhase( 1)
local bApplOk, sApplErrors, sApplWarns = EgtApplyAllMachinings()
-- TODO a cosa serve questo ricalcolo? Con nuovo sistema si può eliminare?
-- eventuale ricalcolo per macchine tipo PF (ma tengo warning del primo calcolo)
if EgtExistsInfo( EgtGetCurrMachGroup(), 'RECALC') then
EgtOutLog( ' **** RECALC ****')
bApplOk, sApplErrors, _ = EgtApplyAllMachinings()
EgtRemoveInfo( EgtGetCurrMachGroup(), 'RECALC')
end
if not bApplOk then
nTotErr = nTotErr + 1
table.insert( Stats, {nErr = 1, sMsg=sApplErrors, nRot=0, idCut=0, idTask=0})
elseif sApplWarns and #sApplWarns > 0 then
local vLine = EgtSplitString( sApplWarns, '\r\n')
for i = 1, #vLine do
local nPos = vLine[i]:find( '(WRN', 1, true)
if nPos then
local sData = vLine[i]:sub( nPos + 1, -2)
local vVal = EgtSplitString( sData, ',')
local nWarn = EgtGetVal( vVal[1] or '', 'WRN', 'i')
local nCutId = EgtGetVal( vVal[2] or '', 'CUTID', 'i')
if nWarn and nCutId then
table.insert( Stats, { nErr=-nWarn, sMsg=vLine[i], nRot=0, idCut=nCutId, idTask=0})
end
end
end
end
-- TODO per il momento non si cicla mai una seconda volta. Controllare anche parametro modalità scelta strategia
-- Gestione da fare completamente. La strategia che ha generato una lavorazione con errore deve essere annullata
-- si fa un reset del MACH e poi si ritorna a inizio ciclo e si rifà tutto
-- se i cicli superano il massimo si esce (nell'ultimo ciclo possibile si potrebbe escludere la feature e basta, anche se avesse altre strategie da provare)
if true or nCurrLoop > nMaxLoops then
ProcessCompleted = true
end
end
return ( nTotErr == 0), Stats
end
-------------------------------------------------------------------------------------------------------------
return BeamExec