-- Processore macchina Essetre-WALL by EgalTech s.r.l.   2021/10/18
-- Con controllo numerico NUM
-- 2021/08/25 DS ver 2.3h1 Ridotta emissione G126 ad uno all'inizio e uno alla fine con EC-1.
-- 2021/09/20 DS ver 2.3i4 Aggiunta gestione origini da BtlInfo/REFPOS.

-- Variabili di modulo
local MLE_INFO = 'Essetre-WALL.NUM.mlpe ver.'..PP_VER..' by EgalTech s.r.l.'
local TEST_USE = false

---------------------------------------------------------------------
-- *** GENERATION ***
---------------------------------------------------------------------
function OnStart()
   EMT.USETO1 = false     -- abilitazione uso origine tavola
   EMT.MODAL = true       -- abilitazione emissione modale
   EMT.INCHES = false     -- unità di misura mm/inches
   EMT.NUM = false        -- abilitazione numerazione linee
   --EMT.Nt = 'N'           -- token per la numerazione di linea
   --EMT.LINENBR = 0        -- numero di linea
   --EMT.LINEINC = 1        -- incremento numerazione linee
   --EMT.Ft = 'F'           -- token per feed
   --EMT.St = 'S'           -- token per speed
   EMT.FMAXPINZE = 154000 -- feed massima pinze
end

---------------------------------------------------------------------
function OnEnd()
   -- Ripristino fase iniziale come corrente
   EgtSetCurrPhase( 1)
   -- Se modalità test, rinomino il programma come XPI
   if TEST_USE then
      local sNewPath = EgtChangePathExtension( EMT.FILE, '.xpi')
      EgtEraseFile( sNewPath)
      EgtRenameFile( EMT.FILE, sNewPath)
   end
end

---------------------------------------------------------------------
function OnProgramStart()
   -- Info da gruppo BtlInfo
   local BtlInfoId = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'BtlInfo') or GDB_ID.NULL
   EMT.ORIG = EgtGetInfo( BtlInfoId, 'REFPOS', 'i') or 1
   -- CASO SPECIALE: questa macchina utilizza origine 3 anche se viene passata origine 1 da TS3
   if EMT.ORIG == 1 then
      EMT.ORIG = 3
   end
   -- Intestazioni
   if not TEST_USE then
      local BtlInfoId = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'BtlInfo') or GDB_ID.NULL
      EMT.PRODID = EgtGetInfo( BtlInfoId, 'PRODID', 'i') or 0
      EMT.PATTID = EgtGetInfo( BtlInfoId, 'PATTID', 'i') or 0
      EmtOutput( '%' .. tostring( EMT.PATTID) .. '.0')
   else
      EmtOutput( '%40.0')
   end
   if EMT.INFO then
      EmtOutput( '('..EMT.INFO..')')
   else
      EmtOutput( '(Program Start)')
   end
   EmtOutput( '('.. MLE_INFO..')')
   -- Parametri macchina
   local sOut = 'L151='..EgtNumToString( Nail11DeltaY - DeltaTabY, 2)..'\n'..
                'L152='..EgtNumToString( DeltaTabX - Nail11DeltaX, 2)..'\n'..
                'L153='..EgtNumToString( DeltaTabZ - Nail11DeltaZ, 2)..'\n'..
                'L154='..EgtNumToString( Nail12DeltaY - DeltaTabY, 2)..'\n'..
                'L155='..EgtNumToString( DeltaTabX - Nail12DeltaX, 2)..'\n'..
                'L156='..EgtNumToString( DeltaTabZ - Nail12DeltaZ, 2)..'\n'..
                'L157='..EgtNumToString( Nail13DeltaY - DeltaTabY, 2)..'\n'..
                'L158='..EgtNumToString( DeltaTabX - Nail13DeltaX, 2)..'\n'..
                'L159='..EgtNumToString( DeltaTabZ - Nail13DeltaZ, 2)..'\n'..
                'L160='..EgtNumToString( Nail14DeltaY - DeltaTabY, 2)..'\n'..
                'L161='..EgtNumToString( DeltaTabX - Nail14DeltaX, 2)..'\n'..
                'L162='..EgtNumToString( DeltaTabZ - Nail14DeltaZ, 2)..'\n'..
                'L163='..EgtNumToString( Orig2DeltaY, 2)..'\n'..
                'L164='..EgtNumToString( -Orig2DeltaX, 2)..'\n'..
                'L165='..EgtNumToString( Orig2DeltaZ, 2)..'\n'..
                'L166='..EgtNumToString( Orig3DeltaY, 2)..'\n'..
                'L167='..EgtNumToString( -Orig3DeltaX, 2)..'\n'..
                'L168='..EgtNumToString( Orig3DeltaZ, 2)..'\n'..
                'L169='..EgtNumToString( Orig4DeltaY, 2)..'\n'..
                'L170='..EgtNumToString( -Orig4DeltaX, 2)..'\n'..
                'L171='..EgtNumToString( Orig4DeltaZ, 2)..'\n'..
                'G114'..' EA'..EgtNumToString( -DeltaTabY, 2)..' EB'..EgtNumToString( DeltaTabX, 2)..
                        ' EC'..EgtNumToString( DeltaTabZ, 2)..' ED'..EgtNumToString( MillOffs, 2)..
                        ' EE'..EgtNumToString( Nail11DeltaY - DeltaTabY, 2, 2)..' EF'..EgtNumToString( DeltaTabX - Nail11DeltaX, 2)..' EG'..EgtNumToString( DeltaTabZ - Nail11DeltaZ, 2)..
                        ' EH'..EgtNumToString( Nail12DeltaY - DeltaTabY, 2, 2)..' EI'..EgtNumToString( DeltaTabX - Nail12DeltaX, 2)..' EL'..EgtNumToString( DeltaTabZ - Nail12DeltaZ, 2)..
                        ' EM'..EgtNumToString( Nail13DeltaY - DeltaTabY, 2, 2)..' EN'..EgtNumToString( DeltaTabX - Nail13DeltaX, 2)..' EO'..EgtNumToString( DeltaTabZ - Nail13DeltaZ, 2)..
                        ' EP'..EgtNumToString( Nail14DeltaY - DeltaTabY, 2, 2)..' EQ'..EgtNumToString( DeltaTabX - Nail14DeltaX, 2)..' ER'..EgtNumToString( DeltaTabZ - Nail14DeltaZ, 2)

   EmtOutput( sOut)
   -- Indice lavorazione
   EMT.CNT = 0
   -- Non effettuate lavorazioni con sega a catena sul fianco
   EMT.CSAWFIRST = true
   -- Non effettuata pausa per rimozione sfridi
   EMT.SCRAPFIRST = true
   -- Cerco primo utensile su testa 1
   EMT.TOOL_1, EMT.TLEN_1 = FindFirstToolOnHead( 'H1')
