-- 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( 'BeamData') local BeamLib = require( 'BeamLib') local FeatureLib = require( 'FeatureLib') local FaceData = require ( 'FaceData') 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 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 -- 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 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.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 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 -- 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 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.vtN) ~= 'table' then error( 'FindBlade : missing tool direction') 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 -- parametri opzionali ToolSearchParameters.dElevation = ToolSearchParameters.dElevation or 0 ToolSearchParameters.bForceLongcutBlade = ToolSearchParameters.bForceLongcutBlade or false local nBestToolIndex local dBestToolResidualDepth = 0 for i = 1, #TOOLS do local bIsToolCompatible = false if TOOLS[i].sFamily == 'SAWBLADE' 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 -- check angolo limite lama if TOOLS[i].SetupInfo.HeadType.bTop and ToolSearchParameters.vtN:getZ() < TOOLS[i].SetupInfo.GetMinNz( ToolSearchParameters.vtN, TOOLS[i]) - GEO.EPS_ZERO then bIsToolCompatible = false end if TOOLS[i].SetupInfo.HeadType.bBottom and ToolSearchParameters.vtN:getZ() > TOOLS[i].SetupInfo.GetMaxNz( ToolSearchParameters.vtN, TOOLS[i]) + GEO.EPS_ZERO then bIsToolCompatible = false end if bIsToolCompatible then -- TODO gestire accorciamento massimo materiale per inclinazione local dCurrentResidualDepth = ToolSearchParameters.dElevation - TOOLS[i].dMaxDepth if not nBestToolIndex or ( dBestToolResidualDepth > 0 and dCurrentResidualDepth <= 10 * GEO.EPS_SMALL) then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth else -- prediligo utensile per tagli lunghi, se richiesto if ToolSearchParameters.bForceLongcutBlade and not TOOLS[nBestToolIndex].bIsUsedForLongCut and TOOLS[i].bIsUsedForLongCut then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth else -- entrambi completi if dBestToolResidualDepth <= 10 * GEO.EPS_SMALL and dCurrentResidualDepth <= 10 * GEO.EPS_SMALL then -- si sceglie quello con le performance migliori if TOOLS[i].dPerformanceIndex > TOOLS[nBestToolIndex].dPerformanceIndex + 10 * GEO.EPS_SMALL then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth 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 end end end end end end ToolInfo.nToolIndex = nBestToolIndex ToolInfo.dResidualDepth = dBestToolResidualDepth 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 + 100 * GEO.EPS_SMALL end if not ToolSearchParameters.dMinToolDiameter then ToolSearchParameters.dMinToolDiameter = ToolSearchParameters.dToolDiameter - 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 -- 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 -- 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 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 dello stesso diametro del foro da lavorare if abs( TOOLS[i].dDiameter - ToolSearchParameters.dToolDiameter) < 10 * GEO.EPS_SMALL then nBestToolIndex = i dBestToolResidualDepth = dCurrentResidualDepth -- 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 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 -- 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 -- scelgo utensile con rapporto lunghezza / diametro minore if 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.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 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 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 if Machining.CloneStepsRadial and Machining.CloneStepsRadial.nCount > 1 then if not AuxiliaryData then AuxiliaryData = {} end AuxiliaryData.Clones = {} local dOriginalRadialOffset = Machining.dRadialOffset local dOriginalLeadInPerpDistance = Machining.LeadIn.dPerpDistance local dOriginalLeadOutPerpDistance = Machining.LeadOut.dPerpDistance for i = 1, Machining.CloneStepsRadial.nCount do AuxiliaryData.Clones[i] = {} AuxiliaryData.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 AuxiliaryData.Clones[i].LeadIn = {} AuxiliaryData.Clones[i].LeadOut = {} AuxiliaryData.Clones[i].LeadIn.dPerpDistance = dOriginalLeadInPerpDistance - AuxiliaryData.Clones[i].dRadialOffset + dOriginalRadialOffset AuxiliaryData.Clones[i].LeadOut.dPerpDistance = dOriginalLeadOutPerpDistance - AuxiliaryData.Clones[i].dRadialOffset + dOriginalRadialOffset end end elseif Machining.CloneStepsLongitudinal and Machining.CloneStepsLongitudinal.nCount > 1 then if not AuxiliaryData then AuxiliaryData = {} end AuxiliaryData.Clones = {} local dOriginalRadialOffset = Machining.dRadialOffset for i = Machining.CloneStepsLongitudinal.nCount, 1, -1 do AuxiliaryData.Clones[i] = {} AuxiliaryData.Clones[i].dRadialOffset = dOriginalRadialOffset + Machining.CloneStepsLongitudinal.dStep * ( i - 1) end end bMachiningAdded = AddNewMachining( Proc, Machining, AuxiliaryData) 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 ( sRotation == 'STD' and not MACHININGS[i].Proc.bDown and not MACHININGS[i].Proc.bSide) 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 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) 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 -- si salva ingombro lavorazione attuale e fasi successive else -- 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 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() local nPhase = EgtGetCurrPhase() local idDisp = EgtGetPhaseDisposition( nPhase) if sRotation == 'DOWN' then BeamLib.RotateRawPart( Part, Part.nInitialPosition - 3) EgtSetInfo( idDisp, 'ROT', -2) EgtSetInfo( idDisp, 'TYPE', 'MID2') elseif sRotation == 'SIDE' then BeamLib.RotateRawPart( Part, Part.nInitialPosition - 2) EgtSetInfo( idDisp, 'ROT', -1) EgtSetInfo( idDisp, 'TYPE', 'MID2') else BeamLib.RotateRawPart( Part, Part.nInitialPosition - 1) 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 -- 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 = ceil( ( dDepthToMachine - 50 * GEO.EPS_SMALL) / TOOLS[Machining.nToolIndex].dStep) local nAddGroupId = BeamLib.GetAddGroup( Part.id) -- 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], nAddGroupId) -- 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, nAddGroupId ) 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, nAddGroupId) 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 -- le geometrie create si settano da cancellare EgtSetLevel( idFaceContour, GDB_LV.TEMP) EgtSetLevel( idPocketingPath, GDB_LV.TEMP) EgtSetLevel( idTestCurve, GDB_LV.TEMP) 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 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 = { -- ordine interno alla feature function( MachiningA, MachiningB) if MachiningA.Proc.id == MachiningB.Proc.id then if MachiningA.Machining.nFeatureInternalIndex < MachiningB.Machining.nFeatureInternalIndex then return 1 elseif MachiningA.Machining.nFeatureInternalIndex > MachiningB.Machining.nFeatureInternalIndex then return -1 end end return 0 end, -- 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, -- dipendenze -- TODO da fare -- segment function ( MachiningA, MachiningB) if MachiningA.Machining.nPartSegment < MachiningB.Machining.nPartSegment then return 1 elseif MachiningA.Machining.nPartSegment > MachiningB.Machining.nPartSegment then return -1 else return 0 end end, -- testa -- TODO da fare -- famiglia utensile function ( MachiningA, MachiningB) -- TODO tirare fuori da qua?? local ToolFamilyOrder = { SAWBLADE = 1, DRILLBIT = 2, MILL = 3, MORTISE = 4 } local nToolFamilyOrderA = ToolFamilyOrder[ TOOLS[ MachiningA.Machining.nToolIndex].sFamily] local nToolFamilyOrderB = ToolFamilyOrder[ TOOLS[ MachiningB.Machining.nToolIndex].sFamily] if nToolFamilyOrderA < nToolFamilyOrderB then return 1 elseif nToolFamilyOrderA > nToolFamilyOrderB then return -1 else return 0 end end, -- performance utensile function( MachiningA, MachiningB) local dToolPerformanceIndexA = TOOLS[MachiningA.Machining.nToolIndex].dPerformanceIndex local dToolPerformanceIndexB = TOOLS[MachiningB.Machining.nToolIndex].dPerformanceIndex if dToolPerformanceIndexA > dToolPerformanceIndexB then return 1 elseif dToolPerformanceIndexA < dToolPerformanceIndexB then return -1 else return 0 end end, -- probabilmente arrivati qui significa che gli utensili A e B sono gli stessi -- se così non fosse e tutte le caratteristiche sopra sono uguali, ordine alfabetico function( MachiningA, MachiningB) local sToolNameA = TOOLS[MachiningA.Machining.nToolIndex].sName local sToolNameB = TOOLS[MachiningB.Machining.nToolIndex].sName if sToolNameA < sToolNameB then return 1 elseif sToolNameA > sToolNameB then return -1 else return 0 end end, -- lato di lavoro -- TODO questo, insieme all'ordinamento X, andrà sostituito dallo shortest path pesato sulla quantità di rotazione della testa function( MachiningA, MachiningB) if MachiningA.Machining.vtToolDirection:getY() < -0.174 and MachiningB.Machining.vtToolDirection:getY() >= 0.174 then return 1 elseif MachiningA.Machining.vtToolDirection:getY() >= 0.174 and MachiningB.Machining.vtToolDirection:getY() < -0.174 then return -1 else return 0 end end, -- ordinamento X -- TODO questo andrà sostituito dallo shortest path pesato sulla quantità di rotazione della testa function( MachiningA, MachiningB) local bIsMachiningOnFront = MachiningA.Machining.vtToolDirection:getY() < 10 * GEO.EPS_SMALL local nResult = 0 -- se lavorazione davanti ordine testa->coda if bIsMachiningOnFront then if MachiningA.Machining.ptCenter:getX() > MachiningB.Machining.ptCenter:getX() + 10 * GEO.EPS_SMALL then nResult = 1 elseif MachiningA.Machining.ptCenter:getX() < MachiningB.Machining.ptCenter:getX() - 10 * GEO.EPS_SMALL then nResult = -1 end -- se lavorazione dietro ordine coda->testa else if MachiningA.Machining.ptCenter:getX() < MachiningB.Machining.ptCenter:getX() - 10 * GEO.EPS_SMALL then nResult = 1 elseif MachiningA.Machining.ptCenter:getX() > MachiningB.Machining.ptCenter:getX() + 10 * GEO.EPS_SMALL then nResult = -1 end end return nResult 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 ------------------------------------------------------------------------------------------------------------- return MachiningLib