-- WinExec.lua by Egalware s.r.l. 2024/06/13 -- Libreria esecuzione lavorazioni per Serramenti -- Tabella per definizione modulo local WinExec = {} -- Include require( 'EgtBase') -- Carico i dati globali local WinData = require( 'WinData') local WinLib = require( 'WinLib') local ID = require( 'Identity') local FeatureData = require( 'FeatureData') local MachiningLib = require( 'MachiningLib') -- carico librerie di lavorazione _G.package.loaded.Drilling = nil _G.package.loaded.Cutting = nil _G.package.loaded.Pocketing = nil _G.package.loaded.Milling = nil _G.package.loaded.Profiling = nil local Drilling = require( 'Drilling') local Cutting = require( 'Cutting') local Pocketing = require( 'Pocketing') local Milling = require( 'Milling') local Profiling = require( 'Profiling') EgtOutLog( ' WinExec started', 1) EgtMdbSave() ------------------------------------------------------------------------------------------------------------- -- *** variabili globali *** ------------------------------------------------------------------------------------------------------------- TOOLS = nil MACHININGS = nil ------------------------------------------------------------------------------------------------------------- -- *** COSTANTI *** TODO -> DA SPOSTARE IN WINDATA??? ------------------------------------------------------------------------------------------------------------- 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 WinExec.GetToolsFromDB() -- TODO gli utensili profilati devono essere messi in una lista a parte ad accesso diretto TOOLS['Prof1'] in modo da non dover scorrere la lista -- dato che saranno molti e l'applicazione sarà diretta. Non serve ciclarli -- 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 WinData 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 = WinData.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 ------------------------------------------------------------------------------------------------------------- -- *** Inserimento delle lavorazioni nelle travi *** ------------------------------------------------------------------------------------------------------------- local function CollectFeatures( vProc, Part) -- recupero le feature local LayerId = {} LayerId[1] = WinLib.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 sType = EgtGetInfo( ProcId, 'FEATURE_TYPE', 's') local nDo = EgtGetInfo( ProcId, 'DO', 'i') or 1 if sType and nDo == 1 then local Proc = {} Proc.idPart = Part.id Proc.id = ProcId Proc.nFlg = 1 Proc.sType = sType Proc.b3Box = EgtGetBBoxGlob( ProcId, GDB_BB.STANDARD) Proc.nPhase = EgtGetInfo( ProcId, 'PHASE', 'i') or nil -- informazioni facce Proc.AffectedFaces = WinLib.GetAffectedFaces( Proc, Part) -- se esiste la geometria if Proc.b3Box and not Proc.b3Box:isEmpty() then -- calcolo dati specifici per tipologia di feature / lavorazione -- se foro if ID.IsDrilling( Proc) then Proc = FeatureData.GetDrillingData( Proc) end -- se taglio if ID.IsCutting( Proc) then Proc = FeatureData.GetCuttingData( Proc) end -- se fresatura if ID.IsMilling( Proc) then Proc = FeatureData.GetMillingData( Proc) end -- se svuotatura if ID.IsPocketing( Proc) then Proc = FeatureData.GetPocketingData( Proc) end -- se profilatura if ID.IsProfiling( Proc) then Proc = FeatureData.GetProfilingData( Proc) end -- inserisco feature in lista table.insert( vProc, Proc) 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 GetFeatureInfoAndDependency( vProc, Part) -- ciclo tutte le feature for i = 1, #vProc do local Proc = vProc[i] -- controllo la feature con tutte le altre per recuperare le dipendenze for j = 1, #vProc do -- non si controlla la feature con se stessa if i ~= j then local ProcB = vProc[j] -- TODO dipendenze da controllare : -- * gruppo di forature con aggregato 2/3 uscite -- * Se 'Left' e 'Out' hanno stesso profilo, vanno concatenati (anche 'Right' se consentito dalle dimensioni) end end end return vProc end ------------------------------------------------------------------------------------------------------------- -- Ordina le feature in base a fase di lavorazione -- L'ordine è indicativamente: -- 1) Tagli di testa -- 2) Fori -- 3) Scassi serratura/maniglia -- 4) Profili testa -- 5) Profili longitudinali -- 6) Incontri/scontri -- TODO Ordinamento da fare local function OrderMachining( MACHININGS) -- local vProcToSort = vProc -- -- funzione di confronto. TRUE = B1 prima di B2. FALSE = B2 prima di B1 -- local function CompareFeatures( B1, B2) -- -- se secondo disabilitato, va lasciato dopo -- if B1.nFlg ~= 0 and B2.nFlg == 0 then -- return true -- elseif B1.nPhase and B2.nPhase and B1.nPhase < B2.nPhase then -- return true -- elseif B1.nPhase and B2.nPhase and B2.nPhase < B1.nPhase then -- return true -- -- 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 true end ------------------------------------------------------------------------------------------------------------- -- applica le lavorazioni local function AddMachinings( vProc, PARTS) local bMachiningOk = true for nFeatureIndex = 1, #vProc do local Proc = vProc[nFeatureIndex] local Part = PARTS[vProc[nFeatureIndex].nIndexPiece] if ID.IsDrilling( Proc) then bMachiningOk = Drilling.Make( Proc, Part) end -- se taglio if ID.IsCutting( Proc) then bMachiningOk = Cutting.Make( Proc, Part) end -- se fresatura if ID.IsMilling( Proc) then bMachiningOk = Milling.Make( Proc, Part) end -- se svuotatura if ID.IsPocketing( Proc) then bMachiningOk = Pocketing.Make( Proc, Part) end -- se profilatura if ID.IsProfiling( Proc) then bMachiningOk = Profiling.Make( Proc, Part) end end return bMachiningOk end ------------------------------------------------------------------------------------------------------------- local function PrintFeatures( vProc, PARTS) EgtOutLog( ' === PARTS ====') for i = 1, #PARTS do EgtOutLog( ' RawBox(' .. tostring( i) .. ') =' .. tostring( PARTS[i].RawBox)) end EgtOutLog( ' === FEATURES ====') for i = 1, #vProc do local Proc = vProc[i] local sOut = string.format( 'Id=%3d Flg=%2d Type=%s', Proc.id, Proc.nFlg, Proc.sType) EgtOutLog( sOut) end end ------------------------------------------------------------------------------------------------------------- local function CheckAndMovePawPart( nIdRawToMove, vtMove) EgtMoveRawPart( nIdRawToMove, vtMove) end ------------------------------------------------------------------------------------------------------------- function WinExec.ProcessFeatures( PARTS) -- ciclo sui pezzi local nTotErr = 0 local Stats = {} local vProc = {} MACHININGS = {} -- si recuperano tutte le feature di tutti i pezzi in una lista unica for nPart = 1, #PARTS do -- recupero le feature di lavorazione della trave vProc = CollectFeatures( vProc, PARTS[nPart]) -- recupero informazioni ausiliarie feature e dipendenze tra feature dello stesso pezzo vProc = GetFeatureInfoAndDependency( vProc, PARTS[nPart]) end -- debug if EgtGetDebugLevel() >= 1 then PrintFeatures( vProc, PARTS) end EgtOutLog( ' *** AddMachinings ***', 1) -- esegue le lavorazioni e le salva in lista AddMachinings( vProc, PARTS) -- ordina le lavorazioni OrderMachining( MACHININGS) -- scrivo lavorazioni prima fase EgtSetCurrPhase( 1) for i = 1, #MACHININGS do if MACHININGS[i].AuxInfo.nPhase == 1 then -- aggiunge effettivamente la lavorazione e restituisce gli ingombri local bIsApplyOk, sErr, b3MachEncumbrance = MachiningLib.AddOperation( MACHININGS[i]) -- TODO ingombro lavorazione mi restituisce dati sbagliati. C'è un riferimento? -- se feature di testa, sposto testa in base a ingombro lavorazione if MACHININGS[i].Proc.bHeadProfile and b3MachEncumbrance then local nIndexPart = ID.GetPieceIndexPart( PARTS, MACHININGS[i].Proc.idPart) PARTS[nIndexPart].DispOffsets.Phase1.dOffsetX = b3MachEncumbrance:getMax():getX() - PARTS[nIndexPart].b3FinishedPart:getMin():getX() + 5 end end end -- TODO lo spostamento, oltre a controllare le collisioni con il profilo di testa, deve anche considerare il pinzaggio del pezzo nel suo insieme? -- Es.: basterebbe aggiungere un offset di 50mm per la testa, ma se metto 60mm, posso utilizzare una morsa in più che altrimenti avrei dovuto togliere per una collisione con una svuotatura -- sposto pezzo per permettere pinzaggio migliore e non aver colisione in testa for i = 1, #PARTS do -- TODO Sbaglia a calcolare ingombro!! --local vtMove = Vector3d( - PARTS[nIndexPart].DispOffsets.Phase1.dOffsetX, - PARTS[nIndexPart].DispOffsets.Phase1.dOffsetY, PARTS[nIndexPart].DispOffsets.Phase1.dOffsetZ) local vtMove = Vector3d( -60, 0, 0) if vtMove ~= V_NULL() then CheckAndMovePawPart( PARTS[i].idRaw, vtMove) end end -- scrivo lavorazioni seconda fase EgtSetCurrPhase( 2) for i = 1, #MACHININGS do if MACHININGS[i].AuxInfo.nPhase == 2 then -- aggiunge effettivamente la lavorazione local bIsApplyOk, sErr, b3MachEncumbrance = MachiningLib.AddOperation( MACHININGS[i]) -- TODO ingombro lavorazione mi restituisce dati sbagliati. C'è un riferimento? -- se feature di testa, sposto testa in base a ingombro lavorazione if MACHININGS[i].Proc.bHeadProfile and b3MachEncumbrance then local nIndexPart = ID.GetPieceIndexPart( PARTS, MACHININGS[i].Proc.idPart) PARTS[nIndexPart].DispOffsets.Phase1.dOffsetX = PARTS[nIndexPart].b3FinishedPart:getMax():getX() - b3MachEncumbrance:getMin():getX() + 5 end end end -- sposto pezzo per permettere pinzaggio migliore e non aver colisione in testa for i = 1, #PARTS do -- TODO Sbaglia a calcolare ingombro!! -- local vtMove = Vector3d( PARTS[nIndexPart].DispOffsets.Phase2.dOffsetX, PARTS[nIndexPart].DispOffsets.Phase2.dOffsetY, PARTS[nIndexPart].DispOffsets.Phase2.dOffsetZ) local vtMove = Vector3d( 60, 0, 0) if vtMove ~= V_NULL() then CheckAndMovePawPart( PARTS[i].idRaw, vtMove) end end EgtOutLog( ' *** End AddMachinings ***', 1) -- Aggiornamento finale di tutto EgtSetCurrPhase( 1) local bApplOk, sApplErrors, sApplWarns = EgtApplyAllMachinings() if not bApplOk then nTotErr = nTotErr + 1 table.insert( Stats, {nErr = 1, sMsg=sApplErrors}) end return ( nTotErr == 0), Stats end ------------------------------------------------------------------------------------------------------------- return WinExec