end

---------------------------------------------------------------------
function OnProgramEnd()
   EmtOutput( '( *** Program End *** )')
   EmitCount()
   -- Dichiarazione conclusione pezzi
   if EMT.PRODID then
      --local PartId = EgtGetFirstPartInRawPart( EMT.RAWID)
      --while PartId do
      --   local CutId = EgtGetInfo( PartId, 'CUTID', 'i') or 0
      --   local sOut = 'G126 EA'..tostring( EMT.PRODID)..' EB'..tostring( EMT.PATTID)..' EC'..tostring( CutId)..' ED2'
      --   EmtOutput( sOut)
      --   PartId = EgtGetNextPartInRawPart( PartId)
      --end
      local sOut = 'G126 EA'..tostring( EMT.PRODID)..' EB'..tostring( EMT.PATTID)..' EC'..tostring( -1)..' ED2'
      EmtOutput( sOut)
   end
   -- Arresto mandrino
   EmtOutput( 'M05 E80000=1')
   -- Tipo di scarico
   if not TEST_USE then
      EmtOutput( 'M27 E30057=1')
   end
   -- Se modalità test, termino il programma
   if TEST_USE then
      EmtOutput( 'M02')
   end
end

---------------------------------------------------------------------
function OnToolData()
   -- emissione dati utensile (esclusa lama e sega a catena)
   if EMT.HEAD == 'H1' then
      -- cerco posizione di attrezzaggio del primo utensile di lavorazione
      if EMT.TOOL == EMT.TOOL_1  and  EMT.TLEN_1 < 215.5 then
         EMT.TCPOS_1 = EMT.TCPOS
      end
   -- emissione dati sega a catena
   elseif EMT.HEAD == 'H3' then
      ;
   end
end

---------------------------------------------------------------------
function OnDispositionStart()
   --EmtOutput( '(Disposition '..EMT.DISPIND..' = '..EMT.DISPID..')')
   EMT.OPEISDISP = true
end

---------------------------------------------------------------------
function OnDispositionEnd()
   -- Se prima disposizione
   if EMT.PHASE == 1 then
      -- Dimensioni grezzo
      local b3Raw = EgtGetRawPartBBox( EMT.RAWID)
      EmtOutput( 'E30050='..EgtNumToString( 1000 * b3Raw:getDimY(), 0)..
                ' E30051='..EgtNumToString( 1000 * b3Raw:getDimX(), 0)..
                ' E30052='..EgtNumToString( 1000 * b3Raw:getDimZ(), 0))
      EMT.ST = b3Raw:getDimZ()
      -- Scelta origine
      EmtOutput( 'M26 E30054='..EgtNumToString( EMT.ORIG, 0))
      -- Posizione cuffia
      local dHCuff = b3Raw:getDimZ() + 32 
      EmtOutput( 'E30039=' .. EgtNumToString( dHCuff * 1000, 0))
      -- Eventuale salto a lavorazione in corso
      EmtOutput( 'G79 NE80000')
      -- Dichiarazione inizio lavorazione pezzi
      if EMT.PRODID then
         EmitCount()
         --local PartId = EgtGetFirstPartInRawPart( EMT.RAWID)
         --while PartId do
         --   local CutId = EgtGetInfo( PartId, 'CUTID', 'i') or 0
         --   local sOut = 'G126 EA'..tostring( EMT.PRODID)..' EB'..tostring( EMT.PATTID)..' EC'..tostring( CutId)..' ED1'
         --   EmtOutput( sOut)
         --   PartId = EgtGetNextPartInRawPart( PartId)
         --end
         local sOut = 'G126 EA'..tostring( EMT.PRODID)..' EB'..tostring( EMT.PATTID)..' EC'..tostring( -1)..' ED1'
         EmtOutput( sOut)
      end
   end
   EMT.OPEISDISP = false
