-- 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.IsFeatureHinderingClamping( Proc, Part) local bFeatureHindersClamping local dFeatureMaxNotClampableLengthHead, dFeatureMaxNotClampableLengthTail = FeatureLib.GetFeatureMaxNotClampableLengths( Proc, Part) bFeatureHindersClamping = FeatureLib.IsMachiningLong( max( dFeatureMaxNotClampableLengthHead, dFeatureMaxNotClampableLengthTail), Part, { dMaxSegmentLength = BeamData.LONGCUT_ENDLEN}) return bFeatureHindersClamping end ------------------------------------------------------------------------------------------------------------- function MachiningLib.GetMachiningSteps( bIsSlot, dMachiningDepth, dStep) local MachiningSteps = {} MachiningSteps.nCount = ceil( ( dMachiningDepth - 50 * GEO.EPS_SMALL) / dStep) -- se è una slot, dMachiningDepth è l'altezza della tasca e nel calcolo step si deve considerare lo spessore dell'utensile if bIsSlot then if MachiningSteps.nCount > 1 then MachiningSteps.dStep = ( dMachiningDepth - dStep) / ( MachiningSteps.nCount - 1) else MachiningSteps.dStep = dMachiningDepth MachiningSteps.nCount = 1 end else MachiningSteps.dStep = dStep 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) function MachiningLib.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 MachiningLib.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.CheckOutOfStrokeFromPoints( 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 = {} local LeadInOutOptionalParameters = { sRestLengthSideForPreSimulation = OptionalParameters.sRestLengthSideForPreSimulation, bCannotSplitRestLength = OptionalParameters.bCannotSplitRestLength, bMoveAfterSplit = bMoveAfterSplit } -- attacco perpendicolare local PerpendicularLeadInOut = LeadInOutLib.CalculateLeadInOut( 'Perpendicular', Parameters, LeadInOutOptionalParameters) -- 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.CheckOutOfStrokeFromPoints( PointsOnToolTipCenter, vtHead, nSCC, Tool) -- attacco perpendicolare non in extracorsa: si verifica se è in collisione if not bOutOfStrokePerpendicular then CheckCollisionOptionalParameters.PointsToCheck = {} table.insert( CheckCollisionOptionalParameters.PointsToCheck, PerpendicularLeadInOut.LeadIn.ptPoint) table.insert( CheckCollisionOptionalParameters.PointsToCheck, PerpendicularLeadInOut.LeadOut.ptPoint) local bCollisionFoundPerpendicular, bMoveAfterSplitPerpendicular = PreSimulationLib.CheckCollision( sBladeEngagement, CheckCollisionParameters, CheckCollisionOptionalParameters) -- attacco perpendicolare possibile if not bCollisionFoundPerpendicular then LeadInOut.Perpendicular = PerpendicularLeadInOut LeadInOut.Perpendicular.bMoveAfterSplit = bMoveAfterSplitPerpendicular end 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, LeadInOutOptionalParameters) -- 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.CheckOutOfStrokeFromPoints( PointsOnToolTipCenter, vtHead, nSCC, Tool) -- attacco tangenziale non in extracorsa: si verifica se è in collisione if not bOutOfStrokeTangent then CheckCollisionOptionalParameters.PointsToCheck = {} table.insert( CheckCollisionOptionalParameters.PointsToCheck, TangentLeadInOut.LeadIn.ptPoint) table.insert( CheckCollisionOptionalParameters.PointsToCheck, TangentLeadInOut.LeadOut.ptPoint) local bCollisionFoundTangent, bMoveAfterSplitTangent = PreSimulationLib.CheckCollision( sBladeEngagement, CheckCollisionParameters, CheckCollisionOptionalParameters) -- 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 = {} -- direzione utensile e fipo fresa obbligatori, altrimenti si esce if not ToolSearchParameters.vtToolDirection or not ToolSearchParameters.sMillShape then return ToolInfo end 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 -- TODO da sostituire con test collisione come lama local bIsFromBottom = ToolSearchParameters.vtToolDirection:getZ() < - 10 * GEO.EPS_SMALL local bIsFromTop = ToolSearchParameters.vtToolDirection:getZ() > 10 * GEO.EPS_SMALL local bIsSlanted = abs( ToolSearchParameters.vtToolDirection:getY()) > 0.707 local bIsOnHeadOrTail = Proc.AffectedFaces.bLeft or Proc.AffectedFaces.bRight if ( ( bIsFromBottom and TOOLS[i].SetupInfo.HeadType.bTop) or ( bIsFromTop and TOOLS[i].SetupInfo.HeadType.bBottom)) and ( not bIsSlanted) and ( not bIsOnHeadOrTail) 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 -- 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 per Engagement serviranno (opzionali) sBlockedAxis e vtAux 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 sRestLengthSideForPreSimulation = ToolSearchParameters.sRestLengthSideForPreSimulation or 'Tail' local bCannotSplitRestLength = ToolSearchParameters.bCannotSplitRestLength or false local bDisableRealElevationCheck = ToolSearchParameters.bDisableRealElevationCheck 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, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength, bDisableRealElevationCheck = bDisableRealElevationCheck } 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 MachiningLib.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 0) + ( 10 * GEO.EPS_SMALL) end if not ToolSearchParameters.dMinToolDiameter then ToolSearchParameters.dMinToolDiameter = ToolSearchParameters.dToolDiameter - ( ToolSearchParameters.dDiameterTolerance or 0) - ( 10 * 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 elseif PreSimulationLib.CheckOutOfStrokeFromPoints( ToolSearchParameters.ptCheckOutStroke, ToolSearchParameters.vtToolDirection, 0, TOOLS[i]) 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 che verifica se ha senso riprocessare tutto dall'inizio, -- perchè ci sono buone probabilità che l'errore trovato in fase di applicazione si possa risolvere (escludendo la lavorazione scelta in precedenza) local function IsReProcessWorthIt( nError) local bReProcess = false -- errori di Extra-corsa if nError == 2110 or nError == 2216 or nError == 2318 or nError == 2424 or nError == 2508 then bReProcess = true end return bReProcess end ------------------------------------------------------------------------------------------------------------- -- funzione per aggiungere una nuova lavorazione function MachiningLib.AddOperations( MACHININGS, Part, sRotation) local nErr local sErr = '' local bAreAllMachiningApplyOk = true local bSplitExecuted = false local bTryToReProcess = 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 non esistono punto iniziale o finale, si disattiva operazione if not MachExtraInfo.MachStartAxesPos or not MachExtraInfo.MachEndAxesPos then EgtSetOperationMode( nOperationId, false) end -- se errore in applicazione if not bIsApplyOk then bAreAllMachiningApplyOk = false nErr, sErr = EgtGetLastMachMgrError() EgtSetOperationMode( nOperationId, false) local nOffsetIndex = EgtIf( Part.bPartInCombiIsInverted, 4, 0) local CurrProc = PROCESSINGS[MACHININGS[i].Proc.nIndexPartInParts].Rotation[MACHININGS[i].Proc.nIndexRotation+nOffsetIndex][MACHININGS[i].Proc.nIndexInVProc] -- si annulla la feature scelta, in modo che un successivo ricalcolo non la tenga in considerazione CurrProc.AvailableStrategies[CurrProc.nIndexBestStrategy].Result.sStatus = 'Not-Applicable' CurrProc.AvailableStrategies[CurrProc.nIndexBestStrategy].Result.sInfo = 'REJECTED (' .. sErr .. ')' CurrProc.ChosenStrategy= nil -- si verifica se vale la pena riprocessare tutto (perchè si pensa possa risolvere il problema) if IsReProcessWorthIt( nErr) then bTryToReProcess = true end -- 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 ignorato, 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( Part.idRaw, BeamData.ptOriXR, BeamData.dPosXR, BeamData.RAW_OFFSET) -- se grezzo successivo senza pezzi e finale, va tolto local nNextRawId = EgtGetNextRawPart( Part.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) -- posizione iniziale considerando eventuiali prerotazioni local nRealInitialPosition = Part.nInitialPosition if sRotation == 'DOWN' then local nRotation = EgtIf( nRealInitialPosition + 2 > 4, nRealInitialPosition + 2 - 4, nRealInitialPosition + 2) - 1 BeamLib.RotateRawPart( Part, nRotation) EgtSetInfo( idDisp, 'ROT', -2) EgtSetInfo( idDisp, 'TYPE', 'MID2') elseif sRotation == 'SIDE' then local nRotation = EgtIf( nRealInitialPosition + 1 > 4, nRealInitialPosition + 1 - 4, nRealInitialPosition + 1) - 1 BeamLib.RotateRawPart( Part, nRotation) EgtSetInfo( idDisp, 'ROT', -1) EgtSetInfo( idDisp, 'TYPE', 'MID2') else local nRotation = nRealInitialPosition - 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, false 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, bTryToReProcess 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.DRILLING then local function fact(n) return n == 0 and 1 or n * fact(n - 1) end local nSteps = ceil( Machining.sDepth / Machining.dStep) local dLengthEachStep = Machining.sDepth / nSteps -- numero dei movimenti a step, compresi andata e ritorno per scarico truciolo local nTotStepMovement = 2 * fact( nSteps) -- in feed si lavorano solo gli step local dFeedTime = ( ( dLengthEachStep + Machining.dStartSafetyLength) * nSteps) / dToolFeed -- ritorno per scaricare e approccio al prossimo step sono in feed finale local dEndFeedTime = ( dLengthEachStep * ( nTotStepMovement - nSteps) + EgtMdbGetGeneralParam( MCH_GP.SAFEZ) * 2) / dToolEndFeed dLengthToMachineAllStepsWithLeadInOut = dLengthEachStep * nTotStepMovement dTimeToMachineTotal = dFeedTime + dEndFeedTime elseif 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 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 EgtOptMachInit() -- feed dalla macchina -- TODO abbassare per considere accelerazioni?? SetupInfo = BeamData.GetSetupInfo() EgtOptMachSetFeeds( SetupInfo.dFeedLinearAxesAverage or 10000, SetupInfo.dFeedRotativeAxesAverage or 1000) -- 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 if DB_MACH_APPLIED[i].MachStartAxesPos and DB_MACH_APPLIED[i].MachEndAxesPos then -- se lavorazione non attiva non va considerata local nOperationId = DB_MACH_APPLIED[i].nOperationId if EgtGetOperationMode( nOperationId) then 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 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 end end i = i + 1 end -- cliclo su tutte le lavorazioni for k = 1, #MachiningOptList do if not DB_MACH_APPLIED[MachiningOptList[k]].Dependances then DB_MACH_APPLIED[MachiningOptList[k]].Dependances = {} end for j = 1, #MachiningOptList do -- se non è stessa lavorazione, imposto dati e dipendenze if k ~= j then local MachiningK = MACHININGS[DB_MACH_APPLIED[MachiningOptList[k]].nIndexInMachinings] local MachiningJ = MACHININGS[DB_MACH_APPLIED[MachiningOptList[j]].nIndexInMachinings] -- se stessa Proc if MachiningK.Proc.id == MachiningJ.Proc.id then -- se lavorazioni della stessa feature, obbligatorio rispettare ordine if MachiningK.Machining.nFeatureInternalIndex < MachiningJ.Machining.nFeatureInternalIndex then local bOk = EgtOptMachAddDependence( MachiningOptList[k], MachiningOptList[j]) if not bOk then error( 'Machining sorting : error in dependencies') end table.insert( DB_MACH_APPLIED[MachiningOptList[k]].Dependances, MachiningOptList[j]) -- se l'indice interno è uguale, sono cloni: si ordinano in base all'ordine in tabella elseif MachiningK.Machining.nFeatureInternalIndex == MachiningJ.Machining.nFeatureInternalIndex then if k < j then local bOk = EgtOptMachAddDependence( MachiningOptList[k], MachiningOptList[j]) if not bOk then error( 'Machining sorting : error in dependencies') end table.insert( DB_MACH_APPLIED[MachiningOptList[k]].Dependances, MachiningOptList[j]) end 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