-- Processore macchina Saomad-KAIROS by EgalTech s.r.l.   2023/12/06
-- Con controllo numerico Siemens

-- Variabili di modulo
local MLE_INFO = 'Saomad-KAIROS-MK3.SIEMENS.mlpe ver.'..PP_VER..' by EgalWare 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.C1t = 'I=AC('        -- X centro arco, dopo valore chiudere la tonda
   EMT.C2t = 'J=AC('        -- Y centro arco, dopo valore chiudere la tonda
   EMT.C3t = 'K=AC('        -- Z centro arco, dopo valore chiudere la tonda
   EMT.RRt = 'CR='          -- raggio per arco
   EMT.FMAXPINZE = 120000   -- feed massima pinze
   -- si inizia parcheggiando tutti i comandi fino a che non si hanno a disposizione le info per precarico
   EMT.PARKLINES = true
   EMT.X1CLAMP = nil
   EMT.X2CLAMP = nil
end

---------------------------------------------------------------------
function OnEnd()
   -- Ripristino fase iniziale come corrente
   EgtSetCurrPhase( 1)
end

---------------------------------------------------------------------
function OnProgramStart()
   -- Intestazioni
   if EMT.INFO then
      MyEmtOutput( ';'..EMT.INFO)
   else
      MyEmtOutput( ';Program Start')
   end
   MyEmtOutput( ';'.. MLE_INFO..'\n')
   -- Cerco primo utensile su testa 1
   EMT.TOOL_1, EMT.TLEN_1 = FindFirstToolOnHead( 'H1')
   -- Dichiaro inizio
   EMT.FIRST = true
   EMT.TLAST = nil
   EMT.NSTEP = 0
end

---------------------------------------------------------------------
function OnProgramEnd()
   -- Emissione scarico
   --for i = 1, #EMT.AUXCMD do
   --   MyEmtOutput( '( ' .. EMT.AUXCMD[i] .. ' )')
   --end
   EMT.AUXCMD = {}
   for i = 1, #EMT.AUXSTR do
      MyEmtOutput( EMT.AUXSTR[i])
   end
   if #EMT.AUXSTR > 0 then
      if EMT.AUXTYPE == 'S'  or  EMT.AUXTYPE == 'R' then
         ;
      elseif EMT.AUXTYPE == 'U' then
         -- emissione conclusione pezzo precedente
         if EMT.PRODID then
            local sOut = ';Part End'
            MyEmtOutput( sOut)
            sOut= 'PRODUCTION('..tostring( EMT.PRODID)..','..tostring( EMT.PATTID)..','..tostring( EMT.CUTID)..',2)'
            MyEmtOutput( sOut)
         end
      end
   end
   MyEmtOutput( 'STOPRE')
   MyEmtOutput( '_STEP=0')
   MyEmtOutput( 'M30')
   EMT.AUXSTR = {}
   EMT.AUXTYPE = nil
   EMT.U_STD = nil
   EMT.UNL = nil
   EMT.PREROT = nil
   EMT.SPLITROT = nil
   EMT.CHY_ON = nil
   EMT.FALL = nil
   EMT.RELOAD = nil
   EMT.RELOAD2 = nil

   -- creazione file dei pinzaggi
   CreateClampingFile()
end

---------------------------------------------------------------------
function OnToolData()
   -- tabella codice -> nome tipo utensile
   local tToolType = { [256] = 'DRILL', [257] = 'DRILL_L', [512] = 'SAW', [513] = 'SAW_F', [1024] = 'MILL', [1025] = 'MILL_NT',
                       [1026] = 'POLISHING', [2048] = 'CHAINSAW', [4096] = 'CHISEL'}
   -- emissione dati utensile
   --local sOut = ';'..EMT.TCPOS..' L='..EmtLenToString( EMT.TLEN, 3)..' D='..EmtLenToString( EMT.TDIAM, 3)..' A='..EgtIf( EMT.TUSED, '1', '0')..' '..( tToolType[EMT.TTYPE] or 'NONE')
   local sOut = ';'..EMT.TCPOS..' L='..EmtLenToString( EMT.TLEN, 3)..' D='..EmtLenToString( EMT.TDIAM, 3)..' A='..EgtIf( EMT.TUSED, '1', '0')..' '..EMT.TOOL
   MyEmtOutput( sOut)
end

---------------------------------------------------------------------
function OnDispositionStart()
   --MyEmtOutput( '(Disposition '..EMT.DISPIND..' = '..EMT.DISPID..')')
   EMT.OPEISDISP = true
   -- Assegnazione parametri disposizione
   EMT.TPOS = EgtGetInfo( EMT.DISPID, 'TPOS', 'd')
   EMT.X1POS = EgtGetInfo( EMT.DISPID, 'X1POS', 'd')
   -- Se prima disposizione
   if EMT.PHASE == 1 then
      -- inizio programma
      MyEmtOutput( '\nRESTART')
      MyEmtOutput( 'STOPRE')
      MyEmtOutput( '_STEP=0')
      -- carico barra
      EMT.LOAD = true
   else
      EMT.LOAD = false
      if IsEnd2Phase( EMT.PHASE - 1) then
         EMT.RELOAD = true
         EMT.RELOAD2 = false
      elseif IsStartPhase( EMT.PHASE) then
         EMT.X1DELTA = EMT.X1POS - LoadT
         EMT.X2DELTA = nil
      end
   end
end

---------------------------------------------------------------------
function OnDispositionEnd()
   -- Se disposizione inizio o rimanenza
   if IsStartOrRestPhase( EMT.PHASE) then
      -- reset recupero sovramateriale in X non più presente
      EMT.X_OFF = nil
      -- dati della barra
      local LBarra = EMT.LENGTHBEAM
      -- dati del pezzo
      local IdTrave = EMT.IDPART
      local LTrave = EMT.LENGTHPART
      local WTrave = EMT.WIDTHPART
      local HTrave = EMT.HEIGHTPART
      local HOverM = EMT.HOVM
      -- se carico barra
      if EMT.LOAD then
         -- assegnazione dati barra
         local sOut = ';Bar Dimensions'
         MyEmtOutput( sOut)
         sOut = 'C_WIDTH='..EmtLenToString( WTrave, 3)
         MyEmtOutput( sOut)
         sOut = 'C_HEIGHT='..EmtLenToString( HTrave, 3)
         MyEmtOutput( sOut)
         sOut = 'C_LENGTH='..EmtLenToString( LBarra, 3)
         MyEmtOutput( sOut)
      -- se altrimenti ricarico barra dopo rotazione speciale
      elseif EMT.RELOAD then
         local sOut = ';Bar Dimensions'
         MyEmtOutput( sOut)
         sOut = 'C_WIDTH='..EmtLenToString( WTrave, 3)
         MyEmtOutput( sOut)
         sOut = 'C_HEIGHT='..EmtLenToString( HTrave, 3)
         MyEmtOutput( sOut)
         sOut = 'C_LENGTH='..EmtLenToString( LBarra, 3)
         MyEmtOutput( sOut)
      -- altrimenti recupero rimanenza   
      else
         -- aggiorno dati aggancio carrelli alla trave
         EMT.X1DELTA = EMT.X1POS - LoadT
         EMT.X2DELTA = nil
      end
      -- Inizio produzione trave
      if IdTrave >= 0 then
         local BtlInfoId = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'BtlInfo') or EgtGetCurrMachGroup() or GDB_ID.NULL
         EMT.PRODID = EgtGetInfo( BtlInfoId, 'PRODID', 'i') or 0
         EMT.PATTID = EgtGetInfo( BtlInfoId, 'PATTID', 'i') or 0
         EMT.CUTID = EgtGetInfo( EMT.IDT, 'CUTID', 'i') or 0
         local sOut = ';Part Start'
         MyEmtOutput( sOut)
         sOut= 'PRODUCTION('..tostring( EMT.PRODID)..','..tostring( EMT.PATTID)..','..tostring( EMT.CUTID)..',1)'
         MyEmtOutput( sOut)
      else
         MyEmtOutput( '')
         MyEmtOutput( ';REMAIN UNLOAD')
         EMT.PRODID = nil
         EMT.PATTID = nil
         EMT.CUTID = nil
      end
      -- eventuale preparazione per rotazione immediata
      if IsStartPhase( EMT.PHASE) and EMT.AUXSTR then
         -- emissione movimento carrelli per rotazione
         EMT.AUXCMD = {}
         if #EMT.AUXSTR > 0 then
            MyEmtOutput( ';ROTATION')
         end
         for i = 1, #EMT.AUXSTR do
            MyEmtOutput( EMT.AUXSTR[i])
         end
         if #EMT.AUXSTR > 0 then EMT.CHAN2 = true end
         EMT.AUXSTR = {}
         EMT.AUXTYPE = nil
      end
   -- se altrimenti disposizione intermedia, eventuale rotazione
   elseif IsMidPhase( EMT.PHASE) or IsEnd2Phase( EMT.PHASE) then
      -- se rotazioni diverse, emetto il comando e aggiorno lo stato
      if VerifyEmitRotation() then
         -- imposto stato post-rotazione
         EMT.POSTROT = true
         -- imposto recupero sovramateriale in X non più presente
         EMT.X_OFF = EMT.HOVM
      end
   -- se altrimenti disposizione intermedia speciale con eventuale rotazione
   elseif IsMid2Phase( EMT.PHASE) then
      -- emissione movimento carrelli
      EMT.AUXCMD = {}
      if #EMT.AUXSTR > 0 then
         MyEmtOutput( ';ROTATION')
      end
      for i = 1, #EMT.AUXSTR do
         MyEmtOutput( EMT.AUXSTR[i])
      end
      if #EMT.AUXSTR > 0 then EMT.CHAN2 = true end
      EMT.AUXSTR = {}
      EMT.AUXTYPE = nil
      -- se rotazioni diverse, emetto il comando e aggiorno lo stato
      if VerifyEmitRotation() then
         -- imposto stato post-rotazione
         EMT.POSTROT = true
         -- imposto recupero sovramateriale in X non più presente
         EMT.X_OFF = EMT.HOVM
      end
   -- altrimenti disposizione finale, eventuale scarico pezzo lavorato se non ci sono lavorazioni
   else
      EMT.AUXCMD = {}
      if #EMT.AUXSTR > 0 then
         -- EmitSpeedOff()
         MyEmtOutput( '')
         MyEmtOutput( ';PART UNLOAD')
         MyEmtOutput( 'STOPRE')
         MyEmtOutput( 'TRANS')
      end
      for i = 1, #EMT.AUXSTR do
         MyEmtOutput( EMT.AUXSTR[i])
      end
      if #EMT.AUXSTR > 0 then
         EMT.CHAN2 = true
         -- emissione conclusione pezzo precedente
         if EMT.PRODID then
            local sOut = ';Part End'
            MyEmtOutput( sOut)
            sOut= 'PRODUCTION('..tostring( EMT.PRODID)..','..tostring( EMT.PATTID)..','..tostring( EMT.CUTID)..',2)'
            MyEmtOutput( sOut)
         end
      end
      EMT.AUXSTR = {}
      EMT.AUXTYPE = nil
      EMT.U_STD = nil
   end

   EMT.OPEISDISP = false