end

---------------------------------------------------------------------
function OnTableData()
end

---------------------------------------------------------------------
function OnFixtureData()
end

---------------------------------------------------------------------
function OnRawMoveData()
end

---------------------------------------------------------------------
function OnToolSelect()
   -- se utensile definito (non definito per disposizioni con movimento)
   if EMT.TOOL ~= '' then
      -- verifiche su utensile
      local sTool = EgtTdbGetToolFromUUID( EgtGetMachiningParam( MCH_MP.TUUID) or "")
      if sTool ~= EMT.TOOL then EmtSetLastError( 1213, 'Tool name mismatch') end
      -- predefinite EMT.TCPOS EMT.HEAD EMT.EXIT
      EMT.TOOLTYPE = EgtTdbGetCurrToolParam( MCH_TP.TYPE)
      EMT.TOOLSEL = EMT.TCPOS..' M06'
      EMT.TDIAM = EgtTdbGetCurrToolParam( MCH_TP.DIAM)
      EMT.TTOTDIAM = EgtTdbGetCurrToolParam( MCH_TP.TOTDIAM)
      EMT.TLEN = EgtTdbGetCurrToolParam( MCH_TP.LEN)
      EMT.TTOTLEN = EgtTdbGetCurrToolParam( MCH_TP.TOTLEN)
   end
end

---------------------------------------------------------------------
function OnToolDeselect()
end

---------------------------------------------------------------------
function OnMachiningStart()
   --EMT.MCHLIBNAME = EgtGetMachiningParam( MCH_MP.NAME)
   EMT.MCHNAME = EgtGetOperationName( EMT.MCHID)
   EMT.MCHTYPE = EgtGetMachiningParam( MCH_MP.TYPE)
   local sNotes = EgtGetMachiningParam( MCH_MP.USERNOTES)
   -- se chiodatura in doppio, imposto attivazione testa associata 12
   local nDouble = GetValNotes( sNotes, 'Dbl', 'i')
   if EMT.HEAD == 'H14' and nDouble and nDouble > 0 then
      EMT.DOUBLE = nDouble
      EMT.DELTA = GetValNotes( sNotes, 'Dlt', 'd') or 0
      EMT.DBLACC = abs( EgtGetMachiningParam( MCH_MP.STARTADDLEN) + EgtGetMachiningParam( MCH_MP.ENDADDLEN))
   end
   -- non ancora iniziata la lavorazione
   EMT.MCHFIRST = true
   -- primo posizionamento sempre in globale
   EMT.REFLOC = nil
   EMT.IPLGL = false
end

---------------------------------------------------------------------
function OnMachiningEnd()
   EMT.PREVTOOL = EMT.TOOL
   EMT.DOUBLE = nil
   EMT.DELTA = nil
   EMT.DBLLEN = nil
end

---------------------------------------------------------------------
function OnPathStart()
   -- reset valori precedenti (per forzare emissione di tutti gli assi del 1° movimento)
   EmtResetPrev()
end

