-- MachiningLib.lua by Egalware s.r.l. 2024/04/02 -- Libreria ricerca lavorazioni per Travi -- 2024/04/02 PRIMA VERSIONE CALCOLO LAVORAZIONI CON STRATEGIE -- Tabella per definizione modulo local MachiningLib = {} -- Include require( 'EgtBase') -- Carico i dati globali local BeamData = require( 'BeamDataNew') local BeamLib = require( 'BeamLib') local FeatureLib = require( 'FeatureLib') local BCS = require( 'BasicCustomerStrategies') local PreSimulationLib = require( 'PreSimulationLib') local LeadInOutLib = require( 'LeadInOutLib') EgtOutLog( ' MachiningLib started', 1) --------------------------------------------------------------------- -- TODO da considerare solo angolo 2D?? local function GetToolEntryAngle( Proc, vtTool) local Angle = {} local dSinAngle = -10 * GEO.EPS_SMALL local vtNorm if Proc.AffectedFaces.bTop then vtNorm = Z_AX() dSinAngle = max( dSinAngle, vtTool * vtNorm) end if Proc.AffectedFaces.bBottom then vtNorm = -Z_AX() dSinAngle = max( dSinAngle, vtTool * vtNorm) end if Proc.AffectedFaces.bFront then vtNorm = -Y_AX() dSinAngle = max( dSinAngle, vtTool * vtNorm) end if Proc.AffectedFaces.bBack then vtNorm = Y_AX() dSinAngle = max( dSinAngle, vtTool * vtNorm) end if Proc.AffectedFaces.bLeft then vtNorm = -X_AX() dSinAngle = max( dSinAngle, vtTool * vtNorm) end if Proc.AffectedFaces.bRight then vtNorm = X_AX() dSinAngle = max( dSinAngle, vtTool * vtNorm) end local dCosAngle = sqrt( 1 - sqr( dSinAngle)) local dAngle = acos( dCosAngle) local dTanAngle if dAngle ~= 0 and dAngle ~= 90 then dTanAngle = sqrt( 1 - dCosAngle * dCosAngle) / dCosAngle end Angle.dValue = dAngle Angle.dSin = dSinAngle Angle.dCos = dCosAngle Angle.dTan = dTanAngle return Angle end ------------------------------------------------------------------------------------------------------------- function MachiningLib.StartsLeftSide( Machining) local bStartsLeftSide = ( Machining.vtEdgeDirection:getX() > 10 * GEO.EPS_SMALL and not Machining.bInvert) or ( not( Machining.vtEdgeDirection:getX() > 10 * GEO.EPS_SMALL) and Machining.bInvert) return bStartsLeftSide end ------------------------------------------------------------------------------------------------------------- -- TODO valutare se c'è un modo più preciso di prevedere i casi in cui le lavorazioni dopo separazione sono da saltare function MachiningLib.CanMoveAfterSplitcut( dLengthOnX, Part) -- TODO controllare. Serve passare anche la posizione per capire il restante. Considerare anche HCING e TCING local bCanMoveAfterSplitcut = ( Part.dLength > BeamData.dMinRaw + 10 * GEO.EPS_SMALL) or ( dLengthOnX < 0.7 * Part.dLength - 10 * GEO.EPS_SMALL and Part.dLength - dLengthOnX > BeamData.dMinClampRaw) return bCanMoveAfterSplitcut end ------------------------------------------------------------------------------------------------------------- function MachiningLib.CanExtendAfterTail( sCanDamageNextPiece, Part) local bCanExtendAfterTail = false if sCanDamageNextPiece == 'ALWAYS' then bCanExtendAfterTail = true elseif sCanDamageNextPiece == 'ONLY_IF_RAWPART' then bCanExtendAfterTail = Part.bIsLastPart end return bCanExtendAfterTail end ------------------------------------------------------------------------------------------------------------- function MachiningLib.GetMachiningSteps( dMachiningDepth, dStep) local MachiningSteps = {} MachiningSteps.dStep = 0 MachiningSteps.nCount = ceil( ( dMachiningDepth - 50 * GEO.EPS_SMALL) / dStep) if MachiningSteps.nCount > 1 then MachiningSteps.dStep = ( dMachiningDepth - dStep) / ( MachiningSteps.nCount - 1) else MachiningSteps.dStep = dMachiningDepth MachiningSteps.nCount = 1 end return MachiningSteps end ------------------------------------------------------------------------------------------------------------- function MachiningLib.GetSplitMachinings( Machinings, SplittingPoints, Part) for i = #Machinings, 1, -1 do local nParts = #SplittingPoints + 1 local dEdgeMaxX = Machinings[i].ptEdge1:getX() local dEdgeMinX = Machinings[i].ptEdge2:getX() if Machinings[i].ptEdge1:getX() < Machinings[i].ptEdge2:getX() - 10 * GEO.EPS_SMALL then dEdgeMaxX = Machinings[i].ptEdge2:getX() dEdgeMinX = Machinings[i].ptEdge1:getX() end local OriginalLeadIn = BeamLib.TableCopyDeep( Machinings[i].LeadIn) local OriginalLeadOut = BeamLib.TableCopyDeep( Machinings[i].LeadOut) local sOriginalStage = Machinings[i].sStage local LeadInForSplit local LeadOutForSplit if Machinings[i].LeadInForSplit then LeadInForSplit = BeamLib.TableCopyDeep( Machinings[i].LeadInForSplit) end if Machinings[i].LeadOutForSplit then LeadOutForSplit = BeamLib.TableCopyDeep( Machinings[i].LeadOutForSplit) end if FeatureLib.IsMachiningLong( Machinings[i].dLengthOnX, Part) then local nCurrentMachiningIndex = i -- lo spezzone attivo è quello precedente al punto di spezzatura corrente for j = 1, nParts do -- check ultimo segmento della lavorazione (NON della feature) local bIsLastSegment = ( nParts == 1) or ( ( ( j ~= 1) and SplittingPoints[j - 1]:getX() > dEdgeMinX + 10 * GEO.EPS_SMALL) and ( j == nParts or SplittingPoints[j]:getX() < dEdgeMinX + 10 * GEO.EPS_SMALL)) -- se non è l'ultimo segmento della lavorazione, il punto di spezzatura deve essere all'interno del lato che si sta lavorando if ( j ~= nParts and ( SplittingPoints[j]:getX() > dEdgeMinX + 10 * GEO.EPS_SMALL and SplittingPoints[j]:getX() < dEdgeMaxX - 10 * GEO.EPS_SMALL)) or bIsLastSegment then if j > 1 then nCurrentMachiningIndex = nCurrentMachiningIndex + 1 table.insert( Machinings, nCurrentMachiningIndex, BeamLib.TableCopyDeep( Machinings[i])) Machinings[nCurrentMachiningIndex].LeadIn = BeamLib.TableCopyDeep( OriginalLeadIn) Machinings[nCurrentMachiningIndex].LeadOut = BeamLib.TableCopyDeep( OriginalLeadOut) end local dStartAddLength = OriginalLeadIn.dStartAddLength local dEndAddLength = OriginalLeadOut.dEndAddLength if MachiningLib.StartsLeftSide( Machinings[i]) then dStartAddLength, dEndAddLength = dEndAddLength, dStartAddLength end if j == 1 then dEndAddLength = - ( SplittingPoints[j]:getX() - dEdgeMinX) + BeamData.MILL_OVERLAP if LeadOutForSplit then Machinings[nCurrentMachiningIndex].LeadOut = BeamLib.TableCopyDeep( LeadOutForSplit) end Machinings[nCurrentMachiningIndex].ptCenter = Point3d( SplittingPoints[j]:getX() + ( dEdgeMaxX - SplittingPoints[j]:getX()) / 2, 0, 0) elseif j == nParts then dStartAddLength = - ( dEdgeMaxX - SplittingPoints[j - 1]:getX()) + BeamData.MILL_OVERLAP if LeadInForSplit then Machinings[nCurrentMachiningIndex].LeadIn = BeamLib.TableCopyDeep( LeadInForSplit) end Machinings[nCurrentMachiningIndex].ptCenter = Point3d( dEdgeMinX + ( SplittingPoints[j - 1]:getX() - dEdgeMinX) / 2, 0, 0) else dStartAddLength = - ( dEdgeMaxX - SplittingPoints[j - 1]:getX()) + BeamData.MILL_OVERLAP dEndAddLength = - ( SplittingPoints[j]:getX() - dEdgeMinX) + BeamData.MILL_OVERLAP if LeadInForSplit then Machinings[nCurrentMachiningIndex].LeadIn = BeamLib.TableCopyDeep( LeadInForSplit) end if LeadOutForSplit then Machinings[nCurrentMachiningIndex].LeadOut = BeamLib.TableCopyDeep( LeadOutForSplit) end Machinings[nCurrentMachiningIndex].ptCenter = Point3d( SplittingPoints[j]:getX() + ( SplittingPoints[j - 1]:getX() - SplittingPoints[j]:getX()) / 2, 0, 0) end if MachiningLib.StartsLeftSide( Machinings[nCurrentMachiningIndex]) then dStartAddLength, dEndAddLength = dEndAddLength, dStartAddLength local LeadInCopy = BeamLib.TableCopyDeep( Machinings[nCurrentMachiningIndex].LeadIn) Machinings[nCurrentMachiningIndex].LeadIn = BeamLib.TableCopyDeep( Machinings[nCurrentMachiningIndex].LeadOut) Machinings[nCurrentMachiningIndex].LeadOut = BeamLib.TableCopyDeep( LeadInCopy) end Machinings[nCurrentMachiningIndex].LeadIn.dStartAddLength = dStartAddLength Machinings[nCurrentMachiningIndex].LeadOut.dEndAddLength = dEndAddLength end if not bIsLastSegment then Machinings[nCurrentMachiningIndex].sStage = '' else Machinings[nCurrentMachiningIndex].sStage = sOriginalStage end Machinings[nCurrentMachiningIndex].nFeatureSegment = j Machinings[nCurrentMachiningIndex].dLengthToMachine = Machinings[nCurrentMachiningIndex].dEdgeLength + Machinings[nCurrentMachiningIndex].LeadIn.dStartAddLength + Machinings[nCurrentMachiningIndex].LeadOut.dEndAddLength Machinings[nCurrentMachiningIndex].dTimeToMachine = MachiningLib.GetTimeToMachineAllStepsWithLeadInOut( Machinings[nCurrentMachiningIndex], Part) end -- anche le lavorazioni non splittate necessitano del segmento assegnato else local dRightAddLength = 0 local dLeftAddLength = 0 -- si considerano allungamenti e accorciamenti solo se la feature è orientata principalmente lungo X -- TODO il modo corretto è proiettare tutto, allungamenti e accorciamenti compresi, sulla faccia frontale if abs( Machinings[i].vtToolDirection:getX()) < 0.707 then dRightAddLength = OriginalLeadIn.dStartAddLength dLeftAddLength = OriginalLeadOut.dEndAddLength if MachiningLib.StartsLeftSide( Machinings[i]) then dRightAddLength, dLeftAddLength = dLeftAddLength, dRightAddLength end end for j = 1, nParts do local dNextSplitX = dEdgeMinX local dPreviousSplitX = dEdgeMaxX if j ~= 1 then dPreviousSplitX = SplittingPoints[j - 1]:getX() end if j ~= nParts then dNextSplitX = SplittingPoints[j]:getX() end if ( dEdgeMinX - dLeftAddLength) > dNextSplitX - 10 * GEO.EPS_SMALL and ( dEdgeMinX - dLeftAddLength) < dPreviousSplitX + 10 * GEO.EPS_SMALL then Machinings[i].nFeatureSegment = j Machinings[i].ptCenter = Point3d( dNextSplitX + ( dPreviousSplitX - dNextSplitX) / 2, 0, 0) end end Machinings[i].dLengthToMachine = Machinings[i].dEdgeLength + Machinings[i].LeadIn.dStartAddLength + Machinings[i].LeadOut.dEndAddLength Machinings[i].dTimeToMachine = MachiningLib.GetTimeToMachineAllStepsWithLeadInOut( Machinings[i], Part) end end return Machinings end ------------------------------------------------------------------------------------------------------------- -- funzione che ritorna la posizione nella quale si trova l'utensile cercato. Serve in caso l'utente voglia rispettare l'ordine degli utensili settato local function GetIndexToolInAvailableToolList( AvailableToolList, sToolName) for nIndex = 1, #AvailableToolList do if sToolName == AvailableToolList[nIndex].sName then return nIndex end end -- se non trovato return nil end ------------------------------------------------------------------------------------------------------------- -- funzione per verifica se l'utensile è presente tra gli utensili disponibili per la strategia local function IsToolInAvailableToolList( AvailableToolList, sTool) local bToolFound = false -- si cerca utensile con UUID se presente for nIndex = 1, #AvailableToolList do if AvailableToolList[nIndex].sUUID then if sTool.sUUID == AvailableToolList[nIndex].sUUID then bToolFound = true break end end end -- se non trovato con UUID, allora ci cerca per nome for nIndex = 1, #AvailableToolList do if AvailableToolList[nIndex].sName then if sTool.sName == AvailableToolList[nIndex].sName then bToolFound = true break end end end return bToolFound end ------------------------------------------------------------------------------------------------------------- -- in base a tipo testa e angolo soglia, verifica se la faccia è troppo inclinata per la lavorazione scelta (per DownUp passare vtNFace opposta) local function IsFaceZOutOfRange( vtNFace, Tool) -- lama sopra: angolo negativo troppo basso if Tool.SetupInfo.HeadType.bTop and vtNFace:getZ() < Tool.SetupInfo.GetMinNz( vtNFace, Tool) - GEO.EPS_ZERO then return true end -- lama sotto: angolo positivo troppo elevato if Tool.SetupInfo.HeadType.bBottom and vtNFace:getZ() > Tool.SetupInfo.GetMaxNz( vtNFace, Tool) + GEO.EPS_ZERO then return true end return false end ------------------------------------------------------------------------------------------------------------- local function TestEngagement( sBladeEngagement, Parameters, OptionalParameters) -- parametri obbligatori (parametri opzionali transitano solamente) local Face = Parameters.Face local Edge = Parameters.Edge local Part = Parameters.Part local Tool = Parameters.Tool local dDepthToMachine = Parameters.dDepthToMachine -- il lato della testa cambia in base all'Engagement local vtHead if sBladeEngagement == 'Standard' then vtHead = Face.vtN elseif sBladeEngagement == 'DownUp' then vtHead = -Face.vtN else error( 'TestEngagement : unknown engagement') end -- se l'angolo non può essere raggiunto dall'utensile la lavorazione non è fattibile if IsFaceZOutOfRange( vtHead, Tool) then return false end local CheckCollisionParameters = { Edge = Edge, vtNFace = Face.vtN, vtHead = vtHead, Part = Part, Tool = Tool, dDepthToMachine = dDepthToMachine } local CheckCollisionOptionalParameters = BeamLib.TableCopyDeep( OptionalParameters or {}) local nSCC = Tool.SetupInfo.GetSCC( Edge.vtN, Edge.vtEdge, Face.vtN) -- check punti lavorazione -- lavorazione oltre le corse: non fattibile local PointsOnToolTipCenter = { PreSimulationLib.GetPointOnToolTipCenter( Edge.ptStart + Edge.vtN * ( Edge.dElevation - dDepthToMachine), vtHead, Face.vtN, Edge.vtN, Tool), PreSimulationLib.GetPointOnToolTipCenter( Edge.ptEnd + Edge.vtN * ( Edge.dElevation - dDepthToMachine), vtHead, Face.vtN, Edge.vtN, Tool) } local bOutOfStroke = PreSimulationLib.CheckOutOfStroke( PointsOnToolTipCenter, vtHead, nSCC, Tool) if bOutOfStroke then return false end -- lavorazione in collisione con il pezzo: non fattibile local bCollisionFound, bMoveAfterSplit = PreSimulationLib.CheckCollision( sBladeEngagement, CheckCollisionParameters, CheckCollisionOptionalParameters) if bCollisionFound then return false end -- calcolo e check attacchi local LeadInOut = {} -- attacco perpendicolare local PerpendicularLeadInOut = LeadInOutLib.CalculateLeadInOut( 'Perpendicular', Parameters) -- check extracorsa nei punti di attacco PointsOnToolTipCenter = { PreSimulationLib.GetPointOnToolTipCenter( PerpendicularLeadInOut.LeadIn.ptPoint, vtHead, Face.vtN, Edge.vtN, Tool), PreSimulationLib.GetPointOnToolTipCenter( PerpendicularLeadInOut.LeadOut.ptPoint, vtHead, Face.vtN, Edge.vtN, Tool) } local bOutOfStrokePerpendicular = PreSimulationLib.CheckOutOfStroke( PointsOnToolTipCenter, vtHead, nSCC, Tool) -- se non è in extracorsa si aggiunge come attacco possibile if not bOutOfStrokePerpendicular then LeadInOut.Perpendicular = PerpendicularLeadInOut LeadInOut.Perpendicular.bMoveAfterSplit = bMoveAfterSplit end -- se c'è almeno un lato chiuso l'unico attacco possibile è il perpendicolare if not ( Edge.bIsStartOpen and Edge.bIsEndOpen) then if bOutOfStrokePerpendicular then return false else LeadInOut.sChosen = 'Perpendicular' return true, bMoveAfterSplit, LeadInOut end end -- attacco tangenziale local TangentLeadInOut = LeadInOutLib.CalculateLeadInOut( 'Tangent', Parameters) -- check extracorsa nei punti di attacco PointsOnToolTipCenter = { PreSimulationLib.GetPointOnToolTipCenter( TangentLeadInOut.LeadIn.ptPoint, vtHead, Face.vtN, Edge.vtN, Tool), PreSimulationLib.GetPointOnToolTipCenter( TangentLeadInOut.LeadOut.ptPoint, vtHead, Face.vtN, Edge.vtN, Tool) } local bOutOfStrokeTangent = PreSimulationLib.CheckOutOfStroke( PointsOnToolTipCenter, vtHead, nSCC, Tool) -- attacco tangenziale non in extracorsa: si verifica se è in collisione if not bOutOfStrokeTangent then local bCollisionFoundTangent, bMoveAfterSplitTangent = PreSimulationLib.CheckCollision( sBladeEngagement, CheckCollisionParameters, CheckCollisionOptionalParameters) -- TODO passare punti custom attacco tangenziale -- attacco tangenziale possibile if not bCollisionFoundTangent then LeadInOut.Tangent = TangentLeadInOut LeadInOut.Tangent.bMoveAfterSplit = bMoveAfterSplitTangent end end -- se disponibili più attacchi si sceglie il più corto, altrimenti quello possibile if LeadInOut.Perpendicular and LeadInOut.Tangent then if LeadInOut.Perpendicular.dTotalLength > LeadInOut.Tangent.dTotalLength + 10 then LeadInOut.sChosen = 'Tangent' else LeadInOut.sChosen = 'Perpendicular' end elseif LeadInOut.Perpendicular and not LeadInOut.Tangent then LeadInOut.sChosen = 'Perpendicular' elseif LeadInOut.Tangent and not LeadInOut.Perpendicular then LeadInOut.sChosen = 'Tangent' -- nessun attacco possibile -- TODO qui invece di uscire si dovranno provare i due attacchi SpecialTangent e SpecialTangentInverted else return false end return true, bMoveAfterSplit, LeadInOut end ------------------------------------------------------------------------------------------------------------- -- ritorna se la faccia e il lato sono lavorabili e, se sì, il modo di lavorare (standard/DownUp) e i tipi di attacco disponibili -- TODO si dovrà decidere come tagliare anche se pezzo corto (il motore non deve ingombrare con il pinzaggio) -- TODO da gestire riduzione percorso function MachiningLib.GetBladeEngagement( Parameters, OptionalParameters) local Engagement -- test lavorazione faccia in modo standard (no DownUp) local bIsEngagementOk, bMoveAfterSplit, LeadInOut = TestEngagement( 'Standard', Parameters, OptionalParameters) if bIsEngagementOk then Engagement = { sBladeEngagement = 'Standard', bMoveAfterSplit = bMoveAfterSplit, LeadInOut = LeadInOut } return true, Engagement end -- faccia non lavorabile in modo standard: si verifica se il DownUp è fattibile bIsEngagementOk, bMoveAfterSplit, LeadInOut = TestEngagement( 'DownUp', Parameters, OptionalParameters) if bIsEngagementOk then Engagement = { sBladeEngagement = 'DownUp', bMoveAfterSplit = bMoveAfterSplit, LeadInOut = LeadInOut } return true, Engagement end return false end ------------------------------------------------------------------------------------------------------------- -- funzione per cercare utensile tipo FRESA con certe caratteristiche -- TODO qui vtToolDirection è in realtà vtN function MachiningLib.FindMill( Proc, ToolSearchParameters) local ToolInfo = {} local nBestToolIndex local dBestToolResidualDepth = 0 for i = 1, #TOOLS do -- prima verifico che utensile sia compatibile local bIsToolCompatible = true -- se viene passato il nome, tutti gli altri sono incompatibili if ToolSearchParameters.sName and ToolSearchParameters.sName ~= TOOLS[i].sName then bIsToolCompatible = false -- se c'è una lista di utensili disponibili, si verifica che l'utensile attuale sia tra quelli elseif ToolSearchParameters.AvailableToolList and not IsToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i]) then bIsToolCompatible = false -- si cercano solo frese standard. Se utensile disegnato manualmente, si setta subito che è incompatibile elseif TOOLS[i].bIsProfiledTool then bIsToolCompatible = false -- controlli standard elseif ToolSearchParameters.dMaxToolDiameter and TOOLS[i].dDiameter > ToolSearchParameters.dMaxToolDiameter then bIsToolCompatible = false elseif ToolSearchParameters.dMinToolDiameter and TOOLS[i].dDiameter < ToolSearchParameters.dMinToolDiameter then bIsToolCompatible = false elseif ( ToolSearchParameters.bCCW and not TOOLS[i].bIsCCW) or ( ToolSearchParameters.bCW and TOOLS[i].bIsCCW) then bIsToolCompatible = false elseif TOOLS[i].SetupInfo.HeadType.bTop and ToolSearchParameters.vtToolDirection:getZ() < TOOLS[i].SetupInfo.GetMinNz( ToolSearchParameters.vtToolDirection, TOOLS[i]) - GEO.EPS_ZERO then bIsToolCompatible = false elseif TOOLS[i].SetupInfo.HeadType.bBottom and ToolSearchParameters.vtToolDirection:getZ() > TOOLS[i].SetupInfo.GetMaxNz( ToolSearchParameters.vtToolDirection, TOOLS[i]) + GEO.EPS_ZERO then bIsToolCompatible = false elseif ToolSearchParameters.sMillShape == 'STANDARD' and ( TOOLS[i].dSideAngle ~= 0 or TOOLS[i].bIsPen) then bIsToolCompatible = false elseif ToolSearchParameters.sMillShape == 'DOVETAIL' and not TOOLS[i].bIsDoveTail then bIsToolCompatible = false elseif ToolSearchParameters.sMillShape == 'TSHAPEMILL' and not TOOLS[i].bIsTMill then bIsToolCompatible = false elseif ToolSearchParameters.sMillShape == 'PEN' and not TOOLS[i].bIsPen then bIsToolCompatible = false elseif ToolSearchParameters.sMillShape == 'VMILL' and not TOOLS[i].bIsVMill then bIsToolCompatible = false elseif ToolSearchParameters.sType and TOOLS[i].sType ~= ToolSearchParameters.sType then -- se sto cercando una fresa che non può lavorare di testa, quelle che lavorano di testa sono comunque ammesse if TOOLS[i].sType == 'MILL_STD' and ToolSearchParameters.sType == 'MILL_NOTIP' then bIsToolCompatible = true else bIsToolCompatible = false end end -- scelgo il migliore if bIsToolCompatible then -- calcolo riduzione del massimo materiale utilizzabile local ToolEntryAngle = GetToolEntryAngle( Proc, ToolSearchParameters.vtToolDirection) -- se ToolHolder più grande dell'utensile, il primo oggetto in collisione è il ToolHolder. Altrimenti il motore. local dDimObjToCheck = EgtIf( TOOLS[i].ToolHolder.dDiameter > TOOLS[i].dDiameter, TOOLS[i].ToolHolder.dDiameter, TOOLS[i].SetupInfo.dCAxisEncumbrance) local dCurrentMaxMatReduction = BeamData.COLL_SIC or 5 -- TODO implementare le funzioni di Tool Collision Avoidance (vedi wiki e FacesBysaw -> CalcLeadInOutPerpGeom) -- TODO considerare anche il caso in cui lo stelo sia più grande del diametro utensile -- TODO nei confronti tra valori gestire tolleranze -- calcolo riduzione per non toccare con ToolHolder / Motore if ToolEntryAngle.dValue > 0 and ToolEntryAngle.dValue < 90 then dCurrentMaxMatReduction = dCurrentMaxMatReduction / ToolEntryAngle.dSin + ( ( dDimObjToCheck - TOOLS[i].dDiameter) / 2) / ToolEntryAngle.dTan end -- dCurrentResidualDepth = negativo -> mm extra disponibili, positivo -> limitare local dCurrentResidualDepth = ToolSearchParameters.dElevation + dCurrentMaxMatReduction - TOOLS[i].dMaxDepth -- se non ancora trovato, oppure se completo e il migliore fino ad ora non è completo: corrente è il migliore if not nBestToolIndex or ( dBestToolResidualDepth > 0 and dCurrentResidualDepth <= 0) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth -- altrimenti scelgo il migliore else -- se entrambi completi if dBestToolResidualDepth <= 0 and dCurrentResidualDepth <= 0 then -- se è presente una lista di utensili disponibili, si prende quello che arriva primo in quella lista if ToolSearchParameters.AvailableToolList and ToolSearchParameters.AvailableToolList.bRespectListOrder then if GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) and GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) < GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[nBestToolIndex].sName) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end -- se il migliore era su aggregato e corrente montanto direttamente, prediligo utensile montato direttamente elseif not TOOLS[i].SetupInfo.bToolOnAggregate and TOOLS[nBestToolIndex].SetupInfo.bToolOnAggregate then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth -- se hanno stesso montaggio elseif TOOLS[i].SetupInfo.bToolOnAggregate == TOOLS[nBestToolIndex].SetupInfo.bToolOnAggregate then -- scelgo utensile con indice di bontà utensile calcolato come: lunghezza / massimo materiale / diametro if TOOLS[i].dPerformanceIndex > TOOLS[nBestToolIndex].dPerformanceIndex then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end end -- se entrambi incompleti elseif dBestToolResidualDepth > 0 and dCurrentResidualDepth > 0 then --scelgo quello che lavora di più if dCurrentResidualDepth < dBestToolResidualDepth then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end end end end end ToolInfo.nToolIndex = nBestToolIndex ToolInfo.dResidualDepth = dBestToolResidualDepth return ToolInfo end ------------------------------------------------------------------------------------------------------------- -- funzione per cercare utensile tipo LAMA con certe caratteristiche -- TODO da rivedere/completare -- TODO il FindBlade dovrà restituire di utilizzare sempre la lama sopra se l'angolo lo permette, ma avendo un'altezza massima (da macchina) oltre cui il DownUp non sarà fattibile (evita collisioni tra asse e pezzo) function MachiningLib.FindBlade( Proc, ToolSearchParameters) local ToolInfo = {} -- parametri obbligatori if type( ToolSearchParameters.FaceToMachine) ~= 'table' then error( 'FindBlade : missing face info') end if type( ToolSearchParameters.bAllowTopHead) ~= 'boolean' then error( 'FindBlade : missing top head info') end if type( ToolSearchParameters.bAllowBottomHead) ~= 'boolean' then error( 'FindBlade : missing bottom head info') end if not ToolSearchParameters.bAllowTopHead and not ToolSearchParameters.bAllowBottomHead then error( 'FindBlade : wrong head info') end local FaceToMachine = ToolSearchParameters.FaceToMachine local bAllowTopHead = ToolSearchParameters.bAllowTopHead local bAllowBottomHead = ToolSearchParameters.bAllowBottomHead -- parametri opzionali local dElevation = ToolSearchParameters.dElevation local bForceLongcutBlade = ToolSearchParameters.bForceLongcutBlade or false local EdgeToMachine = ToolSearchParameters.EdgeToMachine local Part = ToolSearchParameters.Part local bIsDicing = ToolSearchParameters.bIsDicing or false local nBestToolIndex local dBestToolResidualDepth = 0 local CurrentEngagement local BestEngagement for i = 1, #TOOLS do local bIsToolCompatible = false if TOOLS[i].sFamily == 'SAWBLADE' then if bAllowTopHead and not bAllowBottomHead then bIsToolCompatible = TOOLS[i].SetupInfo.HeadType.bTop elseif bAllowBottomHead and not bAllowTopHead then bIsToolCompatible = TOOLS[i].SetupInfo.HeadType.bBottom else bIsToolCompatible = true end end -- se dati sufficienti, si determina se con questo utensile il taglio è fattibile e il modo di lavorare della lama -- TODO corretto il calcolo della dDepthToMachine? è comunque inutile calcolare per una profondità che so già andrà in collisione if bIsToolCompatible then if FaceToMachine and EdgeToMachine and Part and dElevation then local bIsBladeOk = false local BladeEngagementParameters = { Face = FaceToMachine, Edge = EdgeToMachine, Part = Part, Tool = TOOLS[i], dDepthToMachine = min( dElevation, TOOLS[i].dMaxDepth) } local BladeEngagementOptionalParameters = { bIsDicing = bIsDicing } TIMER:startElapsed( 'GetBladeEngagement') bIsBladeOk, CurrentEngagement = MachiningLib.GetBladeEngagement( BladeEngagementParameters, BladeEngagementOptionalParameters) TIMER:stopElapsed( 'GetBladeEngagement') -- orientamento non raggiungibile if not bIsBladeOk then bIsToolCompatible = false end -- se si ha solo la faccia si può verificare se questa è orientata correttamente elseif FaceToMachine then if IsFaceZOutOfRange( FaceToMachine.vtN, TOOLS[i]) then bIsToolCompatible = false end end end -- se c'è una lista di utensili disponibili, si verifica che l'utensile attuale sia tra quelli if ToolSearchParameters.AvailableToolList and not IsToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i]) then bIsToolCompatible = false end if bIsToolCompatible then -- TODO gestire accorciamento massimo materiale per inclinazione local dCurrentResidualDepth = ( dElevation or 0) - TOOLS[i].dMaxDepth if not nBestToolIndex or ( dBestToolResidualDepth > 0 and dCurrentResidualDepth <= 10 * GEO.EPS_SMALL) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth BestEngagement = CurrentEngagement else -- prediligo utensile per tagli lungo vena, se richiesto if bForceLongcutBlade and not TOOLS[nBestToolIndex].bIsUsedForLongCut and TOOLS[i].bIsUsedForLongCut then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth BestEngagement = CurrentEngagement else -- entrambi completi if dBestToolResidualDepth <= 10 * GEO.EPS_SMALL and dCurrentResidualDepth <= 10 * GEO.EPS_SMALL then -- se è presente una lista di utensili disponibili, si prende quello che arriva primo in quella lista if ToolSearchParameters.AvailableToolList and ToolSearchParameters.AvailableToolList.bRespectListOrder then if GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) and GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) < GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[nBestToolIndex].sName) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth BestEngagement = CurrentEngagement end -- si sceglie quello con le performance migliori elseif TOOLS[i].dPerformanceIndex > TOOLS[nBestToolIndex].dPerformanceIndex + 10 * GEO.EPS_SMALL then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth BestEngagement = CurrentEngagement end -- entrambi incompleti elseif dBestToolResidualDepth > 10 * GEO.EPS_SMALL and dCurrentResidualDepth > 10 * GEO.EPS_SMALL then -- si sceglie quello che lavora di più if dCurrentResidualDepth > dBestToolResidualDepth then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth BestEngagement = CurrentEngagement end end end end end end ToolInfo.nToolIndex = nBestToolIndex ToolInfo.dResidualDepth = dBestToolResidualDepth ToolInfo.Engagement = BestEngagement return ToolInfo end ------------------------------------------------------------------------------------------------------------- -- funzione per cercare utensile tipo PUNTA A FORARE con certe caratteristiche function MachiningLib.FindDrill( Proc, ToolSearchParameters) local ToolInfo = {} local nBestToolIndex local dBestToolResidualDepth = 0 -- settio valori obbligatori if not ToolSearchParameters.dToolDiameter then ToolSearchParameters.dToolDiameter = Proc.FeatureInfo.dDrillDiam or 0 end if not ToolSearchParameters.dMaxToolDiameter then ToolSearchParameters.dMaxToolDiameter = ToolSearchParameters.dToolDiameter + ( ToolSearchParameters.dDiameterTolerance or ( 100 * GEO.EPS_SMALL)) end if not ToolSearchParameters.dMinToolDiameter then ToolSearchParameters.dMinToolDiameter = ToolSearchParameters.dToolDiameter - ( ToolSearchParameters.dDiameterTolerance or ( 100 * GEO.EPS_SMALL)) end for i = 1, #TOOLS do -- prima verifico che utensile sia compatibile local bIsToolCompatible = true -- se non è punta, non è compatibile if TOOLS[i].sFamily ~= 'DRILLBIT' then bIsToolCompatible = false -- se viene passato il nome, tutti gli altri sono incompatibili elseif ToolSearchParameters.sName and ToolSearchParameters.sName ~= TOOLS[i].sName then bIsToolCompatible = false -- se c'è una lista di utensili disponibili, si verifica che l'utensile attuale sia tra quelli elseif ToolSearchParameters.AvailableToolList and not IsToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i]) then bIsToolCompatible = false -- controlli standard elseif TOOLS[i].dDiameter > ToolSearchParameters.dMaxToolDiameter then bIsToolCompatible = false elseif TOOLS[i].dDiameter < ToolSearchParameters.dMinToolDiameter then bIsToolCompatible = false elseif TOOLS[i].SetupInfo.HeadType.bTop and ToolSearchParameters.vtToolDirection:getZ() < TOOLS[i].SetupInfo.GetMinNz( ToolSearchParameters.vtToolDirection, TOOLS[i]) - GEO.EPS_ZERO then bIsToolCompatible = false elseif TOOLS[i].SetupInfo.HeadType.bBottom and ToolSearchParameters.vtToolDirection:getZ() > TOOLS[i].SetupInfo.GetMaxNz( ToolSearchParameters.vtToolDirection, TOOLS[i]) + GEO.EPS_ZERO then bIsToolCompatible = false end -- scelgo il migliore if bIsToolCompatible then -- calcolo riduzione del massimo materiale utilizzabile local ToolEntryAngle = GetToolEntryAngle( Proc, ToolSearchParameters.vtToolDirection) -- se ToolHolder più grande dell'utensile, il primo oggetto in collisione è il ToolHolder. Altrimenti il motore. local dDimObjToCheck = EgtIf( TOOLS[i].ToolHolder.dDiameter > TOOLS[i].dDiameter, TOOLS[i].ToolHolder.dDiameter, TOOLS[i].SetupInfo.dCAxisEncumbrance) local dCurrentMaxMatReduction = BeamData.COLL_SIC or 5 -- TODO implementare le funzioni di Tool Collision Avoidance (vedi wiki e FacesBysaw -> CalcLeadInOutPerpGeom) -- TODO considerare anche il caso in cui lo stelo sia più grande del diametro utensile -- calcolo riduzione per non toccare con ToolHolder / Motore if ToolEntryAngle.dValue > 0 and ToolEntryAngle.dValue < 90 then dCurrentMaxMatReduction = dCurrentMaxMatReduction / ToolEntryAngle.dSin + ( ( dDimObjToCheck - TOOLS[i].dDiameter) / 2) / ToolEntryAngle.dTan end -- dCurrentResidualDepth = negativo -> mm extra disponibili, positivo -> limitare local dCurrentResidualDepth = ToolSearchParameters.dElevation + dCurrentMaxMatReduction - TOOLS[i].dMaxDepth -- se non ancora trovato, oppure se completo e il migliore fino ad ora non è completo: corrente è il migliore if not nBestToolIndex or ( dBestToolResidualDepth > 0 and dCurrentResidualDepth <= 0) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth -- altrimenti scelgo il migliore else -- se entrambi completi if dBestToolResidualDepth <= 0 and dCurrentResidualDepth <= 0 then -- se il migliore era su aggregato e corrente montanto direttamente, prediligo utensile montato direttamente if not TOOLS[i].SetupInfo.bToolOnAggregate and TOOLS[nBestToolIndex].SetupInfo.bToolOnAggregate then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth -- se hanno stesso montaggio elseif TOOLS[i].SetupInfo.bToolOnAggregate == TOOLS[nBestToolIndex].SetupInfo.bToolOnAggregate then -- se diametro diverso if abs( TOOLS[i].dDiameter - TOOLS[nBestToolIndex].dDiameter) > 10 * GEO.EPS_SMALL then -- si sceglie utensile con diametro più simile al foro da lavorare if abs( TOOLS[i].dDiameter - ToolSearchParameters.dToolDiameter) < abs( TOOLS[nBestToolIndex].dDiameter - ToolSearchParameters.dToolDiameter) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth -- se è presente una lista di utensili disponibili, si prende quello che arriva primo in quella lista elseif ToolSearchParameters.AvailableToolList and ToolSearchParameters.AvailableToolList.bRespectListOrder then if GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) and GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) < GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[nBestToolIndex].sName) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end -- oppure utensile con performance maggiore elseif TOOLS[i].dPerformanceIndex > TOOLS[nBestToolIndex].dPerformanceIndex then nBestToolIndex = i BestToolResidualDepth = dCurrentResidualDepth end -- scelgo utensile con indice di bontà utensile calcolato come: lunghezza / massimo materiale / diametro elseif TOOLS[i].dPerformanceIndex > TOOLS[nBestToolIndex].dPerformanceIndex then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end end -- se entrambi incompleti elseif dBestToolResidualDepth > 0 and dCurrentResidualDepth > 0 then --scelgo quello che lavora di più if dCurrentResidualDepth < dBestToolResidualDepth then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end end end end end ToolInfo.nToolIndex = nBestToolIndex ToolInfo.dResidualDepth = dBestToolResidualDepth -- si salva se la punta ha lo stesso diametro del foro if nBestToolIndex and TOOLS[nBestToolIndex].dDiameter - ToolSearchParameters.dToolDiameter > 100 * GEO.EPS_SMALL then ToolInfo.bDrillIsExactDiameter = true end return ToolInfo end ------------------------------------------------------------------------------------------------------------- -- funzione per cercare utensile tipo SEGA A CATENA con certe caratteristiche -- TODO da completare function MachiningLib.FindChainSaw( Proc, ToolSearchParameters) local ToolInfo = {} -- parametri obbligatori if type( ToolSearchParameters.vtToolDirection) ~= 'table' then error( 'FindChainSaw : missing tool direction') end if type( ToolSearchParameters.bAllowTopHead) ~= 'boolean' then error( 'FindChainSaw : missing top head info') end if type( ToolSearchParameters.bAllowBottomHead) ~= 'boolean' then error( 'FindChainSaw : missing bottom head info') end if not ToolSearchParameters.bAllowTopHead and not ToolSearchParameters.bAllowBottomHead then error( 'FindChainSaw : wrong head info') end -- parametri opzionali ToolSearchParameters.dElevation = ToolSearchParameters.dElevation or 0 ToolSearchParameters.bExtendWithCornerRadius = ToolSearchParameters.bExtendWithCornerRadius or false local nBestToolIndex local dBestToolResidualDepth = 0 for i = 1, #TOOLS do local bIsToolCompatible = false if TOOLS[i].sFamily == 'MORTISE' then if ToolSearchParameters.bAllowTopHead and not ToolSearchParameters.bAllowBottomHead then bIsToolCompatible = TOOLS[i].SetupInfo.HeadType.bTop elseif ToolSearchParameters.bAllowBottomHead and not ToolSearchParameters.bAllowTopHead then bIsToolCompatible = TOOLS[i].SetupInfo.HeadType.bBottom else bIsToolCompatible = true end end -- se c'è una lista di utensili disponibili, si verifica che l'utensile attuale sia tra quelli if ToolSearchParameters.AvailableToolList and not IsToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i]) then bIsToolCompatible = false end -- TODO nei confronti tra valori gestire tolleranze if bIsToolCompatible then if ToolSearchParameters.dElevation > 10 * GEO.EPS_SMALL and ToolSearchParameters.bExtendWithCornerRadius then ToolSearchParameters.dElevation = ToolSearchParameters.dElevation + TOOLS[i].dCornerRadius end -- TODO gestire accorciamento massimo materiale per inclinazione local dCurrentResidualDepth = ToolSearchParameters.dElevation - TOOLS[i].dMaxDepth -- se non ancora trovato, oppure se completo e il migliore fino ad ora non è completo: corrente è il migliore if not nBestToolIndex or ( dBestToolResidualDepth > 0 and dCurrentResidualDepth <= 0) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth -- altrimenti scelgo il migliore else -- se entrambi completi if dBestToolResidualDepth <= 0 and dCurrentResidualDepth <= 0 then -- se è presente una lista di utensili disponibili, si prende quello che arriva primo in quella lista if ToolSearchParameters.AvailableToolList and ToolSearchParameters.AvailableToolList.bRespectListOrder then if GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) and GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[i].sName) < GetIndexToolInAvailableToolList( ToolSearchParameters.AvailableToolList, TOOLS[nBestToolIndex].sName) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end -- scelgo utensile con rapporto lunghezza / diametro minore elseif TOOLS[i].dPerformanceIndex > TOOLS[nBestToolIndex].dPerformanceIndex then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end -- se entrambi incompleti elseif dBestToolResidualDepth > 10 * GEO.EPS_SMALL and dCurrentResidualDepth > 10 * GEO.EPS_SMALL then --scelgo quello che lavora di più if dCurrentResidualDepth > dBestToolResidualDepth then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth end end end end end ToolInfo.nToolIndex = nBestToolIndex ToolInfo.dResidualDepth = dBestToolResidualDepth return ToolInfo end ------------------------------------------------------------------------------------------------------------- function MachiningLib.GetAvailableToolList( Proc, sToolList, sType) local ToolList = {} -- se ci sono strategie disponibili (dovrebbe essere sempre vero dato che la funzione è chiamata proprio da una strategia) if Proc.AvailableStrategies and #Proc.AvailableStrategies > 0 then -- se è una strategia di base, ci deve essere per forza una lista utensili passata if Proc.AvailableStrategies.bIsBasicStrategy then ToolList = BCS.GetToolsFromMachiningDataFile( Proc, sType) -- se arriva da JSON o NGE else if sToolList and #sToolList > 0 then -- Lista definita come: "UUID_1,Nome_1;UUID_2,Nome_2;UUID_3,Nome_3" local Tool = EgtSplitString( sToolList, ';') for i = 1, #Tool do local ToolData = EgtSplitString( Tool[i]) table.insert( ToolList, { sUUID = ToolData[1], sName = ToolData[2]} ) end -- se non ci sono utensili forzati, annullo lista per cercare tra tutti quelli disponibili in modo automatico else ToolList = nil end end end return ToolList end ------------------------------------------------------------------------------------------------------------- function MachiningLib.InitMachiningParameters( nMachiningType) local Machining = {} Machining.nType = nMachiningType Machining.sDepth = 0 Machining.bInvert = false Machining.nSCC = MCH_SCC.NONE Machining.bToolInvert = false Machining.sBlockedAxis = '' Machining.sInitialAngles = '' Machining.dOverlap = 0 Machining.dSideAngle = 0 Machining.dStartSafetyLength = BeamData.COLL_SIC Machining.dRadialOffset = 0 Machining.dLongitudinalOffset = 0 Machining.sUserNotes = '' Machining.Steps = {} Machining.Steps.dStep = 0 Machining.Steps.dSideStep = 0 if nMachiningType == MCH_MY.MILLING then Machining.nWorkside = MCH_MILL_WS.LEFT Machining.nFaceuse = MCH_MILL_FU.NONE Machining.Steps.nStepType = MCH_MILL_ST.ONEWAY Machining.LeadIn = {} Machining.LeadIn.nType = MCH_MILL_LI.LINEAR Machining.LeadIn.dStartAddLength = 0 Machining.LeadIn.dTangentDistance = 0 Machining.LeadIn.dPerpDistance = 0 Machining.LeadIn.dElevation = 0 Machining.LeadIn.dCompLength = 0 Machining.LeadOut = {} Machining.LeadOut.nType = MCH_MILL_LO.LINEAR Machining.LeadOut.dEndAddLength = 0 Machining.LeadOut.dTangentDistance = 0 Machining.LeadOut.dPerpDistance = 0 Machining.LeadOut.dElevation = 0 Machining.LeadOut.dCompLength = 0 elseif nMachiningType == MCH_MY.MORTISING then Machining.nWorkside = MCH_MORTISE_WS.LEFT Machining.nFaceuse = MCH_MORTISE_FU.NONE Machining.Steps.nStepType = MCH_MORTISE_ST.ONEWAY Machining.LeadIn = {} Machining.LeadIn.dStartAddLength = 0 Machining.LeadOut = {} Machining.LeadOut.dEndAddLength = 0 elseif nMachiningType == MCH_MY.DRILLING then Machining.dReturnPos = 0 Machining.dStartSlowLen = 0 Machining.dEndSlowLen = 0 Machining.dThrouAddLen = 5 elseif nMachiningType == MCH_MY.POCKETING then Machining.nSubType = MCH_POCK_SUB.ONEWAY Machining.LeadIn = {} Machining.LeadIn.nType = MCH_POCK_LI.ZIGZAG Machining.LeadIn.dTangentDistance = 0 Machining.LeadIn.dElevation = 0 Machining.LeadOut = {} Machining.LeadOut.nType = MCH_POCK_LO.NONE Machining.LeadOut.dTangentDistance = 0 elseif nMachiningType == MCH_MY.SAWING then -- TODO da completare Machining.nWorkside = MCH_SAW_WS.CENTER Machining.nHeadSide = MCH_SAW_HS.LEFT Machining.Steps.nStepType = MCH_SAW_ST.ONEWAY Machining.LeadIn.nType = MCH_SAW_LI.CENT Machining.LeadOut.nType = MCH_SAW_LO.CENT else error( 'InitMachiningParameters : unknown machining type') end return Machining end ------------------------------------------------------------------------------------------------------------- -- salva in lista globale la lavorazione appena calcolata local function AddNewMachining( ProcToAdd, MachiningToAdd, AuxiliaryDataToAdd) -- Controllo parametri obbligatori if not MachiningToAdd.nType or not MachiningToAdd.nToolIndex or not MachiningToAdd.Geometry or not ProcToAdd.id then return false end -- se nome non definito, assegno alla lavorazioen un nome standard if not MachiningToAdd.sOperationName then -- Drilling if MachiningToAdd.nType == MCH_MY.DRILLING then MachiningToAdd.sTypeName = 'Drill_' -- Milling elseif MachiningToAdd.nType == MCH_MY.MILLING then -- se utensile lama if TOOLS[MachiningToAdd.nToolIndex].sFamily == 'SAWBLADE' then MachiningToAdd.sTypeName = 'Cut_' else MachiningToAdd.sTypeName = 'Mill_' end -- Pocketing elseif MachiningToAdd.nType == MCH_MY.POCKETING then MachiningToAdd.sTypeName = 'Pocket_' -- Mortising elseif MachiningToAdd.nType == MCH_MY.MORTISING then MachiningToAdd.sTypeName = 'ChSaw_' end MachiningToAdd.sOperationName = MachiningToAdd.sTypeName .. ( EgtGetName( ProcToAdd.id) or tostring( ProcToAdd.id)) -- .. '_' .. tostring( MachiningToAdd.Geometry) end if not MachiningToAdd.sToolName then MachiningToAdd.sToolName = TOOLS[MachiningToAdd.nToolIndex].sName end local Machining = {} Machining.Proc = ProcToAdd Machining.Machining = MachiningToAdd Machining.AuxiliaryData = AuxiliaryDataToAdd or {} table.insert( MACHININGS, Machining) -- aggiornamento info generiche lavorazioni local nHeadCutRotation if ProcToAdd.bDown then nHeadCutRotation = 3 elseif ProcToAdd.bSide then nHeadCutRotation = 2 else -- ProcToAdd.bStd nHeadCutRotation = 1 end MACHININGS.Info.nHeadCutRotation = max( MACHININGS.Info.nHeadCutRotation or 0, nHeadCutRotation) local nSplitCutRotation if ProcToAdd.bDown and MachiningToAdd.sStage == 'AfterTail' then nSplitCutRotation = 3 elseif ProcToAdd.bSide and MachiningToAdd.sStage == 'AfterTail' then nSplitCutRotation = 2 else -- ProcToAdd.bStd nSplitCutRotation = 1 end MACHININGS.Info.nSplitCutRotation = max( MACHININGS.Info.nSplitCutRotation or 0, nSplitCutRotation) return true end ------------------------------------------------------------------------------------------------------------- -- TODO funziona solo con attacco perpendicolare - da aggiungere gestione in caso di attacco tangenziale function MachiningLib.AddMachinings( Proc, Machining, AuxiliaryData) local bMachiningAdded = false local NewAuxiliaryData = {} -- se esistono i dati ausiliari li copio in locale perchè il parametro è una tabella per riferimento if AuxiliaryData then NewAuxiliaryData = BeamLib.TableCopyDeep( AuxiliaryData) end if Machining.CloneStepsRadial and Machining.CloneStepsRadial.nCount > 1 then NewAuxiliaryData.Clones = {} local dOriginalRadialOffset = Machining.dRadialOffset local dOriginalLeadInPerpDistance = Machining.LeadIn.dPerpDistance local dOriginalLeadOutPerpDistance = Machining.LeadOut.dPerpDistance for i = 1, Machining.CloneStepsRadial.nCount do NewAuxiliaryData.Clones[i] = {} NewAuxiliaryData.Clones[i].dRadialOffset = dOriginalRadialOffset + Machining.CloneStepsRadial.dStep * ( Machining.CloneStepsRadial.nCount - i) -- update distanza perpendicolare attacco per contemplare l'offset applicato if abs( dOriginalLeadInPerpDistance) > 10 * GEO.EPS_SMALL and abs( dOriginalLeadOutPerpDistance) > 10 * GEO.EPS_SMALL then NewAuxiliaryData.Clones[i].LeadIn = {} NewAuxiliaryData.Clones[i].LeadOut = {} NewAuxiliaryData.Clones[i].LeadIn.dPerpDistance = dOriginalLeadInPerpDistance - NewAuxiliaryData.Clones[i].dRadialOffset + dOriginalRadialOffset NewAuxiliaryData.Clones[i].LeadOut.dPerpDistance = dOriginalLeadOutPerpDistance - NewAuxiliaryData.Clones[i].dRadialOffset + dOriginalRadialOffset end end elseif Machining.CloneStepsLongitudinal and Machining.CloneStepsLongitudinal.nCount > 1 then NewAuxiliaryData.Clones = {} local dOriginalRadialOffset = Machining.dRadialOffset for i = Machining.CloneStepsLongitudinal.nCount, 1, -1 do NewAuxiliaryData.Clones[i] = {} NewAuxiliaryData.Clones[i].dRadialOffset = dOriginalRadialOffset + Machining.CloneStepsLongitudinal.dStep * ( i - 1) end end bMachiningAdded = AddNewMachining( Proc, Machining, NewAuxiliaryData) return bMachiningAdded end ------------------------------------------------------------------------------------------------------------- -- funzione per aggiungere una nuova lavorazione function MachiningLib.AddOperations( vProc, Part, sRotation) local nErr local sErr = '' local bAreAllMachiningApplyOk = true local bSplitExecuted = false -- parametri generali lavorazione local MachiningParameters = { { sName = 'sDepth', nMchParam = MCH_MP.DEPTH_STR}, { sName = 'bInvert', nMchParam = MCH_MP.INVERT}, { sName = 'nWorkside', nMchParam = MCH_MP.WORKSIDE}, { sName = 'nFaceuse', nMchParam = MCH_MP.FACEUSE}, { sName = 'nSCC', nMchParam = MCH_MP.SCC}, { sName = 'bToolInvert', nMchParam = MCH_MP.TOOLINVERT}, { sName = 'sBlockedAxis', nMchParam = MCH_MP.BLOCKEDAXIS}, { sName = 'sInitialAngles', nMchParam = MCH_MP.INITANGS}, { sName = 'nHeadSide', nMchParam = MCH_MP.HEADSIDE}, { sName = 'nSubType', nMchParam = MCH_MP.SUBTYPE}, { sName = 'dOverlap', nMchParam = MCH_MP.OVERL}, { sName = 'dSideAngle', nMchParam = MCH_MP.SIDEANGLE}, { sName = 'dStartSafetyLength', nMchParam = MCH_MP.STARTPOS}, { sName = 'dReturnPos', nMchParam = MCH_MP.RETURNPOS}, { sName = 'dRadialOffset', nMchParam = MCH_MP.OFFSR}, { sName = 'dLongitudinalOffset', nMchParam = MCH_MP.OFFSL}, { sName = 'dStartSlowLen', nMchParam = MCH_MP.STARTSLOWLEN}, { sName = 'dEndSlowLen', nMchParam = MCH_MP.ENDSLOWLEN}, { sName = 'dThrouAddLen', nMchParam = MCH_MP.THROUADDLEN}, { sName = 'sSystemNotes', nMchParam = MCH_MP.SYSNOTES}, { sName = 'sUserNotes', nMchParam = MCH_MP.USERNOTES} } -- parametri relativi allo step MachiningParameters.Steps = { { sName = 'nStepType', nMchParam = MCH_MP.STEPTYPE}, { sName = 'dStep', nMchParam = MCH_MP.STEP}, { sName = 'dSideStep', nMchParam = MCH_MP.SIDESTEP} } -- parametri relativi all'approccio MachiningParameters.LeadIn = { { sName = 'nType', nMchParam = MCH_MP.LEADINTYPE}, { sName = 'dStartAddLength', nMchParam = MCH_MP.STARTADDLEN}, { sName = 'dTangentDistance', nMchParam = MCH_MP.LITANG}, { sName = 'dPerpDistance', nMchParam = MCH_MP.LIPERP}, { sName = 'dElevation', nMchParam = MCH_MP.LIELEV}, { sName = 'dCompLength', nMchParam = MCH_MP.LICOMPLEN} } -- parametri relativi alla retrazione MachiningParameters.LeadOut = { { sName = 'nType', nMchParam = MCH_MP.LEADOUTTYPE}, { sName = 'dEndAddLength', nMchParam = MCH_MP.ENDADDLEN}, { sName = 'dTangentDistance', nMchParam = MCH_MP.LOTANG}, { sName = 'dPerpDistance', nMchParam = MCH_MP.LOPERP}, { sName = 'dElevation', nMchParam = MCH_MP.LOELEV}, { sName = 'dCompLength', nMchParam = MCH_MP.LOCOMPLEN} } -- parametri da scrivere nelle note utente local UserNotes = { { sName = 'dMaxElev', sMchParam = 'MaxElev'}, { sName = 'dOpenMinSafe', sMchParam = 'OpenMinSafe'}, { sName = 'nVMRS', sMchParam = 'VMRS'}, { sName = 'dStartZmax', sMchParam = 'StartZmax'}, { sName = 'nOutRaw', sMchParam = 'OutRaw'}, { sName = 'nOpenOutRaw', sMchParam = 'OpenOutRaw'}, { sName = 'nPlunge', sMchParam = 'Plunge'}, { sName = 'vtFaceUse', sMchParam = 'VtFaceUse'}, { sName = 'nEdgesFaceUse', sMchParam = 'EdgesFaceUse'} } -- parametri da scrivere nelle note di sistema local SystemNotes = { } for i = 1, #MACHININGS do -- si aggiungono solo quelle della fase richiesta if ( MACHININGS[i].Proc.bStd and sRotation == 'STD') or ( MACHININGS[i].Proc.bDown and sRotation == 'DOWN') or ( MACHININGS[i].Proc.bSide and sRotation == 'SIDE') then local nClonesToAdd = 1 if MACHININGS[i].AuxiliaryData.Clones then nClonesToAdd = #MACHININGS[i].AuxiliaryData.Clones end -- dati da salvare dopo applicazione della lavorazione MACHININGS[i].Machining.MachStartAxesPos = {} MACHININGS[i].Machining.MachEndAxesPos = {} MACHININGS[i].Machining.nOperationId = {} for j = 1, nClonesToAdd do -- creazione lavorazione local nOperationId = EgtCreateMachining( MACHININGS[i].Machining.sOperationName, MACHININGS[i].Machining.nType, MACHININGS[i].Machining.sToolName) if nOperationId then -- impostazione geometria local Geometry if MACHININGS[i].AuxiliaryData.Clones and MACHININGS[i].AuxiliaryData.Clones[j].Geometry then Geometry = MACHININGS[i].AuxiliaryData.Clones[j].Geometry elseif MACHININGS[i].Machining.Geometry then Geometry = MACHININGS[i].Machining.Geometry end EgtSetMachiningGeometry( Geometry) -- si scrive info pezzo e feature su geometria lavorazione if Geometry then EgtSetInfo( Geometry[1][1], 'CUTID', MACHININGS[i].Proc.idCut) EgtSetInfo( Geometry[1][1], 'TASKID', MACHININGS[i].Proc.idTask) end -- impostazione parametri lavorazione -- TODO scrivere sempre Steps, LeadIn, LeadOut nelle tabelle in modo da non dover controllare ogni volta che ci siano for k = 1, #MachiningParameters do local sValue if MACHININGS[i].AuxiliaryData.Clones and MACHININGS[i].AuxiliaryData.Clones[j][MachiningParameters[k].sName] then sValue = MACHININGS[i].AuxiliaryData.Clones[j][MachiningParameters[k].sName] elseif MACHININGS[i].Machining[MachiningParameters[k].sName] then sValue = MACHININGS[i].Machining[MachiningParameters[k].sName] end if sValue then EgtSetMachiningParam( MachiningParameters[k].nMchParam, sValue) end end for k = 1, #MachiningParameters.Steps do local sValue if MACHININGS[i].AuxiliaryData.Clones and MACHININGS[i].AuxiliaryData.Clones[j].Steps and MACHININGS[i].AuxiliaryData.Clones[j].Steps[MachiningParameters.Steps[k].sName] then sValue = MACHININGS[i].AuxiliaryData.Clones[j].Steps[MachiningParameters.Steps[k].sName] elseif MACHININGS[i].Machining.Steps and MACHININGS[i].Machining.Steps[MachiningParameters.Steps[k].sName] then sValue = MACHININGS[i].Machining.Steps[MachiningParameters.Steps[k].sName] end if sValue then EgtSetMachiningParam( MachiningParameters.Steps[k].nMchParam, sValue) end end for k = 1, #MachiningParameters.LeadIn do local sValue if MACHININGS[i].AuxiliaryData.Clones and MACHININGS[i].AuxiliaryData.Clones[j].LeadIn and MACHININGS[i].AuxiliaryData.Clones[j].LeadIn[MachiningParameters.LeadIn[k].sName] then sValue = MACHININGS[i].AuxiliaryData.Clones[j].LeadIn[MachiningParameters.LeadIn[k].sName] elseif MACHININGS[i].Machining.LeadIn and MACHININGS[i].Machining.LeadIn[MachiningParameters.LeadIn[k].sName] then sValue = MACHININGS[i].Machining.LeadIn[MachiningParameters.LeadIn[k].sName] end if sValue then EgtSetMachiningParam( MachiningParameters.LeadIn[k].nMchParam, sValue) end end for k = 1, #MachiningParameters.LeadOut do local sValue if MACHININGS[i].AuxiliaryData.Clones and MACHININGS[i].AuxiliaryData.Clones[j].LeadOut and MACHININGS[i].AuxiliaryData.Clones[j].LeadOut[MachiningParameters.LeadOut[k].sName] then sValue = MACHININGS[i].AuxiliaryData.Clones[j].LeadOut[MachiningParameters.LeadOut[k].sName] elseif MACHININGS[i].Machining.LeadOut and MACHININGS[i].Machining.LeadOut[MachiningParameters.LeadOut[k].sName] then sValue = MACHININGS[i].Machining.LeadOut[MachiningParameters.LeadOut[k].sName] end if sValue then EgtSetMachiningParam( MachiningParameters.LeadOut[k].nMchParam, sValue) end end for k = 1, #UserNotes do local sValue if MACHININGS[i].AuxiliaryData.Clones and MACHININGS[i].AuxiliaryData.Clones[j][UserNotes[k].sName] then sValue = MACHININGS[i].AuxiliaryData.Clones[j][UserNotes[k].sName] elseif MACHININGS[i].Machining[UserNotes[k].sName] then sValue = MACHININGS[i].Machining[UserNotes[k].sName] end if sValue then local sUserNotes = '' sUserNotes = EgtGetMachiningParam( MCH_MP.USERNOTES) sUserNotes = EgtSetValInNotes( sUserNotes, UserNotes[k].sMchParam, sValue) EgtSetMachiningParam( MCH_MP.USERNOTES, sUserNotes) end end -- parametri da settare nelle note di sistema -- TODO da decidere quali sono le note da salvare qui. Probabilmente tutte quelle relative all'ordine delle lavorazioni --if MACHININGS[i].Machining.nMachiningOrder then -- sSystemNotes = EgtSetValInNotes( sSystemNotes, 'MachiningOrder', MACHININGS[i].Machining.nMachiningOrder) --end for k = 1, #SystemNotes do local sValue if MACHININGS[i].AuxiliaryData.Clones and MACHININGS[i].AuxiliaryData.Clones[j][SystemNotes[k].sName] then sValue = MACHININGS[i].AuxiliaryData.Clones[j][SystemNotes[k].sName] elseif MACHININGS[i].Machining[SystemNotes[k].sName] then sValue = MACHININGS[i].Machining[SystemNotes[k].sName] end if sValue then local sSystemNotes = '' sSystemNotes = EgtGetMachiningParam( MCH_MP.SYSNOTES) sSystemNotes = EgtSetValInNotes( sSystemNotes, SystemNotes[k].sMchParam, sValue) EgtSetMachiningParam( MCH_MP.SYSNOTES, sSystemNotes) end end local bIsApplyOk = MachiningLib.ApplyMachining( true, false) -- si salva sulla lista info lavorazione applicata local MachExtraInfo = { sType = 'MACH', nIndexInMachinings = i, nOperationId = nOperationId, MachStartAxesPos = EgtGetMachiningStartAxes(), MachEndAxesPos = EgtGetMachiningEndAxes()} table.insert( DB_MACH_APPLIED, MachExtraInfo) -- se errore in applicazione if not bIsApplyOk then bAreAllMachiningApplyOk = false nErr, sErr = EgtGetLastMachMgrError() EgtSetOperationMode( nOperationId, false) -- update risultati -- TODO è corretto mettere non applicabile????? disattivare e dare un'incompleta gialla? RESULT[MACHININGS[i].Proc.nIndexInResult].ChosenStrategy.sStatus = 'Not-Applicable' RESULT[MACHININGS[i].Proc.nIndexInResult].ChosenStrategy.sApplyInfo = sErr RESULT[MACHININGS[i].Proc.nIndexInResult].ChosenStrategy.nApplyError = nErr -- se applicazione andata a buon fine else -- se non deve essere igniorato, si salva ingombro lavorazione attuale e fasi successive if not MACHININGS[i].AuxiliaryData.bIgnoreNotClampableLength then -- salvo ingombro non pinzabile testa/coda local nCurrRotation = MACHININGS[i].Proc.nIndexRotation local dNotClampHead, dNotClampTail = FeatureLib.GetFeatureRotationNotClampableLengths( MACHININGS[i].Proc, Part, nCurrRotation) Part.NotClampableLength[sRotation].dHead = max( Part.NotClampableLength[sRotation].dHead, dNotClampHead) Part.NotClampableLength[sRotation].dTail = max( Part.NotClampableLength[sRotation].dTail, dNotClampTail) if sRotation == 'DOWN' then local nNextRotation = EgtIf( nCurrRotation - 1 < 1, nCurrRotation - 1 + 4, nCurrRotation - 1) -- se rotazione attiva (SIDE) if string.sub( Part.ChosenCombination, nNextRotation, nNextRotation) == '1' then dNotClampHead, dNotClampTail = FeatureLib.GetFeatureRotationNotClampableLengths( MACHININGS[i].Proc, Part, nNextRotation) Part.NotClampableLength['SIDE'].dHead = max( Part.NotClampableLength['SIDE'].dHead, dNotClampHead) Part.NotClampableLength['SIDE'].dTail = max( Part.NotClampableLength['SIDE'].dTail, dNotClampTail) end nNextRotation = EgtIf( nNextRotation - 1 < 1, nNextRotation - 1 + 4, nNextRotation - 1) -- se rotazione attiva (STD) if string.sub( Part.ChosenCombination, nNextRotation, nNextRotation) == '1' then dNotClampHead, dNotClampTail = FeatureLib.GetFeatureRotationNotClampableLengths( MACHININGS[i].Proc, Part, nNextRotation) Part.NotClampableLength['STD'].dHead = max( Part.NotClampableLength['STD'].dHead, dNotClampHead) Part.NotClampableLength['STD'].dTail = max( Part.NotClampableLength['STD'].dTail, dNotClampTail) end elseif sRotation == 'SIDE' then local nNextRotation = EgtIf( nCurrRotation - 1 < 1, nCurrRotation - 1 + 4, nCurrRotation - 1) -- se rotazione attiva (STD) if string.sub( Part.ChosenCombination, nNextRotation, nNextRotation) == '1' then dNotClampHead, dNotClampTail = FeatureLib.GetFeatureRotationNotClampableLengths( MACHININGS[i].Proc, Part, nNextRotation) Part.NotClampableLength['STD'].dHead = max( Part.NotClampableLength['STD'].dHead, dNotClampHead) Part.NotClampableLength['STD'].dTail = max( Part.NotClampableLength['STD'].dTail, dNotClampTail) end end end end RESULT[MACHININGS[i].Proc.nIndexInResult].ChosenStrategy.bIsApplyOk = bIsApplyOk -- TODO è giusto inserire queste info alla fine della lavorazione? oppure conviene creare un record in MACHININGS apposito per la disposizione? -- se era taglio di separazione, aggiungo nuova fase if MACHININGS[i].AuxiliaryData.bIsSplitOrCut then bSplitExecuted = true MACHININGS.Info.bSplitExecuted = true BeamLib.AddPhaseWithRawParts( MACHININGS[i].Proc.idRaw, BeamData.ptOriXR, BeamData.dPosXR, BeamData.RAW_OFFSET) -- se grezzo successivo senza pezzi e finale, va tolto local nNextRawId = EgtGetNextRawPart( MACHININGS[i].Proc.idRaw) if nNextRawId and EgtGetPartInRawPartCount( nNextRawId) == 0 and EgtGetRawPartBBox( nNextRawId):getDimX() < BeamData.dMinRaw then EgtRemoveRawPartFromCurrPhase( nNextRawId) end MACHININGS.Info.bSplitPhase = EgtGetCurrPhase() -- se combinazione prevede inversione, si gira il pezzo if Part.bPartInCombiIsInverted and not Part.bIsInverted then BeamLib.InvertRawPart( Part, 2) end local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) if sRotation == 'DOWN' then local nRotation = EgtIf( Part.nInitialPosition + 2 > 4, Part.nInitialPosition + 2 - 4, Part.nInitialPosition + 2) - 1 BeamLib.RotateRawPart( Part, nRotation) EgtSetInfo( idDisp, 'ROT', -2) EgtSetInfo( idDisp, 'TYPE', 'MID2') elseif sRotation == 'SIDE' then local nRotation = EgtIf( Part.nInitialPosition + 1 > 4, Part.nInitialPosition + 1 - 4, Part.nInitialPosition + 1) - 1 BeamLib.RotateRawPart( Part, nRotation) EgtSetInfo( idDisp, 'ROT', -1) EgtSetInfo( idDisp, 'TYPE', 'MID2') else local nRotation = Part.nInitialPosition - 1 BeamLib.RotateRawPart( Part, nRotation) EgtSetInfo( idDisp, 'TYPE', 'END') end EgtSetInfo( idDisp, 'ORD', MACHININGS[i].Proc.nIndexPartInParts) end else return false, 'UNEXPECTED ERROR: Error on creating machining', bSplitExecuted end end end end -- si settano aree non pinzabili testa/coda local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) EgtSetInfo( idDisp, 'HCING', Part.NotClampableLength[sRotation].dHead or 0) EgtSetInfo( idDisp, 'TCING', Part.NotClampableLength[sRotation].dTail or 0) -- se è la fase della separazione, setto gli stessi dati anche sulla fase precedente if MACHININGS.Info.bSplitPhase == nPhase then idDisp = EgtGetPhaseDisposition( nPhase - 1) EgtSetInfo( idDisp, 'HCING', Part.NotClampableLength[sRotation].dHead or 0) EgtSetInfo( idDisp, 'TCING', Part.NotClampableLength[sRotation].dTail or 0) end return bAreAllMachiningApplyOk, sErr, bSplitExecuted end ------------------------------------------------------------------------------------------------------------- function MachiningLib.ApplyMachining( bRecalc, bApplyPost) local bResult = EgtApplyMachining( bRecalc, bApplyPost) return bResult end ------------------------------------------------------------------------------------------------------------- -- funzione che restituisce l'indice MRR (Material Removal Rate) della strategia. Dati in ingresso: Step, SideStep, Feed, ToolIndex, MachiningType function MachiningLib.GetToolMRR( Parameters) local dMRR = 1 -- se ho già tutti i parametri if not Parameters.dStep or not Parameters.dSideStep or not Parameters.dFeed then -- se manca qualche parametro, lo recupero da parametro di default dell'utensile if Parameters.nToolIndex then Parameters.dStep = Parameters.dStep or TOOLS[Parameters.nToolIndex].dStep Parameters.dSideStep = Parameters.dSideStep or TOOLS[Parameters.nToolIndex].dSideStep Parameters.dFeed = Parameters.dFeed or TOOLS[Parameters.nToolIndex].Feeds.dFeed -- se non riesco a calcolare, ritorno un indice molto basso else return GEO.EPS_SMALL end end dMRR = Parameters.dStep * Parameters.dSideStep * Parameters.dFeed -- Unità: dm/min per avere un indice vicino alle unità return dMRR / pow( 10, 6) end ------------------------------------------------------------------------------------------------------------- function MachiningLib.GetTimeToMachineAllStepsWithLeadInOut( Machining, Part) local dLengthToMachineAllStepsWithLeadInOut = 0 local dTimeToMachineTotal = 0 local dToolStartFeed = TOOLS[Machining.nToolIndex].Feeds.dStartFeed local dToolEndFeed = TOOLS[Machining.nToolIndex].Feeds.dEndFeed local dToolFeed = TOOLS[Machining.nToolIndex].Feeds.dFeed if Machining.nType == MCH_MY.MILLING then -- stima LeadIn e LeadOut; se non settati si impostano a valori di default if not Machining.LeadIn.dTotalEstimatedDistance then Machining.LeadIn.dTotalEstimatedDistance = sqrt( Machining.LeadIn.dPerpDistance ^ 2 + Machining.LeadIn.dTangentDistance ^ 2) + Machining.dStartSafetyLength end if not Machining.LeadOut.dTotalEstimatedDistance then Machining.LeadOut.dTotalEstimatedDistance = sqrt( Machining.LeadOut.dPerpDistance ^ 2 + Machining.LeadOut.dTangentDistance ^ 2) + Machining.dStartSafetyLength end if not Machining.CloneStepsRadial then Machining.CloneStepsRadial = {} Machining.CloneStepsRadial.nCount = 1 end if not Machining.Steps.nCount then Machining.Steps.nCount = 1 end -- stima tempi di lavorazione per i diversi tratti local dTimeToMachineLeadIn = Machining.LeadIn.dTotalEstimatedDistance / dToolStartFeed local dTimeToMachineLeadOut = Machining.LeadOut.dTotalEstimatedDistance / dToolEndFeed local dTimeToMachineEdge = Machining.dLengthToMachine / dToolFeed -- calcolo lunghezze e tempi if Machining.Steps.nStepType == MCH_MILL_ST.ZIGZAG then dLengthToMachineAllStepsWithLeadInOut = ( Machining.dLengthToMachine * Machining.CloneStepsRadial.nCount + ( Machining.LeadIn.dTotalEstimatedDistance + Machining.LeadOut.dTotalEstimatedDistance)) * Machining.Steps.nCount dTimeToMachineTotal = ( dTimeToMachineEdge * Machining.CloneStepsRadial.nCount + ( dTimeToMachineLeadIn + dTimeToMachineLeadOut)) * Machining.Steps.nCount else dLengthToMachineAllStepsWithLeadInOut = Machining.CloneStepsRadial.nCount * ( Machining.dLengthToMachine + Machining.LeadIn.dTotalEstimatedDistance + Machining.LeadOut.dTotalEstimatedDistance) * Machining.Steps.nCount dTimeToMachineTotal = Machining.CloneStepsRadial.nCount * ( dTimeToMachineEdge + dTimeToMachineLeadIn + dTimeToMachineLeadOut) * Machining.Steps.nCount end elseif Machining.nType == MCH_MY.MORTISING then -- se non settati, imposto valori di default if not Machining.CloneStepsLongitudinal then Machining.CloneStepsLongitudinal = {} Machining.CloneStepsLongitudinal.nCount = 1 end -- stima tempi di lavorazione per i diversi tratti local dTimeToMachineLeadIn = ( Machining.dDepthToMachine + ( TOOLS[Machining.nToolIndex].SetupInfo.dZSafeDelta or 60) + EgtMdbGetGeneralParam( MCH_GP.SAFEZ)) / dToolStartFeed local dTimeToMachineLeadOut = ( Machining.dDepthToMachine + ( TOOLS[Machining.nToolIndex].SetupInfo.dZSafeDelta or 60) + EgtMdbGetGeneralParam( MCH_GP.SAFEZ)) / dToolEndFeed local dTimeToMachineEdge = Machining.dLengthToMachine / dToolFeed -- calcolo lunghezze e tempi if Machining.Steps.nStepType == MCH_MILL_ST.ZIGZAG then dLengthToMachineAllStepsWithLeadInOut = ( Machining.dLengthToMachine * Machining.Steps.nCount + 2 * ( Machining.dDepthToMachine + ( TOOLS[Machining.nToolIndex].SetupInfo.dZSafeDelta or 60) + EgtMdbGetGeneralParam( MCH_GP.SAFEZ))) * Machining.CloneStepsLongitudinal.nCount dTimeToMachineTotal = ( dTimeToMachineEdge * Machining.Steps.nCount + dTimeToMachineLeadIn + dTimeToMachineLeadOut) * Machining.CloneStepsLongitudinal.nCount else dLengthToMachineAllStepsWithLeadInOut = Machining.CloneStepsLongitudinal.nCount * ( ( 2 * Machining.Steps.nCount - 1) * Machining.dLengthToMachine + 2 * 10 * ( Machining.Steps.nCount - 1) + 2 * ( Machining.dDepthToMachine + ( TOOLS[Machining.nToolIndex].SetupInfo.dZSafeDelta or 60) + EgtMdbGetGeneralParam( MCH_GP.SAFEZ))) dTimeToMachineTotal = Machining.CloneStepsLongitudinal.nCount * ( ( 2 * Machining.Steps.nCount - 1) * dTimeToMachineEdge + 2 * 10 / dToolFeed * ( Machining.Steps.nCount - 1) + dTimeToMachineLeadIn + dTimeToMachineLeadOut) end elseif Machining.nType == MCH_MY.POCKETING then -- TODO le chiamate alla pocketing vanno unificate e uniformate local ProcTm = FeatureLib.GetProcFromTrimesh( Machining.Geometry[1][1], Part) local dDepthToMachine = min( ProcTm.Faces[Machining.Geometry[1][2] + 1].dElevation, ( Machining.dMaxElev or ProcTm.Faces[Machining.Geometry[1][2] + 1].dElevation)) - ( max( 0, Machining.dResidualDepth or 0)) local nSteps = max( ceil( ( dDepthToMachine - 50 * GEO.EPS_SMALL) / TOOLS[Machining.nToolIndex].dStep), 1) local idTempGroup = Part.idTempGroup -- TODO in futuro creare flatregion (se ci fossero isole il calcolo del percorso non sarebbe corretto) local idFaceContour = EgtExtractSurfTmFacetLoops( ProcTm.id, Machining.Geometry[1][2], idTempGroup) -- si settano i lati aperti della curva derivata dalla superficie for i = 1, #ProcTm.Faces[Machining.Geometry[1][2] + 1].Edges do if ProcTm.Faces[Machining.Geometry[1][2] + 1].Edges[i].bIsOpen then EgtCurveCompoSetTempProp( idFaceContour, ProcTm.Faces[Machining.Geometry[1][2] + 1].Edges[i].id, 1) end end -- dal momento che la funzione EgtPocketing non considera il grezzo, nei casi ottimizzati si deve correggere l'offset local idTestCurve = EgtCopyGlob( idFaceContour, idTempGroup ) local bIsOptimizedPocketing = ( TOOLS[Machining.nToolIndex].dDiameter > TOOLS[Machining.nToolIndex].dSideStep - 10 * GEO.EPS_SMALL) and EgtOffsetCurve( idTestCurve, -TOOLS[Machining.nToolIndex].dDiameter / 2 - 10 * GEO.EPS_SMALL) if not bIsOptimizedPocketing then local dOffset = TOOLS[Machining.nToolIndex].dDiameter / 2 + 10 * GEO.EPS_SMALL if TOOLS[Machining.nToolIndex].dSideStep < TOOLS[Machining.nToolIndex].dDiameter / 2 - 10 * GEO.EPS_SMALL then dOffset = TOOLS[Machining.nToolIndex].dDiameter - TOOLS[Machining.nToolIndex].dSideStep end EgtOffsetCurve( idFaceContour, dOffset) -- si settano tutti i lati chiusi per la curva offsettata local _, nFaceCountourCurveCount = EgtCurveDomain( idFaceContour) for i = 1, nFaceCountourCurveCount do EgtCurveCompoSetTempProp( idFaceContour, i - 1, 0) end end -- calcolo percorso di svuotatura replicando il calcolo che fa la svuotatura local idPocketingPath = EgtPocketing( idFaceContour, TOOLS[Machining.nToolIndex].dDiameter / 2, TOOLS[Machining.nToolIndex].dSideStep, 0, Machining.nSubType, true, idTempGroup) local dPocketingPathLength = EgtCurveLength( idPocketingPath) -- calcolo lunghezze e tempi totali if dPocketingPathLength then dLengthToMachineAllStepsWithLeadInOut = dPocketingPathLength * nSteps -- TODO da aggiungere correzione volume effettivamente lavorato dTimeToMachineTotal = dLengthToMachineAllStepsWithLeadInOut / dToolFeed -- se percorso non calcolato, si settano dati molto alti else dLengthToMachineAllStepsWithLeadInOut = 99999 * nSteps dTimeToMachineTotal = dLengthToMachineAllStepsWithLeadInOut / dToolFeed end else error( 'GetTimeToMachineAllStepsWithLeadInOut : unknown machining type') end return dTimeToMachineTotal, dLengthToMachineAllStepsWithLeadInOut end ------------------------------------------------------------------------------------------------------------- function MachiningLib.PrepareMachiningsForSorting( Part) local nFeatureInternalIndex = 1 for i = 1, #MACHININGS do local MachiningCurrent = MACHININGS[i].Machining local ProcCurrent = MACHININGS[i].Proc if not MachiningCurrent.ptCenter then MachiningCurrent.ptCenter = Point3d( ProcCurrent.b3Box:getCenter():getX(), 0, 0) end -- conversione campo sStage in nStage, numerico e ordinabile if not MachiningCurrent.sStage or MachiningCurrent.sStage == '' then MachiningCurrent.sStage = 'Middle' end if MachiningCurrent.sStage == 'Head' then MachiningCurrent.nStage = 1 elseif MachiningCurrent.sStage == 'Tail' then MachiningCurrent.nStage = 3 elseif MachiningCurrent.sStage == 'AfterTail' then MachiningCurrent.nStage = 4 else MachiningCurrent.nStage = 2 end if i > 1 then local MachiningPrevious = MACHININGS[i - 1].Machining local ProcPrevious = MACHININGS[i - 1].Proc if ProcCurrent.id == ProcPrevious.id then -- nStage devono essere sempre uguali o crescenti nella stessa feature if MachiningCurrent.nStage < MachiningPrevious.nStage then MachiningCurrent.nStage = MachiningPrevious.nStage end -- assegnazione indice interno alla feature nFeatureInternalIndex = nFeatureInternalIndex + 1 else nFeatureInternalIndex = 1 end end MachiningCurrent.nFeatureInternalIndex = nFeatureInternalIndex -- se fase di lavoro standard, assegnazione dello spezzone if MachiningCurrent.nStage == 2 then local nParts = #Part.SplittingPoints + 1 if nParts > 1 then local dPartMinX = Part.b3Part:getMin():getX() local dPartMaxX = Part.b3Part:getMax():getX() for j = 1, nParts do local dNextSplitX = dPartMinX local dPreviousSplitX = dPartMaxX if j ~= 1 then dPreviousSplitX = Part.SplittingPoints[j - 1]:getX() end if j ~= nParts then dNextSplitX = Part.SplittingPoints[j]:getX() end if MachiningCurrent.ptCenter:getX() > dNextSplitX - 10 * GEO.EPS_SMALL and MachiningCurrent.ptCenter:getX() < dPreviousSplitX + 10 * GEO.EPS_SMALL then MachiningCurrent.nPartSegment = j else MachiningCurrent.nPartSegment = -1 end end else MachiningCurrent.nPartSegment = -1 end else MachiningCurrent.nPartSegment = -1 end end return MACHININGS end -- TODO nShifts è solo per debug, si può togliere?? -- ripristina l'ordine delle lavorazioni interno alla feature, se non rispettato ------------------------------------------------------------------------------------------------------------- function MachiningLib.FinalizeSorting() local nShifts = 0 for i = #MACHININGS, 2, -1 do for j = i - 1, 1, -1 do if MACHININGS[i].Proc.id == MACHININGS[j].Proc.id and MACHININGS[i].Machining.nFeatureInternalIndex < MACHININGS[j].Machining.nFeatureInternalIndex then MACHININGS[i], MACHININGS[j] = MACHININGS[j], MACHININGS[i] nShifts = nShifts + 1 end end end return MACHININGS, nShifts end -- TODO convertire in tabella in cui si chiamano direttamente i nomi delle funzioni, in modo da poter cambiare l'ordine facilmente -- TODO libreria Sorting?? ------------------------------------------------------------------------------------------------------------- local SortingComparisonRules = { -- dipendenze solo spostamento stage 2->4 -- TODO da fare -- stage function( MachiningA, MachiningB) if MachiningA.Machining.nStage < MachiningB.Machining.nStage then return 1 elseif MachiningA.Machining.nStage > MachiningB.Machining.nStage then return -1 else return 0 end end, } ------------------------------------------------------------------------------------------------------------- function MachiningLib.CompareMachinings( MachiningA, MachiningB) -- itera le SortingComparisonRules una per volta. Se non vi è alcuna scelta va avanti (result = 0), altrimenti ritorna true/false in base al risultato for i = 1, #SortingComparisonRules do local CompareFunction = SortingComparisonRules[i] local result = CompareFunction( MachiningA, MachiningB) if result ~= 0 then return result > 0 end end return false end ------------------------------------------------------------------------------------------------------------- local function OrderMachining( MachiningOptList) -- Recupero l'identificativo del gruppo di lavoro corrente for i = 1, #MachiningOptList - 1 do local idSource = DB_MACH_APPLIED[MachiningOptList[i+1]].nOperationId local idRef = DB_MACH_APPLIED[MachiningOptList[i]].nOperationId EgtRelocateGlob( idSource, idRef, GDB_IN.AFTER) end end ------------------------------------------------------------------------------------------------------------- function MachiningLib.ShortestPathSorting() local nStartIndex local i = 1 while i <= #DB_MACH_APPLIED do local nMachInDisp = 0 local MachiningOptList = {} local GroupInfo = {} -- se è una lavorazione if DB_MACH_APPLIED[i].sType ~= 'DISP' then -- inizio nStartIndex = i EgtOptMachInit() -- leggere feed dalla macchina EgtOptMachSetFeeds( 10000, 2000) -- tra gruppi (stage) non si può ottimizzare EgtOptMachSetAllGroupsDependencesAsMandatory( true) EgtOptMachSetOptimizationForGroups( true) -- se è una lavorazione while DB_MACH_APPLIED[i] and DB_MACH_APPLIED[i].sType ~= 'DISP' do local ptMinX, ptMaxX -- aggiungo lavorazioni local nToolIndex = MACHININGS[DB_MACH_APPLIED[i].nIndexInMachinings].Machining.nToolIndex EgtOptMachAddTool( nToolIndex, 2, 2) -- , [ num dTC_X, num dTC_Y, num dTC_Z, num dTC_A, num dTC_B, num dTC_C]) -- viene eseguito prima il gruppo con indice più alto, quindi si inverte indice dato che lo stage è dal più piccolo al più grande local nOperationId = DB_MACH_APPLIED[i].nOperationId local nGroup = 10 - MACHININGS[DB_MACH_APPLIED[i].nIndexInMachinings].Machining.nStage local MachStartAxesPos = DB_MACH_APPLIED[i].MachStartAxesPos local MachEndAxesPos = DB_MACH_APPLIED[i].MachEndAxesPos EgtOptMachAddMachining( i, nToolIndex, nGroup, MachStartAxesPos, MachEndAxesPos) table.insert( MachiningOptList, i) -- si salvano i punti minimi e massimi tra tutte le lavorazioni di ogni gruppo if MachStartAxesPos[1] < MachEndAxesPos[1] then ptMinX = MachStartAxesPos ptMaxX = MachEndAxesPos else ptMinX = MachEndAxesPos ptMaxX = MachStartAxesPos end -- si aggiungono le info di gruppo local bFound = false for t = 1, #GroupInfo do if GroupInfo[t].nGroup == nGroup then if GroupInfo[t].ptMin[1] > ptMinX[1] then GroupInfo[t].ptMin = ptMinX end if GroupInfo[t].ptMax[1] < ptMaxX[1] then GroupInfo[t].ptMax = ptMaxX end bFound = true end end -- se non ho trovato, si aggiunge in lista if not bFound then table.insert( GroupInfo, { nGroup = nGroup, nStage = MACHININGS[DB_MACH_APPLIED[i].nIndexInMachinings].Machining.nStage, ptMin = ptMinX, ptMax = ptMaxX}) end nMachInDisp = nMachInDisp + 1 i = i + 1 end local nStopIndex = nStartIndex + nMachInDisp - 1 -- cliclo su tutte le lavorazioni for k = nStartIndex, nStopIndex do for j = nStartIndex, nStopIndex do -- se non è stessa lavorazione, imposto dati e dipendenze if k ~= j then -- se stessa Proc if MACHININGS[DB_MACH_APPLIED[k].nIndexInMachinings].Proc.id == MACHININGS[DB_MACH_APPLIED[j].nIndexInMachinings].Proc.id then -- se lavorazioni della stessa feature, obbligatorio rispettare ordine if MACHININGS[DB_MACH_APPLIED[k].nIndexInMachinings].Machining.nFeatureInternalIndex <= MACHININGS[DB_MACH_APPLIED[j].nIndexInMachinings].Machining.nFeatureInternalIndex then EgtOptMachAddDependence( k, j) end -- se tra due Proc diverse else -- TODO : -- se ci sono dipendenze tra due feature diverse, la dipendenza è obbligatoria! ATTENZIONE A DIPENDENZE INCROCIATE A->B, B->C, C->A end end end end -- definisco gruppo per gruppo la direzione preferita di ottimizzazione for t = 1, #GroupInfo do -- se gruppo "Taglio di coda" l'ordinamento è al contrario if GroupInfo[t].nStage == 3 then -- start point EgtOptMachSetOpenBoundForGroups( GroupInfo[t].nGroup, true, SHP_OB.NEAR_PNT, GroupInfo[t].ptMin[1], GroupInfo[t].ptMin[2], GroupInfo[t].ptMin[3]) -- end point EgtOptMachSetOpenBoundForGroups( GroupInfo[t].nGroup, false, SHP_OB.NEAR_PNT, GroupInfo[t].ptMax[1], GroupInfo[t].ptMax[2], GroupInfo[t].ptMax[3]) -- in tutti gli altri gruppi si ordina sempre dalla testa alla coda else -- start point EgtOptMachSetOpenBoundForGroups( GroupInfo[t].nGroup, true, SHP_OB.NEAR_PNT, GroupInfo[t].ptMax[1], GroupInfo[t].ptMax[2], GroupInfo[t].ptMax[3]) -- end point EgtOptMachSetOpenBoundForGroups( GroupInfo[t].nGroup, false, SHP_OB.NEAR_PNT, GroupInfo[t].ptMin[1], GroupInfo[t].ptMin[2], GroupInfo[t].ptMin[3]) end end -- calcolo ordine lavorazioni MachiningOptList = EgtOptMachCalculate( MachiningOptList) OrderMachining( MachiningOptList) -- fine EgtOptMachTerminate() end i = i + 1 end end ------------------------------------------------------------------------------------------------------------- return MachiningLib