end

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

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

---------------------------------------------------------------------
function OnRawMoveData()
   -- Se disposizione inizio o rimanenza
   if IsStartOrRestPhase( EMT.PHASE) then
      -- se primo grezzo, reset lunghezza barra
      if EMT.RAWIND == 1 then EMT.LENGTHBEAM = 0 end   
      -- aggiungo la lunghezza del grezzo (solo se movimento in corner)
      if EMT.RAWTYPE == 1 then
         local b3Raw = EgtGetBBoxGlob( EgtGetFirstNameInGroup( EMT.RAWID, 'RawSolid'), GDB_BB.STANDARD)
         local LBarra = b3Raw:getDimX()
         EMT.LENGTHBEAM = EMT.LENGTHBEAM + LBarra
      end
      -- se primo grezzo, calcolo dati del pezzo
      if EMT.RAWIND == 1 then
         local PartId = EgtGetFirstPartInRawPart( EMT.RAWID)
         if PartId then
            EMT.IDT = PartId
            EMT.IDPART = EgtGetInfo( PartId, 'PDN', 'i') or 0
            local b3Part = EgtGetBBoxGlob( EgtGetFirstNameInGroup( PartId, 'Box'), GDB_BB.STANDARD)
            EMT.LENGTHPART = b3Part:getDimX()
            EMT.WIDTHPART = b3Part:getDimY()
            EMT.HEIGHTPART = b3Part:getDimZ()
            EMT.HOVM = EgtGetInfo( EMT.RAWID, 'HOVM', 'd') or 0
         else
            EMT.IDT = GDB_ID.NULL
            EMT.IDPART = -1
            EMT.LENGTHPART = 0
            EMT.WIDTHPART = EMT.WIDTHPART or 0
            EMT.HEIGHTPART = EMT.HEIGHTPART or 0
            EMT.HOVM = 0
         end
      end
   end
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 error( "Tool name mismatch") end
      -- predefinite EMT.TCPOS EMT.HEAD EMT.EXIT
      EMT.TOOLTYPE = EgtTdbGetCurrToolParam( MCH_TP.TYPE)
      EMT.TOOLSEL = string.sub( EMT.TCPOS, 2)
      EMT.TDIAM = EgtTdbGetCurrToolParam( MCH_TP.DIAM)
      EMT.TTOTDIAM = EgtTdbGetCurrToolParam( MCH_TP.TOTDIAM)
      EMT.TLEN = EgtTdbGetCurrToolParam( MCH_TP.LEN)
      EMT.TTOTLEN = EgtTdbGetCurrToolParam( MCH_TP.TOTLEN)
      if MCH_TP.DIST then EMT.TDIST = EgtTdbGetCurrToolParam( MCH_TP.DIST) end
      if EMT.TDIST and abs( EMT.TDIST) < 0.1 then EMT.TDIST = nil end
      -- dichiaro utensile appena selezionato
      EMT.TSELECT = true
   end
end

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

---------------------------------------------------------------------
function OnMachiningStart()
   EMT.MCHNAME = EgtGetOperationName( EMT.MCHID)
   EMT.MCHTYPE = EgtGetMachiningParam( MCH_MP.TYPE)
   EMT.MCHUSERNOTES = EgtGetMachiningParam( MCH_MP.USERNOTES)
   -- non ancora iniziata la lavorazione
   EMT.MCHFIRST = true
   EMT.FEEDFIRST = true
   -- determino subito se taglio di separazione di pezzo a caduta
   EMT.PREFALLCUT = nil
   if EMT.MCHUSERNOTES and EMT.MCHUSERNOTES:find( 'Split') then
      local ClId = EgtGetFirstNameInGroup( EMT.MCHID, 'CL')
      local P1Id = EgtGetFirstNameInGroup( ClId or GDB_ID.NULL, 'P1')
      local sAE1 = EgtGetInfo( P1Id or GDB_ID.NULL, 'AE1') or ''
      if sAE1 == '0,Fall' then
         EMT.PREFALLCUT = true
      end
   end
end

---------------------------------------------------------------------
function OnMachiningEnd()
   --MyEmtOutput( ';Mach End')
   -- Emissione split
   --for i = 1, #EMT.AUXCMD do
   --   MyEmtOutput( '( ' .. EMT.AUXCMD[i] .. ' )')
   --end
   EMT.AUXCMD = {}
   if #EMT.AUXSTR > 0 then
      if EMT.AUXTYPE == 'S' then
         -- EmitSpeedOff()
         MyEmtOutput( ';PART SPLIT')
      elseif EMT.AUXTYPE == 'U' then
         if not EMT.ZMAX then
            EmitZmax()
            EMT.ZMAX = true
         end
         if IsLastMachining( EMT.MCHID) then
            -- EmitSpeedOff()
         end
         MyEmtOutput( ';PART UNLOAD')
         MyEmtOutput( 'STOPRE')
         MyEmtOutput( 'TRANS')
      elseif EMT.AUXTYPE == 'P' then
         if EMT.PREROT then
            if not EMT.ZMAX then
               EmitZmax()
               EMT.ZMAX = true
            end
            MyEmtOutput( ';PART ROTATION')
         elseif EMT.FALL then
            if not EMT.ZMAX then
               EmitZmax()
               EMT.ZMAX = true
            end
            MyEmtOutput( ';FALL')
         else
            if not EMT.ZMAX then
               EmitZmax()
               EMT.ZMAX = true
            end
            MyEmtOutput( ';SPLIT 2')
         end
      end
   end
   for i = 1, #EMT.AUXSTR do
      MyEmtOutput( EMT.AUXSTR[i])
   end
   if #EMT.AUXSTR > 0 then
      EMT.CHAN2 = true
      if EMT.AUXTYPE == 'S' then
         ;
      elseif EMT.AUXTYPE == 'U' then
         -- emissione conclusione pezzo precedente
         if EMT.PRODID then
            local sOut = ';Part End'
            MyEmtOutput( sOut)
            sOut= 'PRODUCTION('..tostring( EMT.PRODID)..','..tostring( EMT.PATTID)..','..tostring( EMT.CUTID)..',2)'
            MyEmtOutput( sOut)
         end
      elseif EMT.AUXTYPE == 'P' then
         if EMT.FALL then
            -- emissione conclusione pezzo precedente
            if EMT.PRODID then
               local sOut = ';Part End'
               MyEmtOutput( sOut)
               sOut= 'PRODUCTION('..tostring( EMT.PRODID)..','..tostring( EMT.PATTID)..','..tostring( EMT.CUTID)..',2)'
               MyEmtOutput( sOut)
            end
         elseif not EMT.PREROT then
            ;
         end
      end
   end
   EMT.AUXSTR = {}
   EMT.AUXTYPE = nil
   EMT.U_STD = nil
   EMT.PREVTOOL = EMT.TOOL
   EMT.PREVR3 = EMT.R3
end

---------------------------------------------------------------------
function OnPathStart()
   -- non ancora iniziata la lavorazione
   EMT.MCHFIRST = true
   -- primo posizionamento sempre in globale
   EMT.REFLOC = nil
   EMT.IPLGL = false
   -- reset valori precedenti (per forzare emissione di tutti gli assi del 1° movimento)
   EMT.L1opp = EMT.L1op or EMT.L1
   EMT.L2opp = EgtIf( EMT.TSELECT or not EMT.L2op, ParkY, EMT.L2op)
   EMT.L3opp = EgtIf( EMT.TSELECT or not EMT.L3op, ParkZ, EMT.L3op)
   EMT.R1pp = EgtIf( EMT.TSELECT or not EMT.R1p, ParkC, EMT.R1p)
   EMT.R2pp = EgtIf( EMT.TSELECT or not EMT.R2p, ParkA, EMT.R2p)
   EmtResetPrev()
   EMT.AUXCMD = {}
   EMT.AUXSTR = {}