---------------------------------------------------------------------
function OnPathEnd()
   if IsNailingHead( EMT.HEAD) then
      local dStep = GetValNotes( EgtGetMachiningParam( MCH_MP.USERNOTES), 'Par', 'd') or 100
      local dSafeZ = max( EgtGetMachiningParam( MCH_MP.STARTPOS), 10) or 20
      --local dSafeZ = EgtMdbGetGeneralParam( MCH_GP.SAFEZ)
      if EMT.HEAD == 'H21' or EMT.HEAD == 'H22' or EMT.HEAD == 'H23' then
         EmtOutput( 'G124 X'..EmtLenToString( EMT.NAIL_START_L1, 3)..' Y'..EmtLenToString( EMT.NAIL_START_L2, 3)..' Z'..EmtLenToString( EMT.NAIL_START_L3, 3)..
                        ' B'..EmtLenToString( EMT.NAIL_END_L1, 3)..' C'..EmtLenToString( EMT.NAIL_END_L2, 3)..' EA'..EmtLenToString( dStep, 3)..' EB1'..
                        ' W'..EmtLenToString( dSafeZ, 3))
      else
         local sExtra = ''
         local bInvert = false
         if EMT.DOUBLE then
            sExtra = ' EH' .. EmtLenToString( EMT.DELTA, 3)
            -- se doppio parallelo alla traversa macchina devo andare sempre verso il negativo
            if EMT.DOUBLE == 2 then
               if EMT.NAIL_END_L1 > EMT.NAIL_START_L1 then
                  bInvert = true
               end
               --if EMT.NAIL_END_L1 > EMT.NAIL_START_L1 then
               --   EMT.NAIL_START_L1, EMT.NAIL_END_L1 = EMT.NAIL_END_L1, EMT.NAIL_START_L1
               --   EMT.NAIL_START_L2, EMT.NAIL_END_L2 = EMT.NAIL_END_L2, EMT.NAIL_START_L2
               --end
            end
         end
         local dX = EMT.NAIL_END_L1 - EMT.NAIL_START_L1
         local dY = EMT.NAIL_END_L2 - EMT.NAIL_START_L2
         local dLen = sqrt( dX * dX + dY * dY)
         local nStep = max( ceil( ( dLen - 1) / dStep), 1)
         for i = 0, nStep do
            local dCoeff = i / nStep
            local PosX = ( 1 - dCoeff) * EMT.NAIL_START_L1 + dCoeff * EMT.NAIL_END_L1
            local PosY = ( 1 - dCoeff) * EMT.NAIL_START_L2 + dCoeff * EMT.NAIL_END_L2
            local sExtra2 = ''
            -- se doppio parallelo alla traversa macchina, disabilito testa 2 quando va oltre (0=abilitata, 2=disabilitata)
            if EMT.DOUBLE == 2 then
               local dCoeff2 = EgtIf( bInvert, 1 - dCoeff, dCoeff)
               if dCoeff2 * dLen + EMT.DELTA < dLen + EMT.DBLACC + 0.1 then
                  sExtra2 = ' EI0'
               else
                  sExtra2 = ' EI2'
               end
            end
            EmtOutput( 'G102 X'..EmtLenToString( PosX, 3)..' Y'..EmtLenToString( PosY, 3)..' Z'..EmtLenToString( EMT.NAIL_START_L3, 3)..
                           ' U'..EmtLenToString( PosX, 3)..' V'..EmtLenToString( PosY, 3)..' A0 W'..EmtLenToString( dSafeZ, 3)..sExtra..sExtra2..EMT.NAIL_END)
         end
      end
      if EMT.TO_ZMAX then
         EmtOutput( 'G103'..GetET()..' E80000='..tostring( EMT.CNT + 1))
         EMT.TO_ZMAX = nil
         EMT.ZMAX = true
      end
   end
end

