771 lines
35 KiB
Lua
771 lines
35 KiB
Lua
-- Strategia: STR0003
|
|
-- Descrizione
|
|
-- Lama + motosega per slot
|
|
-- Feature: tipo lapjoint
|
|
|
|
-- carico librerie
|
|
local BeamLib = require( 'BeamLib')
|
|
local BeamData = require( 'BeamData')
|
|
local MachiningLib = require( 'MachiningLib')
|
|
local FeatureData = require( 'FeatureData')
|
|
|
|
-- Tabella per definizione modulo
|
|
local STR0003 = {}
|
|
local Strategy = {}
|
|
local Blade = {}
|
|
local Chainsaw = {}
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
local function IsTopologyOk( Proc)
|
|
if Proc.Topology.sName == 'Pocket-5-Blind' or
|
|
Proc.Topology.sName == 'Groove-3-Through' or
|
|
Proc.Topology.sName == 'Groove-4-Blind' or
|
|
Proc.Topology.sName == 'Tunnel-4-Through' then
|
|
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- TODO da eliminare
|
|
function GetToolFromMachining( sMachiningName)
|
|
local Tool = {}
|
|
if EgtMdbSetCurrMachining( sMachiningName) then
|
|
local sTuuid = EgtMdbGetCurrMachiningParam( MCH_MP.TUUID)
|
|
if EgtTdbSetCurrTool( EgtTdbGetToolFromUUID( sTuuid) or '') then
|
|
Tool.Name = EgtTdbGetCurrToolParam( MCH_TP.NAME)
|
|
Tool.IsCCW = ( EgtMdbGetCurrMachiningParam( MCH_MP.SPEED) < 0)
|
|
Tool.Type = EgtTdbGetCurrToolParam( MCH_TP.TYPE)
|
|
Tool.Diameter = EgtTdbGetCurrToolParam( MCH_TP.DIAM) or 0
|
|
-- lama
|
|
if Tool.Type == MCH_TY.SAW_STD or Tool.Type == MCH_TY.SAW_FLAT then
|
|
Tool.Thickness = EgtTdbGetCurrToolParam(MCH_TP.THICK) or 0
|
|
Tool.MaxDepth = EgtTdbGetCurrToolMaxDepth() or 0
|
|
Tool.SideStep = EgtTdbGetCurrToolValInNotes( MCH_TP.USERNOTES, 'SIDESTEP', 'd')
|
|
-- sega a catena
|
|
elseif Tool.Type == MCH_TY.MORTISE_STD then
|
|
Tool.Length = EgtTdbGetCurrToolParam( MCH_TP.LEN) or 0
|
|
Tool.MaxMat = EgtTdbGetCurrToolParam( MCH_TP.MAXMAT) or 0
|
|
Tool.Width = EgtTdbGetCurrToolParam( MCH_TP.DIAM) or 0
|
|
Tool.Thickness = EgtTdbGetCurrToolParam( MCH_TP.THICK) or 0
|
|
Tool.CornerRadius = EgtTdbGetCurrToolParam( MCH_TP.CORNRAD) or 0
|
|
-- altri utensili al momento non previsti
|
|
else
|
|
error( 'Wrong tool type')
|
|
end
|
|
end
|
|
end
|
|
|
|
return Tool
|
|
end
|
|
|
|
|
|
local function CalculateLeadInOut( Machining, EdgeToMachine)
|
|
-- TODO implementare le funzioni di Tool Collision Avoidance (vedi wiki e FacesBysaw -> CalcLeadInOutPerpGeom)
|
|
|
|
-- si determina l'eventuale riduzione da applicare in caso di inizio o fine chiusi
|
|
local bIsMortising = ( Machining.nType == MCH_OY.MORTISING)
|
|
local dAddLengthToReduce = 0
|
|
if bIsMortising then
|
|
dAddLengthToReduce = TOOLS[Machining.nToolIndex].dDiameter / 2
|
|
else
|
|
dAddLengthToReduce = sqrt( Machining.dDepthToMachine * TOOLS[Machining.nToolIndex].dDiameter - Machining.dDepthToMachine * Machining.dDepthToMachine)
|
|
end
|
|
|
|
if Machining.bInvert then
|
|
Machining.bIsStartClosed, Machining.bIsEndClosed = Machining.bIsEndClosed, Machining.bIsStartClosed
|
|
end
|
|
|
|
local LeadIn = {}
|
|
local LeadOut = {}
|
|
LeadIn.dStartAddLength = 0
|
|
LeadOut.dEndAddLength = 0
|
|
if not bIsMortising then
|
|
LeadIn.nType = MCH_MILL_LI.LINEAR
|
|
LeadOut.nType = MCH_MILL_LI.LINEAR
|
|
LeadIn.dTangentDistance = 0
|
|
LeadOut.dTangentDistance = 0
|
|
if EdgeToMachine.dElevation > -10 * GEO.EPS_SMALL then
|
|
LeadIn.dPerpDistance = EdgeToMachine.dElevation + BeamData.CUT_SIC
|
|
LeadOut.dPerpDistance = EdgeToMachine.dElevation + BeamData.CUT_SIC
|
|
else
|
|
LeadIn.dPerpDistance = BeamData.CUT_SIC
|
|
LeadOut.dPerpDistance = BeamData.CUT_SIC
|
|
end
|
|
LeadIn.dElevation = 0
|
|
LeadOut.dElevation = 0
|
|
LeadIn.dCompLength = 0
|
|
LeadOut.dCompLength = 0
|
|
if Machining.bIsStartClosed and Machining.bIsEndClosed then
|
|
LeadIn.dStartAddLength = -dAddLengthToReduce
|
|
LeadOut.dEndAddLength = -dAddLengthToReduce
|
|
elseif Machining.bIsStartClosed then
|
|
LeadIn.dStartAddLength = -dAddLengthToReduce
|
|
-- eventuale correzione per accorciamento maggiore di larghezza tasca
|
|
LeadOut.dEndAddLength = max( -LeadIn.dStartAddLength - EdgeToMachine.dLength + 10 * BeamData.CUT_EXTRA, BeamData.CUT_EXTRA)
|
|
elseif Machining.bIsEndClosed then
|
|
LeadOut.dEndAddLength = -dAddLengthToReduce
|
|
-- eventuale correzione per accorciamento maggiore di larghezza tasca
|
|
LeadIn.dStartAddLength = max( -LeadOut.dEndAddLength - EdgeToMachine.dLength + 10 * BeamData.CUT_EXTRA, BeamData.CUT_EXTRA)
|
|
else
|
|
LeadIn.dStartAddLength = BeamData.CUT_EXTRA
|
|
LeadOut.dEndAddLength = BeamData.CUT_EXTRA
|
|
end
|
|
else
|
|
if Machining.bIsStartClosed then
|
|
LeadIn.dStartAddLength = -dAddLengthToReduce
|
|
else
|
|
LeadIn.dStartAddLength = BeamData.CUT_EXTRA
|
|
end
|
|
if Machining.bIsEndClosed then
|
|
LeadOut.dEndAddLength = -dAddLengthToReduce
|
|
else
|
|
LeadOut.dEndAddLength = BeamData.CUT_EXTRA
|
|
end
|
|
end
|
|
|
|
return LeadIn, LeadOut
|
|
end
|
|
|
|
|
|
local function AddNewMachining( Machining, b3Raw)
|
|
local sErr = ''
|
|
local nOperationId = EgtAddMachining( Machining.sOperationName, Machining.Nsame)
|
|
if not nOperationId then
|
|
sErr = 'Error adding machining ' .. Machining.sOperationName .. '-' .. Machining.sName
|
|
EgtOutLog( sErr)
|
|
return false, sErr
|
|
end
|
|
|
|
-- impostazione parametri lavorazione
|
|
local sUserNotes = EgtGetMachiningParam( MCH_MP.USERNOTES)
|
|
EgtSetMachiningGeometry( Machining.Geometry)
|
|
EgtSetMachiningParam( MCH_MP.FACEUSE, Machining.nFaceuse)
|
|
EgtSetMachiningParam( MCH_MP.SCC, Machining.nSCC)
|
|
EgtSetMachiningParam( MCH_MP.INVERT, Machining.bInvert)
|
|
EgtSetMachiningParam( MCH_MP.WORKSIDE, Machining.nWorkside)
|
|
EgtSetMachiningParam( MCH_MP.TOOLINVERT, Machining.bToolInvert)
|
|
EgtSetMachiningParam( MCH_MP.OFFSR, Machining.dRadialOffset)
|
|
EgtSetMachiningParam( MCH_MP.OFFSL, Machining.dLongitudinalOffset)
|
|
if Machining.Type ~= MCH_OY.MORTISING then
|
|
EgtSetMachiningParam( MCH_MP.LEADINTYPE, Machining.LeadIn.nType)
|
|
EgtSetMachiningParam( MCH_MP.LEADOUTTYPE, Machining.LeadOut.nType)
|
|
EgtSetMachiningParam( MCH_MP.LITANG, Machining.LeadIn.dTangentDistance)
|
|
EgtSetMachiningParam( MCH_MP.LOTANG, Machining.LeadOut.dTangentDistance)
|
|
EgtSetMachiningParam( MCH_MP.LIPERP, Machining.LeadIn.dPerpDistance)
|
|
EgtSetMachiningParam( MCH_MP.LOPERP, Machining.LeadOut.dPerpDistance)
|
|
EgtSetMachiningParam( MCH_MP.LIELEV, Machining.LeadIn.dElevation)
|
|
EgtSetMachiningParam( MCH_MP.LOELEV, Machining.LeadOut.dElevation)
|
|
EgtSetMachiningParam( MCH_MP.LICOMPLEN, Machining.LeadIn.dCompLength)
|
|
EgtSetMachiningParam( MCH_MP.LOCOMPLEN, Machining.LeadOut.dCompLength)
|
|
end
|
|
EgtSetMachiningParam( MCH_MP.STARTADDLEN, Machining.LeadIn.dStartAddLength)
|
|
EgtSetMachiningParam( MCH_MP.ENDADDLEN, Machining.LeadOut.dEndAddLength)
|
|
if Machining.Steps then
|
|
if Machining.Steps.nStepType then
|
|
EgtSetMachiningParam( MCH_MP.STEPTYPE, Machining.Steps.nStepType)
|
|
end
|
|
if Machining.Steps.dStepLength then
|
|
EgtSetMachiningParam( MCH_MP.STEP, Machining.Steps.dStepLength)
|
|
end
|
|
end
|
|
EgtSetMachiningParam( MCH_MP.BLOCKEDAXIS, BeamLib.GetBlockedAxis( Machining.sName, Machining.BlockedAxis.sOrientation, b3Raw, Machining.BlockedAxis.vtN, Machining.BlockedAxis.vtOut))
|
|
if Machining.nType == MCH_OY.MORTISING then
|
|
EgtSetMachiningParam( MCH_MP.INITANGS, BeamLib.GetChainSawInitAngs( Machining.SuggestedAngles.vtN, Machining.SuggestedAngles.vtOrtho, Machining.SuggestedAngles.nIndex))
|
|
end
|
|
EgtSetMachiningParam( MCH_MP.OVERL, Machining.dOverlap)
|
|
EgtSetMachiningParam( MCH_MP.STARTPOS, max( Machining.dStartSafetyLength, EgtGetMachiningParam( MCH_MP.STARTPOS)))
|
|
if Machining.dMaxElev then
|
|
sUserNotes = EgtSetValInNotes( sUserNotes, 'MaxElev', Machining.dMaxElev)
|
|
end
|
|
EgtSetMachiningParam( MCH_MP.USERNOTES, sUserNotes)
|
|
|
|
local bIsApplyOk = MachiningLib.ApplyMachining( true, false)
|
|
if not bIsApplyOk then
|
|
local nErr
|
|
nErr, sErr = EgtGetLastMachMgrError()
|
|
-- se mortasatura e l'errore è compatibile (Axes values not calculable) si prova con l'altra configurazione dell'asse bloccato
|
|
-- TODO valutare se c'è modo di capire preventivamente la configurazione dell'asse bloccato e quindi rimuovere questa parte di codice
|
|
if Machining.nType == MCH_OY.MORTISING and nErr == 2507 then
|
|
if Machining.BlockedAxis.sOrientation == 'perpendicular' then
|
|
Machining.BlockedAxis.sOrientation = 'parallel'
|
|
else
|
|
Machining.BlockedAxis.sOrientation = 'perpendicular'
|
|
end
|
|
EgtSetMachiningParam( MCH_MP.BLOCKEDAXIS, BeamLib.GetBlockedAxis( Machining.sName, Machining.BlockedAxis.sOrientation, b3Raw, Machining.BlockedAxis.vtN, Machining.BlockedAxis.vtOut))
|
|
if Machining.SuggestedAngles.nIndex == 1 then
|
|
Machining.SuggestedAngles.nIndex = 2
|
|
else
|
|
Machining.SuggestedAngles.nIndex = 1
|
|
end
|
|
EgtSetMachiningParam( MCH_MP.INITANGS, BeamLib.GetChainSawInitAngs( Machining.SuggestedAngles.vtN, Machining.SuggestedAngles.vtOrtho, Machining.SuggestedAngles.nIndex))
|
|
sErr = ''
|
|
bIsApplyOk = EgtApplyMachining( true, false)
|
|
end
|
|
if not bIsApplyOk then
|
|
nErr, sErr = EgtGetLastMachMgrError()
|
|
EgtSetOperationMode( nOperationId, false)
|
|
return false, sErr
|
|
end
|
|
end
|
|
|
|
return true, sErr
|
|
end
|
|
|
|
|
|
function Blade.GetSCC( vtMachiningDirection)
|
|
-- TODO implementare SCC come per FacesBySaw
|
|
local nSCC = MCH_SCC.NONE
|
|
if AreSameVectorApprox( vtMachiningDirection, Z_AX()) then
|
|
nSCC = MCH_SCC.ADIR_ZP
|
|
elseif AreOppositeVectorApprox( vtMachiningDirection, Z_AX()) then
|
|
nSCC = MCH_SCC.ADIR_ZM
|
|
elseif AreSameVectorApprox( vtMachiningDirection, Y_AX()) then
|
|
nSCC = MCH_SCC.ADIR_YP
|
|
elseif AreOppositeVectorApprox( vtMachiningDirection, Y_AX()) then
|
|
nSCC = MCH_SCC.ADIR_YM
|
|
elseif AreSameVectorApprox( vtMachiningDirection, X_AX()) then
|
|
nSCC = MCH_SCC.ADIR_XP
|
|
elseif AreOppositeVectorApprox( vtMachiningDirection, X_AX()) then
|
|
nSCC = MCH_SCC.ADIR_XM
|
|
end
|
|
|
|
return nSCC
|
|
end
|
|
|
|
|
|
function Blade.CalculateMachiningParameters( Proc, FaceToMachine, EdgeToMachine)
|
|
local Cutting = {}
|
|
Cutting.bCanApply = true
|
|
Cutting.sMessage = ''
|
|
Cutting.idProc = Proc.id
|
|
local dResidualDepth = 0
|
|
|
|
local dPocketHeight = 0
|
|
if Proc.Topology.sFamily == 'Tunnel' then
|
|
dPocketHeight = Proc.MainFaces.TunnelAddedFaces.MiddleFaceTm.dHeight
|
|
else
|
|
if FaceToMachine.Type == 'Long' then
|
|
dPocketHeight = Proc.MainFaces.BottomFace.MainEdges.SideEdges[1].dLength
|
|
elseif FaceToMachine.Type == 'Side' then
|
|
dPocketHeight = Proc.MainFaces.BottomFace.MainEdges.LongEdges[1].dLength
|
|
end
|
|
end
|
|
|
|
-- ricerca utensile
|
|
local ToolSearchParameters = {}
|
|
ToolSearchParameters.dElevation = abs( EdgeToMachine.dElevation)
|
|
ToolSearchParameters.vtToolDirection = EdgeToMachine.vtToolDirection
|
|
ToolSearchParameters.bAllowTopHead = true
|
|
ToolSearchParameters.bAllowBottomHead = false
|
|
ToolSearchParameters.bForceLongcutBlade = Strategy.Parameters.bForceLongcutBlade
|
|
local ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters)
|
|
Cutting.nToolIndex = ToolInfo.nToolIndex
|
|
Cutting.nType = MCH_OY.MILLING
|
|
if not TOOLS[Cutting.nToolIndex].sName then
|
|
Cutting.sMessage = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not applicable - saw blade not found'
|
|
Cutting.bCanApply = false
|
|
EgtOutLog( Cutting.sMessage)
|
|
return Cutting, EdgeToMachine.dElevation
|
|
end
|
|
|
|
-- verifica dimensioni tasca compatibili
|
|
-- se tasca meno spessa della lama la strategia non è applicabile
|
|
if TOOLS[Cutting.nToolIndex].dThickness > dPocketHeight + 10 * GEO.EPS_SMALL then
|
|
Cutting.sMessage = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not applicable - pocket too narrow for saw blade thickness'
|
|
Cutting.bCanApply = false
|
|
EgtOutLog( Cutting.sMessage)
|
|
return Cutting, EdgeToMachine.dElevation
|
|
end
|
|
if #( Proc.MainFaces.SideFaces) > 1 then
|
|
-- se tasca più stretta della lama la strategia non è applicabile
|
|
if TOOLS[Cutting.nToolIndex].dDiameter > EdgeToMachine.dLength + 10 * GEO.EPS_SMALL then
|
|
Cutting.sMessage = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not applicable - pocket too narrow for saw blade diameter'
|
|
Cutting.bCanApply = false
|
|
EgtOutLog( Cutting.sMessage)
|
|
return Cutting, EdgeToMachine.dElevation
|
|
end
|
|
end
|
|
|
|
-- parametri della lavorazione
|
|
-- inizio e fine aperti o chiusi
|
|
Cutting.bIsStartClosed = not EdgeToMachine.bIsStartOpen
|
|
Cutting.bIsEndClosed = not EdgeToMachine.bIsEndOpen
|
|
-- lato di lavoro e inversioni
|
|
if TOOLS[Cutting.nToolIndex].bIsCCW then
|
|
Cutting.nWorkside = MCH_MILL_WS.RIGHT
|
|
Cutting.bInvert = true
|
|
else
|
|
Cutting.nWorkside = MCH_MILL_WS.LEFT
|
|
Cutting.bInvert = false
|
|
end
|
|
if EdgeToMachine.dElevation < -10 * GEO.EPS_SMALL then
|
|
Cutting.bInvert = not Cutting.bInvert
|
|
end
|
|
-- TODO gestire lama da sotto e lama downUp
|
|
if FaceToMachine.vtN:getZ() < - 10 * GEO.EPS_SMALL then
|
|
Cutting.bToolInvert = true
|
|
Cutting.bInvert = not Cutting.bInvert
|
|
else
|
|
Cutting.bToolInvert = false
|
|
end
|
|
-- profondità e offset radiale
|
|
if TOOLS[Cutting.nToolIndex].dMaxDepth > abs( EdgeToMachine.dElevation) - 10 * GEO.EPS_SMALL then
|
|
-- TODO la depth dovrebbe essere quella del machining
|
|
Cutting.dDepthToMachine = abs( EdgeToMachine.dElevation)
|
|
if EdgeToMachine.dElevation > -10 * GEO.EPS_SMALL then
|
|
Cutting.dRadialOffset = 0
|
|
else
|
|
Cutting.dRadialOffset = EdgeToMachine.dElevation
|
|
end
|
|
else
|
|
Cutting.dDepthToMachine = TOOLS[Cutting.nToolIndex].dMaxDepth - 1
|
|
dResidualDepth = EdgeToMachine.dElevation - Cutting.dDepthToMachine
|
|
if EdgeToMachine.dElevation > -10 * GEO.EPS_SMALL then
|
|
Cutting.dRadialOffset = EdgeToMachine.dElevation - Cutting.dDepthToMachine
|
|
else
|
|
Cutting.dRadialOffset = -Cutting.dDepthToMachine
|
|
end
|
|
if EdgeToMachine.dElevation > -10 * GEO.EPS_SMALL and Strategy.Parameters.bApplyOnlyBlade then
|
|
Cutting.sMessage = 'Feature '.. Proc.idFeature .. ' : sawblade elevation (' .. EgtNumToString( EdgeToMachine.dElevation, 1) .. ') bigger than max tool depth (' .. EgtNumToString( Cutting.dDepthToMachine, 1) .. ')'
|
|
EgtOutLog( Cutting.sMessage)
|
|
end
|
|
end
|
|
-- step verticale e offset longitudinale
|
|
Cutting.Steps = MachiningLib.GetMachiningSteps( dPocketHeight, TOOLS[Cutting.nToolIndex].dThickness)
|
|
Cutting.Steps.StepType = MCH_MILL_ST.ONEWAY
|
|
Cutting.dMaxElev = Cutting.Steps.dStepLength * Cutting.Steps.nCount - 10 * GEO.EPS_SMALL
|
|
if Cutting.bToolInvert and Cutting.Steps.nCount > 1 then
|
|
Cutting.dLongitudinalOffset = - dPocketHeight
|
|
else
|
|
Cutting.dLongitudinalOffset = 0
|
|
end
|
|
-- distanza di sicurezza
|
|
Cutting.dStartSafetyLength = 10
|
|
-- overlap
|
|
Cutting.dOverlap = 0
|
|
-- faceuse
|
|
if EdgeToMachine.dElevation > - 10 * GEO.EPS_SMALL then
|
|
Cutting.nFaceuse = BeamLib.GetNearestOrthoOpposite( EdgeToMachine.vtToolDirection)
|
|
else
|
|
Cutting.nFaceuse = BeamLib.GetNearestOrthoOpposite( -EdgeToMachine.vtToolDirection)
|
|
end
|
|
-- SCC
|
|
Cutting.nSCC = Blade.GetSCC( EdgeToMachine.vtToolDirection)
|
|
-- asse bloccato
|
|
Cutting.BlockedAxis = {}
|
|
Cutting.BlockedAxis.sOrientation = 'perpendicular'
|
|
Cutting.BlockedAxis.vtN = FaceToMachine.vtN
|
|
Cutting.BlockedAxis.vtOut = EgtIf( FaceToMachine.vtN:getX() > 0, X_AX(), -X_AX())
|
|
-- approccio e retrazione
|
|
Cutting.LeadIn, Cutting.LeadOut = CalculateLeadInOut( Cutting, EdgeToMachine)
|
|
-- eventuale step orizzontale
|
|
Cutting.HorizontalSteps = {}
|
|
if TOOLS[Cutting.nToolIndex].dSideStep then
|
|
Cutting.HorizontalSteps = MachiningLib.GetMachiningSteps( Cutting.dDepthToMachine, TOOLS[Cutting.nToolIndex].dSideStep)
|
|
else
|
|
Cutting.HorizontalSteps.nCount = 1
|
|
Cutting.HorizontalSteps.dStepLength = 0
|
|
end
|
|
-- geometria
|
|
Cutting.Geometry = {{Proc.id, FaceToMachine.id}}
|
|
-- nome operazione
|
|
Cutting.sOperationName = 'Cut_' .. ( EgtGetName( Cutting.idProc) or tostring( Cutting.idProc)) .. '_' .. tostring( FaceToMachine.id + 1)
|
|
|
|
-- eventuale avviso di danneggiamento pezzo successivo
|
|
-- TODO da sostituire con check se si riesce a separare e il grezzo dietro è lungo a sufficienza
|
|
local dOffsideLength = max( Cutting.LeadIn.dStartAddLength, Cutting.LeadOut.dEndAddLength) + TOOLS[Cutting.nToolIndex].dDiameter / 2 + 10 * GEO.EPS_SMALL
|
|
if ( not Proc.Tail or Proc.AdvTail) and Proc.AffectedFaces.Left and ( Proc.DistanceToNextPart < dOffsideLength) then
|
|
local sDamageNextPieceMessage = 'Feature '.. Proc.idFeature .. ' : sawblade can damage next piece.'
|
|
if #Cutting.sMessage > 0 then
|
|
Cutting.sMessage = Cutting.sMessage .. '\n' .. sDamageNextPieceMessage
|
|
else
|
|
Cutting.sMessage = sDamageNextPieceMessage
|
|
end
|
|
EgtOutLog( sDamageNextPieceMessage)
|
|
end
|
|
|
|
return Cutting, dResidualDepth
|
|
end
|
|
|
|
|
|
function Blade.AddMachiningAllSteps( Cutting, b3Raw)
|
|
local bIsCuttingOk = false
|
|
local sCuttingApplyMessage = ''
|
|
|
|
local dOriginalRadialOffset = Cutting.dRadialOffset
|
|
local dOriginalLeadInPerpDistance = Cutting.LeadIn.dPerpDistance
|
|
local dOriginalLeadOutPerpDistance = Cutting.LeadOut.dPerpDistance
|
|
for i = Cutting.HorizontalSteps.nCount, 1, -1 do
|
|
Cutting.dRadialOffset = dOriginalRadialOffset + Cutting.HorizontalSteps.dStepLength * ( i - 1)
|
|
-- update distanza perpendicolare attacco per contemplare l'offset applicato
|
|
Cutting.LeadIn.dPerpDistance = dOriginalLeadInPerpDistance - Cutting.dRadialOffset
|
|
Cutting.LeadOut.dPerpDistance = dOriginalLeadOutPerpDistance - Cutting.dRadialOffset
|
|
-- applicazione lavorazione
|
|
bIsCuttingOk, sCuttingApplyMessage = AddNewMachining( Cutting, b3Raw)
|
|
-- update messaggi
|
|
if sCuttingApplyMessage and #sCuttingApplyMessage > 0 then
|
|
sCuttingApplyMessage = sCuttingApplyMessage .. 'Apply : ' .. sCuttingApplyMessage .. '\n'
|
|
end
|
|
end
|
|
|
|
return bIsCuttingOk, sCuttingApplyMessage
|
|
end
|
|
|
|
|
|
function Chainsaw.CalculateMachiningParameters( Proc, FaceToMachine, EdgeToMachine)
|
|
local Mortising = {}
|
|
Mortising.bCanApply = true
|
|
Mortising.sMessage = ''
|
|
Mortising.idProc = Proc.id
|
|
-- OneSide | OneSideAndExtend | BothSides | BothSidesAndExtend
|
|
local sMortisingType
|
|
local dResidualDepth = 0
|
|
|
|
local dPocketHeight = 0
|
|
if Proc.Topology.sFamily == 'Tunnel' then
|
|
dPocketHeight = Proc.MainFaces.TunnelAddedFaces.MiddleFaceTm.dHeight
|
|
else
|
|
if FaceToMachine.Type == 'Long' then
|
|
dPocketHeight = Proc.MainFaces.BottomFace.MainEdges.SideEdges[1].dLength
|
|
elseif FaceToMachine.Type == 'Side' then
|
|
dPocketHeight = Proc.MainFaces.BottomFace.MainEdges.LongEdges[1].dLength
|
|
end
|
|
end
|
|
|
|
-- ricerca utensile
|
|
local ToolSearchParameters = {}
|
|
local ToolInfo = {}
|
|
ToolSearchParameters.vtToolDirection = EdgeToMachine.vtToolDirection
|
|
ToolSearchParameters.bAllowTopHead = true
|
|
ToolSearchParameters.bAllowBottomHead = false
|
|
if Proc.Topology.sFamily == 'Tunnel' then
|
|
sMortisingType = 'OneSideAndExtend'
|
|
ToolSearchParameters.bExtendWithCornerRadius = true
|
|
ToolSearchParameters.dElevation = abs( EdgeToMachine.dElevation) + BeamData.CUT_EXTRA
|
|
ToolInfo = MachiningLib.FindChainSaw( Proc, ToolSearchParameters)
|
|
if ToolInfo.dResidualDepth > 10 * GEO.EPS_SMALL then
|
|
sMortisingType = 'BothSidesAndExtend'
|
|
ToolSearchParameters.dElevation = abs( EdgeToMachine.dElevation) / 2 + BeamData.CUT_EXTRA_MIN
|
|
ToolInfo = MachiningLib.FindChainSaw( Proc, ToolSearchParameters)
|
|
end
|
|
elseif EdgeToMachine.sType == 'Side' and #( Proc.MainFaces.SideFaces) == 0 then
|
|
sMortisingType = 'BothSidesAndExtend'
|
|
ToolSearchParameters.bExtendWithCornerRadius = true
|
|
ToolSearchParameters.dElevation = abs( EdgeToMachine.dElevation) / 2 + BeamData.CUT_EXTRA_MIN
|
|
ToolInfo = MachiningLib.FindChainSaw( Proc, ToolSearchParameters)
|
|
else
|
|
sMortisingType = 'OneSide'
|
|
ToolSearchParameters.bExtendWithCornerRadius = false
|
|
ToolSearchParameters.dElevation = abs( EdgeToMachine.dElevation) + BeamData.CUT_EXTRA
|
|
ToolInfo = MachiningLib.FindChainSaw( Proc, ToolSearchParameters)
|
|
end
|
|
if ToolInfo.dResidualDepth > 10 * GEO.EPS_SMALL then
|
|
if sMortisingType == 'BothSidesAndExtend' then
|
|
sMortisingType = 'BothSides'
|
|
elseif sMortisingType == 'OneSideAndExtend' then
|
|
sMortisingType = 'OneSide'
|
|
end
|
|
ToolSearchParameters.bExtendWithCornerRadius = false
|
|
ToolSearchParameters.dElevation = nil
|
|
ToolInfo = MachiningLib.FindChainSaw( Proc, ToolSearchParameters)
|
|
end
|
|
Mortising.nToolIndex = ToolInfo.nToolIndex
|
|
Mortising.nType = MCH_OY.MORTISING
|
|
if not TOOLS[Mortising.nToolIndex].sName then
|
|
Mortising.sMessage = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not applicable - chainsaw not found'
|
|
Mortising.bCanApply = false
|
|
EgtOutLog( Mortising.sMessage)
|
|
return Mortising, EdgeToMachine.dElevation
|
|
end
|
|
|
|
-- verifica dimensioni tasca compatibili
|
|
-- se tasca meno spessa della sega a catena la strategia non è applicabile
|
|
if TOOLS[Mortising.nToolIndex].dThickness > dPocketHeight + 10 * GEO.EPS_SMALL then
|
|
Mortising.sMessage = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not applicable - pocket too narrow for chainsaw thickness'
|
|
Mortising.bCanApply = false
|
|
EgtOutLog( Mortising.sMessage)
|
|
return Mortising, EdgeToMachine.dElevation
|
|
end
|
|
if #( Proc.MainFaces.SideFaces) > 1 then
|
|
-- se tasca più stretta della sega a catena la strategia non è applicabile
|
|
if TOOLS[Mortising.nToolIndex].dWidth > EdgeToMachine.dLength + 10 * GEO.EPS_SMALL then
|
|
Mortising.sMessage = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not applicable - pocket too narrow for chainsaw width'
|
|
Mortising.bCanApply = false
|
|
EgtOutLog( Mortising.sMessage)
|
|
return Mortising, EdgeToMachine.dElevation
|
|
end
|
|
end
|
|
|
|
-- parametri della lavorazione
|
|
-- inizio e fine aperti o chiusi
|
|
Mortising.bIsStartClosed = not EdgeToMachine.bIsStartOpen
|
|
Mortising.bIsEndClosed = not EdgeToMachine.bIsEndOpen
|
|
-- lato di lavoro e inversioni
|
|
Mortising.bInvert = false
|
|
if EdgeToMachine.dElevation > -10 * GEO.EPS_SMALL then
|
|
Mortising.nWorkside = MCH_MILL_WS.RIGHT
|
|
Mortising.bToolInvert = false
|
|
else
|
|
Mortising.nWorkside = MCH_MILL_WS.LEFT
|
|
Mortising.bToolInvert = true
|
|
end
|
|
-- profondità e offset longitudinale
|
|
if sMortisingType == 'OneSide' then
|
|
Mortising.dDepthToMachine = abs( EdgeToMachine.dElevation)
|
|
elseif sMortisingType == 'OneSideAndExtend' then
|
|
Mortising.dDepthToMachine = abs( EdgeToMachine.dElevation) + TOOLS[Mortising.nToolIndex].dCornerRadius + BeamData.CUT_EXTRA_MIN
|
|
elseif sMortisingType == 'BothSides' then
|
|
Mortising.dDepthToMachine = abs( EdgeToMachine.dElevation) / 2
|
|
elseif sMortisingType == 'BothSidesAndExtend' then
|
|
Mortising.dDepthToMachine = abs( EdgeToMachine.dElevation) / 2 + TOOLS[Mortising.nToolIndex].dCornerRadius + BeamData.CUT_EXTRA_MIN
|
|
end
|
|
if TOOLS[Mortising.nToolIndex].dMaxMat > Mortising.dDepthToMachine - 10 * GEO.EPS_SMALL then
|
|
if EdgeToMachine.dElevation > -10 * GEO.EPS_SMALL then
|
|
Mortising.dLongitudinalOffset = 0
|
|
else
|
|
Mortising.dLongitudinalOffset = abs( EdgeToMachine.dElevation) - Mortising.dDepthToMachine
|
|
end
|
|
else
|
|
Mortising.dDepthToMachine = TOOLS[Mortising.nToolIndex].dMaxMat - 1
|
|
dResidualDepth = EdgeToMachine.dElevation - Mortising.dDepthToMachine
|
|
if EdgeToMachine.dElevation > -10 * GEO.EPS_SMALL then
|
|
Mortising.dLongitudinalOffset = EdgeToMachine.dElevation - Mortising.dDepthToMachine
|
|
else
|
|
Mortising.dLongitudinalOffset = 0
|
|
end
|
|
Mortising.sMessage = 'Feature '.. Proc.idFeature .. ' : chainsaw elevation (' .. EgtNumToString( EdgeToMachine.dElevation, 1) .. ') bigger than max tool depth (' .. EgtNumToString( Mortising.dDepthToMachine, 1) .. ')'
|
|
EgtOutLog( Mortising.sMessage)
|
|
end
|
|
-- offset radiale
|
|
Mortising.dRadialOffset = 0
|
|
-- distanza di sicurezza
|
|
Mortising.dStartSafetyLength = EdgeToMachine.dElevation
|
|
-- overlap
|
|
Mortising.dOverlap = 0
|
|
-- faceuse
|
|
if EdgeToMachine.dElevation > - 10 * GEO.EPS_SMALL then
|
|
Mortising.nFaceuse = BeamLib.GetNearestParalOpposite( EdgeToMachine.vtToolDirection)
|
|
else
|
|
Mortising.nFaceuse = BeamLib.GetNearestParalOpposite( -EdgeToMachine.vtToolDirection)
|
|
end
|
|
-- SCC
|
|
Mortising.SCC = MCH_SCC.NONE
|
|
-- asse bloccato e angoli suggeriti
|
|
Mortising.BlockedAxis = {}
|
|
Mortising.BlockedAxis.sOrientation = 'perpendicular'
|
|
Mortising.BlockedAxis.vtN = FaceToMachine.vtN
|
|
Mortising.SuggestedAngles = {}
|
|
Mortising.SuggestedAngles.nIndex = 1
|
|
Mortising.SuggestedAngles.vtN = FaceToMachine.vtN
|
|
Mortising.SuggestedAngles.vtOrthO = EdgeToMachine.vtToolDirection
|
|
-- approccio e retrazione
|
|
Mortising.LeadIn, Mortising.LeadOut = CalculateLeadInOut( Mortising, EdgeToMachine)
|
|
-- eventuale step verticale
|
|
Mortising.VerticalSteps = MachiningLib.GetMachiningSteps( dPocketHeight, TOOLS[Mortising.nToolIndex].dThickness)
|
|
-- geometria
|
|
Mortising.Geometry = {{Proc.id, FaceToMachine.id}}
|
|
-- nome operazione
|
|
Mortising.sOperationName = 'Chainsaw_' .. ( EgtGetName( Mortising.idProc) or tostring( Mortising.idProc)) .. '_' .. tostring( FaceToMachine.id + 1)
|
|
|
|
-- eventuale avviso di danneggiamento pezzo successivo
|
|
local dOffsideLength = max( Mortising.LeadIn.dStartAddLength, Mortising.LeadOut.dEndAddLength) + TOOLS[Mortising.nToolIndex].dWidth / 2 + 10 * GEO.EPS_SMALL
|
|
if ( not Proc.Tail or Proc.AdvTail) and Proc.AffectedFaces.Left and ( Proc.DistanceToNextPart < dOffsideLength) then
|
|
local sDamageNextPieceMessage = 'Feature '.. Proc.idFeature .. ' : chainsaw can damage next piece.'
|
|
if #Mortising.sMessage > 0 then
|
|
Mortising.sMessage = Mortising.sMessage .. '\n' .. sDamageNextPieceMessage
|
|
else
|
|
Mortising.sMessage = sDamageNextPieceMessage
|
|
end
|
|
EgtOutLog( sDamageNextPieceMessage)
|
|
end
|
|
|
|
return Mortising, dResidualDepth
|
|
end
|
|
|
|
|
|
function Chainsaw.AddMachiningAllSteps( Mortising, b3Raw)
|
|
local bIsMortisingOk = false
|
|
local sMortisingApplyMessage = ''
|
|
|
|
local dOriginalRadialOffsetMortising = Mortising.dRadialOffset
|
|
for i = Mortising.VerticalSteps.nCount, 1, -1 do
|
|
Mortising.dRadialOffset = dOriginalRadialOffsetMortising + Mortising.VerticalSteps.dStepLength * ( i - 1)
|
|
-- applicazione lavorazione
|
|
bIsMortisingOk, sMortisingApplyMessage = AddNewMachining( Mortising, b3Raw)
|
|
-- update messaggi
|
|
if sMortisingApplyMessage and #sMortisingApplyMessage > 0 then
|
|
Mortising.sMessage = Mortising.sMessage .. '\n' .. 'Apply : ' .. sMortisingApplyMessage
|
|
end
|
|
end
|
|
|
|
return bIsMortisingOk, sMortisingApplyMessage
|
|
end
|
|
|
|
|
|
function STR0003.Make( bAddMachining, Proc, Part, CustomParameters)
|
|
-- TODO da implementare gestione feature lunghe e spezzatura
|
|
-- carico parametri da default e li aggiorno con quelli passati dal chiamante (potrebbero non essere congruenti)
|
|
local StrategyLib = {}
|
|
StrategyLib.Config = require( 'STR0003\\STR0003Config')
|
|
Strategy.sName = StrategyLib.Config.sStrategyId
|
|
CustomParameters = BeamLib.GetUpdateCustomParameters( CustomParameters, StrategyLib.Config.Parameters)
|
|
Strategy.Parameters = BeamLib.LoadCustomParametersInStrategy( CustomParameters)
|
|
Strategy.Result = {}
|
|
Blade.Result = {}
|
|
Chainsaw.Result = {}
|
|
|
|
local b3Raw = EgtGetRawPartBBox( Part.idRaw)
|
|
local dResidualDepth = 0
|
|
|
|
if not IsTopologyOk( Proc) then
|
|
local sErr = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not implemented'
|
|
EgtOutLog( sErr)
|
|
return false, sErr
|
|
end
|
|
|
|
-- se tasca su faccia sotto la strategia non è applicabile (la sega a catena in generale non può lavorare da sotto)
|
|
-- TODO se OnlySaw questo test è da rimuovere ma bisogna considerare anche la lama da sotto
|
|
if Proc.AffectedFaces.Bottom and ( Proc.Fct > 3 or not Proc.AffectedFaces.Top) then
|
|
local sErr = 'Feature '.. Proc.idFeature .. ' : strategy ' .. Strategy.sName .. ' not applicable - pocket on bottom face'
|
|
EgtOutLog( sErr)
|
|
return false, sErr
|
|
end
|
|
|
|
-- lama
|
|
-- lavorazione di lama - fondo della tasca o fino a massimo materiale se tunnel
|
|
table.insert( Blade.Result, {})
|
|
local Cutting = {}
|
|
if Proc.Topology.sFamily == 'Tunnel' then
|
|
Cutting, dResidualDepth = Blade.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.OppositeEdges[1])
|
|
Blade.Result[#Blade.Result].sType = 'Tunnel'
|
|
else
|
|
Cutting, dResidualDepth = Blade.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.BottomEdge)
|
|
Blade.Result[#Blade.Result].sType = 'Bottom'
|
|
end
|
|
Blade.Result[#Blade.Result].bCanApply = Cutting.bCanApply
|
|
Blade.Result[#Blade.Result].sMessage = Cutting.sMessage
|
|
if bAddMachining and Cutting.bCanApply then
|
|
Blade.Result[#Blade.Result].bIsApplyOk, Blade.Result[#Blade.Result].sApplyMessage = Blade.AddMachiningAllSteps( Cutting, b3Raw)
|
|
end
|
|
local dBottomDepthToMachine = dResidualDepth
|
|
-- lato opposto del tunnel
|
|
if Proc.Topology.sFamily == 'Tunnel' then
|
|
table.insert( Blade.Result, {})
|
|
Cutting = Blade.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.OppositeEdges[2])
|
|
Blade.Result[#Blade.Result].sType = 'Tunnel'
|
|
Blade.Result[#Blade.Result].bCanApply = Cutting.bCanApply
|
|
Blade.Result[#Blade.Result].sMessage = Cutting.sMessage
|
|
if bAddMachining and Cutting.bCanApply then
|
|
Blade.Result[#Blade.Result].bIsApplyOk, Blade.Result[#Blade.Result].sApplyMessage = Blade.AddMachiningAllSteps( Cutting, b3Raw)
|
|
end
|
|
else
|
|
-- se la lama non è arrivata sul fondo e c'è almeno un lato aperto va lavorato
|
|
if dResidualDepth > 10 * GEO.EPS_SMALL then
|
|
table.insert( Blade.Result, {})
|
|
-- eventuale lavorazione di lama - lato della tasca da cui inizia la lavorazione
|
|
if Proc.MainFaces.LongFaces[1].MainEdges.BottomEdge.IsStartOpen then
|
|
Cutting = Blade.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.SideEdges[1])
|
|
Blade.Result[#Blade.Result].sType = 'Side'
|
|
Blade.Result[#Blade.Result].bCanApply = Cutting.bCanApply
|
|
Blade.Result[#Blade.Result].sMessage = Cutting.sMessage
|
|
if bAddMachining and Cutting.bCanApply then
|
|
Blade.Result[#Blade.Result].bIsApplyOk, Blade.Result[#Blade.Result].sApplyMessage = Blade.AddMachiningAllSteps( Cutting, b3Raw)
|
|
end
|
|
end
|
|
-- eventuale lavorazione di lama - lato della tasca in cui finisce la lavorazione
|
|
if Proc.MainFaces.LongFaces[1].MainEdges.BottomEdge.IsEndOpen then
|
|
Cutting = Blade.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces, Proc.MainFaces.LongFaces[1].MainEdges.SideEdges[2])
|
|
Blade.Result[#Blade.Result].sType = 'Side'
|
|
Blade.Result[#Blade.Result].bCanApply = Cutting.bCanApply
|
|
Blade.Result[#Blade.Result].sMessage = Cutting.sMessage
|
|
if bAddMachining and Cutting.bCanApply then
|
|
Blade.Result[#Blade.Result].bIsApplyOk, Blade.Result[#Blade.Result].sApplyMessage = Blade.AddMachiningAllSteps( Cutting, b3Raw)
|
|
end
|
|
end
|
|
-- la lama è arrivata sul fondo e tasca passante, non servono ulteriori lavorazioni
|
|
elseif #( Proc.MainFaces.SideFaces) == 0 then
|
|
Strategy.Parameters.bApplyOnlyBlade = true
|
|
end
|
|
end
|
|
|
|
if Strategy.Parameters.bApplyOnlyBlade then
|
|
-- TODO da rivedere
|
|
Strategy.Result.sInfo = Cutting.Message
|
|
Strategy.Result.bIsApplyOk = true
|
|
if dResidualDepth > 10 * GEO.EPS_SMALL then
|
|
Strategy.Result.sStatus = 'Not-Completed'
|
|
else
|
|
Strategy.Result.sStatus = 'Completed'
|
|
end
|
|
return Strategy.Result
|
|
end
|
|
|
|
-- sega a catena
|
|
-- parametri lavorazione con sega a catena - fondo della tasca o tunnel
|
|
local Mortising = {}
|
|
local dResidualDepth = 0
|
|
if Proc.Topology == 'Tunnel' then
|
|
Mortising, dResidualDepth = Chainsaw.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.OppositeEdges[1])
|
|
else
|
|
Mortising = Chainsaw.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.BottomEdge)
|
|
-- si lavora solo quanto non lavorato dalla lama
|
|
Mortising.MaxElev = dBottomDepthToMachine + BeamData.CUT_EXTRA
|
|
end
|
|
local bIsMortisingOk = false
|
|
if bAddMachining and Mortising.bCanApply then
|
|
bIsMortisingOk, Mortising.Message = Chainsaw.AddMachiningAllSteps( Mortising, b3Raw)
|
|
end
|
|
-- lato opposto del tunnel
|
|
if Proc.Topology == 'Tunnel' and ( dResidualDepth > 10 * GEO.EPS_SMALL) then
|
|
Mortising = Chainsaw.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.OppositeEdges[2])
|
|
bIsMortisingOk = false
|
|
if bAddMachining and Mortising.bCanApply then
|
|
bIsMortisingOk, Mortising.Message = Chainsaw.AddMachiningAllSteps( Mortising, b3Raw)
|
|
end
|
|
if not bIsMortisingOk then
|
|
return bIsMortisingOk, Mortising.Message
|
|
end
|
|
else
|
|
-- se la sega a catena non è arrivata sul fondo e c'è almeno un lato aperto va lavorato
|
|
if Mortising.LongitudinalOffset > 10 * GEO.EPS_SMALL then
|
|
-- eventuale lavorazione di sega a catena - lato della tasca da cui inizia la lavorazione
|
|
if Proc.MainFaces.LongFaces[1].MainEdges.BottomEdge.IsStartOpen then
|
|
Mortising = Chainsaw.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].MainEdges.SideEdges[1])
|
|
bIsMortisingOk = false
|
|
if bAddMachining and Mortising.bCanApply then
|
|
bIsMortisingOk, Mortising.Message = Chainsaw.AddMachiningAllSteps( Mortising, b3Raw)
|
|
end
|
|
if not bIsMortisingOk then
|
|
return bIsMortisingOk, Mortising.Message
|
|
end
|
|
end
|
|
-- eventuale lavorazione di sega a catena - lato della tasca in cui finisce la lavorazione
|
|
if Proc.MainFaces.LongFace[1].MainEdges.BottomEdge.IsEndOpen then
|
|
Mortising = Chainsaw.CalculateMachiningParameters( Proc, Proc.MainFaces.LongFaces[1], Proc.MainFaces.LongFaces[1].Edges.SideEdges[2])
|
|
bIsMortisingOk = false
|
|
if bAddMachining and Mortising.bCanApply then
|
|
bIsMortisingOk, Mortising.Message = Chainsaw.AddMachiningAllSteps( Mortising, b3Raw)
|
|
end
|
|
if not bIsMortisingOk then
|
|
return bIsMortisingOk, Mortising.Message
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local sFinalMessage = ''
|
|
if #Cutting.Message > 0 or #Mortising.Message > 0 then
|
|
sFinalMessage = Cutting.Message .. '\n' .. Mortising.Message
|
|
end
|
|
|
|
return bIsMortisingOk, sFinalMessage
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
|
|
return STR0003 |