end

---------------------------------------------------------------------
function OnPathEnd()
   EMT.AUXTYPE = nil
   EMT.UNL = nil
   EMT.PREROT = nil
   EMT.SPLITROT = nil
   EMT.CHY_ON = nil
   EMT.FALL = nil
   EMT.AUXCMD = {}
   EMT.AUXSTR = {}
end

---------------------------------------------------------------------
function OnPathStartAux()
   --EgtOutLog( 'OnPathStartAux - ' .. EgtNumToString( EMT.AUXIND, 0) .. ' - ' .. EMT.AUX)

   -- se richiesto, preparo il carico barra
   if EMT.LOAD or EMT.RELOAD then
      PrepareLoad( EMT.AUX, true)
   -- se altrimenti carico dopo rotazione
   elseif EMT.POSTROT then
      PreparePostRotation( EMT.AUX)
   -- altrimenti, preparo lo spostamento carrelli
   else
      PrepareMoveChar( EMT.AUX)
   end
end

---------------------------------------------------------------------
function OnPathEndAux()
   --EgtOutLog( 'OnPathEndAux - ' .. EgtNumToString( EMT.AUXIND, 0) .. ' - ' .. EMT.AUX)
   -- verifico tipo di emissione
   if EMT.OPEISDISP then
      if not EMT.AUXTYPE then
         local Cmd = EgtSplitString( EMT.AUX)
         if Cmd[1] == '0'  and  Cmd[2] == 'Unloading' then
            EMT.AUXTYPE = 'U'
         elseif EgtGetInfo( EMT.DISPID, 'TYPE') == 'REST' then
            EMT.AUXTYPE = 'R'
         else
            EMT.AUXTYPE = 'P'
         end
      end
   else
      if not EMT.AUXTYPE then
         local Cmd = EgtSplitString( EMT.AUX)
         if Cmd[1] == '0'  and  Cmd[2] == 'Split' then
            EMT.AUXTYPE = 'S'
         elseif Cmd[1] == '0'  and  Cmd[2] == 'Unloading' then
            EMT.AUXTYPE = 'U'
         else
            EMT.AUXTYPE = 'P'
            if Cmd[1] == '0' and Cmd[2] == 'Fall' then
               EMT.FALL = true
            end
         end
      elseif EMT.AUXTYPE == 'P' then
         local Cmd = EgtSplitString( EMT.AUX)
         if  Cmd[1] == '0'  and  Cmd[2] == 'Unloading' then
            EMT.CHY_ON = true
            EMT.AUXTYPE = 'U'
         end
      end
   end
   -- preparo a seconda del tipo
   if EMT.AUXTYPE == 'R' then
      -- per il carico della rimanenza dopo rotazione speciale
      if EMT.RELOAD and not EMT.RELOAD2 then
         PrepareLoad( EMT.AUX, false)
      -- per lo scarico della rimanenza
      else
         PrepareResidue( EMT.AUX)
      end
   elseif EMT.AUXTYPE == 'S' then
      -- per lo split
      PrepareSplit( EMT.AUX)
   elseif EMT.AUXTYPE == 'U' then
      -- per lo scarico
      PrepareUnload( EMT.AUX)
   elseif EMT.AUXTYPE == 'P' then
      -- per la pre-rotazione
      PreparePreRotation( EMT.AUX)
   end
end