---------------------------------------------------------------------
function OnRapid()
   -- se primo movimento della lavorazione, gestione speciale
   if EMT.MCHFIRST and not EMT.OPEISDISP then
      MyAdjustLinearAxes()
      EmtAdjustRotaryAxes()
      EmtOutput( '( *** '..EMT.MCHNAME..'/'..EMT.TOOL..' *** )')
      EmitCount()
      -- se lavorazione con sega a catena
      if EMT.HEAD == 'H3' then
         -- se prima lavorazione sui fianchi con sega a catena
         if EMT.CSAWFIRST and string.sub( EMT.MCHNAME, 1, 5) == 'Csaw_' then
            EMT.CSAWFIRST = false
            -- eventuale pausa per rimozione sfridi
            if EMT.SCRAPFIRST then
               EMT.SCRAPFIRST = false
               local b3Raw = EgtGetRawPartBBox( EMT.RAWID)
               local ParkY = min( -( b3Raw:getMin():getX() - 2000), -MinY)
               EmtOutput( 'G104 Y' .. EmtLenToString( ParkY, 3) .. ' ET1' .. GetEF())
            end
            -- pre-posizionamento
            EmitCount()
            EmtResetPrev()
            local sOut = 'G101' .. EmtGetAxis( 'R1')
            local sET = GetET()
            local sEU = GetEU()
            local sEE = GetEE()
            local sEF = GetEF()
            local sEM = ' EM'..EgtIf( EMT.S > 0, '3', '4')
            local sES = ' ES'..EgtNumToString( abs( EMT.S), 0)
            local sEL = ' EL' .. EmtLenToString( ChSawLen, 3)
            local sER = ' ER' .. EmtLenToString( EMT.TLEN, 3)
            sOut = sOut .. sET .. sEU .. sEE .. sEF .. sEM .. sES .. sEL .. sER
            EmtOutput( sOut)
         end
      -- altrimenti verifico se necessaria pausa
      else
         if EMT.SCRAPFIRST and EgtExistsInfo( EMT.MCHID, 'MOVE_AFTER') then
            EMT.SCRAPFIRST = false
            -- pausa per rimozione sfridi
            local b3Raw = EgtGetRawPartBBox( EMT.RAWID)
            local ParkY = min( -( b3Raw:getMin():getX() - 2000), -MinY)
            EmtOutput( 'G104 Y' .. EmtLenToString( ParkY, 3) .. ' ET1' .. GetEF())
         end
      end
      -- primo posizionamento
      EmtResetPrev()
      local sOut = 'G101' .. EmtGetAxis( 'L1') .. EmtGetAxis( 'L2') .. EmtGetAxis( 'L3') ..
                             EmtGetAxis( 'R2') .. EmtGetAxis( 'R1')
      if EMT.HEAD == 'H1' or EMT.HEAD == 'H12' or EMT.HEAD == 'H3' or ( EMT.HEAD == 'H14' and not EMT.DOUBLE) then
         sOut = sOut .. ' U' .. EmtLenToString( -MaxX2, 3) .. ' W0 V0 A0'
      elseif EMT.HEAD == 'H14' then
         sOut = sOut .. ' U' .. EmtLenToString( EMT.L2 - EMT.DELTA, 3) .. ' W0 V0 A0'
      elseif EMT.HEAD == 'H2' or EMT.HEAD == 'H21' or EMT.HEAD == 'H22' or EMT.HEAD == 'H23' then
         sOut = sOut .. ' U' .. EmtLenToString( -MinX1, 3) .. ' W0 V0 A0'
      else
         EmtSetLastError( 1211, "Unknown Head : " .. EMT.HEAD)
      end
      local sET = GetET()
      local sEU = GetEU()
      local sEE = GetEE()
      local sEF = GetEF()
      local sEM = ' EM'..EgtIf( EMT.S > 0, '3', '4')
      local sES = ' ES'..EgtNumToString( abs( EMT.S), 0)
      local sEL = ' EL' .. EmtLenToString( EgtIf( EMT.HEAD ~= 'H3', EMT.TLEN, ChSawLen), 3)
      local sER = ' ER' .. EmtLenToString( EgtIf( EMT.HEAD ~= 'H3', EMT.TDIAM / 2, EMT.TLEN), 3)
      sOut = sOut .. sET .. sEU .. sEE .. sEF .. sEM .. sES .. sEL .. sER
      EmtOutput( sOut)
      EMT.REFLOC = 0
      EMT.MCHFIRST = false
      EMT.ZMAX = false
   -- se standard
   elseif EMT.FLAG == 0 or EMT.FLAG == 1 or EMT.FLAG == 2 then
      -- se necessario, impostazione riferimento locale
      if EMT.REFLOC == 0 then
         -- calcolo per piano generico
         CalcInterpPlane()
         EMT.REFLOC = 1
         -- trasformo i punti nel piano
         MyAdjustLinearAxes()
         EmtAdjustRotaryAxes()
         EMT.L1p = nil
         EMT.L2p = nil
         EMT.L3p = nil
         EMT.R1p = nil
         EMT.R2p = nil
         local sET = GetET()
         local sEU = GetEU()
         local sEE = GetEE()
         local sEF = ' EF20000'
         local sEM = ' EM'..EgtIf( EMT.S > 0, '3', '4')
         local sES = ' ES'..EgtNumToString( abs( EMT.S), 0)
         local sEL = ' EL' .. EmtLenToString( EgtIf( EMT.HEAD ~= 'H3', EMT.TLEN, ChSawLen), 3)
         local sER = ' ER' .. EmtLenToString( EgtIf( EMT.HEAD ~= 'H3', EMT.TDIAM / 2, EMT.TLEN), 3)
         if not IsNailingHead( EMT.HEAD) then
            EmtOutput( 'G102'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..EmtGetAxis('R2')..EmtGetAxis('R1')..EMT.IPLGLSTR..sET..sEU..sEE..sEF..sEM..sES..sEL..sER)
         else
            EMT.NAIL_START_L1 = EMT.L1
            EMT.NAIL_START_L2 = EMT.L2
            EMT.NAIL_START_L3 = EMT.L3
            EMT.NAIL_END_L1 = EMT.L1
            EMT.NAIL_END_L2 = EMT.L2
            EMT.NAIL_END = EMT.IPLGLSTR..sET..sEU..sEE..sEF..sEM..sES..sEL..sER
         end
      else
         MyAdjustLinearAxes()
         EmtAdjustRotaryAxes()
      end
      -- emissione movimento
      if not IsNailingHead( EMT.HEAD) then
         local sOut = 'G0'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..
                            EmtGetAxis('R2')..EmtGetAxis('R1')
         EmtOutput( sOut)
      else
         if EMT.L1 then EMT.NAIL_END_L1 = EMT.L1 end
         if EMT.L2 then EMT.NAIL_END_L2 = EMT.L2 end
         if EMT.L3 and EMT.L3 < EMT.NAIL_START_L3 then
            EMT.NAIL_START_L3 = EMT.L3
         end
      end
   -- se altrimenti risalita a Z max a fine lavorazione
   elseif EMT.FLAG == 3 then
      if not IsNailingHead( EMT.HEAD) then
         if EMT.REFLOC then
            EMT.REFLOC = nil
            EMT.IPLGL = false
            EmtResetPrev()
         end
         MyAdjustLinearAxes()
         EmtAdjustRotaryAxes()
         -- se fresa o punta su testa 1 raddrizzo l'utensile in risalita
         if ( EMT.HEAD == 'H1' or EMT.HEAD == 'H12') and EMT.TCPOS ~= 'T201' and EMT.TCPOS ~= 'T202' and EMT.TCPOS ~= 'T203' then
            local sOut = 'G101' .. EmtGetAxis( 'L1') .. EmtGetAxis( 'L2') .. EmtGetAxis( 'L3') ..
                                   EmtGetAxis( 'R2') .. EmtGetAxis( 'R1') ..
                                   ' U' .. EmtLenToString( -MaxX2, 3) .. ' W0 V0 A0'
            local sET = GetET()
            local sEU = GetEU()
            local sEE = GetEE()
            local sEF = GetEF()
            local sEM = ' EM'..EgtIf( EMT.S > 0, '3', '4')
            local sES = ' ES'..EgtNumToString( abs( EMT.S), 0)
            local sEL = ' EL' .. EmtLenToString( EMT.TLEN, 3)
            local sER = ' ER' .. EmtLenToString( EMT.TDIAM / 2, 3)
            sOut = sOut .. sET .. sEU .. sEE .. sEF .. sEM .. sES .. sEL .. sER
            EmtOutput( sOut)
         -- se altrimenti sega a catena
         elseif EMT.HEAD == 'H3' then
            local sOut = 'G101' .. EmtGetAxis( 'L1') .. EmtGetAxis( 'L2') .. EmtGetAxis( 'L3') ..
                                   EmtGetAxis( 'R2') .. EmtGetAxis( 'R1') ..
                                   ' U' .. EmtLenToString( -MaxX2, 3) .. ' W0 V0 A0'
            local sET = GetET()
            local sEU = GetEU()
            local sEE = GetEE()
            local sEF = GetEF()
            local sEM = ' EM'..EgtIf( EMT.S > 0, '3', '4')
            local sES = ' ES'..EgtNumToString( abs( EMT.S), 0)
            local sEL = ' EL' .. EmtLenToString( EMT.TLEN, 3)
            local sER = ' ER' .. EmtLenToString( EMT.TDIAM / 2, 3)
            sOut = sOut .. sET .. sEU .. sEE .. sEF .. sEM .. sES .. sEL .. sER
            EmtOutput( sOut)
            sOut = 'G101' .. ' X' .. EmtLenToString( ( MinX1 + MaxX1) / 2, 3)..
                   ' U' .. EmtLenToString( -MaxX2, 3) .. ' W0 V0 A0'
            EmtOutput( sOut)
         end
         -- dichiarazione risalita a Zmax
         EmtOutput( 'G103'..GetET()..' E80000='..tostring( EMT.CNT + 1))
         EMT.ZMAX = true
      else
         EMT.TO_ZMAX = true
      end
   -- se altrimenti movimento in Home
   elseif EMT.FLAG == 4 then
      -- non previsto
   -- altrimenti errore
   else
      EmtSetLastError( 1214, 'Unknown Rapid flag')
   end
   -- aggiorno valori come precedenti
   EmtUpdatePrev()
end

---------------------------------------------------------------------
function OnLinear()
   -- se necessario, impostazione riferimento locale
   if EMT.REFLOC == 0 then
      -- calcolo per piano generico
      CalcInterpPlane()
      EMT.REFLOC = 1
      -- salvo posizione attuale
      local OldL1 = EMT.L1
      local OldL2 = EMT.L2
      local OldL3 = EMT.L3
      -- imposto posizione precedente
      EMT.L1 = EMT.L1o
      EMT.L2 = EMT.L2o
      EMT.L3 = EMT.L3o
      -- trasformo i punti nel piano
      MyAdjustLinearAxes()
      EmtAdjustRotaryAxes()
      EMT.L1p = nil
      EMT.L2p = nil
      EMT.L3p = nil
      EMT.R1p = nil
      EMT.R2p = nil
      local sET = GetET()
      local sEU = GetEU()
      local sEE = GetEE()
      local sEF = ' EF20000'
      local sEM = ' EM'..EgtIf( EMT.S > 0, '3', '4')
      local sES = ' ES'..EgtNumToString( abs( EMT.S), 0)
      local sEL = ' EL' .. EmtLenToString( EgtIf( EMT.HEAD ~= 'H3', EMT.TLEN, ChSawLen), 3)
      local sER = ' ER' .. EmtLenToString( EgtIf( EMT.HEAD ~= 'H3', EMT.TDIAM / 2, EMT.TLEN), 3)
      if not IsNailingHead( EMT.HEAD) then
         EmtOutput( 'G102'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..EmtGetAxis('R2')..EmtGetAxis('R1')..EMT.IPLGLSTR..sET..sEU..sEE..sEF..sEM..sES..sEL..sER)
         -- emissione movimento
         local sOut = 'G0'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..
                            EmtGetAxis('R2')..EmtGetAxis('R1')
         EmtOutput( sOut)
         EmtUpdatePrev()
         EMT.Fp = nil
      else
         EMT.NAIL_START_L1 = EMT.L1
         EMT.NAIL_START_L2 = EMT.L2
         EMT.NAIL_START_L3 = EMT.L3
         EMT.NAIL_END_L1 = EMT.L1
         EMT.NAIL_END_L2 = EMT.L2
         EMT.NAIL_END = EMT.IPLGLSTR..sET..sEU..sEE..sEF..sEM..sES..sEL..sER
      end
      -- ripristino posizione attuale
      EMT.L1 = OldL1
      EMT.L2 = OldL2
      EMT.L3 = OldL3
   end

   -- aggiustamento valori
   MyAdjustLinearAxes()
   EmtAdjustRotaryAxes()

   if not IsNailingHead( EMT.HEAD) then
      -- valori degli assi
      local sAxes = EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..
                  EmtGetAxis('R2')..EmtGetAxis('R1')
      -- se nulla da emettere, esco
      if string.len( sAxes) == 0 then
         return
      end
      -- aggiungo feed
      local sFeed = EmtGetFeed()
      -- emetto linea
      EmtOutput( "G1"..sAxes..sFeed)
   else
      if EMT.L1 then EMT.NAIL_END_L1 = EMT.L1 end
      if EMT.L2 then EMT.NAIL_END_L2 = EMT.L2 end
      if EMT.L3 and EMT.L3 < EMT.NAIL_START_L3 then
         EMT.NAIL_START_L3 = EMT.L3
      end
   end

   -- aggiorno valori come precedenti
   EmtUpdatePrev()