---------------------------------------------------------------------
function OnRapid()
   MyBackupAxes()
   -- se primo movimento della lavorazione, gestione speciale
   if EMT.MCHFIRST and not EMT.OPEISDISP then
      -- decido se muovere prima testa o carrelli (standard prima testa)
      local bHeadFirst = true
      local sLateG101
      if not ( EMT.LOAD or EMT.RELOAD) and not EMT.ZMAX then
         local DeltaT = EMT.L1 - EMT.TLAST
         if ( DeltaT < -GEO.EPS_SMALL and EMT.X2DELTA) or ( DeltaT > GEO.EPS_SMALL and EMT.X1DELTA) then bHeadFirst = false end
      end
      -- sistemo movimenti
      MyAdjustLinearAxes()
      EmtAdjustRotaryAxes()
      -- verifico presenza movimento carrelli
      local bMoveChar = ( #EMT.AUXSTR > 0)
      -- carico
      if EMT.LOAD then
         local dPress = 2.5
         local dX1LoadPos = EMT.X1DELTA
         local dAcc = 4
         local dFeed = 45000
         SetStartValue( 1, EMT.LENGTHBEAM)
         SetStartValue( 2, EMT.WIDTHPART)
         SetStartValue( 3, EMT.HEIGHTPART)
         SetStartValue( 4, dPress)
         SetStartValue( 5, dX1LoadPos)
         SetStartValue( 6, EmtLenToString( ParkX1 + EMT.X1DELTA + 500, 3))  -- posizione per portare trave più vicino all'area di lavoro, ma con 500mm di sicurezza
         SetStartValue( 7, dAcc)
         SetStartValue( 8, dFeed)

         -- ora che ci sono tutti i dati, si scrivono info precarico e linee parcheggiate
         EmitStartValues()
         EmitParkedLines()
         EMT.PARKLINES = nil
         MyEmtOutput( 'STOPRE')
         EMT.NSTEP = EMT.NSTEP + 10
         local sStep = EgtNumToString( EMT.NSTEP, 0)
         MyEmtOutput( '_STEP='..sStep)
         -- eseguo carico
         EMT.AUXCMD = {}
         for i = 1, #EMT.AUXSTR do
            MyEmtOutput( EMT.AUXSTR[i])
         end
         EMT.AUXSTR = {}
         -- supporti in posizione intermedia
         local sOut = 'SUPPORT(1,1)'
         MyEmtOutput( sOut)
         -- prima lavorazione
         sOut = EMT.MCHNAME..'/'..EMT.TOOL
         MyEmtOutput( '; *** ' .. sOut .. ' ***')
      -- altri casi
      else
         if not EMT.RELOAD and not EMT.ZMAX and #EMT.AUXSTR > 0 then
            EmitZmax()
            EMT.ZMAX = true
            EMT.PREVTOOL = EMT.TOOL
         end
         if bMoveChar then
            local bStoPre
            if EMT.CHAN2 then
               -- MyEmtOutput( 'WAIT_CHAN(2)')
               MyEmtOutput( 'STOPRE')
               bStoPre = true
               EMT.CHAN2 = nil
            end
            if EMT.TRAILON then
               MyEmtOutput( 'TRAILOF(X2,X1)')
               MyEmtOutput( 'STOPRE')
               MyEmtOutput( 'TRANS')
               bStoPre = true
               EMT.TRAILON = nil
            end
            if not bStoPre then MyEmtOutput( 'STOPRE') end
            MyEmtOutput( '')
            EMT.NSTEP = EMT.NSTEP + 10
            local sStep = EgtNumToString( EMT.NSTEP, 0)
            MyEmtOutput( '_STEP='..sStep)
         end
         local sOut = EMT.MCHNAME..'/'..EMT.TOOL
         MyEmtOutput( '; *** ' .. sOut .. ' ***')
         -- movimento carrelli
         EMT.AUXCMD = {}
         for i = 1, #EMT.AUXSTR do
            MyEmtOutput( EMT.AUXSTR[i])
         end
         EMT.AUXSTR = {}
         EMT.POSTROT = false
         EMT.RELOAD = false
         EMT.RELOAD2 = nil
         EMT.CHAN2 = bMoveChar
      end
      -- se utensile appena selezionato, lo devo dichiarare
      if EMT.TSELECT then
         EMT.TSELECT = nil
         if bMoveChar then
            local sStep = EgtNumToString( EMT.NSTEP, 0)
            MyEmtOutput( '_N'..sStep..':STOPRE')
         end
         local sOut = 'PRE_WORK(1,1,' .. EMT.TOOLSEL .. ','..EgtNumToString( EMT.S, 0) ..',,,,)'
         MyEmtOutput( sOut)
         EMT.SPEED_ON = true
      -- eventuali emissioni per ripartenza successiva al carico
      elseif bMoveChar then
         local sStep1 = EgtNumToString( EMT.NSTEP + 1, 0)
         MyEmtOutput( 'GOTOF '..'_N'..sStep1)
         local sStep = EgtNumToString( EMT.NSTEP, 0)
         MyEmtOutput( '_N'..sStep..':STOPRE')
         local sOut = 'PRE_WORK(1,1,' .. EMT.TOOLSEL .. ','..EgtNumToString( EMT.S, 0) ..',,,,)'
         MyEmtOutput( sOut)
         EMT.SPEED_ON = true
         MyEmtOutput( '_N'..sStep1..':STOPRE')
      end
      -- eventuale avvio mandrino (se non già avviato)
      EmitSpeedOn( EMT.S)
      -- primo posizionamento testa rispetto a 0M
      EmtResetPrev()
      local sOut
      if EMT.ZMAX then
         sOut = 'SUPA G0 D0 Z'..EgtNumToString( ParkZ, 3)
      else
         -- sOut = 'SUPA G0 D0' .. EmtGetAxis( 'L3')
      end
      MyEmtOutput( sOut)
      -- sega a catena da gestire
      if EMT.HEAD == 'H3' then
         MyEmtOutput( '!!! Chainsaw start movements to be calculated !!!')
         EgtOutLog( 'ERROR : CHAINSAW START MOVEMENTS TO BE CALCULATED !!!')
      end
      -- movimento YAC con interpolazione approssimata tramite F opportune per ogni asse
      local dFeedY, dFeedA, dFeedC = CalcFeedYAC( EMT.L2opp - EMT.L2, EMT.R2pp - EMT.R2, EMT.R1pp - EMT.R1)
      --sOut = '_POSIZ_YAC('..EgtNumToString( EMT.L2, 3)..','..EgtNumToString( EMT.R2, 3)..','..EgtNumToString( EMT.R1, 3)..','.. EgtNumToString( FmaxY, 0)..',,)'
      sOut = 'TRANS'
      MyEmtOutput( sOut)

      sOut = 'G1 Y=' .. EgtNumToString( EMT.L2, 3)..' A1='..EgtNumToString( EMT.R2, 3)..' C1='..EgtNumToString( EMT.R1, 3) .. ' F' .. EgtNumToString( FmaxY, 3)
      MyEmtOutput( sOut)

      -- si ripete macro di carico con opzione di precarico eseguito
      if EMT.LOAD then
         MyEmtOutput( EMT.PRELOADCMD)
         EMT.PRELOADCMD = nil
      end

      EMT.LOAD = false

      -- se ho lanciato split in modo asincrono, dopo POSIZ_YAC devo attendere
      if EMT.WAIT_SPLIT then
         sOut = 'WAITP(X1)'
         MyEmtOutput( sOut)
         EMT.WAIT_SPLIT = nil
      end

      -- se necessario, attesa fine movimenti carrelli e presa assi relativi
      if bMoveChar then
         sOut = 'WAIT_CHAN(2)'
         MyEmtOutput( sOut)
         EMT.CHAN2 = nil

         if EMT.X1DELTA and EMT.X2DELTA then
            MyEmtOutput( 'GET(X1,X2)')
            MyEmtOutput( 'STOPRE')
            MyEmtOutput( 'TRAILON(X2,X1)')
            EMT.TRAILON = true
         elseif EMT.X1DELTA then
            MyEmtOutput( 'GET(X1)')
         elseif EMT.X2DELTA then
            MyEmtOutput( 'GET(X2)')
         end
      end
      -- assegnazione assi geometrici (GEOAX)
      MyEmtOutput( 'TRANS')
      if EMT.X1DELTA then
         sOut = 'GEOAX(1,X1,2,Y1,3,Z1)'
      else
         sOut = 'GEOAX(1,X2,2,Y1,3,Z1)'
      end
      MyEmtOutput( sOut)

      -- dopo aver spostato i carrelli, lancio precarico
      -- se taglio di coda senza residuo da scaricare, emetto macro per accelerare il carico della barra successiva
      if EMT.MCHUSERNOTES and EMT.MCHUSERNOTES:find( 'Cut') and not EMT.PRELOAD then
         EMT.PRELOAD = true
         sOut = '_POSIZ_T(4,,,,,,,,,)'  -- Lancio macro per carico anticipato: i valori sono presi dal plc (passati dall'interfaccia)
         MyEmtOutput( sOut)
      end

      MyEmtOutput( 'G90 G17')
      -- primo posizionamento carrello rispetto a 0M
      if EMT.X1DELTA then
         local dPosX1 = EMT.L1 + EMT.X1DELTA
         sOut = 'G1 X' .. EmtLenToString( dPosX1, 3) .. ' F' .. GetClampFeed( EMT.LENGTHBEAM)
         MyEmtOutput( sOut)
      elseif EMT.X2DELTA then
         local dPosX2 = EMT.L1 + EMT.X2DELTA
         sOut = 'G1 X' .. EmtLenToString( dPosX2, 3) .. ' F' .. GetClampFeed( EMT.LENGTHBEAM)
         MyEmtOutput( sOut)
      else
         EmtSetLastError( 1213, "Part to machine without charriots")
      end
      if EMT.ZMAX and EMT.L3 < ParkZ - 10 then
         sOut = 'SUPA G0 D0' .. EmtGetAxis( 'L3')
         MyEmtOutput( sOut)
      end
      -- se taglio di separazione prima di scarico a caduta, emissione stop tappeto scarico trucioli
      if EMT.PREFALLCUT then
         MyEmtOutput( ';M29')
         EMT.PREFALLCUT = nil
      end
      EMT.REFLOC = 0
      EMT.MCHFIRST = false
      EMT.ZMAX = false

      -- se alto in Z (non serve l'avvicinamento finale)
      if EMT.FLAG == 2 then
         -- aggiorno valori come precedenti
         EMT.TLAST = EMT.L1b
         EmtUpdatePrev()
         return
      -- altrimenti ripristino i valori originali degli assi
      else
         MyRestoreAxes()
      end

   end
   -- se standard
   if EMT.FLAG == 0 or EMT.FLAG == 1 or EMT.FLAG == 2 then
      -- se necessario, impostazione riferimento locale
      if EMT.REFLOC == 0 then
         -- calcolo del piano generico
         CalcInterpPlane()
         EMT.REFLOC = 1
         -- emissione del piano generico
         MyEmtOutput( EMT.IPLGLSTR)
      end
      MyAdjustLinearAxes()
      EmtAdjustRotaryAxes()
      -- emissione movimento
      -- local sOut = 'G0'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')
      local sOut = 'G1'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..' F100000'
      MyEmtOutput( sOut)
   -- se altrimenti risalita a Z max a fine lavorazione
   elseif EMT.FLAG == 3 then
      EMT.REFLOC = nil
      EMT.IPLGL = false
      MyAdjustLinearAxes()
      EmtAdjustRotaryAxes()
      EmitZmax()
      EMT.ZMAX = true
   -- se altrimenti movimento in Home
   elseif EMT.FLAG == 4 then
      -- non previsto
   -- se altrimenti rotazione a Z max
   elseif EMT.FLAG == 5 then
      -- viene gestito all'inizio della lavorazione successiva
   -- altrimenti errore
   else
      EmtSetLastError( 1212, "Unknown Rapid flag")
   end
   -- aggiorno valori come precedenti
   EMT.TLAST = EMT.L1b
   EmtUpdatePrev()
end

---------------------------------------------------------------------
function OnLinear()
   MyBackupAxes()
   EMT.TLAST = EMT.L1
   -- se necessario, impostazione riferimento locale
   if EMT.REFLOC == 0 then
      -- calcolo del piano generico
      CalcInterpPlane()
      EMT.REFLOC = 1
      -- emissione del piano generico
      MyEmtOutput( EMT.IPLGLSTR)
      -- salvo posizione attuale
      local OldL1 = EMT.L1
      local OldL2 = EMT.L2
      local OldL3 = EMT.L3
      -- imposto posizione precedente (non contiene l'offset in X per sovramateriale di testa EMT.X_OFF)
      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
      -- emissione movimento
      -- local sOut = 'G0'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')
      local sOut = 'G1'..EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..' F100000'
      MyEmtOutput( sOut)
      -- ripristino posizione attuale
      EMT.L1 = OldL1
      EMT.L2 = OldL2
      EMT.L3 = OldL3
   end

   if EMT.FEEDFIRST then
      local sOut = 'M_OK(1,'..EgtNumToString( EMT.S, 0)..')'
      MyEmtOutput( sOut)
      EMT.FEEDFIRST = false
   end

   -- aggiustamento valori
   MyAdjustLinearAxes()
   EmtAdjustRotaryAxes()

   -- valori degli assi
   local sAxes = EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')
   -- se nulla da emettere, esco
   if #sAxes == 0 then
      return
   end
   -- aggiungo feed
   local sFeed = EmtGetFeed()
   -- emetto linea
   MyEmtOutput( "G1"..sAxes..sFeed)

   -- aggiorno valori come precedenti
   EmtUpdatePrev()
end

---------------------------------------------------------------------
function OnArc()
   MyBackupAxes()
   EMT.TLAST = EMT.L1
   
   if EMT.FEEDFIRST then
      local sOut = 'M_OK(1,'..EgtNumToString( EMT.S, 0)..')'
      MyEmtOutput( sOut)
      EMT.FEEDFIRST = false
   end

   -- non modale su archi
   EmtResetPrevLinear()
   -- aggiustamento valori
   MyAdjustLinearAxes()
   EmtAdjustRotaryAxes()

   -- valori degli assi
   local sAxes = EmtGetAxis('L1')..EmtGetAxis('L2')..EmtGetAxis('L3')..
                 EmtGetAxis('R2')..EmtGetAxis('R1')
   -- se nulla da emettere, esco
   if #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)
   -- emetto arco
   MyEmtOutput( sArc..sAxes..sRad..sFeed)

   -- aggiorno valori come precedenti
   EmtUpdatePrev()
end

---------------------------------------------------------------------
function CalcDinamicaPinze( dH, dS, dL)
   local MinTempoAcc = 0.3                         -- [s]
   local MaxTempoAcc = 4.0                         -- [s]
   local KgMtCubo= 550                             -- densità legno [Kg / metro cubo]
   local Massa = ( dH * dS * dL * KgMtCubo ) / 1e9 -- massa [Kg]
   local FMaxPinze = EMT.FMAXPINZE                 -- Feed massima pinze [mm/min]
   local ForzaAttrito = 350 * 9.8 * 0.2            -- Forza chiusura pinze [Kgf] * g * Coeff_Attrito -> [N]
   local TempoAcc = ( ( Massa * FMaxPinze) / ( 60 * ForzaAttrito) / 1000)
   if ( TempoAcc < MinTempoAcc) then TempoAcc = MinTempoAcc end
   if ( TempoAcc > MaxTempoAcc) then TempoAcc = MaxTempoAcc end
   local AccMaxPinze = FMaxPinze / ( 60 * MinTempoAcc)
   local AccPinze = FMaxPinze / ( 60 * TempoAcc)
   local RidFeed = 100 / Massa * 100
   if RidFeed > 100 then
      RidFeed = 100
   elseif RidFeed < 10 then
      RidFeed = 10
   end
   return AccPinze, AccMaxPinze, RidFeed, TempoAcc
end

---------------------------------------------------------------------
function CalcCharStatus( sCmd, bSkipPress)
   -- aperto
   if sCmd == '0' then
      return '1'
   -- chiuso
   elseif sCmd == '1' then
      return '2'
   -- chiuso con pressore attivato
   elseif sCmd == '2' then
      return EgtIf( bSkipPress, '2', '0')
   end
end

---------------------------------------------------------------------
function CalcFeedYAC( dDeltaY, dDeltaA, dDeltaC)
   local dTY = abs( dDeltaY) / FmaxY
   local dTA = abs( dDeltaA) / FmaxA
   local dTC = abs( dDeltaC) / FmaxC
   local dT = max( dTY, dTA, dTC)
   local dFeedY = FmaxY
   if dTY > 0.001 then dFeedY = FmaxY * dTY / dT end
   local dFeedA = FmaxA
   if dTA > 0.001 then dFeedA = FmaxA * dTA / dT end
   local dFeedC = FmaxC
   if dTC > 0.001 then dFeedC = FmaxC * dTC / dT end
   return dFeedY, dFeedA, dFeedC
end

---------------------------------------------------------------------
function PrepareLoad( sCmd, bStart)
   local Cmd = EgtSplitString( sCmd)
   if Cmd[1] == '0' then
      -- se inizio
      if EMT.AUXIND == 1 then
         -- reset stato pinze
         EMT.X1STAT = 0
         EMT.X2STAT = 0
      end
   elseif Cmd[1] == '1' then
   elseif Cmd[1] == '2' then
   elseif Cmd[1] == '3' then
   elseif Cmd[1] == '11' then
      EMT.X1STAT = tonumber( Cmd[2])
   elseif Cmd[1] == '12' then
      EMT.X2STAT = tonumber( Cmd[2])
   elseif Cmd[1] == '21' then
      local dX1Delta = tonumber( Cmd[2])
      local dX2Delta = tonumber( Cmd[3])
      if dX1Delta > 0 and dX2Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = dX2Delta
      elseif dX1Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = nil
      elseif dX2Delta > 0 then
         EMT.X1DELTA = nil
         EMT.X2DELTA = dX2Delta
      end
      if EMT.X1DELTA and not EMT.X2DELTA then
         local sInfo = string.format( ';LOAD D1=%.1f', EMT.X1DELTA)
         table.insert( EMT.AUXSTR, sInfo)
         local nFunz = 1
         local nVertClamp = EgtIf( EMT.X1STAT == 2, 1, 0)
         local dPress = 2.5
         local dFeed = GetClampFeed( EMT.LENGTHBEAM)
         local dAcc = 4
         local nOpt = 0
         local sRef1 = EmtLenToString( EMT.X1DELTA, 3)
         local sRef2 = EmtLenToString( ParkX1 + EMT.X1DELTA, 3)
         local sOut = string.format( '_POSIZ_T(%d,%d,%.1f,%.1f,%.1f,%d,%s,%s,,)', nFunz, nVertClamp, dPress, dFeed, dAcc, nOpt, sRef1, sRef2)
         table.insert( EMT.AUXSTR, sOut)
         -- preparo funzione di carico in caso di precarico attivo
         nOpt = 1
         EMT.PRELOADCMD = string.format( '_POSIZ_T(%d,%d,%.1f,%.1f,%.1f,%d,%s,%s,,)', nFunz, nVertClamp, dPress, dFeed, dAcc, nOpt, sRef1, sRef2)
         EMT.X1CLAMP = true
      end
   end
end

---------------------------------------------------------------------
-- funzione che restituisce il valore del paraemtro, scritto nell'indice successivo della stringa passata
function GetValInString( sString, sValToSearch)
   local vsSplitString = EgtSplitString( sString, ',')
   local dValue
   for i = 1, #vsSplitString do
      if vsSplitString[i] == sValToSearch then
         if vsSplitString[i+1] then
            dValue = vsSplitString[i+1]
         end
      end
   end
   return dValue
end

---------------------------------------------------------------------
function PrepareMoveChar( sCmd)
   local Cmd = EgtSplitString( sCmd)
   if Cmd[1] == '0' then
   elseif Cmd[1] == '1' then
      if Cmd[2] ~= 'Z' then
         local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dX1Pos = GetValInString( sCmd, 'X1')
         local dX2Pos = GetValInString( sCmd, 'X2')
         table.insert( CLAMP_POS, '###_ADD_STEP_###')
         if dX1Pos and dX2Pos then
            table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
         elseif dX1Pos then
            table.insert( CLAMP_POS, string.format( 'G1 X1=%.3f F=%d', dX1Pos, dFeed1))
         elseif dX2Pos then
            table.insert( CLAMP_POS, string.format( 'G1 X2=%.3f F=%d', dX2Pos, dFeed2))
         end
      end
   elseif Cmd[1] == '2' then
      local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dX1Pos = GetValInString( sCmd, 'X1')
      local dX2Pos = GetValInString( sCmd, 'X2')
      table.insert( CLAMP_POS, '###_ADD_STEP_###')
      if dX1Pos and dX2Pos then
         table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
      elseif dX1Pos then
         table.insert( CLAMP_POS, string.format( 'G1 X1=%.3f F=%d', dX1Pos, dFeed1))
      elseif dX2Pos then
         table.insert( CLAMP_POS, string.format( 'G1 X2=%.3f F=%d', dX2Pos, dFeed2))
      end
   elseif Cmd[1] == '3' then
      local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dX1Pos = GetValInString( sCmd, 'X1')
      local dX2Pos = GetValInString( sCmd, 'X2')
      table.insert( CLAMP_POS, '###_ADD_STEP_###')
      table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
   elseif Cmd[1] == '11' then
      if Cmd[2] == '0' and EMT.X1CLAMP then
         EMT.X1CLAMP = nil
         table.insert( CLAMP_POS, 'GRIPPER("AP1",,)')
      elseif not EMT.X1CLAMP and Cmd[2] ~= '0' then
         EMT.X1CLAMP = true
         local sVertClamp = EgtIf( Cmd[2] == '2', '1', '0')
         table.insert( CLAMP_POS, 'GRIPPER("CH1",'.. sVertClamp ..',2.5)')
      end
   elseif Cmd[1] == '12' then
      if Cmd[2] == '0' and EMT.X2CLAMP then
         EMT.X2CLAMP = nil
         table.insert( CLAMP_POS, 'GRIPPER("AP2",,)')
      elseif not EMT.X2CLAMP and Cmd[2] ~= '0' then
         EMT.X2CLAMP = true
         local sVertClamp = EgtIf( Cmd[2] == '2', '1', '0')
         table.insert( CLAMP_POS, 'GRIPPER("CH2",'.. sVertClamp ..',2.5)')
      end
   elseif Cmd[1] == '21' then
      local dX1Delta = tonumber( Cmd[2])
      local dX2Delta = tonumber( Cmd[3])
      if dX1Delta > 0 and dX2Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = dX2Delta
      elseif dX1Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = nil
      elseif dX2Delta > 0 then
         EMT.X1DELTA = nil
         EMT.X2DELTA = dX2Delta
      end
      table.insert( ALL_CLAMP_POS, CLAMP_POS)
      CLAMP_POS = {}
      local nFunz = 77
      local nVertClamp = 0
      local dPress = 2.5
      local dFeed = GetClampFeed( EMT.LENGTHPART)
      local dAcc = 4
      local nOpt = #ALL_CLAMP_POS * 100
      local sOut = string.format( '_POSIZ_T(%d,,,,,%d,,,,,,)', nFunz, nOpt)
      table.insert( EMT.AUXSTR, sOut)
   end
end

---------------------------------------------------------------------
function PrepareResidue( sCmd)
   local Cmd = EgtSplitString( sCmd)
   if Cmd[1] == '0' then
      -- se non è scarico
      if Cmd[2] ~= 'Unloading' then
         -- se inizio
         if EMT.AUXIND <= 2 then
            -- reset stato
            EMT.MCIND = 0
            EMT.X1STAT = 0
            EMT.X2STAT = 0
            EMT.MCX1 = {}
            EMT.MCX2 = {}
         end
      else
         EMT.UNL = true
      end
   elseif Cmd[1] == '1' then
      if Cmd[2] ~= 'Z' then
         EMT.MCIND = EMT.MCIND + 1
         if Cmd[2] == 'X1' then
            EMT.MCX1[EMT.MCIND] = tonumber( Cmd[3])
            EMT.MCX2[EMT.MCIND] = 0
         elseif Cmd[2] == 'X2' then
            EMT.MCX1[EMT.MCIND] = 0
            EMT.MCX2[EMT.MCIND] = tonumber( Cmd[3])
         end
      end
   elseif Cmd[1] == '2' then
      EMT.MCIND = EMT.MCIND + 1
      for i = 2, 4, 2 do
         if Cmd[i] == 'X1' then
            EMT.MCX1[EMT.MCIND] = tonumber( Cmd[i+1])
         elseif Cmd[i] == 'X2' then
            EMT.MCX2[EMT.MCIND] = tonumber( Cmd[i+1])
         end
      end
      if not EMT.MCX1[EMT.MCIND] then EMT.MCX1[EMT.MCIND] = 0 end
      if not EMT.MCX2[EMT.MCIND] then EMT.MCX2[EMT.MCIND] = 0 end
   elseif Cmd[1] == '3' then
      EMT.MCIND = EMT.MCIND + 1
      for i = 2, 6, 2 do
         if Cmd[i] == 'X1' then
            EMT.MCX1[EMT.MCIND] = tonumber( Cmd[i+1])
         elseif Cmd[i] == 'X2' then
            EMT.MCX2[EMT.MCIND] = tonumber( Cmd[i+1])
         end
      end
   elseif Cmd[1] == '11' then
      EMT.X1STAT = tonumber( Cmd[2])
      if Cmd[2] == 0 then
         EMT.X1CLAMP = nil
      else
         EMT.X1CLAMP = true
      end
   elseif Cmd[1] == '12' then
      if Cmd[2] == 0 then
         EMT.X2CLAMP = nil
      else
         EMT.X2CLAMP = true
      end
      EMT.X2STAT = tonumber( Cmd[2])
      if EMT.UNL and EMT.X2STAT == 0 then
         -- local sWait = 'WAIT_CHAN(2)'
         -- table.insert( EMT.AUXSTR, sWait)
         local sSync = 'STOPRE'
         table.insert( EMT.AUXSTR, sSync)
         local nFunz = 6
         local nVertClamp = 0
         local dPress = 2.5
         local dFeed = GetClampFeed( EMT.LENGTHPART)
         local dAcc = 4
         local nOpt = 0
         local sRef1 = EMT.MCX2[#EMT.MCX2]
         local sRef2 = EmtLenToString( ParkX2 - ( EMT.LENGTHBEAM - EMT.X2DELTA), 3)
         local sOut = string.format( '_POSIZ_T(%d,%d,%.1f,%.1f,%.1f,%d,%s,%s,,)', nFunz, nVertClamp, dPress, dFeed, dAcc, nOpt, sRef1, sRef2)
         table.insert( EMT.AUXSTR, sOut)
      end
   elseif Cmd[1] == '21' then
      local dX1Delta = tonumber( Cmd[2])
      local dX2Delta = tonumber( Cmd[3])
      if dX1Delta > 0 and dX2Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = dX2Delta
      elseif dX1Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = nil
      elseif dX2Delta > 0 then
         EMT.X1DELTA = nil
         EMT.X2DELTA = dX2Delta
      end
      if EMT.AUXIND > 1 then
         local sInfo = string.format( ';X1->X2 D2=%.1f', EMT.LENGTHBEAM - EMT.X2DELTA)
         table.insert( EMT.AUXSTR, sInfo)
         local sWait = 'WAIT_CHAN(2)'
         table.insert( EMT.AUXSTR, sWait)
         local sSync = 'STOPRE'
         table.insert( EMT.AUXSTR, sSync)
         local nFunz = 31
         local nVertClamp = 0
         local dPress = 2.5
         local dFeed = GetClampFeed( EMT.LENGTHBEAM)
         local dAcc = 4
         local nOpt = EgtIf( EMT.MCIND >= 3, 2, 1)
         local sRef1 = EmtLenToString( EMT.MCX1[1], 3)
         local sRef2 = EmtLenToString( EMT.MCX2[1], 3)
         local sRef3 = EmtLenToString( EMT.MCX1[2], 3)
         local sRef4 = EmtLenToString( EgtIf( EMT.MCX2[2] == 0, EMT.MCX2[1], EMT.MCX2[2]), 3)
         local sOut = string.format( '_POSIZ_T(%d,%d,%.1f,%.1f,%.1f,%d,%s,%s,%s,%s)', nFunz, nVertClamp, dPress, dFeed, dAcc, nOpt, sRef1, sRef2, sRef3, sRef4)
         table.insert( EMT.AUXSTR, sOut)
      end
   end
end

---------------------------------------------------------------------
function PrepareSplit( sCmd)
   local Cmd = EgtSplitString( sCmd)
   if Cmd[1] == '0' then
      -- non interessa
   elseif Cmd[1] == '1' then
      local sMsg = Cmd[2] .. Cmd[3]
      table.insert( EMT.AUXCMD, sMsg)
      if Cmd[2] == 'X1' then
         local sOut = 'STOPRE'
         table.insert( EMT.AUXSTR, sOut)
         EMT.NSTEP = EMT.NSTEP + 10
         local sStep = EgtNumToString( EMT.NSTEP, 0)
         sOut = '_STEP='..sStep
         table.insert( EMT.AUXSTR, sOut)
         sOut = 'TRAILOF(X2,X1)'
         table.insert( EMT.AUXSTR, sOut)
         sOut = '_N'..sStep..':STOPRE'
         table.insert( EMT.AUXSTR, sOut)
         sOut = 'STOPRE'
         table.insert( EMT.AUXSTR, sOut)
         sOut = 'TRANS'
         table.insert( EMT.AUXSTR, sOut)
         EMT.TRAILON = nil
         -- POSA manda in posiuzione asse X1 in modo asincrono (quindi continua con altri movimenti)
         sOut = 'POSA[X1]='..Cmd[3]..' FA[X1]='..GetFmaxClamp()
         EMT.WAIT_SPLIT = true
         table.insert( EMT.AUXSTR, sOut)
         EMT.X1POS = tonumber( Cmd[3])
      end   
   elseif Cmd[1] == '21' then
      local dX1Delta = tonumber( Cmd[2])
      local dX2Delta = tonumber( Cmd[3])
      if dX1Delta > 0 and dX2Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = dX2Delta
      elseif dX1Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = nil
      elseif dX2Delta > 0 then
         EMT.X1DELTA = nil
         EMT.X2DELTA = dX2Delta
      end
   end
end

---------------------------------------------------------------------
function PrepareUnload( sCmd)
   local Cmd = EgtSplitString( sCmd)
   -- se ultima lavorazione si utilizza il POSIZ_T(5) per eseguire su processo 1
   if IsLastMachining( EMT.MCHID) then
      if Cmd[1] == '0' then
      elseif Cmd[1] == '1' then
         if Cmd[2] ~= 'Z' then
            EMT.NSTEP = EMT.NSTEP + 10
            local sStep = EgtNumToString( EMT.NSTEP, 0)
            local sOut = '_STEP='..sStep
            table.insert( EMT.AUXSTR, sOut)
            local sX2Pos =  Cmd[3]
            sOut = string.format( 'G1 X=%s F=%d', sX2Pos, EMT.FMAXPINZE)
            table.insert( EMT.AUXSTR, sOut)
            sOut = '_POSIZ_T(5,,,,,2,,,,)'  -- attiva spintore scarico pezzo restanto su processo 1
            table.insert( EMT.AUXSTR, sOut)
            sOut = '_N'..sStep..':STOPRE\n'
            table.insert( EMT.AUXSTR, sOut)
         end
      elseif Cmd[1] == '2' then
         EMT.NSTEP = EMT.NSTEP + 10
         local sStep = EgtNumToString( EMT.NSTEP, 0)
         local sOut = '_STEP='..sStep
         table.insert( EMT.AUXSTR, sOut)
         local sX2Pos = Cmd[5]
         sOut = string.format( 'G1 X=%s F=%d', sX2Pos, EMT.FMAXPINZE)
         table.insert( EMT.AUXSTR, sOut)
         sOut = '_N'..sStep..':STOPRE\n'
         table.insert( EMT.AUXSTR, sOut)
      elseif Cmd[1] == '3' then
         -- non gestito
      elseif Cmd[1] == '11' then
         -- non gestito
      elseif Cmd[1] == '12' then
         EMT.NSTEP = EMT.NSTEP + 10
         local sStep = EgtNumToString( EMT.NSTEP, 0)
         local sOut = '_STEP='..sStep
         table.insert( EMT.AUXSTR, sOut)
         sOut = '_POSIZ_T(5,,,,,1,,,,)'  -- apre pinza 2 restando su processo 1
         table.insert( EMT.AUXSTR, sOut)
         sOut = '_N'..sStep..':STOPRE\n'
         table.insert( EMT.AUXSTR, sOut)
      elseif Cmd[1] == '21' then
         -- non interessa
      end
   -- se scarico di un pezzo della barra, si utilizza POSIZ_T(6) per mettere su processo 2
   else
      if Cmd[1] == '0' then
         -- non interessa
      elseif Cmd[1] == '1' then
         ;
      elseif Cmd[1] == '2' then
         local sOut = 'STOPRE'
         table.insert( EMT.AUXSTR, sOut)
         EMT.NSTEP = EMT.NSTEP + 10
         local sStep = EgtNumToString( EMT.NSTEP, 0)
         sOut = '_STEP='..sStep
         table.insert( EMT.AUXSTR, sOut)
         local nFunz = 6
         local nVertClamp = 0
         local dPress = 2.5
         local dFeed = GetClampFeed( EMT.LENGTHPART)
         local dAcc = 4
         local nOpt = 0
         local sRef1 = Cmd[5]
         local sRef2 = EmtLenToString( ParkX2 - ( EMT.LENGTHPART - EMT.X2DELTA), 3)
         sOut = string.format( '_POSIZ_T(%d,%d,%.1f,%.1f,%.1f,%d,%s,%s,,)', nFunz, nVertClamp, dPress, dFeed, dAcc, nOpt, sRef1, sRef2)
         table.insert( EMT.AUXSTR, sOut)
         sOut = '_N'..sStep..':STOPRE'
         table.insert( EMT.AUXSTR, sOut)
         EMT.U_STD = true
      elseif Cmd[1] == '3' then
         ;
      elseif Cmd[1] == '11' then
         ;
      elseif Cmd[1] == '12' then
         ;
      elseif Cmd[1] == '21' then
         -- non interessa
      end
   end
end

---------------------------------------------------------------------
function PreparePreRotation( sCmd)
   local Cmd = EgtSplitString( sCmd)
   if Cmd[1] == '0' then
      if Cmd[2] == 'Move_End' then
         table.insert( ALL_CLAMP_POS, CLAMP_POS)
         CLAMP_POS = {}
         local nFunz = 77
         local nVertClamp = 0
         local dPress = 2.5
         local dFeed = GetClampFeed( EMT.LENGTHPART)
         local dAcc = 4
         local nOpt = #ALL_CLAMP_POS * 100
         local sOut = string.format( '_POSIZ_T(%d,,,,,%d,,,,,,)', nFunz, nOpt)
         table.insert( EMT.AUXSTR, sOut)
      end
   elseif Cmd[1] == '1' then
      if Cmd[2] ~= 'Z' then
         local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dX1Pos = GetValInString( sCmd, 'X1')
         local dX2Pos = GetValInString( sCmd, 'X2')
         table.insert( CLAMP_POS, '###_ADD_STEP_###')
         if dX1Pos and dX2Pos then
            table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
         elseif dX1Pos then
            table.insert( CLAMP_POS, string.format( 'G1 X1=%.3f F=%d', dX1Pos, dFeed1))
         elseif dX2Pos then
            table.insert( CLAMP_POS, string.format( 'G1 X2=%.3f F=%d', dX2Pos, dFeed2))
         end
      end
   elseif Cmd[1] == '2' then
      local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dX1Pos = GetValInString( sCmd, 'X1')
      local dX2Pos = GetValInString( sCmd, 'X2')
      table.insert( CLAMP_POS, '###_ADD_STEP_###')
      if dX1Pos and dX2Pos then
         table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
      elseif dX1Pos then
         table.insert( CLAMP_POS, string.format( 'G1 X1=%.3f F=%d', dX1Pos, dFeed1))
      elseif dX2Pos then
         table.insert( CLAMP_POS, string.format( 'G1 X2=%.3f F=%d', dX2Pos, dFeed2))
      end
   elseif Cmd[1] == '3' then
      local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
      local dX1Pos = GetValInString( sCmd, 'X1')
      local dX2Pos = GetValInString( sCmd, 'X2')
      table.insert( CLAMP_POS, '###_ADD_STEP_###')
      table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
   elseif Cmd[1] == '11' then
      if Cmd[2] == '0' and EMT.X1CLAMP then
         EMT.X1CLAMP = nil
         table.insert( CLAMP_POS, 'GRIPPER("AP1",,)')
      elseif not EMT.X1CLAMP and Cmd[2] ~= '0' then
         EMT.X1CLAMP = true
         local sVertClamp = EgtIf( Cmd[2] == '2', '1', '0')
         table.insert( CLAMP_POS, 'GRIPPER("CH1",'.. sVertClamp ..',2.5)')
      end
   elseif Cmd[1] == '12' then
      if Cmd[2] == '0' and EMT.X2CLAMP then
         EMT.X2CLAMP = nil
         table.insert( CLAMP_POS, 'GRIPPER("AP2",,)')
      elseif not EMT.X2CLAMP and Cmd[2] ~= '0' then
         EMT.X2CLAMP = true
         local sVertClamp = EgtIf( Cmd[2] == '2', '1', '0')
         table.insert( CLAMP_POS, 'GRIPPER("CH2",'.. sVertClamp ..',2.5)')
      end
   elseif Cmd[1] == '21' then
      local dX1Delta = tonumber( Cmd[2])
      local dX2Delta = tonumber( Cmd[3])
      if dX1Delta > 0 and dX2Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = dX2Delta
      elseif dX1Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = nil
      elseif dX2Delta > 0 then
         EMT.X1DELTA = nil
         EMT.X2DELTA = dX2Delta
      end
   end
end

---------------------------------------------------------------------
function PreparePostRotation( sCmd)
   local Cmd = EgtSplitString( sCmd)
   if Cmd[1] == '0' then
      if Cmd[2] == 'CARR_MOVE' then
         EMT.CARRMOVE = true
      elseif Cmd[2] == 'Move_End' then
         EMT.CARRMOVE = nil
         table.insert( ALL_CLAMP_POS, CLAMP_POS)
         CLAMP_POS = {}
         local nFunz = 77
         local nVertClamp = 0
         local dPress = 2.5
         local dFeed = GetClampFeed( EMT.LENGTHPART)
         local dAcc = 4
         local nOpt = #ALL_CLAMP_POS * 100
         local sOut = string.format( '_POSIZ_T(%d,,,,,%d,,,,,,)', nFunz, nOpt)
         table.insert( EMT.AUXSTR, sOut)
      end
      -- se inizio
      if EMT.AUXIND == 1 then
         -- reset stato pinze
         EMT.X1STAT = 0
         EMT.X2STAT = 0
      end
   elseif Cmd[1] == '1' then
      if Cmd[2] ~= 'Z' then
         if EMT.CARRMOVE then
            local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
            local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
            local dX1Pos = GetValInString( sCmd, 'X1')
            local dX2Pos = GetValInString( sCmd, 'X2')
            table.insert( CLAMP_POS, '###_ADD_STEP_###')
            if dX1Pos and dX2Pos then
               table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
            elseif dX1Pos then
               table.insert( CLAMP_POS, string.format( 'G1 X1=%.3f F=%d', dX1Pos, dFeed1))
            elseif dX2Pos then
               table.insert( CLAMP_POS, string.format( 'G1 X2=%.3f F=%d', dX2Pos, dFeed2))
            end
         end
      end
   elseif Cmd[1] == '2' then
      if EMT.CARRMOVE then
         local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dX1Pos = GetValInString( sCmd, 'X1')
         local dX2Pos = GetValInString( sCmd, 'X2')
         table.insert( CLAMP_POS, '###_ADD_STEP_###')
         if dX1Pos and dX2Pos then
            table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
         elseif dX1Pos then
            table.insert( CLAMP_POS, string.format( 'G1 X1=%.3f F=%d', dX1Pos, dFeed1))
         elseif dX2Pos then
            table.insert( CLAMP_POS, string.format( 'G1 X2=%.3f F=%d', dX2Pos, dFeed2))
         end
      end
   elseif Cmd[1] == '3' then
      if EMT.CARRMOVE then
         local dFeed1 = EgtIf( EMT.X1CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dFeed2 = EgtIf( EMT.X2CLAMP, GetClampFeed( EMT.LENGTHPART), EMT.FMAXPINZE)
         local dX1Pos = GetValInString( sCmd, 'X1')
         local dX2Pos = GetValInString( sCmd, 'X2')
         table.insert( CLAMP_POS, '###_ADD_STEP_###')
         table.insert( CLAMP_POS, string.format( 'POS[X1]=%.3f POS[X2]=%.3f FA[X1]=%d FA[X2]=%d', dX1Pos, dX2Pos, dFeed1, dFeed2))
      end
   elseif Cmd[1] == '11' then
      EMT.X1STAT = tonumber( Cmd[2])
      if EMT.CARRMOVE then
         if Cmd[2] == '0' and EMT.X1CLAMP then
            EMT.X1CLAMP = nil
            table.insert( CLAMP_POS, 'GRIPPER("AP1",,)')
         elseif not EMT.X1CLAMP and Cmd[2] ~= '0' then
            EMT.X1CLAMP = true
            local sVertClamp = EgtIf( Cmd[2] == '2', '1', '0')
            table.insert( CLAMP_POS, 'GRIPPER("CH1",'.. sVertClamp ..',2.5)')
         end
      end
   elseif Cmd[1] == '12' then
      EMT.X2STAT = tonumber( Cmd[2])
      if EMT.CARRMOVE then
         if Cmd[2] == '0' and EMT.X2CLAMP then
            EMT.X2CLAMP = nil
            table.insert( CLAMP_POS, 'GRIPPER("AP2",,)')
         elseif not EMT.X2CLAMP and Cmd[2] ~= '0' then
            EMT.X2CLAMP = true
            local sVertClamp = EgtIf( Cmd[2] == '2', '1', '0')
            table.insert( CLAMP_POS, 'GRIPPER("CH2",'.. sVertClamp ..',2.5)')
         end
      end
   elseif Cmd[1] == '21' then
      local dX1Delta = tonumber( Cmd[2])
      local dX2Delta = tonumber( Cmd[3])
      if dX1Delta > 0 and dX2Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = dX2Delta
      elseif dX1Delta > 0 then
         EMT.X1DELTA = dX1Delta
         EMT.X2DELTA = nil
      elseif dX2Delta > 0 then
         EMT.X1DELTA = nil
         EMT.X2DELTA = dX2Delta
      end
      if EMT.X1DELTA and not EMT.X2DELTA then
         local sInfo = string.format( ';LOAD D1=%.1f', EMT.X1DELTA)
         table.insert( EMT.AUXSTR, sInfo)
         local nFunz = 1
         local nVertClamp = EgtIf( EMT.X1STAT == 2, 1, 0)
         local dPress = 2.5
         local dFeed = GetClampFeed( EMT.LENGTHBEAM)
         local dAcc = 4
         local nOpt = 0
         local sRef1 = EmtLenToString( EMT.X1DELTA, 3)
         local sRef2 = EmtLenToString( ParkX1 + EMT.X1DELTA, 3)
         local sOut = string.format( '_POSIZ_T(%d,%d,%.1f,%.1f,%.1f,%d,%s,%s,,)', nFunz, nVertClamp, dPress, dFeed, dAcc, nOpt, sRef1, sRef2)
         table.insert( EMT.AUXSTR, sOut)
         EMT.X1CLAMP = true
         EMT.X2CLAMP = nil
      end
   end
end

---------------------------------------------------------------------
function MyBackupAxes()
   EMT.L1b = EMT.L1
   EMT.L2b = EMT.L2
   EMT.L3b = EMT.L3
   EMT.R1b = EMT.R1
   EMT.R2b = EMT.R2
   EMT.R3b = EMT.R3
   EMT.R4b = EMT.R4
   EMT.RRb = EMT.RR
   EMT.C1b = EMT.C1
   EMT.C2b = EMT.C2
   EMT.C3b = EMT.C3
end

---------------------------------------------------------------------
function MyRestoreAxes()
   EMT.L1 = EMT.L1b
   EMT.L2 = EMT.L2b
   EMT.L3 = EMT.L3b
   EMT.R1 = EMT.R1b
   EMT.R2 = EMT.R2b
   EMT.R3 = EMT.R3b
   EMT.R4 = EMT.R4b
   EMT.RR = EMT.RRb
   EMT.C1 = EMT.C1b
   EMT.C2 = EMT.C2b
   EMT.C3 = EMT.C3b
end

---------------------------------------------------------------------
function MyAdjustLinearAxes()
   local MyL1o = EMT.L1
   local MyL2o = EMT.L2
   local MyL3o = EMT.L3

   if EMT.X_OFF then EMT.L1 = EMT.L1 + EMT.X_OFF end

   EmtAdjustLinearAxes()

   EMT.L1o = MyL1o
   EMT.L2o = MyL2o
   EMT.L3o = MyL3o

   -- Assi geometrici sempre XYZ (indipendentemente dal nome assi fisici)
   EMT.L1t = 'X'
   EMT.L2t = 'Y'
   EMT.L3t = 'Z'
end

---------------------------------------------------------------------
function CalcInterpPlane()
   -- origine del piano
   local xS = EMT.START[1] + LoadT
   if EMT.X_OFF then xS = xS + EMT.X_OFF end
   local ptS = Point3d( xS, DeltaTabY, DeltaTabZ)
   -- calcolo per piano generico
   local vtE
   if EMT.HEAD ~= 'H3' then
      vtE = Vector3d( EMT.EXTR)
   else
      vtE = EgtGetCalcAuxDirFromAngles( EMT.R1, EMT.R2, EMT.R3)
   end
   EMT.IPLGLFR = Frame3d( ptS, vtE)
   --MyEmtOutput( 'IPLGLFR='..tostring(EMT.IPLGLFR))
   local _, dAngV, dAngO = SphericalFromVector( vtE)
   local dAngO2 = EmtGetAngO2( EMT.IPLGLFR:getVersZ(), EMT.IPLGLFR:getVersX(), dAngV, dAngO)
   local sXout, sYout, sZout
   if EMT.X1DELTA then
      sXout = ' X='..EmtLenToString( xS + EMT.X1DELTA)..'+OFS_T5_X1[0]'
      sYout = ' Y=OFS_T5_X1[1]'
      sZout = ' Z=OFS_T5_X1[2]'
   elseif EMT.X2DELTA then
      sXout = ' X='..EmtLenToString( xS + EMT.X2DELTA)..'+OFS_T5_X2[0]'
      sYout = ' Y=OFS_T5_X2[1]'
      sZout = ' Z=OFS_T5_X2[2]'
   else
      EmtSetLastError( 1214, "Part to machine without charriots")
   end
   EMT.IPLGLSTR = 'STOPRE\nTRANS'..sXout..sYout..sZout..
                  '\nAROT Z'..EgtNumToString( dAngO)..'\nAROT Y'..EgtNumToString( dAngV)..'\nAROT Z'..EgtNumToString( dAngO2)
   EMT.IPLGL = true
end

---------------------------------------------------------------------
function VerifyEmitRotation()
   -- recupero le rotazioni delle fasi corrente e precedente
   local nRot = GetPhaseRot( EMT.PHASE)
   local nPrevRot = GetPhaseRot( EMT.PHASE - 1)
   -- se sono uguali non devo fare alcunchè
   if nRot == nPrevRot then
      return false
   end
   -- determino quanto ruotare
   local nDeltaRot = nRot - nPrevRot

   -- TODO RIBALTAMENTO DA GESTIRE
   local sOut = 'M00 ; RUOTARE TRAVE n.' .. tostring( nDeltaRot)
   MyEmtOutput( sOut)

   return true
end

---------------------------------------------------------------------
function EmitZmax()
   local sOut = 'SUPA G0 D0 Z'..EgtNumToString( ParkZ, 3)
   MyEmtOutput( sOut)
end

---------------------------------------------------------------------
function EmitSpeedOn( nSpeed)
   if not EMT.SPEED_ON then
      MyEmtOutput( 'M_ON(1,'..EgtNumToString( nSpeed, 0) ..')')
      EMT.SPEED_ON = true
   end
end

---------------------------------------------------------------------
function EmitSpeedOff()
   if EMT.SPEED_ON then
      MyEmtOutput( 'M_OFF(1)')
      EMT.SPEED_ON = nil
   end
end

---------------------------------------------------------------------
function GetFmaxClamp()
   return EmtLenToString( EMT.FMAXPINZE, 0)
end

---------------------------------------------------------------------
function GetClampFeed( dLen)
   local KgMtCubo= 550                                         -- densità legno [Kg / metro cubo]
   local Massa = ( EMT.HEIGHTPART * EMT.WIDTHPART * dLen * KgMtCubo ) / 1e9   -- massa [Kg]
   local RefMassa = 100                                        -- massa di riferimento con massima feed
   local dCoeff = EgtClamp( RefMassa / Massa, 0.1, 1)
   return EmtLenToString( dCoeff * EMT.FMAXPINZE, 0)
end

---------------------------------------------------------------------
function CreateClampingFile()
   local sOriginalFileName = string.sub( EMT.FILE, 1, -5)
   local sCalmpingFile = sOriginalFileName .. '_POSIZ.mpf'

   local nfile = io.open( sCalmpingFile, "w")
   if nfile then
      nfile:write( ';file dei posizionamenti\n')
      nfile:write( 'DEF STRING[255] JUMP3\n\n')
      nfile:write( 'JUMP3="_N"<<_STEP3<<""\n\n')

      -- si scrivono i riposizionamenti salvati in precedenza
      for i = 1, #ALL_CLAMP_POS do
         local nReposNumber = i * 100
         local nReposNumber2 = (i + 1) * 100
         nfile:write( '\nIF ((_STEP3>=' .. EgtNumToString( nReposNumber) ..') AND (_STEP3<'.. EgtNumToString( nReposNumber2)  .. '))\n')
         nfile:write( '  IF _STEP3<>' .. EgtNumToString( nReposNumber) ..' GOTOF JUMP3\n')
         local nInternalStep = 0
         for j = 1, #ALL_CLAMP_POS[i] do
            -- comando speciale per aggiungere etichetta per ripartenza
            if ALL_CLAMP_POS[i][j] == '###_ADD_STEP_###' then
               nInternalStep = nInternalStep + 1
               local nStep = nReposNumber + nInternalStep
               nfile:write( '  _N' .. EgtNumToString( nStep) ..':_STEP3=' .. EgtNumToString( nStep) .. '\n')
            else
               nfile:write( '  ' .. ALL_CLAMP_POS[i][j] .. '\n')
            end
         end
         nfile:write( '  M17\n')
         nfile:write( 'ENDIF\n')
      end

      nfile:close()
      ALL_CLAMP_POS = {}
   else
      EmtSetLastError( 1201, 'UNEXPECTED ERROR')
   end

end

---------------------------------------------------------------------
local ParkedLines = {}

---------------------------------------------------------------------
function MyEmtOutput( sLine)
   if EMT.PARKLINES then
      table.insert( ParkedLines, sLine)
   else
      EmtOutput( sLine)
   end
end

---------------------------------------------------------------------
function EmitParkedLines()
   for i = 1, #ParkedLines do
      EmtOutput( ParkedLines[i])
   end
   ParkedLines = {}
end

---------------------------------------------------------------------
local StartValues = {}

---------------------------------------------------------------------
function SetStartValue( nI, dVal)
   StartValues[nI] = dVal
end

---------------------------------------------------------------------
function EmitStartValues()
   local sLine = ''
   for i = 1, 8 do
      sLine = sLine .. string.format( ';V%02d=', i) .. EgtNumToString( StartValues[i] or 0, 3)
   end
   EmtOutput( sLine)
end

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