end

---------------------------------------------------------------------
function OnArc()
   -- non modale su archi
   local dPrevZ = EMT.L3p
   EmtResetPrevLinear()
   -- aggiustamento valori
   MyAdjustLinearAxes()
   EmtAdjustRotaryAxes()

   if not IsNailingHead( EMT.HEAD) then
      -- valori degli assi
      local sAxes = EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..
                  EmtGetAxis('R2')..EmtGetAxis('R1')
      -- se nulla da emettere, esco
      if string.len( sAxes) == 0 then
         return
      end
      -- valori delle coordinate del centro
      EmtAdjustCenterAxes()
      -- coordinate centro (per ora solo archi nel piano XY)
      --local sCen = ' ' .. EMT.C1t .. EmtLenToString(EMT.C1,3) ..
      --             ' ' .. EMT.C2t .. EmtLenToString(EMT.C2,3)
      -- raggio
      local sRad = ' ' .. EMT.RRt .. EmtLenToString(EMT.RR,3)
      -- aggiungo feed
      local sFeed = EmtGetFeed()
      -- tipo arco
      local sArc = 'G' .. EgtNumToString(EMT.MOVE,0)
      -- se elica, aggiungo il pitch
      local sPitch = ''
      local dDeltaZ = EMT.L3 - dPrevZ
      if abs( dDeltaZ) > GEO.EPS_SMALL then
         local dPitch = dDeltaZ * 360.0 / abs( EMT.AC)
         sPitch = ' K' .. EmtLenToString( dPitch, 3)
      end
      -- emetto arco
      EmtOutput( sArc..sAxes..sRad..sPitch..sFeed)
   else
      -- Archi non previsti con chiodature
      EmtSetLastError( 1212, 'Arcs non allowed with nailing')
   end

   -- aggiorno valori come precedenti
   EmtUpdatePrev()
end

---------------------------------------------------------------------
function MyAdjustLinearAxes()
   if EMT.REFLOC then
      local vtDelta = V_NULL()
      local vtE = V_NULL()
      if EMT.HEAD == 'H1' or EMT.HEAD == 'H12' then
         local dAddLen = EgtIf( EMT.HEAD == 'H12', -SawOffsZ, 0)
         local Len = EMT.TLEN + dAddLen + MillOffs
         local LenRef = MillOffs
         vtE = Vector3d( EMT.TDIR) * Len - Z_AX() * LenRef
      elseif EMT.HEAD == 'H2' then
         vtDelta = -Vector3d( SawDeltaX, SawDeltaY, SawDeltaZ)
      elseif EMT.HEAD == 'H3' then
         local Len = ChSawLen + MillOffs
         local LenRef = MillOffs
         local vtAux = EgtGetCalcAuxDirFromAngles( EMT.R1, EMT.R2, EMT.R3)
         vtE = vtAux * Len - Z_AX() * LenRef
      elseif EMT.HEAD == 'H14' then
         vtDelta = -Vector3d( Nail14DeltaX, Nail14DeltaY, Nail14DeltaZ)
         vtE = Z_AX() * EMT.TLEN
      elseif EMT.HEAD == 'H21' then
         vtDelta = -Vector3d( Nail11DeltaX, Nail11DeltaY, Nail11DeltaZ)
         vtE = Z_AX() * EMT.TLEN
      elseif EMT.HEAD == 'H22' then
         vtDelta = -Vector3d( Nail12DeltaX, Nail12DeltaY, Nail12DeltaZ)
         vtE = Z_AX() * EMT.TLEN
      elseif EMT.HEAD == 'H23' then
         vtDelta = -Vector3d( Nail13DeltaX, Nail13DeltaY, Nail13DeltaZ)
         vtE = Z_AX() * EMT.TLEN
      end
      EMT.L1 = EMT.L1 - vtDelta:getX() - vtE:getX()
      EMT.L2 = EMT.L2 - vtDelta:getY() - vtE:getY()
      EMT.L3 = EMT.L3 - vtDelta:getZ() - DeltaTabZ - vtE:getZ()
   end

   EmtAdjustLinearAxes()

   if not EMT.REFLOC then
      EMT.L2 = - EMT.L2
      EMT.L1t = 'Y'
      EMT.L2t = 'X'
   else
      EMT.L1t = 'X'
      EMT.L2t = 'Y'
   end
end

---------------------------------------------------------------------
function CalcInterpPlane()
   -- origine del piano
   local xS = EMT.START[1]
   local yS = EMT.START[2]
   local zS = EgtIf( IsNailingHead( EMT.HEAD), EMT.ST, 0)
   local ptS = Point3d( xS, yS, zS)
   -- calcolo per piano generico
   local vtE
   if EMT.HEAD == 'H1' or EMT.HEAD == 'H12' then
      vtE = EgtGetCalcToolDirFromAngles( EMT.R1, EMT.R2, EMT.R3)
   elseif EMT.HEAD == 'H3' then
      vtE = EgtGetCalcAuxDirFromAngles( EMT.R1, EMT.R2, EMT.R3)
   else
      vtE = Z_AX()
   end
   if EMT.HEAD == 'H1' or EMT.HEAD == 'H12' or EMT.HEAD == 'H3' then
      EMT.IPLGLFR = Frame3d( ptS, vtE)
   else
      EMT.IPLGLFR = Frame3d( ptS, -Y_AX(), X_AX(), Z_AX())
   end
   --EmtOutput( 'IPLGLFR='..tostring(EMT.IPLGLFR))
   local _, dAngV, dAngO = SphericalFromVector( vtE)
   local dAngO2 = EmtGetAngO2( EMT.IPLGLFR:getVersZ(), EMT.IPLGLFR:getVersX(), dAngV, dAngO)
   dAngO = dAngO - 90
   while dAngO < 0 do
      dAngO = dAngO + 360
   end
   -- se rotazione solo attorno alla C allora si mette come rotazione finale
   if abs( dAngV) < GEO.EPS_ANG_SMALL then
      dAngO2 = dAngO2 + dAngO
      while dAngO2 >= 360 do
         dAngO2 = dAngO2 - 360
      end
      dAngO = 0 
   end
   
   local xOut = - ( yS - DeltaTabY)
   local yOut = xS - DeltaTabX - DimTabX
   local zOut = zS
   if EMT.ORIG == 2 then
      xOut = - ( yS - DeltaTabY - DimTabY)
      yOut =  xS - DeltaTabX - DimTabX
   elseif EMT.ORIG == 3 then
      xOut = - ( yS - DeltaTabY)
      yOut = xS - DeltaTabX
   elseif EMT.ORIG == 4 then
      xOut = - ( yS - DeltaTabY - DimTabY)
      yOut = xS - DeltaTabX
   end
   EMT.IPLGLSTR = ' EX'..EmtLenToString( xOut)..' EY'..EmtLenToString( yOut)..' EZ'..EmtLenToString( zOut)..
                  ' EA0'..' EB'..EgtNumToString( dAngV)..' EC'..EgtNumToString( dAngO)..' ED'..EgtNumToString( dAngO2)
   EMT.IPLGL = true
end

---------------------------------------------------------------------
function GetET()
   local sET
   if EMT.HEAD == 'H1' or EMT.HEAD == 'H12' or EMT.HEAD == 'H3' then
      sET = ' ET2'
   elseif EMT.HEAD == 'H2' then
      sET = ' ET1'
   elseif EMT.HEAD == 'H21' then
      sET = ' ET11'
   elseif EMT.HEAD == 'H22' then
      sET = ' ET12'
   elseif EMT.HEAD == 'H23' then
      sET = ' ET13'
   end
   return sET
end

---------------------------------------------------------------------
function GetEU()
   local sEU
   if EMT.HEAD == 'H1' or EMT.HEAD == 'H12' then
      sEU = ' EU'..string.sub( EMT.TCPOS, 2)
   elseif EMT.HEAD == 'H2' then
      sEU = ' EU0'
   elseif EMT.HEAD == 'H3' then
      if abs( EMT.R3 - 0) < 0.1 then
         sEU = ' EU104'
      elseif abs( EMT.R3 - 270) < 0.1 then
         sEU = ' EU101'
      elseif abs( EMT.R3 - 180) < 0.1 then
         sEU = ' EU102'
      elseif abs( EMT.R3 - 90) < 0.1 then
         sEU = ' EU103'
      else
         EmtSetLastError( 1210, 'Chain saw orientation not allowed')
      end
   else
      sEU = ' EU0'
   end
   return sEU
end

---------------------------------------------------------------------
function GetEE()
   return ' EE' .. EgtNumToString( EMT.ORIG, 0)
end

---------------------------------------------------------------------
function GetEF()
   return ' EF60000'
end

---------------------------------------------------------------------
function EmitCount()
   EMT.CNT = EMT.CNT + 1
   EmtOutput( 'N'..tostring( EMT.CNT)..' E80000='..tostring( EMT.CNT))
end

---------------------------------------------------------------------
function IsNailingHead( sHead)
   return ( sHead == 'H14' or sHead == 'H21' or sHead == 'H22' or sHead == 'H23')
end

---------------------------------------------------------------------
function GetValNotes( sNotes, sKey, sType)
   local vsChunk = EgtSplitString( sNotes or '', ';') 
   local dVal
   for i = 1, #vsChunk do
      local dTmp = EgtGetVal( vsChunk[i], sKey, sType)
      if dTmp then dVal = dTmp end
   end
   return dVal
end

---------------------------------------------------------------------
-- *** END GENERATION ***
---------------------------------------------------------------------
