-- Special Operations macchina Saomad-KAIROS by EgalWare s.r.l.  2023/12/01

-- Intestazioni
require( 'EmtGenerator')
EgtEnableDebug( false)

-- Carico i dati globali
local sBaseDir = EgtGetSourceDir()
local BD = dofile( sBaseDir .. 'Beam\\BeamData.lua')

---------------------------------------------------------------------
-- *** Generic Machinings ***
---------------------------------------------------------------------
pcall( require, 'EmtGenMachining')  -- si fa una require con PCALL perchè la libreria è opzionale

---------------------- OnSpecialMoveZup -----------------------------
---------------------------------------------------------------------
function OnSpecialMoveZup()
   --EgtOutLog( 'OnSpecialMoveZup : ' .. EMC.HEAD .. '.' .. tostring( EMC.EXIT))
   
   -- Inizializzazioni
   EMC.ERR = 0
   EMC.MODIF = false
   
   -- se sega a catena
   if EMC.HEAD == 'H3' then
      
      
   end

end

---------- OnSpecialApplyDisposition & OnPostApplyMachining ---------
----------------------- Costanti ------------------------------------
local DELTA_TOL_V = 110
local DELTA_TOL_S = 160
local DELTA_TOL_L = 310
local DELTA_TOL_FIXED = 50
local DeltaTol = DELTA_TOL_S
local DELTA_SIC = 1
local AGG_LOAD = 0
local MIN_JOIN_VV = 75
local MIN_JOIN_SS = 100
local MIN_JOIN_LS = 100
local MIN_JOIN_SL = 290
local MIN_JOIN_LL = 400
local MinJoin = MIN_JOIN_SS
local MinOther = abs( MinX1) + abs( MaxX2) + MinJoin
local MaxLenSmT = 1500             -- massima lunghezza pezzo scaricato con nastri verdi

----------------------- Variabili -----------------------------------
local Test = false

---------------------------------------------------------------------
local function PrepareClGroup( nParentId)

   local nClId = EgtGetFirstNameInGroup( nParentId, 'CL')
   -- se non c'è, lo aggiunge
   if not nClId then
      nClId = EgtGroup( EMC.DISPID)
      if not nClId then
         return nil
      end
      EgtSetName( nClId, 'CL')
   -- altrimenti lo svuoto
   else
      EgtEmptyGroup( nClId)
   end

   return nClId
end

---------------------------------------------------------------------
local function IsStartOrRestPhase( nPhase)
   local sVal = EgtGetInfo( EgtGetPhaseDisposition( nPhase) or GDB_ID.NULL, 'TYPE')
   return ( sVal == 'START' or sVal == 'REST')
end

---------------------------------------------------------------------
local function IsMidPhase( nPhase)
   local sVal = EgtGetInfo( EgtGetPhaseDisposition( nPhase) or GDB_ID.NULL, 'TYPE')
   return ( sVal == 'MID')
end

---------------------------------------------------------------------
local function IsEndPhase( nPhase)
   local sVal = EgtGetInfo( EgtGetPhaseDisposition( nPhase) or GDB_ID.NULL, 'TYPE')
   return ( sVal == 'END')
end

---------------------------------------------------------------------
local function IsMid2Phase( nPhase)
   local sVal = EgtGetInfo( EgtGetPhaseDisposition( nPhase) or GDB_ID.NULL, 'TYPE')
   return ( sVal == 'MID2')
end

---------------------------------------------------------------------
local function IsEnd2Phase( nPhase)
   local sVal = EgtGetInfo( EgtGetPhaseDisposition( nPhase) or GDB_ID.NULL, 'TYPE')
   return ( sVal == 'END2')
end

---------------------------------------------------------------------
local function GetNextStartOrRestPhase( nPhase)
   local nNextPhase = nPhase + 1
   while nNextPhase <= EgtGetPhaseCount() do
      if IsStartOrRestPhase( nNextPhase) then
         break ;
      end
      nNextPhase = nNextPhase + 1
   end
   return nNextPhase
end

---------------------------------------------------------------------
local function GetPhaseRot( nPhase)
   return ( EgtGetInfo( EgtGetPhaseDisposition( nPhase) or GDB_ID.NULL, 'ROT', 'i') or 0)
end

---------------------------------------------------------------------
local function IsLastOperationBeforeRotation( nOperId)
   -- se ultima fase o ultima fase del pezzo, ritorno risultato negativo
   if EMC.PHASE == EgtGetPhaseCount() or IsEndPhase( EMC.PHASE) or IsEnd2Phase( EMC.PHASE) then
      return false
   end
   -- recupero la successiva operazione attiva
   local nNextOperId = EgtGetNextActiveOperation( nOperId)
   -- se non esiste o non è una disposizione, ritorno risultato negativo
   if not nNextOperId or EgtGetOperationType( nNextOperId) ~= MCH_OY.DISP then
      return false
   end
   -- recupero le rotazioni della fase corrente e della prossima fase
   local nRot = GetPhaseRot( EMC.PHASE)
   local nNextRot = GetPhaseRot( EMC.PHASE + 1)
   -- ritorno se sono diverse
   return ( nRot ~= nNextRot)
end

---------------------------------------------------------------------
local function IsFirstMachiningAfterRotation( nMchId)
   -- se prima fase o prima fase del pezzo, ritorno risultato negativo
   if EMC.PHASE == 1 or IsStartOrRestPhase( EMC.PHASE) then
      return false
   end
   -- recupero la precedente operazione attiva
   local nPrevOperId = EgtGetPrevActiveOperation( nMchId)
   -- se non esiste o non è una disposizione, ritorno risultato negativo
   if not nPrevOperId or EgtGetOperationType( nPrevOperId) ~= MCH_OY.DISP then
      return false
   end
   -- recupero le rotazioni della fase corrente e della fase precedente
   local nRot = GetPhaseRot( EMC.PHASE)
   local nPrevRot = GetPhaseRot( EMC.PHASE - 1)
   -- ritorno se sono diverse
   return ( nRot ~= nPrevRot)
end

---------------------------------------------------------------------
local function UpdateMinJoinDeltaTol()
   local L_SMALL = 800
   local H_V = 90
   local H_S = 150
   local H_L = 250
   local W_V = 90
   local W_S = 150
   local W_L = 450
   if EMC.SB <= W_V and EMC.HB <= H_V then
      MinJoin = MIN_JOIN_VV
      DeltaTol = DELTA_TOL_V
   elseif EMC.LB <= L_SMALL then
      MinJoin = MIN_JOIN_SS
      DeltaTol = DELTA_TOL_S
   else
      local dMinJoinS
      local dMinJoinL
      if EMC.SB <= W_S then
         dMinJoinS = MIN_JOIN_SS
         dMinJoinL = MIN_JOIN_LS
      elseif EMC.SB <= W_L then
         local Coeff = ( EMC.SB - W_S) / ( W_L - W_S)
         dMinJoinS = ( 1 - Coeff) * MIN_JOIN_SS + Coeff * MIN_JOIN_SL
         dMinJoinL = ( 1 - Coeff) * MIN_JOIN_LS + Coeff * MIN_JOIN_LL
      else
         dMinJoinS = MIN_JOIN_SL
         dMinJoinL = MIN_JOIN_LL
      end
      if EMC.HB <= H_S then
         MinJoin = dMinJoinS
         DeltaTol = DELTA_TOL_S
      elseif EMC.HB <= H_L then
         local Coeff = ( EMC.HB - H_S) / ( H_L - H_S)
         MinJoin = ( 1 - Coeff) * dMinJoinS + Coeff * dMinJoinL
         DeltaTol = ( 1 - Coeff) * DELTA_TOL_S + Coeff * DELTA_TOL_L
      else
         MinJoin = dMinJoinL
         DeltaTol = DELTA_TOL_L
      end
      if EMC.SB < W_V then
         DeltaTol = DELTA_TOL_V
      end
   end
   MinOther = abs( MinX1) + abs( MaxX2) + MinJoin
end

---------------------------------------------------------------------
local function GetCUTID()
   -- recupero CUTID del pezzo in lavoro
   local nOrd = GetPhaseOrd( EMC.PHASE)
   local nPartRawId
   local nRawId = EgtGetFirstRawPart()
   while nRawId do
      local nRawOrd = EgtGetInfo( nRawId, 'ORD', 'i')
      if nRawOrd == nOrd then
         nPartRawId = nRawId
         break
      end
      nRawId = EgtGetNextRawPart( nRawId)
   end
   local CutID = EgtGetInfo( EgtGetFirstPartInRawPart( nPartRawId or GDB_ID.NULL) or GDB_ID.NULL, 'CUTID', 'i') or 0
   return CutID
end

---------------------------------------------------------------------
local function EnsureZmax( bZmaxOk, vCmd)
   if not bZmaxOk then table.insert( vCmd, { 1, 'Z', EgtGetAxisHomePos( 'Z')}) end
   return true
end

---------------------------------------------------------------------
local function EmitComment( vCmd, sOut)
   EgtOutLog( '  ' .. sOut, 1)
   if Test then
      table.insert( vCmd, { 0, sOut})  
   end
end

---------------------------------------------------------------------
function OnSpecialApplyDisposition()

   EgtOutLog( ' *** Fase : ' .. EgtNumToString( EMC.PHASE, 0) .. ' ***', 1)

   -- Inizializzo codice di errore
   EMC.ERR = 0

   -- Campi obbligatori ma non usati
   EMC.HEAD = ""
   EMC.EXIT = 1
   EMC.TCPOS = ""
   EMC.SHIFTS = 0
   EMC.SBH = false

   -- Se disposizione da saltare non devo fare alcunché
   if EgtExistsInfo( EMC.DISPID, 'SKIP') then return end

   -- Assegno flag di pezzo separato dal resto del grezzo
   local bSplit = IsEndPhase( EMC.PHASE) or IsMid2Phase( EMC.PHASE) or IsEnd2Phase( EMC.PHASE)

   -- Recupero il tipo dell'operazione successiva
   local nNextOpeType = EgtGetOperationType( EgtGetNextActiveOperation( EMC.DISPID) or GDB_ID.NULL)

   -- Se ci sono lavorazioni successive non devo fare alcunché
   if nNextOpeType ~= MCH_OY.NONE  and  nNextOpeType ~= MCH_OY.DISP then return end
   
   -- Imposto gruppo e path di movimento
   local nClId = PrepareClGroup( EMC.DISPID)
   if not nClId then
      EMC.ERR = 3
      return
   end
   local nPathId = EgtGroup( nClId)
   if not nPathId then
      EMC.ERR = 6
      return
   end
   EgtSetName( nPathId, 'Empty')
   EMC.PATHID = nPathId
   EMC.SHIFTS = -1

   -- Se l'operazione successiva è una disposizione con rotazione, devo preparare il pezzo alla rotazione
   if IsLastOperationBeforeRotation( EMC.DISPID) then
      -- aggiornamento posizioni
      if IsStartPhase( EMC.PHASE) then
         -- carico le posizioni
         local dPosT = EgtGetInfo( EMC.DISPID, 'TPOS', 'd')
         local dPosY = EgtGetInfo( EMC.DISPID, 'YPOS', 'd')
         EMC.TPOS = dPosT
         EMC.YDELTA = dPosY - dPosT
         EMC.VDELTA = nil
         EMC.CNT = 1
      else
         local nPrevOpeId = EgtGetPrevActiveOperation( EMC.DISPID)
         local nLastPathId = EgtGetLastInGroup( EgtGetFirstNameInGroup( nPrevOpeId, 'CL') or GDB_ID.NULL)
         local nLastEntId = EgtGetLastInGroup( nLastPathId)
         local vAxes = EmtGetAxesPos( nLastEntId)
         if #vAxes > 0 then EMC.TPOS = vAxes[1] end
         EMC.YDELTA = EgtGetInfo( nLastPathId, 'YDELTA', 'd')
         EMC.VDELTA = EgtGetInfo( nLastPathId, 'VDELTA', 'd')
         EMC.CNT = EgtGetInfo( nLastPathId, 'CNT', 'i')
      end
      -- Determinazione delle dimensioni del grezzo in lavoro
      local b3Raw = BBox3d()
      local nRawId = EgtGetFirstRawPart()
      while nRawId do
         if EgtVerifyRawPartPhase( nRawId, EMC.PHASE) then
            b3Raw = EgtGetRawPartBBox( nRawId)
            break
         end
         nRawId = EgtGetNextRawPart( nRawId)
      end
      EMC.LB = b3Raw:getDimX() + 10 * GEO.EPS_SMALL
      -- Assegno sovramateriale di testa e ingombro tagli di testa e di coda
      EMC.HOVM = EgtGetInfo( nRawId, 'HOVM', 'd') or 0
      EMC.HCING = 0
      EMC.TCING = EgtGetInfo( nRawId, 'TCING', 'd') or 0
      -- Eseguo preparazione alla rotazione
      local vCmd = SpecCalcPreRot()
      SpecOutputCmds( vCmd, true)
      return
   end

   -- Se l'operazione successiva è ancora una disposizione, devo scaricare il pezzo
   if nNextOpeType == MCH_OY.DISP  and  bSplit  and  not IsEnd2Phase( EMC.PHASE) then
      -- aggiornamento posizioni
      local nPrevOpeId = EgtGetPrevActiveOperation( EMC.DISPID)
      local nLastPathId = EgtGetLastInGroup( EgtGetFirstNameInGroup( nPrevOpeId, 'CL') or GDB_ID.NULL)
      local nLastEntId = EgtGetLastInGroup( nLastPathId)
      local vAxes = EmtGetAxesPos( nLastEntId)
      if #vAxes > 0 then EMC.TPOS = vAxes[1] end
      EMC.YDELTA = EgtGetInfo( nLastPathId, 'YDELTA', 'd')
      EMC.VDELTA = EgtGetInfo( nLastPathId, 'VDELTA', 'd')
      EMC.CNT = EgtGetInfo( nLastPathId, 'CNT', 'i')
      -- Determinazione delle dimensioni del grezzo in lavoro
      local b3Raw = BBox3d()
      local nRawId = EgtGetFirstRawPart()
      while nRawId do
         if EgtVerifyRawPartPhase( nRawId, EMC.PHASE)  and  not EgtVerifyRawPartPhase( nRawId, EMC.PHASE + 1) then
            b3Raw = EgtGetRawPartBBox( nRawId)
            break
         end
         nRawId = EgtGetNextRawPart( nRawId)
      end
      EMC.LB = b3Raw:getDimX() + 10 * GEO.EPS_SMALL
      EMC.HOVM = EgtGetInfo( nRawId or GDB_ID.NULL, 'HOVM', 'd') or 0
      -- Eseguo scarico
      local vCmd = SpecCalcUnload()
      SpecOutputCmds( vCmd, true)
      return
   end

   -- Verifico ci sia un solo grezzo nella fase corrente
   local nRawCount = 0
   local nCurrRawId = GDB_ID.NULL
   local nRawId = EgtGetFirstRawPart()
   while nRawId do
      if EgtVerifyRawPartPhase( nRawId, EMC.PHASE) then
         nRawCount = nRawCount + 1
         nCurrRawId = nRawId
      end
      nRawId = EgtGetNextRawPart( nRawId)
   end

   -- Determinazione delle sue dimensioni
   local b3Raw = EgtGetRawPartBBox( nCurrRawId)
   if not b3Raw or b3Raw:isEmpty() then
      EMC.ERR = 11
      return
   end
   EMC.LB = b3Raw:getDimX() + 10 * GEO.EPS_SMALL
   EMC.SB = b3Raw:getDimY()
   EMC.HB = b3Raw:getDimZ()
   EMC.ZMIN = b3Raw:getMin():getZ()

   -- Aggiorno limiti di presa e tolleranza
   UpdateMinJoinDeltaTol()

   -- Assegno sovramateriale di testa e ingombro tagli di testa e di coda
   EMC.HOVM = 0
   EMC.HCING = 0
   EMC.TCING = 0
   
   -- Devo scaricare il grezzo rimasto (deve essere unico)
   -- Posizione trave
   local dPosT

   -- Se fase 1 o dopo rotazione  eseguo carico con carrello Y
   local vCmd  = {}
   if EMC.PHASE == 1 or IsEnd2Phase( EMC.PHASE) then
      dPosT = LoadT
      if IsEnd2Phase( EMC.PHASE) then dPosT = dPosT + TurnerOffs end
      vCmd = SpecCalcLoad( dPosT, 0, min( EMC.LB - MinOther - AGG_LOAD, MaxX1 - dPosT))
   -- se altrimenti fase successiva alla prima di tipo inizio o rimanenza
   elseif IsStartOrRestPhase( EMC.PHASE) then
      -- recupero posizione trave e quota di aggancio carrello
      dPosT = EgtGetInfo( EMC.DISPID, 'TPOS', 'd')
      local dPosY = EgtGetInfo( EMC.DISPID, 'YPOS', 'd')
      -- se carrello agganciato
      if dPosY then
         -- confermo i nuovi parametri di aggancio
         table.insert( vCmd, { 21, dPosY - dPosT, 0})
         -- recupero CNT
         local nPrevOpeId = EgtGetPrevActiveOperation( EMC.DISPID)
         if EgtGetOperationType( nPrevOpeId) == MCH_OY.DISP and EgtExistsInfo( nPrevOpeId, 'SKIP') then
            nPrevOpeId = EgtGetPrevActiveOperation( nPrevOpeId)
         end
         local nLastPathId = EgtGetLastInGroup( EgtGetFirstNameInGroup( nPrevOpeId, 'CL') or GDB_ID.NULL)
         EMC.CNT = EgtGetInfo( nLastPathId, 'CNT', 'i')
      -- altrimenti è grezzo scaricato al carico e devo ricaricarlo
      else
         vCmd = SpecCalcLoad( dPosT, 0, min( EMC.LB - MinOther - AGG_LOAD, MaxX1 - dPosT))
      end
   -- altrimenti fase successiva pari
   else
      local dPosV = EgtGetInfo( EMC.DISPID, 'VPOS', 'd')
      EMC.VDELTA = dPosV
   end
   -- Se fase inizio o rimanenza, eseguo scambio per avere solo pinza V
   local vCmd2 = {}
   if IsStartOrRestPhase( EMC.PHASE) or IsEnd2Phase( EMC.PHASE) then
      EMC.TPOS = dPosT
      SpecSetCarrPosFromCmds( vCmd)
      local CurrUnloadT = EgtIf( EMC.LB < MaxLenSmT, UnloadSmT, UnloadT)
      local dDistFront = EgtIf( EMC.LB < abs( MinX2 - CurrUnloadT), MinJoin, EMC.LB - abs( MinX2 - CurrUnloadT) + MinJoin + DeltaTol) 
      vCmd2 = SpecCalcCarriages( dDistFront, 0)
      if vCmd and #vCmd > 1 and vCmd2 and #vCmd2 > 1 then
         table.insert( vCmd, { 0, 'CARR_MOVE'})
      end
   end
   -- eseguo scarico
   SpecSetCarrPosFromCmds( vCmd2)
   local vCmd3 = SpecCalcUnload()
   -- unisco ed emetto i comandi
   vCmd = EgtJoinTables( vCmd, vCmd2)
   vCmd = EgtJoinTables( vCmd, vCmd3)
   SpecOutputCmds( vCmd, true)
end

---------------------------------------------------------------------
function OnPostApplyMachining()

   EgtOutLog( ' Lavorazione : ' .. EgtGetName( EMC.MCHID), 1)

   -- Inizializzo codice di errore
   EMC.ERR = 0

   -- Recupero la posizione della trave e dei carrelli al termine della precedente operazione
   local nPrevOpeId = EgtGetPrevActiveOperation( EMC.MCHID)
   -- se precedente operazione non esiste, errore
   if not nPrevOpeId then
      EMC.ERR = 1
      return
   -- se precedente operazione è disposizione
   elseif  EgtGetOperationType( nPrevOpeId) == MCH_OY.DISP then
      if EMC.PHASE == 1 or IsFirstMachiningAfterRotation( EMC.MCHID) then
         -- posizioni home
         EMC.TPOS = nil
         EMC.YDELTA = nil
         EMC.VDELTA = nil
         EMC.HCING_IGNORE = true
      elseif IsStartOrRestPhase( EMC.PHASE) then
         -- carico le posizioni
         local dPosT = EgtGetInfo( nPrevOpeId, 'TPOS', 'd')
         local dPosY = EgtGetInfo( nPrevOpeId, 'YPOS', 'd')
         -- se carrello agganciato
         if dPosY then
            EMC.TPOS = dPosT
            EMC.YDELTA = dPosY - dPosT
            EMC.VDELTA = nil
         -- altrimenti è grezzo scaricato al carico e devo ricaricarlo
         else
            EMC.TPOS = nil
            EMC.YDELTA = nil
            EMC.VDELTA = nil
         end
         EMC.HCING_IGNORE = true
      else
         -- aggiornamento posizioni (da lavorazione precedente a disposizione)
         local nPrev2OpeId = EgtGetPrevActiveOperation( nPrevOpeId)
         if not nPrev2OpeId then
            EMC.ERR = 2
            return
         end
         local nLastPathId = EgtGetLastInGroup( EgtGetFirstNameInGroup( nPrev2OpeId, 'CL') or GDB_ID.NULL)
         local nLastEntId = EgtGetLastInGroup( nLastPathId)
         local vAxes = EmtGetAxesPos( nLastEntId or GDB_ID.NULL)
         if not vAxes then
            EMC.ERR = 3
            return
         end
         if #vAxes > 0 then EMC.TPOS = vAxes[1] end
         EMC.YDELTA = EgtGetInfo( nLastPathId, 'YDELTA', 'd')
         EMC.VDELTA = EgtGetInfo( nLastPathId, 'VDELTA', 'd')
      end
      EMC.CNT = nil
   -- altrimenti precedente operazione è lavorazione
   else
      -- aggiornamento posizioni
      local nLastPathId = EgtGetLastInGroup( EgtGetFirstNameInGroup( nPrevOpeId, 'CL') or GDB_ID.NULL)
      local nLastEntId = EgtGetLastInGroup( nLastPathId)
      local vAxes = EmtGetAxesPos( nLastEntId or GDB_ID.NULL)
      if not vAxes then
         EMC.ERR = 4
         return
      end
      if #vAxes > 0 then EMC.TPOS = vAxes[1] end
      EMC.YDELTA = EgtGetInfo( nLastPathId, 'YDELTA', 'd')
      EMC.VDELTA = EgtGetInfo( nLastPathId, 'VDELTA', 'd')
      EMC.CNT = EgtGetInfo( nLastPathId, 'CNT', 'i')
   end

   -- Verifico se ultima lavorazione della fase
   local nNextOpeId = EgtGetNextActiveOperation( EMC.MCHID)
   local bMchLast = ( not nNextOpeId  or  EgtGetOperationPhase( nNextOpeId) ~= EMC.PHASE)

   -- Verifico se ultima lavorazione prima di una rotazione
   local bPreRotMch = IsLastOperationBeforeRotation( EMC.MCHID)

   -- Verifico flag di separazione e fase di scarico
   local sNotes = EgtGetMachiningParam( MCH_MP.USERNOTES)
   local bPreSplit = ( sNotes:find( 'Presplit') ~= nil)
   local bSplitting = ( sNotes:find( 'Split') ~= nil)
   local bPreCut = ( sNotes:find( 'Precut') ~= nil)
   local bCutting = ( sNotes:find( 'Cut') ~= nil)
   local bUnload = IsEndPhase( EMC.PHASE) or IsEnd2Phase( EMC.PHASE)
   if bPreSplit or bPreCut then
      EgtSetInfo( EMC.MCHID, 'IS_PRE', '1')
   else
      EgtRemoveInfo( EMC.MCHID, 'IS_PRE')
   end

   -- Agisco sui diversi percorsi della lavorazione
   local nPathId = EgtGetFirstInGroup( EgtGetFirstNameInGroup( EMC.MCHID, 'CL') or GDB_ID.NULL)
   while nPathId do
      -- assegno id percorso da elaborare
      EMC.PATHID = nPathId
      -- recupero id del successivo
      nPathId = EgtGetNext( nPathId)
      -- verifico se ultimo percorso di ultima lavorazione della fase
      local bLast = ( bMchLast and ( not nPathId))
      -- se ultimo, elimino ritorno in home
      if bLast then EgtRemoveOperationHome( EMC.MCHID) end
      -- salvo lo stato di trave e carrelli
      local OriTPos = EMC.TPOS
      local OriYDelta = EMC.YDELTA
      local OriVDelta = EMC.VDELTA
      local OriCnt = EMC.CNT 
      -- eseguo le elaborazioni
      SpecApplyPath( bPreSplit, bSplitting, bPreCut, bCutting, bLast and bUnload, bLast and bPreRotMch)
      -- se separazione, verifico il risultato
      if bSplitting then
         -- recupero CUTID del pezzo in lavoro
         local CutID = GetCUTID()
         -- in caso di errore mancato pinzaggio uscita riprovo dopo aver disabilitato le lavorazioni finali
         if EMC.ERR == 18 then
            -- segnalazione warning
            EMC.ERR = -101
            EMC.MSG = 'Warning : skipped final processes (WRN=101,CUTID='..tostring( CutID)..')'
            -- ripristino lo stato originale di trave e carrelli
            EMC.TPOS = OriTPos
            EMC.YDELTA = OriYDelta
            EMC.VDELTA = OriVDelta
            EMC.CNT = OriCnt
            -- eseguo le elaborazioni
            SpecApplyPath( bPreSplit, bSplitting, bPreCut, bCutting, bLast and bUnload, bLast and bPreRotMch)
            -- pinzaggio ancora impossibile, pezzo a caduta
            if EMC.ERR == 18 then
               -- segnalazione warning
               EMC.ERR = -102
               EMC.MSG = 'Warning : skipped final processes and unload by fall (WRN=102,CUTID='..tostring( CutID)..')'
            end
         -- scarico standard
         elseif EMC.ERR == 0 then
            -- segnalazione warning
            EMC.ERR = -100
            EMC.MSG = 'Warning : standard unload (WRN=100,CUTID='..tostring( CutID)..')'
         end
      -- se taglio del residuo finale, scarico standard
      elseif bCutting then
         -- recupero CUTID del pezzo in lavoro
         local CutID = GetCUTID()
         -- se non ci sono errori, segnalazione warning
         if EMC.ERR == 0 then
            EMC.ERR = -100
            EMC.MSG = 'Warning : standard unload (WRN=100,CUTID='..tostring( CutID)..')'
         end
      end
      if EMC.ERR > 0 then return end
      -- determino la posizione finale della trave
      local nLastEntId = EgtGetLastInGroup( EMC.PATHID)
      local vAxes = EmtGetAxesPos( nLastEntId)
      if #vAxes > 0 then EMC.TPOS = vAxes[1] end
   end

end

---------------------------------------------------------------------
function SpecApplyPath( bPreSplit, bSplitting, bPreCut, bCutting, bUnload, bPreRotMch)

   -- Assegno flag di pezzo separato dal resto del grezzo
   local bSplit = IsEndPhase( EMC.PHASE) or IsMid2Phase( EMC.PHASE) or IsEnd2Phase( EMC.PHASE)
   
   -- Assegno flag di pezzo in separazione
   local bFixedDelta = ( bPreSplit or bSplitting or bPreCut or bCutting)

   -- Se separazione o taglio del grezzo finale, verifico se precedente era una preparazione
   local bFixedPos = false
   if bPreSplit or bSplitting or bPreCut or bCutting then
      local nPrevOpeId = EgtGetPrevActiveOperation( EMC.MCHID)
      bFixedPos = EgtExistsInfo( nPrevOpeId or GDB_ID.NULL, 'IS_PRE')
   end

   -- Determinazione delle dimensioni totali dei grezzi e del grezzo in lavoro
   local b3Tot = BBox3d()
   local b3Raw = BBox3d()
   local nNextOddPhase = GetNextStartOrRestPhase( EMC.PHASE)
   local nRawId = EgtGetFirstRawPart()
   local nCurrRawId = GDB_ID.NULL
   while nRawId do
      if EgtVerifyRawPartPhase( nRawId, EMC.PHASE) then
         local b3Tmp = EgtGetRawPartBBox( nRawId)
         b3Tot:Add( b3Tmp)
         if EgtGetPartInRawPartCount( nRawId) > 0  and  not EgtVerifyRawPartPhase( nRawId, nNextOddPhase) then
            b3Raw = b3Tmp
            nCurrRawId = nRawId
         end
      end
      nRawId = EgtGetNextRawPart( nRawId)
   end
   if b3Tot:isEmpty() then
      EMC.ERR = 11
      return
   end
   EMC.LB = EgtIf( bSplit, b3Raw:getDimX(), b3Tot:getDimX()) + 10 * GEO.EPS_SMALL
   EMC.SB = b3Tot:getDimY()
   EMC.HB = b3Tot:getDimZ()
   EMC.LR = b3Raw:getDimX() + 10 * GEO.EPS_SMALL
   EMC.YMIN = b3Raw:getMin():getY()
   EMC.ZMIN = b3Raw:getMin():getZ()

   -- Aggiorno limiti di presa e tolleranza
   UpdateMinJoinDeltaTol()

   -- Recupero sovramateriale di testa e ingombro tagli di testa e di coda dal pezzo
   EMC.HOVM = EgtGetInfo( nCurrRawId, 'HOVM', 'd') or 0
   EMC.HCING = EgtGetInfo( nCurrRawId, 'HCING', 'd') or 0
   EMC.TCING = EgtGetInfo( nCurrRawId, 'TCING', 'd') or 0
   EMC.XMAX = b3Raw:getMax():getX() - EMC.HOVM

   -- correggo area non pinzabile in testa o coda in base alle info sulla disposizione, se presenti
   local sNotes = EgtGetMachiningParam( MCH_MP.USERNOTES)

   local idDisp = EgtGetPhaseDisposition( EMC.PHASE)
   local dHCING = EgtGetInfo( idDisp, 'HCING', 'd') or 0
   if dHCING then
      EMC.HCING = dHCING
   end
   local dTCING = EgtGetInfo( idDisp, 'TCING', 'd') or 0
   if dTCING then
      EMC.TCING = dTCING
   end

   -- Calcolo dell'ingombro della lavorazione
   local dDistFront, dDistBack = SpecialCalcMachiningEncumbrance( EMC.MCHID, bPreCut)
   if not dDistFront or not dDistBack then return end
   if bPreSplit  or  bSplitting then
      local dDistF = SpecialCalcPhaseEncumbrance( EMC.PHASE + 1)
      dDistFront = min( dDistFront, dDistF)
      local dNextHOVM = EgtGetInfo( EgtGetNextRawPart( nCurrRawId) or GDB_ID.NULL, 'HOVM', 'd') or 0
      local dBackOther = b3Tot:getDimX() - b3Raw:getDimX() - MinOther - dNextHOVM
      EgtOutLog( 'DistBack='..EgtNumToString( dDistBack)..' OtherBack='..EgtNumToString( dBackOther), 3)
      dDistBack = min( dDistBack, dBackOther)
   elseif bPreCut  or  bCutting then
      local dDistF = SpecialCalcPhaseEncumbrance( EMC.PHASE + 1)
      dDistFront = min( dDistFront, dDistF)
      dDistBack = 0.0
   end

   -- Verifico lunghezza pezzo
   if not bSplit and not VerifyPartLength() then
      return
   end

   -- Se inizio o appena dopo rotazione, eseguo il carico
   if not EMC.TPOS then
      local dPosT = LoadT
      if IsFirstMachiningAfterRotation( EMC.MCHID) then dPosT = dPosT + TurnerOffs end
      local vCmd = SpecCalcLoad( dPosT, dDistFront, max( dDistBack, MinJoin))
      local vCmd2 = SpecCalcCarriages( dDistFront, dDistBack, bFixedDelta, bFixedPos)
      if vCmd2 and #vCmd2 > 1 then
         table.insert( vCmd, { 0, 'CARR_MOVE'})
      end
      EgtJoinTables( vCmd, vCmd2)
      SpecOutputCmds( vCmd)

   -- Se altrimenti carri entrambi diponibili, eseguo calcoli per carrelli
   elseif not IsEndPhase( EMC.PHASE) then
      local vCmd = SpecCalcCarriages( dDistFront, dDistBack, bFixedDelta, bFixedPos)
      -- Se non ci sono spostamenti, confermo i parametri di aggancio
      if SpecTestOnlyRemarkInCmds( vCmd) then
         table.insert( vCmd, { 21, EgtIf( EMC.YDELTA, EMC.YDELTA, 0), EgtIf( EMC.VDELTA, EMC.VDELTA, 0)})
      end
      SpecOutputCmds( vCmd)

   -- Altrimenti, non muovo i carrelli rispetto alla trave
   else
      local vCmd = {}
      SpecOutputCmds( vCmd)
   end
   EMC.HCING_IGNORE = nil

   -- Se taglio di separazione
   local vCmd = {}
   if bSplitting then
      -- rimuovo eventuale vecchia info di Skip
      local NextDispId = EgtGetPhaseDisposition( EMC.PHASE + 1) or GDB_ID.NULL
      EgtRemoveInfo( NextDispId, 'SKIP')
      -- verifico se separazione con caduta
      if  not EMC.VDELTA  then
         EgtOutLog( '  Warning SPLITTING -> separazione con caduta pezzo')
         if IsEndPhase( EMC.PHASE + 1) then
            EgtSetInfo( NextDispId, 'SKIP', '1')
            local NextOpeId = EgtGetNextOperation( NextDispId) 
            while NextOpeId and EgtGetOperationPhase( NextOpeId) == EMC.PHASE + 1 do
               EgtSetOperationMode( NextOpeId, false)
               NextOpeId = EgtGetNextOperation( NextOpeId)
            end
         end
         EMC.ERR = 18
      -- verifico che la barra sia agganciata ad entrambi i carrelli
      elseif  not EMC.YDELTA  or  not EMC.VDELTA  then
         EMC.ERR = 19
         EMC.MSG = '  Error SPLIT : Y or V not clamped'
         return false
      end
      -- eseguo la separazione ( standard o di pezzo ruotato)
      if not IsMid2Phase( EMC.PHASE + 1) then
         vCmd = SpecCalcSplit( b3Raw:getDimX())
      else
         vCmd = SpecCalcSplitRot( b3Raw:getDimX())
      end
   end
   -- Se taglio finale di grezzo a perdere
   if bCutting then
      -- salvo distanza carrello V da inizio grezzo rimasto nella disposizione della prossima fase
      local NextDispId = EgtGetPhaseDisposition( EMC.PHASE + 1)
      if NextDispId then
         EgtSetInfo( NextDispId, 'VPOS', EMC.VDELTA)
      end
   end   

   -- Se previsto scarico, lo eseguo
   if bUnload then
      EMC.LB = b3Raw:getDimX() + 10 * GEO.EPS_SMALL
      local vCmdTmp = SpecCalcUnload()
      vCmd = EgtJoinTables( vCmd, vCmdTmp)
   end

   -- Se ritorno al carico per rotazione
   if bPreRotMch then
      -- determino posizione testa trave
      local nLastEntId = EgtGetLastInGroup( EMC.PATHID)
      local vAxes = EmtGetAxesPos( nLastEntId)
      if #vAxes > 0 then EMC.TPOS = vAxes[1] end
      -- eseguo movimento prima di rotazione
      local vCmdTmp = SpecCalcPreRot()
      vCmd = EgtJoinTables( vCmd, vCmdTmp)
   end

   -- Emetto eventuali comandi di separazione e/o scarico
   if #vCmd > 0 then
      SpecOutputCmds( vCmd, true)
   end

end  --SpecApplyPath( bLast)

---------------------------------------------------------------------
function SpecialCalcMachiningEncumbrance( nMchId, bPreCut)
   -- gruppi della lavorazione
   local nClId = EgtGetFirstNameInGroup( nMchId, 'CL')
   local nPathId = EgtGetFirstInGroup( nClId or GDB_ID.NULL)
   if not nPathId then
      EMC.ERR = 12
      return
   end
   -- recupero ptMin ptMax della lavorazione
   local ptMin = EgtGetInfo( nClId, 'MMIN', 'p')
   local ptMax = EgtGetInfo( nClId, 'MMAX', 'p')
   if not ptMin or not ptMax then
      EMC.ERR = 13
      return
   end
   -- se pre-taglio, aggiorno ptMax con quello del taglio finale
   if bPreCut then
      local ptFinMax = GetFinalCutPmax( nMchId)
      if ptFinMax then
         ptMax = ptFinMax
      end   
   end
   -- Recupero del vettore estrusione (coincide con il vettore utensile)
   local vtTool = EgtGetInfo( nPathId, 'EXTR', 'v')
   if not vtTool then
      EMC.ERR = 14
      return
   end
   -- Recupero testa
   local sHead = EgtTdbGetCurrToolParam( MCH_TP.HEAD)
   -- Calcolo del vettore ausiliario
   local vAxes = EmtGetAxesPos( EgtGetFirstInGroup( nPathId))
   if not vAxes  or  #vAxes < 5 or ( sHead == 'H3' and #vAxes < 6) then
      EMC.ERR = 15
      return
   end
   local vtAux = EgtGetCalcAuxDirFromAngles( vAxes[4], vAxes[5], vAxes[6])
   if not vtAux then
      EMC.ERR = 16
      return
   end
   local vtArm = vtAux
   -- Recupero dei dati dell'utensile
   local nToolType = EgtTdbGetCurrToolParam( MCH_TP.TYPE)
   local bSaw = ( nToolType == MCH_TY.SAW_STD or nToolType == MCH_TY.SAW_FLAT)
   local bChain = ( nToolType == MCH_TY.MORTISE_STD)
   local dTLen = EgtTdbGetCurrToolParam( MCH_TP.LEN)
   local dTDiam = EgtTdbGetCurrToolParam( MCH_TP.DIAM)
   local dTDist = EgtIf( EgtTdbGetCurrToolParam( MCH_TP.DIST) > 1, EgtTdbGetCurrToolParam( MCH_TP.DIST), ChSawLen)
   -- Se sega a catena, devo correggere il versore Aux per farlo coincidere con la direzione del braccio C
   if bChain then
      if abs( vAxes[6] or 0) < 1 then
         vtArm = vtTool
      else
         vtArm = vtTool ^ vtAux
      end
   end
   -- Calcolo limiti derivanti dalla lavorazione
   local dDistFront, dDistBack = SpecCalcEncumbrance( vtTool, vtArm, vtAux, ptMin, ptMax, bSaw, bChain, dTLen, dTDiam, dTDist)
   return dDistFront, dDistBack
end

---------------------------------------------------------------------
function SpecialCalcPhaseEncumbrance( nPhase)
   -- Deve essere la fase finale di lavorazione di un pezzo (già staccato dal resto della trave)
   local dDistFront = EMC.LB
   local dDistBack = EMC.LB
   -- Salvo lavorazione e utensile correnti, per ripristinarli alla fine
   local nOrigMchId = EgtGetCurrMachining()
   local sOrigTool = EgtTdbGetCurrToolParam( MCH_TP.NAME)
   local sOrigHead = EgtTdbGetCurrToolParam( MCH_TP.HEAD)
   local nOrigExit = EgtTdbGetCurrToolParam( MCH_TP.EXIT)
   -- Ciclo sulle lavorazioni
   local nMchId = EgtGetNextActiveOperation( EgtGetPhaseDisposition( nPhase) or GDB_ID.NULL)
   while nMchId and EgtGetOperationPhase( nMchId) == nPhase do
      -- imposto lavorazione e utensile correnti
      EgtSetCurrMachining( nMchId)
      local sTool = EgtGetMachiningParam( MCH_MP.TOOL)
      if not EgtTdbSetCurrTool( sTool) then
         local sTuuid = EgtGetMachiningParam( MCH_MP.TUUID)
         sTool = EgtTdbGetToolFromUUID( sTuuid)
         EgtTdbSetCurrTool( sTool)
      end
      local sHead = EgtTdbGetCurrToolParam( MCH_TP.HEAD)
      local nExit = EgtTdbGetCurrToolParam( MCH_TP.EXIT)
      if sTool and sHead and nExit then EgtSetCalcTool( sTool, sHead, nExit) end
      -- calcolo ingombri
      local dDistF, dDistB = SpecialCalcMachiningEncumbrance( nMchId)
      if dDistF and dDistB then
         dDistFront = min( dDistFront, dDistF)
         dDistBack = min( dDistBack, dDistB)
      end
      nMchId = EgtGetNextActiveOperation( nMchId)
   end
   -- Ripristino lavorazione e utensile correnti
   if nOrigMchId then EgtSetCurrMachining( nOrigMchId) end
   if sOrigTool then EgtTdbSetCurrTool( sOrigTool) end
   if sOrigTool and sOrigHead and nOrigExit then EgtSetCalcTool( sOrigTool, sOrigHead, nOrigExit) end
   -- Restituisco gli ingombri trovati
   return dDistFront, dDistBack
end

---------------------------------------------------------------------
function GetFinalCutPmax( nMchId)
   local nFinalCutId
   local nId = EgtGetNextActiveOperation( nMchId)
   while nId  and  EgtGetOperationPhase( nId) == EMC.PHASE do
      nFinalCutId = nId
      nId = EgtGetNextActiveOperation( nId)
   end
   if not nFinalCutId then return end
   local nCLId = EgtGetFirstNameInGroup( nFinalCutId, 'CL')
   if not nCLId then return end
   return EgtGetInfo( nCLId, 'MMAX', 'p')
end

---------------------------------------------------------------------
function SpecCalcEncumbrance( vtTool, vtArm, vtAux, ptMin, ptMax, bSaw, bChain, dTLen, dTDiam, dTDist)
   -- Quota in Z dal punto di inclinazione dei carrelli
   local dCompZ = sqrt( 1 - vtTool:getZ() * vtTool:getZ())
   local dZup = ptMin:getZ() - 0.5 * dCompZ * dTDiam - ( EMC.ZMIN + 130)
   -- Posizione min e max del naso mandrino (rispetto a testa pezzo in X e riferimento pezzo in Y e Z)
   local ptHeadMin = ptMin + vtTool * dTLen - Vector3d( EMC.XMAX, EMC.YMIN + EMC.SB, EMC.ZMIN)
   local ptHeadMax = ptMax + vtTool * dTLen - Vector3d( EMC.XMAX, EMC.YMIN + EMC.SB, EMC.ZMIN)
   -- Ingombro a sinistra
   local dDistBack = EMC.LB + ptMin:getX() + LoadT
   local dHeadBack = 350
   if bSaw then
      if vtTool:getX() > 0 and abs( vtTool:getY()) < 0.088 and abs( vtTool:getZ()) < 0.088 then
         dHeadBack = 50 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
      elseif abs( vtTool:getZ()) < 0.26 and abs( vtTool:getX()) < 0.35 then
         dHeadBack = EgtIf( vtArm:getX() < 0, 540, 350)
      elseif abs( vtTool:getZ()) < 0.26 and abs( vtTool:getX()) < 0.71 then
         if vtArm:getX() < 0 then
            dHeadBack = 450
         else
            dHeadBack = EgtIf( vtTool:getX() > 0, 50, 90) + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
         end
      elseif ( vtTool:getX() > 0.7 and abs( vtTool:getY()) < 0.2 and ptMax:getZ() > EMC.ZMIN + 0.9 * EMC.HB) then
         if vtTool:getZ() > 0 then
            dHeadBack = max( 50, 90 - 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX()))
         else
            dHeadBack = max( 50, 40 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX()))
         end
         if ptMax:getZ() < EMC.ZMIN + BD.VICE_MINH then 
            dHeadBack = dHeadBack + BD.VICE_MINH
         end
      elseif ( vtTool:getX() > 0.2 and abs( vtTool:getZ()) < 0.5) then
         dHeadBack = max( 90, 40 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX()))
      elseif abs( vtTool:getZ()) < 0.93 then
         if vtTool:getX() > 0 and abs( vtTool:getY()) < 0.2 and ptMax:getZ() > EMC.ZMIN + BD.VICE_MINH then
            dHeadBack = 180
         elseif vtTool:getX() > -0.05 then
            dHeadBack = 50 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
         elseif vtTool:getX() > -0.3 then
            dHeadBack = 250
         elseif vtTool:getX() > -0.707 then
            dHeadBack = 350
         elseif vtTool:getX() > -0.8667 then
            dHeadBack = 450
         else
            dHeadBack = 650
         end
      else
         dHeadBack = 50 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
      end
      -- per limiti corsa asse X1
      dHeadBack = max( dHeadBack, MinX1 + 1 - vtTool:getX() * ( MillOffs + dTLen))
   else
      if ( vtTool:getX() > -0.1 and vtArm:getX() > -0.1) or
         ( abs( vtTool:getX()) < 0.1 and abs( vtTool:getZ()) < 0.1) then
         dHeadBack = EgtIf( EMC.CNT == 1, 180, 130)
      elseif ( vtTool:getX() > -0.1  and  vtArm:getX() > -0.95) then
         dHeadBack = 180
      elseif ( vtTool:getX() < -0.8) then
         dHeadBack = 675
      elseif ( vtTool:getX() < -0.75) then
         dHeadBack = 650
      elseif ( vtTool:getX() < -0.5) then
         dHeadBack = 450
      end
      if vtTool:getX() < -0.25 then
         dHeadBack = dHeadBack + max( dTLen - 130, 0) * abs( vtTool:getX())
      elseif vtTool:getX() < 0 then 
         dHeadBack = dHeadBack + ( dTLen + 180) * abs( vtTool:getX())
      end
      if vtTool:getX() > 0.866 then
         dHeadBack = 50
      elseif vtTool:getX() >= 0 and dZup > 0 then
         dHeadBack = max( EgtIf( EMC.CNT == 1, 180, 130), dHeadBack - dZup)
      end
      if abs( vtTool:getX()) < 0.5 and abs( vtTool:getZ()) > 0.259 and dZup < 0 then
         if vtArm:getX() < -0.259 then
            dHeadBack = 510
         else
            dHeadBack = EgtIf(  vtTool:getZ() > 0.966, 160, 280)
         end
      end
      -- per fresature longitudinali con utensile di fianco
      if abs( vtTool:getX()) < 0.1 and vtTool:getZ() < 0.707 and vtArm:getX() < -0.5 then
         dHeadBack = 500
      end
      -- per sega a catena di fianco
      if bChain and vtTool:getX() < 0.5 and vtTool:getZ() < 0.5 and vtArm:getX() < -0.5 then
         dHeadBack = max( dHeadBack, 510)
      end
      -- per fresa diretta quasi esattamente come Y+/- e con la testa non troppo nel pezzo
      if not bChain and abs( vtTool:getX()) < 0.017 and abs( vtTool:getZ()) < 0.017 and
         (( vtTool:getY() > 0 and ptHeadMin:getY() > 80) or ( vtTool:getY() < 0 and ptHeadMax:getY() < -EMC.SB - 80)) then
         dHeadBack = EgtIf( EMC.CNT == 1, 180, 130)
      end
      -- per limiti corsa asse X1
      if not bChain then
         dHeadBack = max( dHeadBack, MinX1 + 1 - vtTool:getX() * ( MillOffs + dTLen))
      else
         dHeadBack = max( dHeadBack, MinX1 + 1 - vtAux:getX() * ( MillOffs + dTDist) - vtTool:getX() * dTLen)
      end
   end
   -- Ingombro a destra
   local dDistFront = - ptMax:getX() - LoadT
   local dHeadFront = 350
   if bSaw then
      if vtTool:getX() < 0 and abs( vtTool:getY()) < 0.088 and abs( vtTool:getZ()) < 0.088 then
         dHeadFront = 50 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
      elseif abs( vtTool:getZ()) < 0.26 and abs( vtTool:getX()) < 0.35 then
         dHeadFront = EgtIf( vtArm:getX() > 0, 540, 350)
      elseif abs( vtTool:getZ()) < 0.26 and abs( vtTool:getX()) < 0.71 then
         if vtArm:getX() > 0 then
            dHeadFront = 450
         else
            dHeadFront = EgtIf( vtTool:getX() < 0, 50, 90) + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
         end
      elseif ( vtTool:getX() < - 0.7 and abs( vtTool:getY()) < 0.2 and ptMax:getZ() > EMC.ZMIN + 0.9 * EMC.HB) then
         if vtTool:getZ() > 0 then
            dHeadFront = max( 50, 90 - 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX()))
         else
            dHeadFront = max( 50, 40 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX()))
         end
         if ptMax:getZ() < EMC.ZMIN + BD.VICE_MINH then 
            dHeadFront = dHeadFront + BD.VICE_MINH
         end
      elseif ( vtTool:getX() < -0.2 and abs( vtTool:getZ()) < 0.5) then
         dHeadFront = max( 90, 40 + 0.5 * dTDiam * sqrt( vtTool:getY() * vtTool:getY() + vtTool:getZ() * vtTool:getZ()))
      elseif abs( vtTool:getZ()) < 0.93 then
         if vtTool:getX() < 0 and abs( vtTool:getY()) < 0.2 and ptMax:getZ() > EMC.ZMIN + BD.VICE_MINH then
            dHeadFront = 180
         elseif vtTool:getX() < 0.05 then
            dHeadFront = 50 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
         elseif vtTool:getX() < 0.3 then
            dHeadFront = 250
         elseif vtTool:getX() < 0.707 then
            dHeadFront = 350
         elseif vtTool:getX() < 0.8667 then
            dHeadFront = 450
         else
            dHeadFront = 650
         end
      else
         dHeadFront = 50 + 0.5 * dTDiam * sqrt( 1 - vtTool:getX() * vtTool:getX())
      end
      -- per limiti corsa asse X2
      dHeadFront = max( dHeadFront, -MaxX2 + 1 + vtTool:getX() * ( MillOffs + dTLen))
   else
      if ( vtTool:getX() < -0.5 and vtArm:getX() < 0.1) then
         dHeadFront = max( 50, dTDiam / 2 * abs( vtTool:getZ()) + 20)
      elseif ( vtTool:getX() < 0.1 and vtArm:getX() < 0.1) or
         ( abs( vtTool:getX()) < 0.1 and abs( vtTool:getZ()) < 0.1) then
         dHeadFront = 130
      elseif ( vtTool:getX() < 0.1  and  vtArm:getX() < 0.95) then
         dHeadFront = 180
      elseif ( vtTool:getX() > 0.8) then
         dHeadFront = 675
      elseif ( vtTool:getX() > 0.75) then
         dHeadFront = 650
      elseif ( vtTool:getX() > 0.5) then
         dHeadFront = 450
      end
      if vtTool:getX() > 0.25 then 
         dHeadFront = dHeadFront + max( dTLen - 130, 0) * vtTool:getX()
      elseif vtTool:getX() > 0 then 
         dHeadFront = dHeadFront + ( dTLen + 180) * vtTool:getX()
      end
      if vtTool:getX() < -0.866 then
         dHeadFront = 50
      elseif vtTool:getX() <= 0 and dZup > 0 then
         dHeadFront = max( 130, dHeadFront - dZup)
      end
      if abs( vtTool:getX()) < 0.5 and abs( vtTool:getZ()) > 0.259 and dZup < 0 then
         if vtArm:getX() > 0.259 then
            dHeadFront = 510
         else
            dHeadFront = EgtIf(  vtTool:getZ() > 0.966, 160, 280)
         end
      end
      -- per fresature longitudinali con utensile di fianco
      if abs( vtTool:getX()) < 0.1 and vtTool:getZ() < 0.707 and vtArm:getX() > 0.5 then
         dHeadFront = 500
      end
      -- per sega a catena di fianco
      if bChain and vtTool:getX() > -0.5 and vtTool:getZ() < 0.5 and vtArm:getX() > 0.5 then
         dHeadFront = max( dHeadFront, 510)
      end
      -- per fresa diretta quasi esattamente come Y+/- e con la testa non troppo nel pezzo
      if not bChain and abs( vtTool:getX()) < 0.017 and abs( vtTool:getZ()) < 0.017 and
         (( vtTool:getY() > 0 and ptHeadMin:getY() > 80) or ( vtTool:getY() < 0 and ptHeadMax:getY() < -EMC.SB - 80)) then
         dHeadFront = 130
      end
      -- per limiti corsa asse X2
      if not bChain then
         dHeadFront = max( dHeadFront, -MaxX2 + 1 + vtTool:getX() * ( MillOffs + dTLen))
      else
         dHeadFront = max( dHeadFront, -MaxX2 + 1 + vtAux:getX() * ( MillOffs + dTDist) + vtTool:getX() * dTLen)
      end
   end
   -- Stampe debug
   EgtOutLog( '  Tdir=' .. tostring( vtTool) .. ' Adir=' .. tostring( vtArm) .. ' Zup=' .. EgtNumToString( dZup), 3)
   EgtOutLog( '  DistFront=' .. EgtNumToString( dDistFront) .. ' DistBack=' .. EgtNumToString( dDistBack) ..
              ' HeadFront=' .. EgtNumToString( dHeadFront) .. ' HeadBack=' .. EgtNumToString( dHeadBack), 3)
   -- Restituisco ingombri effettivi
   return ( dDistFront - dHeadFront), ( dDistBack - dHeadBack)
end

---------------------------------------------------------------------
function VerifyPartLength()

   -- Verifico lunghezza pezzo
   if EMC.LB < MinJoin + MinOther + AGG_LOAD + EMC.HCING + EMC.HOVM then
      EgtOutLog( '  Error CLAMP -> pezzo troppo corto')
      EMC.ERR = 17
      return false
   end

   return true
end

---------------------------------------------------------------------
function SpecCalcLoad( dPosT, dDistFront, dDistBack)
   --[L]
   local dMinDistBack= max( dDistBack, MinJoin + EgtIf( IsMid2Phase( EMC.PHASE) or IsEnd2Phase( EMC.PHASE), EMC.TCING, 0))
   local dNewYDelta = max( EMC.LB - dMinDistBack, MinOther + AGG_LOAD + EMC.HCING + EMC.HOVM)
   local dNewVDelta = nil
   local dNewY = dPosT + TurnerOffs + dNewYDelta
   local vCmd = {}
   EgtOutLog( '  *[L]', 1)
   -- [L-1]
   if  dNewY - MaxX1 > 0   then
      dNewYDelta = min( EMC.LB - MinJoin + AGG_LOAD, MaxX1 - dPosT - TurnerOffs)
      EgtOutLog( '  *[L1]', 1)
   end --[L-2]
   if EMC.LB - dNewYDelta <  MinJoin then
      dNewYDelta = min( EMC.LB - MinJoin + AGG_LOAD, MaxX1 - dPosT - TurnerOffs)
      EgtOutLog( '  *[L2]', 1)
   end
   -- Commento
   table.insert( vCmd, { 0, 'Loading'})
   -- risalita testa a Zmax
   local bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
   -- Apro entrambe le morse
   table.insert( vCmd, { 11, 0})
   table.insert( vCmd, { 12, 0})
   -- Sposto il carrello Y per il carico
   table.insert( vCmd, { 2, 'X1', dPosT + dNewYDelta, 'X2', ParkX2})
   -- Chiudo morsa Y
   table.insert( vCmd, { 11, EgtIf( EMC.LB - dNewYDelta < LenToPress, 1, 2)})
   -- confermo i nuovi parametri di aggancio
   table.insert( vCmd, { 21, dNewYDelta, 0})
   -- Inizializzo contatore globale
   EMC.CNT = 1
   SpecOutputCNT()
   -- Assegno stato corrente
   EMC.TPOS = dPosT
   EMC.YDELTA = dNewYDelta
   EMC.VDELTA = nil
   -- Restituisco i comandi
   return vCmd
end -- SpecAdjustLoad [L]

---------------------------------------------------------------------
function SpecCalcCarriages( dDistFront, dDistBack, bFixedDelta, bFixedPos)

   local MinFrontJoin = MinJoin + EMC.HCING + EMC.HOVM
   local MinBackJoin = MinJoin + EgtIf( IsMid2Phase( EMC.PHASE) or IsEnd2Phase( EMC.PHASE), EMC.TCING, 0)
   local MyMinOther = MinOther + EgtIf( EMC.CNT == 1, AGG_LOAD, 0) 

   local dDistFrontEff = min( dDistFront, EMC.LB - MyMinOther - EMC.TCING)
   if bFixedDelta and IsMid2Phase( EMC.PHASE + 1) then
      dDistFrontEff = min( dDistFrontEff, EMC.LR - MinOther - EMC.TCING)
   end
   local dDistBackEff = min( dDistBack, EMC.LB - MyMinOther - EMC.HCING - EMC.HOVM)

   EgtOutLog( '  Dist/Min : Back=' .. EgtNumToString( dDistBackEff, 1) .. '/' .. EgtNumToString( MinBackJoin, 1) ..
              ' Front=' .. EgtNumToString( dDistFrontEff, 1) .. '/' .. EgtNumToString( MinFrontJoin, 1) ..
              '  Fixed : Delta=' .. EgtIf( bFixedDelta, 'T', 'F') .. ' Pos=' .. EgtIf( bFixedPos, 'T', 'F'), 3)

   -- [A] se posso mettere solo carrello Y
   if dDistFrontEff < MinFrontJoin  and  dDistBackEff > MinBackJoin - GEO.EPS_SMALL then

      local dPosT = EMC.TPOS
      local dYDelta = EMC.YDELTA
      local dVDelta = EMC.VDELTA

      local dNewPosT = nil
      local dNewYDelta = EMC.LB - dDistBackEff
      local dNewVDelta = nil

      return SpecAdjustCarriages( dPosT, dYDelta, dVDelta, dNewPosT, dNewYDelta, dNewVDelta, bFixedDelta, bFixedPos)

   -- [B] se altrimenti posso mettere entrambi i carrelli Y e V
   elseif dDistBackEff > MinBackJoin - GEO.EPS_SMALL  and
          dDistFrontEff > MinFrontJoin - GEO.EPS_SMALL then

      local dPosT = EMC.TPOS
      local dYDelta = EMC.YDELTA
      local dVDelta = EMC.VDELTA

      local dNewPosT = nil
      local dNewYDelta = EMC.LB - dDistBackEff
      local dNewVDelta = dDistFrontEff

      return SpecAdjustCarriages( dPosT, dYDelta, dVDelta, dNewPosT, dNewYDelta, dNewVDelta, bFixedDelta, bFixedPos)

   -- [C] se altrimenti posso mettere solo carrello V
   elseif dDistBackEff < MinBackJoin  and  dDistFrontEff > MinFrontJoin - GEO.EPS_SMALL then

      local dPosT = EMC.TPOS
      local dYDelta = EMC.YDELTA
      local dVDelta = EMC.VDELTA

      local dNewPosT = nil
      local dNewYDelta = nil
      local dNewVDelta = dDistFrontEff

      return SpecAdjustCarriages( dPosT, dYDelta, dVDelta, dNewPosT, dNewYDelta, dNewVDelta, bFixedDelta, bFixedPos)

   -- altrimenti errore
   else
      if EgtGetDebugLevel() < 3 then
         EgtOutLog( '  Dist/Min : Back=' .. EgtNumToString( dDistBackEff, 1) .. '/' .. EgtNumToString( MinBackJoin, 1) ..
                    ' Front=' .. EgtNumToString( dDistFrontEff, 1) .. '/' .. EgtNumToString( MinFrontJoin, 1))
      end
      EgtOutLog( '  Error CLAMP impossible')
      EMC.ERR = 18
      return {}
   end

end

---------------------------------------------------------------------
function SpecCalcSplit( dLenRaw)
   local vCmd = {}
   EgtOutLog( '  *[S]', 1)
   table.insert( vCmd, { 0, EgtIf( EMC.VDELTA, 'Split', 'Fall')})
   -- determino i grezzi da agganciare al carrello Y (sono quelli presenti nella fase successiva dispari)
   local nNextOddPhase = GetNextStartOrRestPhase( EMC.PHASE)
   local nRawId = EgtGetFirstRawPart()
   while nRawId do
      if EgtVerifyRawPartPhase( nRawId, nNextOddPhase) then
         table.insert( vCmd, { 31, nRawId, 'X1'})
      end
      nRawId = EgtGetNextRawPart( nRawId)
   end
   -- riporto il carrello Y al carico con il resto della trave
   local dLDelta = EMC.YDELTA - dLenRaw
   table.insert( vCmd, { 1, 'X1', LoadT + dLDelta})
   table.insert( vCmd, { 21, 0, EMC.VDELTA or 0})
   -- imposto subito Y non più attaccato al trave in lavoro
   EMC.YDELTA = nil
   -- salvo posizione carrello Y in disposizione del pezzo dopo split
   local PostDispId = EgtGetPhaseDisposition( EMC.PHASE + 1)
   if PostDispId then
      EgtSetInfo( PostDispId, 'YPOS', LoadT + dLDelta)
   end
   -- salvo posizione grezzo rimasto e posizione carrello Y nella disposizione iniziale del pezzo succ (prossima fase dispari)
   local NextDispId = EgtGetPhaseDisposition( nNextOddPhase)
   if NextDispId then
      EgtSetInfo( NextDispId, 'TPOS', LoadT)
      EgtSetInfo( NextDispId, 'YPOS', LoadT + dLDelta)
   end
   return vCmd
end

---------------------------------------------------------------------
function SpecCalcSplitRot( dLenRaw)
   local vCmd = {}
   EgtOutLog( '  *[SR]', 1)
   table.insert( vCmd, { 0, 'SplitRot'})
   -- determino i grezzi da agganciare al carrello Y (sono quelli presenti nella fase successiva dispari)
   local vRaw = {}
   local nNextOddPhase = GetNextStartOrRestPhase( EMC.PHASE)
   local nRawId = EgtGetFirstRawPart()
   while nRawId do
      if EgtVerifyRawPartPhase( nRawId, nNextOddPhase) then
         table.insert( vRaw, nRawId)
      end
      nRawId = EgtGetNextRawPart( nRawId)
   end
   for _, nId in ipairs( vRaw) do
      table.insert( vCmd, { 31, nId, 'X1'})
   end
   -- riporto il carrello Y al carico con il resto della trave
   local dLDelta = EMC.YDELTA - dLenRaw
   table.insert( vCmd, { 1, 'X1', LoadT + TurnerOffs + dLDelta})
   table.insert( vCmd, { 21, 0, EMC.VDELTA})
   -- imposto subito Y non più attaccato alla trave in lavoro
   EMC.YDELTA = nil
   -- apro il carrello Y
   table.insert( vCmd, { 11, 0})
   -- sgancio i grezzi dal carrello Y
   for _, nId in ipairs( vRaw) do
      table.insert( vCmd, { 31, nId, ''})
   end
   -- lo porto in parcheggio
   table.insert( vCmd, { 1, 'X1', ParkX1})
   -- salvo posizione grezzo rimasto nella disposizione iniziale del pezzo succ (prossima fase dispari)
   local NextDispId = EgtGetPhaseDisposition( nNextOddPhase)
   if NextDispId then
      EgtSetInfo( NextDispId, 'TPOS', LoadT)
   end
   return vCmd
end

---------------------------------------------------------------------
function SpecCalcUnload()
   local vCmdPre = {}
   EgtOutLog( '  *[U]', 1)
   -- Se pinza Y chiusa , devo effettuare uno scambio
   if EMC.YDELTA  then
      -- determino posizione testa trave
      local nLastEntId = EgtGetLastInGroup( EMC.PATHID)
      local vAxes = EmtGetAxesPos( nLastEntId)
      if #vAxes > 0 then EMC.TPOS = vAxes[1] end
      -- imposto quote aggancio per avere solo pinza V
      local dDistFront = EMC.LB - MinOther - EMC.TCING - 10 * GEO.EPS_SMALL
      local dDistBack = 0
      -- effettuo scambio
      vCmdPre = SpecCalcCarriages( dDistFront, dDistBack)
      if EMC.ERR ~= 0 then
         return {}
      end
      -- recupero nuova posizione carrelli
      SpecSetCarrPosFromCmds( vCmdPre)
      EgtOutLog( '  *[U1]', 1)
   end
   local vCmd = {}
   -- Tipo di scarico
   local bStdUl = ( MaxUnloadLen < 1 or EMC.LB - EMC.HOVM < MaxUnloadLen + 1)
   -- Commento
   table.insert( vCmd, { 0, 'Unloading', EgtIf( bStdUl, 'Unloading', 'Manual Unloading')})
   -- risalita testa a Zmax
   local bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
   -- Se pinza Y chiusa, la apro
   if EMC.YDELTA then
      table.insert( vCmd, { 11, 0})
   end
   -- Se non supero la lunghezza massima di scarico, sposto il pezzo in posizione di scarico
   if bStdUl then
      local dFinT = EgtIf( EMC.LB < MaxLenSmT, UnloadSmT, UnloadT) - EMC.LB
      local dFinV = dFinT + EMC.VDELTA
      table.insert( vCmd, { 2, 'T', dFinT, 'X2', dFinV})
   else
      table.insert( vCmd, { 1, 'X2', MaxX2})
   end
   -- apro la morsa
   table.insert( vCmd, { 12, 0})
   -- riporto il carrello in home
   table.insert( vCmd, { 1, 'X2', ParkX2})

   -- eventuale unione tabelle
   if #vCmdPre > 0 then
      vCmd = EgtJoinTables( vCmdPre, vCmd)
   end

   return vCmd
end

---------------------------------------------------------------------
function SpecCalcPreRot()
   local vCmdPre = {}
   EgtOutLog( '  *[PR]', 1)
   -- Se pinza V chiusa , devo effettuare uno scambio
   if EMC.VDELTA  then
      -- imposto quote aggancio per avere solo pinza Y
      local dDistFront = 0
      local dDistBack = EMC.LB - MinOther - EMC.HOVM + 10 * GEO.EPS_SMALL
      -- effettuo scambio
      vCmdPre = SpecCalcCarriages( dDistFront, dDistBack)
      -- recupero nuova posizione carrelli
      SpecSetCarrPosFromCmds( vCmdPre)
      EgtOutLog( '  *[PR1]', 1)
   end
   -- porto il pezzo alla zona di rotazione con il carro Y
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'Pre-Rotation'})
   -- risalita testa a Zmax
   local bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
   -- Se pinza V chiusa, la apro
   if EMC.VDELTA then
      table.insert( vCmd, { 12, 0})
   end
   -- riporto la trave al carico
   local RotT = LoadT + TurnerOffs - EMC.HOVM
   table.insert( vCmd, { 2, 'X1', RotT + EMC.YDELTA, 'T', RotT})
   -- apro la morsa
   table.insert( vCmd, { 11, 0})
   -- riporto il carrello in home
   table.insert( vCmd, { 1, 'X1', ParkX1})

   -- eventuale unione tabelle
   if #vCmdPre > 0 then
      vCmd = EgtJoinTables( vCmdPre, vCmd)
   end

   return vCmd
end

---------------------------------------------------------------------
function SpecAdjustCarriages( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta, bFixedPos)

   -- [A] richiesto solo carrello Y
   if dYDeltaF and  ( not dVDeltaF) then
      -- [A1] rimango sul carrello Y
      if dYDeltaI and ( not dVDeltaI) then
         return SpecAdjustCarrA1( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF)
      -- [A2] torno da entrambi
      elseif dYDeltaI and dVDeltaI then
         return SpecAdjustCarrA2( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF)
      -- [A3] torno da carrello V
      elseif dVDeltaI and (not dYDeltaI) then
         return SpecAdjustCarrA3( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF)
      else
         EgtOutLog( '  Error CLAMP NULL-> Y impossibile')
         error( 'Error CLAMP NULL-> Y impossibile')
      end

   -- (B) richiesti entrambi i carrelli
   elseif dYDeltaF and dVDeltaF then
      -- [B1] passo da carrello Y a entrambi
      if dYDeltaI and ( not dVDeltaI) then
         return SpecAdjustCarrB1( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
      -- [B2] continuo con entrambi i carrelli
      elseif dYDeltaI and dVDeltaI then
         return SpecAdjustCarrB2( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
      -- [B3] passo da carrello V a entrambi
      elseif dVDeltaI and (not dYDeltaI) then
         return SpecAdjustCarrB3( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
      else
         EgtOutLog( '  Error CLAMP NULL-> Y+V impossibile')
         error( 'Error CLAMP NULL-> Y+V impossibile')
      end

   -- [C] richiesto solo carrello V
   elseif ( not dYDeltaF) and dVDeltaF then
      -- [C1] provengo da solo carrello Y
      if dYDeltaI and ( not dVDeltaI) then
         return SpecAdjustCarrC1( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
      -- [C2] provengo da carrelli Y e V
      elseif dYDeltaI and dVDeltaI then
         return SpecAdjustCarrC2( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
      -- [C3] rimango sul carrello V
      elseif ( not dYDeltaI) and dVDeltaI then
         return SpecAdjustCarrC3( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta, bFixedPos)
      else
         EgtOutLog( '  Error CLAMP NULL-> V impossibile')
         error( 'Error CLAMP NULL-> V impossibile')
      end

   end

end

---------------------------------------------------------------------
local function CalcCharStatus( sType, dDelta)
   -- se per carrello Y
   if sType == 'X1' then
      return EgtIf( EMC.LB - dDelta < LenToPress, 1, 2)
   -- altrimenti per carrello V
   else
      return EgtIf( dDelta < LenToPress, 1, 2)
   end
end

---------------------------------------------------------------------
local function GetDeltaTol( dLenPresa, TCING, HCING, HOVM, Carr, bFixedDelta)
   local dDeltaTolEff = DELTA_SIC
   if Carr == 'X1' then
      local dLenPreEff = dLenPresa - TCING
      if dLenPreEff < MinJoin + DeltaTol then
         dDeltaTolEff = max( DELTA_SIC, dLenPreEff - MinJoin + 10 * GEO.EPS_SMALL)
      else
         dDeltaTolEff = DeltaTol
      end
   elseif Carr == 'X2' then
      local dLenPreEff = dLenPresa - HCING - HOVM
      if dLenPreEff < MinJoin + DeltaTol then
         dDeltaTolEff = max( DELTA_SIC, dLenPreEff - MinJoin + 10 * GEO.EPS_SMALL)
      else
         dDeltaTolEff = DeltaTol
      end
   end
   return EgtIf( bFixedDelta, min( dDeltaTolEff, DELTA_TOL_FIXED), dDeltaTolEff)
end

---------------------------------------------------------------------
local function GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistN, sSide)
   local dRecTotn = min( dDistN, dCorsaYfc+ dCorsaVfc) -- recupero possibile al netto di dCorsa..TrA)
   local dCorsaYd, dCorsaYTd, dCorsaVd, dCorsaVTd
   if sSide == 'Fr' then
      if dRecTotn / 2 <= dCorsaYfc then
         if dRecTotn / 2 <= dCorsaVfc then 
            dCorsaYTd = dRecTotn / 2
            dCorsaVd = dRecTotn / 2
         else   
            dCorsaVd = dCorsaVfc
            dCorsaYTd = min( dCorsaYfc, dRecTotn - dCorsaVd)
         end
      else    
         dCorsaYTd = dCorsaYfc 
         dCorsaVd = min( dCorsaVfc, dRecTotn - dCorsaYTd)
      end
      dCorsaY = dCorsaYTd
      dCorsaV = dCorsaVd
   else
      if dRecTotn / 2 <= dCorsaVfc then
         if dRecTotn / 2 <= dCorsaYfc then 
            dCorsaVTd = dRecTotn / 2
            dCorsaYd = dRecTotn / 2
         else   
            dCorsaYd = dCorsaYfc
            dCorsaVTd = min( dCorsaVfc, dRecTotn - dCorsaYd)
         end
      else    
         dCorsaVTd = dCorsaVfc 
         dCorsaYd = min( dCorsaYfc, dRecTotn - dCorsaVTd)
      end
      dCorsaY = dCorsaYd
      dCorsaV = dCorsaVTd
   end
   return dCorsaY, dCorsaV
end

---------------------------------------------------------------------
local function PosxExtraYV( dX1PosA, dX2PosA, dTPosA, dMinX1, dMaxX2, sYV)
   -- svolge la predisposizione iniziale di trave e carrelli in caso di situazione iniziale con un solo carrello chiuso
   -- con un solo carrello chiuso per l'aggancio del carrello aperto
   local dX1Pos, dX2Pos, dTPos
   -- per aggancio del carrello X2
   if sYV == 'X2' then
      dTPos = min( dTPosA, dX2PosA - MinJoin - EgtIf( EMC.HCING_IGNORE, 0, EMC.HCING) - EMC.HOVM)
      dX1Pos = dX1PosA + ( dTPos - dTPosA)
      if dX1Pos < dMinX1 then
         dX2Pos = dX2PosA + ( dMinX1 - dX1Pos)
         if dX2Pos > dMaxX2 + GEO.EPS_SMALL then
            EmitComment( vCmd, ' Error CLAMP X2')
            error( 'Error CLAMP X2')
            return
         elseif dX2Pos > dMaxX2 - GEO.EPS_SMALL then
            dX2Pos = dMaxX2
         end
         dX1Pos = dMinX1
         dTPos = dTPosA + ( dX1Pos - dX1PosA)
      else
         --dX1Pos = dMinX1
         --dX1Pos = dX1Pos!
         --dTPos = dTPosA + ( dX1Pos - dX1PosA)
         --dTPos = dTPos!
         dX2Pos = dX2PosA
      end
   end
   -- per aggancio del carrello X1
   if sYV  == 'X1' then
      dTPos = max( dTPosA, dX1PosA + MinJoin + EMC.TCING - EMC.LB)
      dX2Pos = dX2PosA + ( dTPos - dTPosA)
      if dX2Pos > dMaxX2 then
         dX1Pos = dX1PosA - ( dX2Pos - dMaxX2)
         if dX1Pos < dMinX1 - GEO.EPS_SMALL then
            EmitComment( vCmd, ' Error CLAMP X1')
            error( 'Error CLAMP X1')
            return
         elseif dX1Pos < dMinX1 + GEO.EPS_SMALL then
            dX1Pos = dMinX1
         end
         dX2Pos = dMaxX2
         dTPos = dTPosA + ( dX2Pos - dX2PosA)
      else
         -- ++++++++++++++++++++++++++++++++
         --dX2Pos = dMaxX2
         --dX2Pos = dX2Pos! 
         --dTPos = dTPosA + ( dX2Pos - dX2PosA)
         --dTpos = dTPos!!
         dX1Pos = dX1PosA
      end
   end
   return dX1Pos, dX2Pos, dTPos
end

---------------------------------------------------------------------
local function PosXs2Enl (dYa, dVa, dTa, dExtraC, dCorsaTra, dCorsaYd, dCorsaVd, sYV) 
   -- svolge la fase di allontanamento dei carrelli per il recupero di 'ExtraX2'/'ExtraX1' 
   -- richiesto alle posizioni [--.xs2] delle funzioni SpecAdjustCarr..
   if sYV == 'X2' then   -- caso di ExtraX2 (ed eventuale ulteriore allontanamento di X1)
      local dCorsaYTrA = dCorsaTra
      local dExtraX2n = dExtraC - dCorsaYTrA
      -- eseguo allontanamento di Y e (V+T)
      if dExtraX2n / 2 <=  dCorsaYd then
         if dExtraX2n / 2 <= dCorsaVd then
            dYa = dYa + dExtraX2n / 2
            dVa = dVa - dExtraX2n / 2
            dTa = dTa - dExtraX2n / 2
         else
            dVa = dVa - dCorsaVd
            dTa = dTa - dCorsaVd
            dYa = dYa + min( dExtraX2n - dCorsaVd, dCorsaYd)
         end
      else
         dYa = dYa + dCorsaYd
         dVa = dVa - min( dExtraX2n - dCorsaYd, dCorsaVd)
         dTa = dTa - min( dExtraX2n - dCorsaYd, dCorsaVd)
      end
   end

   if sYV  == 'X1' then  -- caso di ExtraX1 (ed eventuale ulteriore allontanamento di X2)
      local dCorsaVTrA = dCorsaTra
      local dExtraX1n = -dExtraC - dCorsaVTrA
      -- eseguo allontanamento di (Y+T) e V
      if dExtraX1n / 2 <=  dCorsaVd then
         if dExtraX1n / 2 <= dCorsaYd then
            dVa = dVa - dExtraX1n / 2
            dYa = dYa + dExtraX1n / 2
            dTa = dTa + dExtraX1n / 2
         else
            dYa = dYa + dCorsaYd
            dTa = dTa + dCorsaYd
            dVa = dVa - min( dExtraX1n - dCorsaYd, dCorsaVd)
         end
      else
         dVa = dVa - dCorsaVd
         dYa = dYa + min( dExtraX1n - dCorsaVd, dCorsaYd)
         dTa = dTa + min( dExtraX1n - dCorsaVd, dCorsaYd)
      end
   end
   return dYa, dVa, dTa
end

---------------------------------------------------------------------
--   *** [A1] da carrello X1 a X1 : X1 -> X1 ***
---------------------------------------------------------------------
function 
SpecAdjustCarrA1( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF)
   EgtOutLog( '  *[A1] = X1 -> X1', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X1 -> X1'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   -- recupero le posizioni correnti dei carrelli 
   local dX1PosA = dTPosI + dYDeltaI
   local dX2PosA = ParkX2
   local dTPosA = dTPosI
   local dYDeltaA = dYDeltaI  
   local dVDeltaA = dX2PosA - dTPosA
   local dNewYDelta -- = dYDeltaF
   local dCorsaYfc = MaxX1 - dX1PosA
   local dCorsaYd = min( dCorsaYfc, EMC.LB - dYDeltaI - MinJoin)

   -- tolleranza
   local dYDeltaTol = GetDeltaTol( EMC.LB - dYDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, 'X1')
   local bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or  dYDeltaF > dYDeltaA + DELTA_SIC)
   
   if bYDeltaS then
      dNewYDelta = dYDeltaF + dYDeltaTol / 4
   else
      dNewYDelta = dYDeltaA
   end
   EgtOutLog( '  YDeltaI=' .. EgtNumToString( dYDeltaI) .. ' TPosI=' .. EgtNumToString( dTPosI), 1)
   EgtOutLog( '  YDeltaF=' .. EgtNumToString( dYDeltaF) .. ' NewYDelta=' .. EgtNumToString( dNewYDelta), 1)  
   -- flag per risalita resta a Zmax
   local bZmaxOk = false

   -- **[A1Ys]** |pos. di Y cambia in modo significativo| 
   if bYDeltaS then
      -- eventuale risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
      -- definisco 'ExtraX1' con (Y+T) e V accentrati q.b. per la presa con V
      local dX1Pos, dX2Pos, dTPos = PosxExtraYV( dX1PosA, dX2PosA, dTPosA, MyMinX1, MaxX2, 'X2')
      local dNewY = dTPos + dNewYDelta
      local dExtraX1 = dNewY - MyMinX1
      -- effettuo spostamenti di (Y+T) e V per predisporre all'aggancio di T con V
      dX1PosA = dX1Pos
      dX2PosA = dX2Pos
      dTPosA = dTPos
      table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
      dVDeltaA = dX2PosA - dTPosA
               
      -- **[A1Ys-x]** posizione di |Y non raggiungibile| (oltre MyMinX1)
      if dExtraX1 < 0 then
         -- **[A1Ys-xs]** posizione di Y non raggiungibile, |con ExtraX1 'significativo'|
         if -dExtraX1 > dYDeltaTol /2 then
            --EmitComment( vCmd, '[A1Ys-xs]')
            -- calcolo le **corse disponibili dei carrelli a partire da Y e V c.s.** per allontanare q.p. (Y+T) e V
            local dCorsaYfc = MaxX1 - dX1Pos
            local dCorsaVfc = dX2Pos - MinX2
            local dDistFrN = (dX2Pos-dTPos) - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
            local dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
            -- valuto **le corse di 'recupero'** dai due carrelli possibili riaccentrando Y e (V+T)
            local dCorsaVTr = dCorsaVd + (MaxX2 - dX2Pos)
            local dCorsaYr = dCorsaYTd
            -- **[A1Ys-xsw]** posizione finale dNewV non raggiungibile, con |dExtraX1 > CorsaVr + CorsaYr|
            if -dExtraX1 > ( dCorsaVTr + dCorsaYr) and bYDeltaS then
               EmitComment( vCmd, '[A1Ys-xsw]' .. 'CASO NON GESTITO')
               return
            end
         
            local dCorsaVTrA = MaxX2 - dX2Pos -- !! att.ne: non dX2PosA !!
            -- **[A1Ys-xs1]** se posso recuperare ExtraX1 semplicem' accentrando Y e (V+T)
            -- (dalle posizione impostate sopra per l'aggancio di V)
            if dCorsaVTrA >= -dExtraX1 then
               EmitComment( vCmd, '[A1Ys-xs1]')              
               -- chiudo V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **1: accentro** Y e (V+T) 
               dX1PosA = MyMinX1     -- (pos. finale)
               dX2PosA = dX2PosA + (-dExtraX1)
               dTPosA = dTPosA + (-dExtraX1)
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               dYDeltaA = dX1PosA - dTPosA  
         
            else -- **[A1Ys-xs2]**
               -- ci sarebbe un doppio movimento di Y ? => caso impossibile ?
               EmitComment( vCmd, '[A1Ys-xs2]')
               -- **1:** posiziono (ulteriormente!) (Y+T) e V
               dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX1, dCorsaVTrA, dCorsaYTd, dCorsaVd, 'X1')
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
               dVDeltaA = dX2PosA - dTPosA
               -- chiudo V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **2: accentro** Y e (V+T) 
               dX1PosA = MyMinX1            -- (pos. finale) 
               dX2PosA = MaxX2
               dTPosA = dX2PosA - dVDeltaA 
               --dVDeltaA = dX2PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
            end  -- [A1Ys-xs1/-xs2]

         -- **[A1Ys-xn]**  |dExtraX1 ancora < 0 ma non 'significativo')|     
         else --if -dExtraX1 < DeltaToll/2
            EmitComment( vCmd, '[A1Ys-xn]')              
            -- chiudo V e apro Y
            table.insert( vCmd, { 12, 1})
            table.insert( vCmd, { 11, 0})
            -- **1:** accentro Y
            dExtraX1 = 0
            dX1PosA = MyMinX1     -- (pos. finale)
            table.insert( vCmd, { 1, 'X1', dX1PosA})
            -- dYDeltaA = dX1PosA - dTPosA
         end  --[A1Ys-xs/xn]

      -- **[A1Ys-r]**  |posizione di Y raggiungibile|  (ExtraX1 >=0) 
      --           (si esclude la possibilità di extra corsa oltre maxX1)
      else
         EmitComment( vCmd, '[A1Ys-r]')
         -- chiudo V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         -- **1:** posiziono Y alla posizione richiesta
            dX1PosA = dNewY
         table.insert( vCmd, { 1, 'X1', dX1PosA})
      end -- [A1Ys]

      -- calcolo il nuovo parametro di aggancio
      dYDeltaA = dX1PosA - dTPosA 
      -- chiudo Y e apro V
      table.insert( vCmd, { 11, CalcCharStatus( 'X1', dYDeltaA)})
      table.insert( vCmd, { 12, 0})
      -- sposto il carrello V in parcheggio
      table.insert( vCmd, { 1, 'X2', ParkX2})
      -- imposto il nuovo parametro di aggancio
      table.insert( vCmd, { 21, dYDeltaA, 0})

      -- reset contatore
      EMC.CNT = nil

   else -- **[A1Yns]** |spostamento| finale richiesto (ev' residuo) di |Y non 'significativo'|
      EmitComment( vCmd, '[A1Yns]')
   end   --[A1Ys/ns]

   EgtOutLog('  YDeltaA =' .. EgtNumToString( dYDeltaA), 1)

   SpecOutputCNT()
   return vCmd
end --SpecAdjustCarrA1

---------------------------------------------------------------------
--   *** [A2] da entrambi a carrello X1 : X1+X2 -> X1 ***
---------------------------------------------------------------------
function SpecAdjustCarrA2( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF)
   EgtOutLog( '  *[A2] = X1+X2 -> X1 ', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X1+X2 -> X1'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   local dCorsaY = MaxX1 - MyMinX1
   local dCorsaV = MaxX2 - MinX2
   -- recupero le posizioni correnti dei carrelli 
   local dX1PosA = dTPosI + dYDeltaI
   local dX2PosA = dTPosI + dVDeltaI
   local dTPosA = dTPosI
   local dYDeltaA = dYDeltaI  
   local dVDeltaA = dVDeltaI 
   local dNewYDelta -- = dYDeltaF
   -- tolleranze
   local dYDeltaTol = GetDeltaTol( EMC.LB - dYDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, 'X1')
   local bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)

   if bYDeltaS then
      dNewYDelta = dYDeltaF + dYDeltaTol / 4
   else
      dNewYDelta = dYDeltaA
   end
   -- definisco 'ExtraX1'
   local dNewY = dTPosA + dNewYDelta
   local dExtraX1 = dNewY - MyMinX1   -- < 0 = nuova pos. di Y 'non raggiungibile' (= oltre MyMinX1)
   -- definisco 'ExtraX1 significativo'
   local bYxs = -dExtraX1 > dYDeltaTol / 2
   
   EgtOutLog( '  YDeltaI=' .. EgtNumToString( dYDeltaI) .. ' TPosI=' .. EgtNumToString( dTPosI) ..
              ' VDeltaI=' .. EgtNumToString( dVDeltaI), 1)
   EgtOutLog( '  YDeltaF=' .. EgtNumToString( dYDeltaF) .. ' NewYDelta=' .. EgtNumToString( dNewYDelta), 1)
   -- flag per risalita resta a Zmax
   local bZmaxOk = false
   -- risalita testa a Zmax (da effettuare comunque, per il parcheggio di V)
   bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
   
   -- calcoli preliminari in caso di spostamento richiesto per Y significativo
   local dCorsaYfc, dCorsaVfc, dDistFrN, dCorsaYTd, dCorsaVd, dCorsaVTr, dCorsaYr, bXsw
   if bYDeltaS  then
      if bYxs then
         -- calcolo le **corse disponibili** dei carrelli **a partire dalle posizioni attuali** (=iniziali)
         -- per allontanare q.p. (Y+T) e V 
         dCorsaYfc = MaxX1 - dX1PosA
         dCorsaVfc = dX2PosA - MinX2
         dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
         dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
         -- valuto **le corse di 'recupero'** dai due carrelli possibili riaccentrando Y e (V+T)
         dCorsaVTr = dCorsaVd + (MaxX2 - dX2PosA)
         dCorsaYr = dCorsaYTd
         bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaYr + dCorsaVTr
      end
   end
            
   -- **[A2Ys-xsw]** posizione NewY non raggiungibile (oltre MyMinX1), con ||dExtraX1 | > CorsaYr + CorsaVTr|
   while bXsw do
      EmitComment( vCmd, '[A2Ys-xsw]')
      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
      -- chiudo eventualmente il carrello Y e apro V
      table.insert( vCmd, { 11, 1})
      table.insert( vCmd, { 12, 0})
         -- **1:** allontano q.p. (Y+T) e V  
      dX1PosA = dX1PosA + dCorsaYTd  
      dTPosA = dTPosA + dCorsaYTd    
      dX2PosA = dX2PosA - dCorsaVd
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      -- chiudo il carrello V e apro Y
      table.insert( vCmd, { 12, 1})
      table.insert( vCmd, { 11, 0})
         -- **2:** accentro Y e (V+T)
      dX1PosA = MyMinX1    
      dX2PosA = dX2PosA + dCorsaVTr
      dTPosA = dTPosA + dCorsaVTr
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      -- valuto Delta attuali
      dYDeltaA  = dX1PosA - dTPosA  
      dVDeltaA  = dX2PosA - dTPosA  
      -- aggiorno la verifica di spostamento significativo
      bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
      -- aggiorno l'extra corsa residua per Y
      dExtraX1 = dExtraX1 + dCorsaYr + dCorsaVTr
      bYxs = -dExtraX1 > dYDeltaTol/2
      -- **aggiorno la valutazione delle corse disponibili**
      -- a partire da Y e V c.s. per allontanare q.p. (Y+T) e V
      dCorsaYfc = MaxX1 - dX1PosA
      dCorsaVfc = dX2PosA - MinX2
      dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
      dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
      -- valuto le corse di 'recupero' dai due carrelli possibili riaccentrando Y e (V+T)
      dCorsaVTr = dCorsaVd
      dCorsaYr = dCorsaYTd
      -- aggiorno verifica per ripetizione del ciclo
      bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaVTr + dCorsaYr
   end -- [A2Ys-xw]
      
   -- **[A2Ys]** se lo |spostamento| richiesto (ev' residuo) per |Y| è |'significativo'|
   if bYDeltaS then
      -- **[A2Ys-x]** se la |posizione per Y non è 'raggiungibile'|
      if dExtraX1 < 0 then
         -- **[A2Ys-xs]** se |ExtraX1 'significativo'|
         if bYxs then   
            local dCorsaVTrA = MaxX2 - dX2PosA
            -- **[A2Ys-xs1]** se posso recuperare ExtraX1 solo accentrando (V+T) e Y 
            if dCorsaVTrA >= -dExtraX1 then
               EmitComment( vCmd, '[A2Ys-xs1]')
               -- chiudo ev' V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **1:** accentro (V+T) q.b. e Y
               dX2PosA = dX2PosA + (-dExtraX1)
               dTPosA = dTPosA + (-dExtraX1)
               dX1PosA = MyMinX1   -- (pos. finale)
               --dYDeltaA = dX1PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})

            else -- **[A2Ys-xs2]**    
               EmitComment( vCmd, '[A2Ys-xs2]')
               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **1:** allontano (Y+T) e V
               dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX1, dCorsaVTrA, dCorsaYTd, dCorsaVd, 'X1')
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
               dVDeltaA = dX2PosA - dTPosA
               -- chiudo V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})  
               -- **2:** accentro Y e (V+T) q.b. per recupero di dExtraX1
               dX1PosA = MyMinX1
               dX2PosA = MaxX2
               dTPosA = dX2PosA - dVDeltaA
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
            end --[A2Ys-xs1/-xs2]
            
         -- [A2Ys-xn] |ExtraX1  ancora <= 0, ma 'non significativo'|   
         else  --if dExtraX1 <= 0 then 
            EmitComment( vCmd, '[A2Ys-xn]')
            dExtraX1 = 0
            -- chiudo ev' V e apro Y
            table.insert( vCmd, { 12, 1})
            table.insert( vCmd, { 11, 0})
            -- 1: accentro Y 
            dX1PosA = MyMinX1   -- (pos. finale)
            table.insert( vCmd, { 1, 'X1', dX1PosA})
         end   -- [A2Ys-xs/xn]

      --[A2Ys-r]  dExtraX2 > 0 ( |pos. Y'raggiungibile|) 
      else 
         EmitComment( vCmd, '[A2Ys-r]')
         -- chiudo ev' V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         -- posizione Y
         dX1PosA = dTPosA + dNewYDelta     -- (pos. finale)
         table.insert( vCmd, { 1, 'X1', dX1PosA})
      end  -- [A2Ys-x/r]

   else  -- [A2Yns] |spostamento Y| (ev' residuo) |non significativo|
      EmitComment( vCmd, '[A2Yns]')
   end -- [A2Ys/ns]

   -- calcolo i nuovi parametri di aggancio
   dYDeltaA = dX1PosA - dTPosA  
   -- chiudo ev' Y e apro V
   table.insert( vCmd, { 11, CalcCharStatus( 'X1', dYDeltaA)})
   table.insert( vCmd, { 12, 0})
   -- sposto il carrello V in parcheggio
   table.insert( vCmd, { 1, 'X2', ParkX2})
   -- imposto i nuovi parametri di aggancio
   table.insert( vCmd, { 21, dYDeltaA, 0})

   EgtOutLog( '  YDeltaA =' .. EgtNumToString( dYDeltaA), 1)
   -- reset contatore
   EMC.CNT = nil

   SpecOutputCNT()
   return vCmd
end --SpecAdjustCarrA2

---------------------------------------------------------------------
--   *** [A3] da carrello X2 a X1 :  X2 -> X1 ***
---------------------------------------------------------------------
function SpecAdjustCarrA3( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF)
   EgtOutLog( '  *[A3] = X2 -> X1', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X2 -> X1'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   -- recupero le posizioni correnti
   local dX1PosA = ParkX1
   local dTPosA = dTPosI
   local dX2PosA = dTPosI + dVDeltaI
   local dYDeltaA = dX1PosA - dTPosA
   local dVDeltaA = dVDeltaI
   local dNewYDelta -- = dVDeltaF
   -- tolleranza
   local dYDeltaTol = GetDeltaTol( EMC.LB - dYDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, 'X1') 
   local bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
   -- reimposto i delta finali in caso di spostamenti richiesti significativi, tenuto conto delle tolleranze
   if bYDeltaS then 
      dNewYDelta = dYDeltaF + dYDeltaTol / 4
   else
      dNewYDelta = dYDeltaA
   end
   --
   EgtOutLog('  VDeltaI=' .. EgtNumToString( dVDeltaA) .. ' YDeltaI(Park)='.. EgtNumToString( dYDeltaA)..
             ' TPosI=' .. EgtNumToString( dTPosI)  , 1)
   EgtOutLog('  YDeltaF=' .. EgtNumToString( dYDeltaF) .. ' NewYDelta=' .. EgtNumToString( dNewYDelta) , 1)
   local bZmaxOk = false
   -- risalita testa a Zmax (da effettuare comunque, dato lo scambio di carrelli)
   bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
   
   -- calcoli preliminari in caso di spostamento richiesto per Y significativo
   local dX1Pos, dX2Pos, dTPos
   local dNewY, dExtraX1, bYxs
   local dCorsaYfc, dCorsaVfc, dDistFrN, dCorsaYTd, dCorsaVd, dCorsaVTr, dCorsaYr, bXsw
   if bYDeltaS then
      -- definisco 'ExtraX1' con Y e (V+T) accentrati q.b. per la presa con Y
      dX1Pos, dX2Pos, dTPos = PosxExtraYV( dX1PosA, dX2PosA, dTPosA, MyMinX1, MaxX2, 'X1')
      dNewY = dTPos + dNewYDelta
      dExtraX1 = dNewY - MyMinX1  --( <0 <=> pos. 'non raggiungibile')
      bYxs = -dExtraX1 > dYDeltaTol/2
      if bYxs then
         -- calcolo le **corse disponibili dei carrelli a partire da Y e V c.s.** per allontanare q.p. (Y+T) e V
         dCorsaYfc = MaxX1 - dX1Pos
         dCorsaVfc = dX2Pos - MinX2
         --local dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
         dDistFrN = (dX2Pos-dTPos) - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
         dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
         -- valuto **le corse di 'recupero'** dai due carrelli possibili riaccentrando Y e (V+T)
         dCorsaVTr = dCorsaVd + (MaxX2 - dX2Pos)
         dCorsaYr = dCorsaYTd
         bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaVTr + dCorsaYr
      end
      -- inizializzo
      dX1PosA = dX1Pos
      dX2PosA = dX2Pos
      dTPosA = dTPos
   end
   
   -- **[A3Ys-xsw]** posizione finale dNewY non raggiungibile, con |dExtraX1 > CorsaVr + CorsaYr|
   while bXsw do
      EmitComment( vCmd, '[A3Ys-xsw]')
      -- |1:| posiziono (V+T) e Y come calcolato sopra
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})  
      
      -- chiudo ev' il carrello Y e apro V
      table.insert( vCmd, { 11, 1})
      table.insert( vCmd, { 12, 0})
      -- |2:| allontano (Y+T) e V quanto possibile
      dX1PosA = dX1PosA + dCorsaYTd
      dTPosA = dTPosA + dCorsaYTd
      dX2PosA = dX2PosA - dCorsaVd
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- chiudo il carrello V e apro Y
      table.insert( vCmd, { 12, 1})
      table.insert( vCmd, { 11, 0})
      -- |3:| accentro (V+T) e Y
      dX2PosA = dX2PosA + dCorsaVTr 
      dTPosA = dTPosA + dCorsaVTr 
      dX1PosA = MyMinX1
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- valuto i Delta ottenuti
      dYDeltaA = dX1PosA - dTPosA  
      dVDeltaA = dX2PosA - dTPosA
      -- aggiorno la verifica di spostamento significativo
      bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
      -- aggiorno ExtraX1
      dExtraX1 = dExtraX1 + dCorsaYr + dCorsaVTr
      -- **aggiorno la valutazione delle corse disponibili**
      -- a partire da Y e V c.s. per allontanare q.p. (Y+T) e V
      dCorsaYfc = MaxX1 - dX1PosA
      dCorsaVfc = dX2PosA - MinX2
      dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
      dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
      -- valuto le corse di 'recupero' dai due carrelli possibili riaccentrando Y e (V+T)
      dCorsaVTr = dCorsaVd
      dCorsaYr = dCorsaYTd
      -- aggiorno verifica per ripetizione del ciclo
      bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaVTr + dCorsaYr
   end --[A3Ys-xw]
   
   -- **[A3Ys]** |spostamento| richiesto (ev' residuo) |di Y| |'significativo'|
   if bYDeltaS then
      EmitComment( vCmd, '[A3Ys]')
      -- **[A3Ys-x]** posizione  di |Y non raggiungibile|  
      if dExtraX1 < 0 then

         -- **[A3Ys-xs]**  |pos. NewY non raggiungibile, con ExtraX1 'significativo'|
         --          ( -dExtraX1 <= (CorsaYr+CorsaVr) da ciclo precedente )
         if bYxs  then
            local dCorsaVTrA = MaxX2 - dX2PosA
            -- **[A3Ys-xs1]** se posso recuperare ExtraX1 semplicem' accentrando Y e (V+T)
            --           ulteriormente rispetto a YPos e Vpos definiti c.s.
            if dCorsaVTrA >= -dExtraX1 then
               EmitComment( vCmd, '[A3Ys-xs1]')
               -- chiudo ev' V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **1: accentro** Y e (V+T) 
               dX1PosA = MyMinX1     -- (pos. finale)
               dX2PosA = dX2PosA - dExtraX1
               dTPosA = dTPosA - dExtraX1
               dYDeltaA = dX1PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               
            else -- **[A3Ys-xs2]**
               EmitComment( vCmd, '[A3Ys-xs2]')
               
               -- chiudo ev' V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **1: accentro** ev' Y e (V+T) alle posizioni impostate
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               dYDeltaA = dX1PosA - dTPosA
               
               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **2: allontano** (Y+T) e V
               dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX1, dCorsaVTrA, dCorsaYTd, dCorsaVd, 'X1')
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
               dVDeltaA = dX2PosA - dTPosA
               
               -- chiudo il carrello V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **3: accentro** Y e (V+T) 
               dX1PosA = MyMinX1            -- (pos. finale) 
               dX2PosA = MaxX2    
               dTPosA = dX2PosA - dVDeltaA 
               --dYDeltaA = dX1PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
            end  -- [A3Ys-xs1/-xs2]
                  
         -- **[A3Ys-xn]**  |dExtraX1 ancora < 0 ma non 'significativo')|     
         else --if -dExtraX1 < DeltaToll/2
            EmitComment( vCmd, '[A3Ys-xn]')
            -- ev' chiudo V e apro Y
            table.insert( vCmd, { 12, 1})
            table.insert( vCmd, { 11, 0})
            -- **1:** accentro Y e porto (V+T) alla posizione impostata
            dExtraX1 = 0
            dX1PosA = MyMinX1     -- (pos. finale)
            table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         end  --[A3Ys-xs/xn]
         
      -- **[A3Ys-r]**  |posizione di Y raggiungibile (ExtraX1 >=0)|     
      else
         EmitComment( vCmd, '[A3Ys-r]')
         -- chiudo ev' V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         -- posizione Y in posizione finale ed ev' (V+T) come impostato sopra
         dX1PosA = dTPosA + dNewYDelta                 
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      end -- [A3Ys]
            
   else -- **[A3Yns]** |spostamento| finale richiesto (ev' residuo) di |Y non 'significativo'|
      EmitComment( vCmd, '[A3Yns]')
   end   --[A3Ys/ns]

   -- calcolo il nuovo parametro di aggancio
   dYDeltaA = dX1PosA - dTPosA 
   -- chiudo ev' Y e apro V
   table.insert( vCmd, { 11, CalcCharStatus( 'X1', dYDeltaA)})
   table.insert( vCmd, { 12, 0})
   -- sposto il carrello V in parcheggio
   table.insert( vCmd, { 1, 'X2', ParkX2})
   -- imposto il nuovo parametro di aggancio
   table.insert( vCmd, { 21, dYDeltaA, 0})

   EgtOutLog( '  YDeltaA =' .. EgtNumToString( dYDeltaA), 1)
   -- reset contatore
   EMC.CNT = nil

   SpecOutputCNT()
   return vCmd
end --SpecAdjustCarrA3

---------------------------------------------------------------------
local function AdjustPositionsForB( dNewYDelta, dNewVDelta, TCING, HCING, HOVM, bFixedDelta)
   local REF_DIST = 1400
   local dYDelta = 0
   local dVDelta = 0
   -- incremento se possibile la distanza tra le due posizioni
   if not bFixedDelta and ( dNewYDelta - dNewVDelta) < REF_DIST then
      local dEffAddDist = ( REF_DIST - ( dNewYDelta - dNewVDelta)) / 2
      dYDelta = max( min( dEffAddDist, EMC.LB - dNewYDelta - MinJoin - TCING), 0)
      dVDelta = max( min( dEffAddDist, dNewVDelta - MinJoin - HOVM - HCING), 0)
   end
   return dYDelta, dVDelta
end

---------------------------------------------------------------------
--   *** [B1]  da carrello X1 a entrambi : X1 -> X1+X2 ***
---------------------------------------------------------------------
function SpecAdjustCarrB1( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
   EgtOutLog( '  *[B1] = X1 -> X1+X2', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X1 -> X1+X2'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   local dCorsaY = MaxX1 - MyMinX1
   local dCorsaV = MaxX2 - MinX2
   -- recupero le posizioni correnti 
   local dX1PosA = dTPosI + dYDeltaI
   local dX2PosA = ParkX2
   local dTPosA = dTPosI
   local dYDeltaA = dYDeltaI
   local dVDeltaA = dX2PosA - dTPosA
   local dNewYDelta
   local dNewVDelta
   -- incremento la distanza tra le due posizioni ( se abilitato e possibile)
   local dYDeltaAgg, dVDeltaAgg = AdjustPositionsForB( dYDeltaF, dVDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, bFixedDelta)
   -- tolleranze
   local dYDeltaTol = dYDeltaAgg + GetDeltaTol( EMC.LB - dYDeltaF - dYDeltaAgg, EMC.TCING, EMC.HCING, EMC.HOVM, 'X1', bFixedDelta) 
   local dVDeltaTol = dVDeltaAgg + GetDeltaTol( dVDeltaF - dVDeltaAgg, EMC.TCING, EMC.HCING, EMC.HOVM, 'X2', bFixedDelta) 
   -- definisco criteri per movimenti 'significativi' in base alle 'nuove' tolleranze
   local bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
   local bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
   -- reimposto i delta finali in caso di spostamenti richiesti significativi, tenuto conto delle tolleranze
   if bYDeltaS then 
      dNewYDelta = dYDeltaF + dYDeltaTol / 2
   else
      dNewYDelta = dYDeltaA
   end
   if bVDeltaS then
      dNewVDelta = dVDeltaF - dVDeltaTol / 2
   else
      dNewVDelta = dVDeltaA
   end

   EgtOutLog('  YDeltaI=' .. EgtNumToString( dYDeltaA) .. ' VDeltaI(Park)='.. EgtNumToString( dVDeltaA) ..
             ' TPosI='.. EgtNumToString( dTPosI), 1)
   EgtOutLog('  YDeltaF=' .. EgtNumToString( dYDeltaF) .. ' VDeltaF=' .. EgtNumToString( dVDeltaF), 1)
   EgtOutLog('  NewYDelta=' .. EgtNumToString( dNewYDelta) .. ' NewVDelta=' .. EgtNumToString( dNewVDelta), 1)

   -- risalita testa a Zmax
   local bZmaxOk = false

   -- calcoli preliminari in caso di spostamento richiesto per V significativo
   local dX1Pos, dX2Pos, dTPos
   local dNewV, dExtraX2, bVxs
   local dCorsaYfc, dCorsaVfc, dDistBkN, dCorsaYd, dCorsaVTd, dCorsaYTr, dCorsaVr, bXsw
   if bVDeltaS then
      -- definisco 'ExtraX2' con (Y+T) e V accentrati q.b. per la presa di V
      dX1Pos, dX2Pos, dTPos = PosxExtraYV( dX1PosA, dX2PosA, dTPosA, MyMinX1, MaxX2, 'X2')
      dNewV = dTPos + dNewVDelta
      dExtraX2 = dNewV - MaxX2     -- se > 0 pos. V non direttamente 'raggiungibile'
      bVxs = dExtraX2 > dVDeltaTol/2 -- ExtraX2 'significativo'
      if bVxs then
         -- calcolo le **corse disponibili** dei carrelli a partire da Y e V c.s.| 
         -- per allontanare q.p. Y e (V+T)
         dCorsaYfc = MaxX1 - dX1Pos
         dCorsaVfc = dX2Pos - MinX2
         dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
         dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
         -- valuto le **corse di 'recupero'** dai due carrelli possibili riaccentrando (Y+T) e V
         dCorsaYTr = dCorsaYd + (dX1Pos - MyMinX1)
         dCorsaVr = dCorsaVTd
         bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaVr + dCorsaYTr
      end
      -- inizializzo
      dX1PosA = dX1Pos
      dX2PosA = dX2Pos
      dTPosA = dTPos
   end
   
   -- **[B1Vs-xsw]** posizione finale dNewV non raggiungibile, con |dExtraX2 > CorsaVr + CorsaYr|
   while  bXsw do
      EmitComment( vCmd, '[B1Vs-xsw]')
      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)

      -- |1:| imposto (Y+T) e V come calcolato sopra
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA}) 

      -- chiudo ev' V e apro Y
      table.insert( vCmd, { 12, 1})
      table.insert( vCmd, { 11, 0})
      -- |2:| allontano Y e (V+T)  quanto possibile
      dX1PosA = dX1PosA + dCorsaYd
      dX2PosA = dX2PosA - dCorsaVTd
      dTPosA = dTPosA - dCorsaVTd
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- chiudo il carrello Y e apro V
      table.insert( vCmd, { 11, 1})
      table.insert( vCmd, { 12, 0})
      -- |3:| accentro (Y+T) e V
      dX1PosA = dX1PosA - dCorsaYTr 
      dTPosA = dTPosA - dCorsaYTr 
      dX2PosA = MaxX2
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- valuto i Delta ottenuti
      dYDeltaA = dX1PosA - dTPosA  
      dVDeltaA = dX2PosA - dTPosA
      -- aggiorno la verifica di spostamento significativo
      bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
      bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
      -- aggiorno l'extra corsa residuo per V
      dExtraX2 = dExtraX2 - dCorsaYTr - dCorsaVr
   
      -- **aggiorno la valutazione delle corse disponibili**
      -- a partire da Y e V c.s. per allontanare q.p. Y e (V+T)
      dCorsaYfc = MaxX1 - dX1PosA
      dCorsaVfc = dX2PosA - MinX2
      dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
      dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
      -- aggiorno i prossimi recuperi disponibili
      dCorsaYTr = dCorsaYd
      dCorsaVr = dCorsaVTd
      -- aggiorno verifica per ripetizione del ciclo
      bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaVr + dCorsaYTr
   end --[B1Vs-xw]
   
   -- **[B1Vs]** se lo |spostamento| finale richiesto (ev' residuo) di |V| è |'significativo'|
   if bVDeltaS then
      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
      -- **[B1Vs-x]** posizione  di |V non raggiungibile|
      if dExtraX2 > 0 then
         -- **[B1Vs-xs]** |ExtraX2 'signfificativo'|
         -- (ExtraX2 <= (CorsaYr+CorsaVr) da ciclo precedente )
         if bVxs then
            local dCorsaYTrA = dX1PosA - MyMinX1
            local dCorsaVra = MaxX2 - dX2PosA
            -- **[B1Vs-xs1]** se posso recuperare ExtraX2 semplicem' accentrando V e (Y+T)
            --            ulteriormente rispetto a YPos e Vpos definiti c.s.
            if dCorsaYTrA >= dExtraX2 then
               EmitComment( vCmd, '[B1Vs-xs1]')
               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **1: accentro** (Y+T) e V 
               dX1PosA = dX1PosA - dExtraX2
               dTPosA = dTPosA - dExtraX2
               dX2PosA = MaxX2     -- (pos. finale)
               dVDeltaA = dX2PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
                  
            else -- **[B1Vs-xs2]**
               EmitComment( vCmd, '[B1Vs-xs2]')
               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **1: accentro** ev' (Y+T) e V alle posizioni impostate
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               dVDeltaA = dX2PosA - dTPosA
               
               -- chiudo ev' V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **2: allontano** (V+T) e Y 
               -- tenendo conto di dover 'recuperare' ExtraX2.... 
               local dX1Pos1, dX2Pos1, dTPos1 = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX2, dCorsaYTrA, dCorsaYd, dCorsaVTd, 'X2')
               local dYDispl1 = dX1Pos1 - dX1PosA
               local dVDispl1 = dX2Pos1 - dX2PosA
               --local dTDispl1 = dTPos1 - dTPosA
               local dYDeltaDiff = dNewYDelta - (dX1Pos1 - dTPos1)
               -- ...e anche di posizionare possibilmente Y alla posizione finale
               if dYDeltaDiff > 0 then
                  dCorsaYfc = MaxX1 - dX1Pos1
                  dCorsaVfc = dX2Pos1 - MinX2
                  dDistBkN = EMC.LB - (dX1Pos1-dTPos1) - MinJoin - EMC.TCING    -- DistBack1 'netta'
                  dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
                  dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1Pos1, dX2Pos1, dTPos1, dYDeltaDiff, 0, dCorsaYd, dCorsaVTd, 'X2')
               else
                  dX1PosA = dX1Pos1;  dX2PosA = dX2Pos1;  dTPosA = dTPos1
               end
               
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })

               -- chiudo il carrello Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **3: accentro** (Y+T)  e V 
               dX1PosA = dX1PosA - dYDispl1 - dCorsaYTrA                -- POS. FINALE
               dTPosA = dTPosA - dYDispl1 - dCorsaYTrA
               dX2PosA = dX2PosA - dVDispl1 + dCorsaVra                 -- POS. FINALE
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
            end  -- [B1Vs-xs1/-xs2]

         -- **[B1Vs-xn]**  |dExtraX2 ancora > 0 ma non 'significativo')|     
         else --if dExtraX2 < DeltaToll/2
            EmitComment( vCmd, '[B1Vs-xn]')
            -- ev' chiudo Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0})
            -- 1: accentro (Y+T) alla posizione impostate e V al max
            dExtraX2 = 0 
            dX2PosA = MaxX2     -- (pos. finale)
            table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         end  -- [B1s-xs/xn]

      -- **[B1Vs-r]**  |dExtraX2 < 0 (pos. V raggiungibile)|     
      else
         EmitComment( vCmd, '[B1Vs-r]')
         -- chiudo ev' Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- posiziono ev' (Y+T) come impostato sopra e V in posizione finale
         dX2PosA = dTPosA + dNewVDelta      
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      end -- [B1Vs]

      dYDeltaA = dX1PosA - dTPosA  
      dVDeltaA = dX2PosA - dTPosA

   else -- **[B1Vns]** |spostamento| finale richiesto (ev' residuo) di |V non 'significativo'|
      EmitComment( vCmd, '[B1Vns]')
      if SpecTestOnlyRemarkInCmds( vCmd) then
         -- risalita testa a Zmax
         bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
         -- emetto posizione di V
         table.insert( vCmd, { 1, 'X2', dX2PosA})
      end
   end   -- [B1-s/ns]
      
   -- aggiorno la verifica di movimento 'significativo' per Y
   bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)     
   -- **[B1Ys]**  Se vi è uno |spostamento residuo di Y significativo'|
   if bYDeltaS then
      EmitComment( vCmd, '[B1Ys]')
      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)

      local dNewY = dTPosA + dNewYDelta
      local dExtraX1 = dNewY - MyMinX1
      -- **[B1Ys-r]** se pos. NewY è raggiungibile direttamente
      if dExtraX1 >= 0 then --dNewY > MyMinX1 then
         EmitComment( vCmd, '[B1Ys-r]')
         -- chiudo ev' V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         -- sposto il carrello Y
         dX1PosA = dTPosA + dNewYDelta
         table.insert( vCmd, { 1, 'X1', dX1PosA})

      -- **[B1Ys-xn]** se pos. NewY non è raggiungibile direttamente, ma ExtraX1 non è 'significativo'
      elseif -dExtraX1 <= dYDeltaTol / 2 then
         EmitComment( vCmd, '[B1Ys-xn]')
         -- chiudo ev' V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         -- accentro Y
         dX1PosA = MyMinX1
         table.insert( vCmd, { 1, 'X1', dX1PosA})
      -- [B1sY-nr.xs] ma ExtraX1 'significativo'  

      else -- **[B1Ys-xs]** 
         EmitComment( vCmd, '[B1Ys-xs]')
         EgtOutLog( '  CLAMP : caso [B1Ys-xs] non gestito')
         error( '  CLAMP : caso [B1Ys-xs] non gestito')  
            
      end --[B1Ys-r/-nr]
   else -- [B1Yns]  spostamento residuo di Y non significativo
      EmitComment( vCmd, '[B1Yns]')
   end -- [B1Ys/ns]

   -- calcolo i nuovi parametri di aggancio
   dYDeltaA = dX1PosA - dTPosA
   dVDeltaA = dX2PosA - dTPosA
   -- se non emessi movimenti, imposto posizione V
   if not bZmaxOk then
      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
      table.insert( vCmd, { 1, 'X2', dX2PosA})
   end
   -- imposto stato carrelli, per eventuale uso pressori (sempre eseguita risalita Z)
   table.insert( vCmd, { 11, CalcCharStatus( 'X1', dYDeltaA)})
   table.insert( vCmd, { 12, CalcCharStatus( 'X2', dVDeltaA)})
   -- imposto i nuovi parametri di aggancio
   table.insert( vCmd, { 21, dYDeltaA, dVDeltaA})

   EgtOutLog( '  YDeltaA =' .. EgtNumToString( dYDeltaA) .. ' VDeltaA =' .. EgtNumToString( dVDeltaA), 1)
   -- reset contatore
   EMC.CNT = nil

   SpecOutputCNT()
   return vCmd
end   -- SpecAdjustCarrB1

---------------------------------------------------------------------
--   *** [B2]  da entrambi a entrambi i carrelli : X1+X2 -> X1+X2 ***
---------------------------------------------------------------------
function SpecAdjustCarrB2( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
   EgtOutLog( '  *[B2] = X1+X2 -> X1+X2', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X1+X2 -> X1+X2'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   -- recupero le posizioni correnti 
   local dX1PosA = dTPosI + dYDeltaI
   local dX2PosA = dTPosI + dVDeltaI
   local dTPosA = dTPosI
   local dYDeltaA = dYDeltaI
   local dVDeltaA = dVDeltaI
   local dNewYDelta
   local dNewVDelta
   -- incremento la distanza tra le due posizioni ( se abilitato e possibile)
   local dYDeltaAgg, dVDeltaAgg = AdjustPositionsForB( dYDeltaF, dVDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, bFixedDelta)
   -- tolleranze
   local dYDeltaTol = dYDeltaAgg + GetDeltaTol( EMC.LB - dYDeltaF - dYDeltaAgg, EMC.TCING, EMC.HCING, EMC.HOVM, 'X1', bFixedDelta)
   local dVDeltaTol = dVDeltaAgg + GetDeltaTol( dVDeltaF - dVDeltaAgg, EMC.TCING, EMC.HCING, EMC.HOVM, 'X2', bFixedDelta)
   -- definisco criteri per movimenti 'significativi' in base alle 'nuove' tolleranze
   local bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
   local bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
   -- reimposto i delta finali in caso di spostamenti richiesti significativi, tenuto conto delle tolleranze
   if bYDeltaS then 
      dNewYDelta = dYDeltaF + dYDeltaTol / 2
   else
      dNewYDelta = dYDeltaA
   end
   if bVDeltaS then
      dNewVDelta = dVDeltaF - dVDeltaTol / 2
   else
      dNewVDelta = dVDeltaA
   end
   -- definisco 'ExtraX1' e ExtraX2' 
   local dNewY = dTPosA + dNewYDelta
   local dNewV = dTPosA + dNewVDelta 
   local dExtraX1 = dNewY - MyMinX1  -- < 0 => nuova pos. di Y 'non raggiungibile'
   local dExtraX2 = dNewV - MaxX2   -- > 0 => nuova pos. di V 'non raggiungibile'
   local bYxs = -dExtraX1 > dYDeltaTol/2
   local bVxs = dExtraX2 > dVDeltaTol/2
      
   EgtOutLog( '  YDeltaI=' .. EgtNumToString( dYDeltaI) .. ' VDeltaI=' .. EgtNumToString( dVDeltaI) .. 
              ' TPosI='.. EgtNumToString( dTPosI), 1)
   EgtOutLog( '  YDeltaF=' .. EgtNumToString( dYDeltaF) .. ' VDeltaF=' .. EgtNumToString( dVDeltaF), 1)
   EgtOutLog( '  NewYDelta=' .. EgtNumToString( dNewYDelta) .. ' NewVDelta=' .. EgtNumToString( dNewVDelta), 1)
   -- flag risalita a Zmax
   local bZmaxOk = false
   
   -- **[B2V]**  |accentramento di V| 
   if dVDeltaF > dVDeltaI then
      EmitComment( vCmd, '[B2V]')
      
      -- calcoli preliminari in caso di spostamento richiesto per V significativo
      local dCorsaYfc, dCorsaVfc, dDistBkN, dCorsaYd, dCorsaVTd, dCorsaYTr, dCorsaVr, bXsw
      if bVDeltaS then
         -- eventuale risalita testa a Zmax
         bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
         if bVxs then
            -- calcolo le corse disponibili dei carrelli **dalle posizioni iniziali**
            -- per allontanare Y e (V+T)  
            dCorsaYfc = MaxX1 - dX1PosA
            dCorsaVfc = dX2PosA - MinX2
            dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
            dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
            -- calcolo le corse di 'recupero' accentrando (Y+T) e V dopo l'allontanamento 
            dCorsaYTr = dCorsaYd + (dX1PosA - MyMinX1)
            dCorsaVr = dCorsaVTd -- ((MaxX2-dNewV) non fa parte del recupero in quanto già disponibile!)
            bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaYTr + dCorsaVr
         end
      end
         
      -- **[B2V-xsw]** posizione NewV non raggiungibile, con |dExtraX2 > CorsaVr + CorsaYTr|
      --         e spostamento richiesto di V significativo; 
      while bXsw do
         EmitComment( vCmd, '[B2V-xsw]')
         
         -- chiudo ev' V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         -- **1**: allontano Y e V(+T) quanto possibile 
         dX1PosA = dX1PosA + dCorsaYd
         dX2PosA = dX2PosA - dCorsaVTd 
         dTPosA = dTPosA - dCorsaVTd
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         
         -- chiudo il carrello Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- **2:** accentro (Y+T) e V
         dX1PosA = dX1PosA - dCorsaYTr   -- = MyMinX1    
         dTPosA = dTPosA - dCorsaYTr      
         dX2PosA = MaxX2
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         
         -- valuto Delta ottenuti
         dYDeltaA  = dX1PosA - dTPosA 
         dVDeltaA  = dX2PosA - dTPosA 
         -- aggiorno la verifica di spostamento significativo
         bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or  dYDeltaF > dYDeltaA + DELTA_SIC)
         bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol  or  dVDeltaF < dVDeltaA - DELTA_SIC)
         -- aggiorno ExtraX2 residuo
         dExtraX2 = dExtraX2 - dCorsaYTr - dCorsaVr
         -- **aggiorno i recuperi disponibili** riallontanando Y e (V+T) e riaccentrando (Y+T) e V
         dCorsaYfc = dCorsaY   -- MaxX1 - dNewY
         dCorsaVfc = dCorsaV   -- dNewV - MinX2
         dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
         dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
         dCorsaYTr = dCorsaYd
         dCorsaVr = dCorsaVTd
         -- aggiorno verifica per ripetizione del ciclo
         bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaYTr + dCorsaVr
      end -- [B2V-xsw]
               
               
      --[B2Vs] |accentramento| (ev' residuo) |di V 'significativo'|
      if bVDeltaS then
         --[B2Vs-x] accentramento di V 'significativo' |con pos. NewV 'non raggiungibile'|(= oltre MaxX2)
         if  dExtraX2 >  0  then
             -- (ExtraX2 <= (CorsaYTr+CorsaVr) da ciclo precedente)
            --[B2Vs-xs] accentramento di V con |ExtraX2 'significativo'|
            if bVxs then
               local dCorsaYTrA = dX1PosA - MyMinX1
               local dCorsaVra = MaxX2 - dX2PosA
               -- **[B2Vs-xs1]** se posso recuperare ExtraX2 solo accentrando V e (Y+T)
               if dCorsaYTrA >= dExtraX2 then
                  EmitComment( vCmd, '[B2Vs-xs1]')
                  -- chiudo ev' Y e apro V
                  table.insert( vCmd, { 11, 1})
                  table.insert( vCmd, { 12, 0})
                  -- **1:** accentro (Y+T) q.b. e V 
                  dX1PosA = dX1PosA - dExtraX2
                  dTPosA = dTPosA - dExtraX2
                  dX2PosA = MaxX2     -- (pos. finale)
                  dExtraX2 = 0
                  table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               
               else -- **[B2Vs-xs2]**   
                  EmitComment( vCmd, '[B2Vs-xs2]')
                  -- chiudo ev' V e apro Y
                  table.insert( vCmd, { 12, 1})
                  table.insert( vCmd, { 11, 0})
                  -- **1: allontano** (V+T) e Y 
                  -- tenendo conto di dover 'recuperare' ExtraX2... 
                  local dX1Pos1, dX2Pos1, dTPos1 = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX2, dCorsaYTrA, dCorsaYd, dCorsaVTd, 'X2')
                  local dYDispl1 = dX1Pos1 - dX1PosA
                  local dVDispl1 = dX2Pos1 - dX2PosA
                  --local dTDispl1 = dTPos1 - dTPosA
                  local dYDeltaDiff = dNewYDelta - (dX1Pos1 - dTPos1)
                  -- ...e anche possibilmente di posizionare Y alla posizione finale
                  if dYDeltaDiff > 0 then
                     dCorsaYfc = MaxX1 - dX1Pos1
                     dCorsaVfc = dX2Pos1 - MinX2
                     dDistBkN = EMC.LB - (dX1Pos1-dTPos1) - MinJoin - EMC.TCING    -- DistBack1 'netta'
                     dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
                     --dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1Pos1, dX2Pos1, dTPos1, dYDeltaDiff, 0, dCorsaYd-dYDispl1, dCorsaVTd+dVDispl1, 'X2')
                     dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1Pos1, dX2Pos1, dTPos1, dYDeltaDiff, 0, dCorsaYd, dCorsaVTd, 'X2')
                  else
                     dX1PosA = dX1Pos1;  dX2PosA = dX2Pos1;  dTPosA = dTPos1
                  end
                  table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
                  -- chiudo il carrello Y e apro V
                  table.insert( vCmd, { 11, 1})
                  table.insert( vCmd, { 12, 0})
                  -- **2: accentro** (Y+T) e V 
                  dX1PosA = dX1PosA - dYDispl1 - dCorsaYTrA                -- POS. FINALE
                  dTPosA = dTPosA - dYDispl1 - dCorsaYTrA
                  dX2PosA = dX2PosA - dVDispl1 + dCorsaVra              -- POS. FINALE
                  table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
               end  -- [B2Vs-xs1/xs2]
               
            -- **[B2Vs-xn]**  |dExtraX2 non 'significativo'|         
            else 
               EmitComment( vCmd, '[B2Vs-xn]')
               dExtraX2 = 0 
               -- ev' chiudo Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- 1: posiziono V assumendo maxX2 come pos. finale
               dX2PosA = MaxX2     -- (pos finale)
               table.insert( vCmd, { 1, 'X2', dX2PosA})
            end -- [B2Vs-xs/xn]
         
         -- **[B2Vs-r]** accentramento di |V| 'significativo' con pos.|'raggiungibile'| (= non oltre MaxX2)
         else
            EmitComment( vCmd, '[B2Vs-r]')
            -- chiudo ev' Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0})
            -- 1: posiziono V    -- (pos finale)
            dX2PosA = dTPosA + dNewVDelta
            dExtraX2 = 0
            table.insert( vCmd, { 1, 'X2', dX2PosA})
         end   --[B2Vs]
         
      -- **[B2Vns]**  accentramento di V non 'significativo'|
      else
         EmitComment( vCmd, '[B2Vns]')
         dExtraX2 = 0
      end --[B2Vs/ns]
   end --[B2V] ( accentramento di V) 
   
   -- ---------------------------------------------------------------------------
   -- **[B2Y]**  |accentramento di Y| 
   if dYDeltaF < dYDeltaI then
      EmitComment( vCmd, '[B2Y]')
      
      -- calcoli preliminari in caso di spostamento richiesto per Y significativo
      local dCorsaYfc, dCorsaVfc, dDistFrN, dCorsaYTd, dCorsaVd, dCorsaVTr, dCorsaYr
      if bYDeltaS then
         -- eventuale risalita testa a Zmax
         bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
         if bYxs then
            -- calcolo le **corse disponibili** dei carrelli dalle posizioni iniziali
            -- per allontanare (Y+T) e V
            dCorsaYfc = MaxX1 - dX1PosA
            dCorsaVfc = dX2PosA - MinX2
            dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
            dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
            -- valuto le corse di 'recupero' dai due carrelli possibili riaccentrando Y e (V+T)
            dCorsaVTr = dCorsaVd + (MaxX2 - dX2PosA)
            dCorsaYr = dCorsaYTd
            bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaVTr + dCorsaYr
         end
      end
    
      -- **[B2Ys-xsw]** |-dExtraX1 > dCorsaYr + dCorsaVTr|
      while bXsw do
         EmitComment( vCmd, '[B2Y-xsw]')
         
         -- chiudo ev' Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- |1:| allontano (Y+T) e V quanto possibile 
         dX1PosA = dX1PosA + dCorsaYTd 
         dTPosA = dTPosA + dCorsaYTd
         dX2PosA = dX2PosA - dCorsaVd
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         
         -- chiudo il carrello V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         
         -- |2:| accentro Y e (V+T)
         dX1PosA = MyMinX1 -- = dX1PosA - dCorsaYr      
         dX2PosA = dX2PosA + dCorsaVTr   -- = MaxX2
         dTPosA = dTPosA + dCorsaVTr      
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         
         -- valuto Delta 'attuali'
         dYDeltaA  = dX1PosA - dTPosA 
         dVDeltaA  = dX2PosA - dTPosA 
         -- aggiorno la verifica di spostamento significativo
         bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol  or  dYDeltaF > dYDeltaA + DELTA_SIC)
         bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol  or  dVDeltaF < dVDeltaA - DELTA_SIC)
         -- aggiorno ExtraX1 residuo
         dExtraX1 = dExtraX1 + dCorsaYr + dCorsaVTr
         -- aggiorno i recuperi disponibili riallontanando (Y+T) V e riaccentrando succ' Y e (V+T)
         dCorsaYfc = dCorsaY   -- MaxX1 - dNewY
         dCorsaVfc = dCorsaV   -- dNewV - MinX2
         dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
         dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
         -- valuto le corse di 'recupero' dai due carrelli possibili riaccentrando Y e (V+T)
         dCorsaVTr = dCorsaVd
         dCorsaYr = dCorsaYTd
         -- aggiorno verifica per ripetizione del ciclo
         bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaVTr + dCorsaYr
      end   -- [B2Ys-xsw]
      
      -- **[B2Ys]** |accentramento di Y 'significativo'|
      if bYDeltaS then
         -- **[B2Ys-x]** accentramento di Y 'significativo' |con pos. NewY 'non raggiungibile'|(= oltre MyMinX1)
         if  dExtraX1 <  0  then
            -- (-ExtraX1 <= (dCorsaYr + dCorsaVTr ) a ciclo precedente
            -- **[B2Ys-xs]** accentramento di Y  con |'ExtraX1 'significativo'|
            if bYxs then
               local dCorsaVTrA = MaxX2 - dX2PosA
               local dCorsaYra = dX1PosA - MyMinX1
               -- **[B2Ys-xs1]** se posso recuperare ExtraX1 solo accentrando Y e (V+T)
               if dCorsaVTrA >= -dExtraX1 then
                  EmitComment( vCmd, '[B2Ys-xs1]')
                  -- chiudo ev' V e apro Y
                  table.insert( vCmd, { 12, 1})
                  table.insert( vCmd, { 11, 0})
                  -- accentro Y e (V+T) q.b. 
                  dX1PosA = MyMinX1     -- (pos. finale)
                  dX2PosA = dX2PosA - dExtraX1  -- = +(-dExtraX1)
                  dTPosA = dTPosA - dExtraX1  -- = +(-dExtraX1)
                  dExtraX1 = 0
                  table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               
               else  -- **[B2Ys-xs2]**   
                  EmitComment( vCmd, '[B2Ys-xs2]')
                  -- chiudo ev' Y e apro V
                  table.insert( vCmd, { 11, 1})
                  table.insert( vCmd, { 12, 0})
                  -- **1: allontano** (Y+T) e V 
                  -- tenendo conto di dover 'recuperare' ExtraX1... 
                  dX1Pos1, dX2Pos1, dTPos1 = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX1, dCorsaVTrA, dCorsaYTd, dCorsaVd, 'X1')
                  local dYDispl1 = dX1Pos1 - dX1PosA
                  local dVDispl1 = dX2Pos1 - dX2PosA
                  local dVDeltaDiff = dNewVDelta - (dX2Pos1 - dTPos1)
                  -- ...e anche possibilmente di posizionare V alla posizione finale
                  if dVDeltaDiff < 0 then
                     dCorsaYfc = MaxX1 - dX1Pos1
                     dCorsaVfc = dX2Pos1 - MinX2
                     dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront1 'netta'
                     dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
                     dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1Pos1, dX2Pos1, dTPos1, dVDeltaDiff, 0, dCorsaYTd, dCorsaVd, 'X1')
                  else
                     dX1PosA = dX1Pos1;  dX2PosA = dX2Pos1;  dTPosA = dTPos1
                  end
                  table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
                  --dVDeltaA = dX2PosA - dTPosA
                  -- chiudo il carrello V e apro Y
                  table.insert( vCmd, { 12, 1})
                  table.insert( vCmd, { 11, 0})
                  -- |2:| accentro Y e (V+T)
                  dX1PosA = dX1PosA - dYDispl1 - dCorsaYra       -- POS. FINALE
                  dX2PosA = dX2PosA - dVDispl1 + dCorsaVTrA      -- POS. FINALE
                  dTPosA = dTPosA - dVDispl1 + dCorsaVTrA
                  table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
               end  -- [B2Ys-xs1/xs2]
            
            -- **[B2Ys-xn]**  |dExtraX1 non 'significativo'|         
            else 
               EmitComment( vCmd, '[B2Ys-xn]')
               dExtraX1 = 0 
               -- ev' chiudo V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- 1: posiziono Y assumendo MyMinX1 come pos. finale
               dX1PosA = MyMinX1     -- (pos finale)
               table.insert( vCmd, { 1, 'X1', dX1PosA})
            end -- [B2Ys-xs/xn]
            
         --[B2Ys-r] accentramento di |Y| 'significativo' con pos.|'raggiungibile'| (non oltre MyMinX1)
         else
            EmitComment( vCmd, '[B2Ys-r]')
            -- chiudo ev' V e apro Y
            table.insert( vCmd, { 12, 1})
            table.insert( vCmd, { 11, 0})
            -- |1:| posiziono Y    -- (pos finale)
            dX1PosA = dTPosA + dNewYDelta
            dExtraX2 = 0
            table.insert( vCmd, { 1, 'X1', dX1PosA})
         end   --[B2Ys]
            
      -- [B2Yns]  accentramento di V non 'significativo'|
      else
         EmitComment( vCmd, '[B2Yns]')
         dExtraX2 = 0
      end --[B2Ys/ns]
   end --[B2Y] ( accentramento di V) 
   
   -- ricalcolo gli 'spostamenti significativi'
   dYDeltaA = dX1PosA - dTPosA  
   dVDeltaA = dX2PosA - dTPosA  
   bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
   bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
   
   -- ------------------------------------------------------------------------------------------
   -- **[B2C]**  |Spostamenti| di Y e/o V (ev' |residui| da cicli precedenti)
   --        ++ possono essere solo allontanamenti ? +++++++++++++++++++++
   EmitComment( vCmd, '[B2C]')
   -- **[B2Cs]** se c'è uno |spostamento (ev' residuo) significativo|
   if bYDeltaS or bVDeltaS  then   
      -- **[B2CYs]** se lo |spostamento (ev' residuo) di Y è 'significativo'|
      if  bYDeltaS then
         EmitComment( vCmd, '[B2CYs]')
         -- eventuale risalita testa a Zmax
         bZmaxOk = EnsureZmax( bZmaxOk, vCmd) 

         local dNewY = dTPosA + dNewYDelta
         local dExtraX1 = dNewY - MyMinX1 
         
         -- **[B2CYs-r]** se posizione di |Y raggiungibile|
         if dExtraX1 >= 0  and dNewY <= MaxX1 then
            EmitComment( vCmd, '[B2CYs-r]')
            -- chiudo eventualmente V e apro Y
            table.insert( vCmd, { 12, 1})
            table.insert( vCmd, { 11, 0})
            -- sposto il carrello Y
            dX1PosA = dNewY
            table.insert( vCmd, { 1, 'X1', dX1PosA})
         -- **[B2CYs-nr]**        
         else
            EmitComment( vCmd, '[B2CYs-nr]')
            EgtOutLog( '  CLAMP : posizione Y non raggiungibile' .. '- caso [B2Ys-nr] non gestito')
            error( 'CLAMP : posizione Y non raggiungibile')  
         end
      end -- [B2CYs]
      
      -- **[B2CVs]** se lo |spostamento (ev' residuo) di V è 'significativo'|
      if  bVDeltaS then
         EmitComment( vCmd, '[B2CVs]')
         -- eventuale risalita testa a Zmax
         bZmaxOk = EnsureZmax( bZmaxOk, vCmd) 
         
         local dNewV = dTPosA + dNewVDelta
         local dExtraX2 = dNewV - MaxX2
         
         -- **[B2CVs-r]** se posizione NewV raggiungibile
         if dExtraX2 <= 0 and dNewV >= MinX2 then
            EmitComment( vCmd, '[B2CVs-r]')
            -- chiudo eventualmente Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0})
            -- sposto il carrello V
            dX2PosA = dNewV
            table.insert( vCmd, { 1, 'X2', dX2PosA})
         -- **[B2CVs-nr]**        
         else -- se ExtraX2 > 0 anche se non significativo
            EmitComment( vCmd, '[B2CVs-nr]')
            EgtOutLog( '  CLAMP : posizione V non raggiungibile' .. '- caso [B2cVs-nr] non gestito')
            error( 'CLAMP : posizione V non raggiungibile')  
         end
      end   --[B2CsV]
         
   -- **[B2Cn]** |nessuno spostamento (ev' residuo significativo)|
   else  
      EmitComment( vCmd, '[B2Cn]')
   end   -- [B2C]

   -- calcolo i nuovi parametri di aggancio
   dYDeltaA = dX1PosA - dTPosA
   dVDeltaA = dX2PosA - dTPosA
   -- imposto stato carrelli, per eventuale uso pressori
   table.insert( vCmd, { 11, CalcCharStatus( 'X1', dYDeltaA)})
   table.insert( vCmd, { 12, CalcCharStatus( 'X2', dVDeltaA)})
   -- imposto i nuovi parametri di aggancio
   table.insert( vCmd, { 21, dYDeltaA, dVDeltaA})

   EgtOutLog( '  YDeltaA =' .. EgtNumToString( dYDeltaA) .. ' VDeltaA =' .. EgtNumToString( dVDeltaA), 1)
   -- reset contatore
   EMC.CNT = nil

   SpecOutputCNT()
   return vCmd
end --SpecAdjustCarrB2

---------------------------------------------------------------------------------------------------------
--   *** [B3]  da carrello X2 a entrambi i carrelli : X2 -> X1+X2 ***
---------------------------------------------------------------------------------------------------------
function SpecAdjustCarrB3( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
   EgtOutLog( '  *[B3] = X2 -> X1+X2', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X2 -> X1+X2'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   local dCorsaY = MaxX1 - MyMinX1
   local dCorsaV = MaxX2 - MinX2
   -- recupero le posizioni correnti 
   local dX2PosA = dTPosI + dVDeltaI
   local dX1PosA = ParkX1
   local dTPosA = dTPosI
   local dVDeltaA = dVDeltaI
   local dYDeltaA = dX1PosA - dTPosA
   local dNewYDelta
   local dNewVDelta
   -- incremento la distanza tra le due posizioni ( se abilitato e possibile)
   local dYDeltaAgg, dVDeltaAgg = AdjustPositionsForB( dYDeltaF, dVDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, bFixedDelta)
   -- tolleranze
   local dYDeltaTol = dYDeltaAgg + GetDeltaTol( EMC.LB - dYDeltaF - dYDeltaAgg, EMC.TCING, EMC.HCING, EMC.HOVM, 'X1', bFixedDelta) 
   local dVDeltaTol = dVDeltaAgg + GetDeltaTol( dVDeltaF - dVDeltaAgg, EMC.TCING, EMC.HCING, EMC.HOVM, 'X2', bFixedDelta) 
   -- definisco criteri per movimenti 'significativi' in base alle 'nuove' tolleranze
   local bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
   local bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
   -- reimposto i delta finali in caso di spostamenti richiesti significativi, tenuto conto delle tolleranze
   if bYDeltaS then 
      dNewYDelta = dYDeltaF + dYDeltaTol / 2
   else
      dNewYDelta = dYDeltaA
   end
   if bVDeltaS then
      dNewVDelta = dVDeltaF - dVDeltaTol / 2
   else
      dNewVDelta = dVDeltaA
   end

   EgtOutLog( ' YDeltaI(Park)='.. EgtNumToString( dYDeltaA)..' VDeltaI=' .. EgtNumToString( dVDeltaA) ..
              ' TPosI='.. EgtNumToString( dTPosI), 1)
   EgtOutLog( ' YDeltaF=' .. EgtNumToString( dYDeltaF) .. ' VDeltaF=' .. EgtNumToString( dVDeltaF), 1)
   EgtOutLog( ' NewYDelta=' .. EgtNumToString( dNewYDelta) .. ' NewVDelta=' .. EgtNumToString( dNewVDelta), 1)
   -- risalita testa a Zmax
   local bZmaxOk = false

   -- calcoli preliminari in caso di spostamento richiesto per Y significativo
   local dX1Pos, dX2Pos, dTPos
   local dNewY, dExtraX1, bYxs
   local dCorsaYfc, dCorsaVfc, dDistFrN, dCorsaYTd, dCorsaVd, dCorsaVTr, dCorsaYr, bXsw
   if bYDeltaS then
      -- definisco 'ExtraX1' con (V+T) e Y accentrati q.b. per la presa di Y
      dX1Pos, dX2Pos, dTPos = PosxExtraYV( dX1PosA, dX2PosA, dTPosA, MyMinX1, MaxX2, 'X1')
      dNewY = dTPos + dNewYDelta
      dExtraX1 = dNewY - MyMinX1     -- se < 0 pos. Y non direttamente 'raggiungibile'
      bYxs = -dExtraX1 > dYDeltaTol/2     -- ExtraX1 'significativo'
      if bYxs then
         -- calcolo le **corse disponibili dei carrelli a partire da Y e V c.s.** per allontanare q.p. (Y+T) e V
         dCorsaYfc = MaxX1 - dX1Pos
         dCorsaVfc = dX2Pos - MinX2
         dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
         dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
         -- valuto **le corse di 'recupero'** dai due carrelli possibili riaccentrando Y e (V+T)
         dCorsaVTr = dCorsaVd + (MaxX2 - dX2Pos)
         dCorsaYr = dCorsaYTd
         bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaVTr + dCorsaYr
      end
      -- inizializzo
      dX1PosA = dX1Pos
      dX2PosA = dX2Pos
      dTPosA = dTPos
   end

   -- **[B3Ys-xsw]** posizione finale dNewV non raggiungibile, con |dExtraX1 > CorsaVr + CorsaYr|
   while bXsw do
      EmitComment( vCmd, '[B3Ys-xsw]')

      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
      
      -- |1:| posiziono (V+T) e Y come calcolato sopra
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})  
      
      -- chiudo ev' Y e apro V
      table.insert( vCmd, { 11, 1})
      table.insert( vCmd, { 12, 0})
      -- |2:| allontano (Y+T) e V quanto possibile
      dX1PosA = dX1PosA + dCorsaYTd
      dTPosA = dTPosA + dCorsaYTd
      dX2PosA = dX2PosA - dCorsaVd
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- chiudo il carrello V e apro Y
      table.insert( vCmd, { 12, 1})
      table.insert( vCmd, { 11, 0})
      -- |3:| accentro (V+T) e Y
      dX2PosA = dX2PosA + dCorsaVTr 
      dTPosA = dTPosA + dCorsaVTr 
      dX1PosA = MyMinX1
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- valuto i Delta ottenuti
      dYDeltaA = dX1PosA - dTPosA  
      dVDeltaA = dX2PosA - dTPosA
      -- aggiorno la verifica di spostamento significativo
      bYDeltaS = ( dYDeltaF < dYDeltaA - dYDeltaTol or dYDeltaF > dYDeltaA + DELTA_SIC)
      bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
      -- aggiorno ExtraX1
      dExtraX1 = dExtraX1 + dCorsaYr + dCorsaVTr
      
      -- **aggiorno la valutazione delle corse disponibili**
      -- a partire da Y e V c.s. per allontanare q.p. (Y+T) e V
      dCorsaYfc = MaxX1 - dX1PosA
      dCorsaVfc = dX2PosA - MinX2
      dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront 'netta'
      dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
      -- valuto le corse di 'recupero' dai due carrelli possibili riaccentrando Y e (V+T)
      dCorsaVTr = dCorsaVd
      dCorsaYr = dCorsaYTd
      -- aggiorno verifica per ripetizione del ciclo
      bXsw = bYDeltaS and bYxs and -dExtraX1 > dCorsaVTr + dCorsaYr
   end --[B3Ys-xsw]
            
   -- **[B3Ys]** |spostamento| finale richiesto (ev' residuo) di |Y| |'significativo'|
   if bYDeltaS then
      EmitComment( vCmd, '[B3Ys]')

      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)

      -- **[B3Ys-x]** posizione di |Y non raggiungibile|
      --          ( -dExtraX1 <= (CorsaYr+CorsaVr) da ciclo precedente )
      if dExtraX1 < 0 then
         --EmitComment( vCmd, '[B3Ys-x]')
         
         -- **[B3Ys-xs]** |ExtraX1 'significativo'|
         if bYxs then
            --EmitComment( vCmd, '[B3Ys-xs]')
            
            local dCorsaVTrA = MaxX2 - dX2PosA
            local dCorsaYra = dX1PosA - MyMinX1
            -- **[B3Ys-xs1]** se posso recuperare ExtraX1 semplicem' accentrando Y e (V+T)
            --           ulteriormente rispetto a YPos e Vpos definiti c.s.
            if dCorsaVTrA >= -dExtraX1 then
               EmitComment( vCmd, '[B3Ys-xs1]')
               -- chiudo ev' V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **1: accentro** Y e (V+T) 
               dX1PosA = MyMinX1     -- (pos. finale)
               dX2PosA = dX2PosA - dExtraX1
               dTPosA = dTPosA - dExtraX1
               dYDeltaA = dX1PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               
            else -- **[B3Ys-xs2]**
               EmitComment( vCmd, '[B3Ys-xs2]')
               -- chiudo ev' V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **1: accentro** ev' Y e (V+T) alle posizioni impostate
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               dYDeltaA = dX1PosA - dTPosA
               
               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **2: allontano** (Y+T) e V
               -- tenendo conto di dover 'recuperare' ExtraX1... 
               dX1Pos1, dX2Pos1, dTPos1 = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX1, dCorsaVTrA, dCorsaYTd, dCorsaVd, 'X1')
               local dYDispl1 = dX1Pos1 - dX1PosA
               local dVDispl1 = dX2Pos1 - dX2PosA
               local dVDeltaDiff = dNewVDelta - (dX2Pos1 - dTPos1)
               -- ...e anche di posizionare possibilmente V alla posizione finale
               if dVDeltaDiff < 0 then
                  CorsaYfc = MaxX1 - dX1Pos1
                  dCorsaVfc = dX2Pos1 - MinX2
                  dDistFrN = dVDeltaA - MinJoin - EMC.HCING - EMC.HOVM   -- DistFront1 'netta'
                  dCorsaYTd, dCorsaVd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistFrN, 'Fr')
                  dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1Pos1, dX2Pos1, dTPos1, dVDeltaDiff, 0, dCorsaYTd, dCorsaVd, 'X1')
               else
                  dX1PosA = dX1Pos1;  dX2PosA = dX2Pos1;  dTPosA = dTPos1
               end
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
               
               -- chiudo il carrello V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **3: accentro** Y e (V+T) 
               dX1PosA = dX1PosA - dYDispl1 - dCorsaYra       -- POS. FINALE
               dX2PosA = dX2PosA - dVDispl1 + dCorsaVTrA      -- POS. FINALE
               dTPosA = dTPosA - dVDispl1 + dCorsaVTrA
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
            end  -- [B3Ys-xs1/-xs2]
                  
         -- **[B3Ys-xn]**  |dExtraX1 ancora < 0 ma non 'significativo')|     
         else --if -dExtraX1 < DeltaToll/2
            EmitComment( vCmd, '[B3Ys-xn]')
            -- ev' chiudo V e apro Y
            table.insert( vCmd, { 12, 1})
            table.insert( vCmd, { 11, 0})
            -- 1: accentro Y e porto (V+T) alla posizione impostata
            dExtraX1 = 0
            dX1PosA = MyMinX1     -- (pos. finale)
            table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         end  --[B3Ys-xs/xn]
                  
      -- **[B3Ys-r]**  |dExtraX1 > 0 (pos. Y raggiungibile)|     
      else
         EmitComment( vCmd, '[B3Ys-r]')
         -- chiudo ev' V e apro Y
         table.insert( vCmd, { 12, 1})
         table.insert( vCmd, { 11, 0})
         -- posizione ev' (V+T) come impostato sopra e Y in posizione finale
         dX1PosA = dTPosA + dNewYDelta                 
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      end -- [B3Ys]
      -- 
      dYDeltaA = dX1PosA - dTPosA  
      dVDeltaA = dX2PosA - dTPosA
            
   else -- [B3Yns] |spostamento| finale richiesto (ev' residuo) di |Y non 'significativo'|
      EmitComment( vCmd, '[B3Yns]')
   end   --[B3Ys/ns]
                  
   -- aggiorno la verifica di movimento 'significativo' per V
   bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)  
   -- **[B3Vs]** Se vi è uno |spostamento residuo di V significativo'|
   if bVDeltaS then
      EmitComment( vCmd, '[B3Vs]')

      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)

      local dNewV = dTPosA + dNewVDelta
      local dExtraX2 = dNewV - MaxX2

      -- **[B3Vs-x]** se pos. |NewV non è raggiungibile direttamente| (oltre MaxX2)
      if dExtraX2 > 0 then --dNewV <= MaxX2 then
         -- **[B3Vs-xs]**  |ExtraX2 'significativo'|
         if dExtraX2 > dVDeltaTol /2 then
            EmitComment( vCmd, '[B3Vs-xs]')
            EgtOutLog( '  CLAMP : caso [B3Vs-xs] non gestito')
            error( '  CLAMP : caso [B3Vs-xs] non gestito')  

         -- **[B3Vs-xn]**  |ExtraX2 non 'significativo'|
         else  
            EmitComment( vCmd, '[B3Vs-xn]')
            -- chiudo ev' Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0})
            -- accentro V q.p.
            dX2PosA = MaxX2
            table.insert( vCmd, { 1, 'X2', dX2PosA})
         end   -- [B3Vs-xs/xn]

      else -- **[B3Vs-r]** pos. |NewV raggiungibile direttamente| (non oltre MaxX2)
         EmitComment( vCmd, '[B3Vs-r]')
         -- chiudo ev' Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- sposto il carrello V
         dX2PosA = dNewV
         table.insert( vCmd, { 1, 'X2', dX2PosA})
      end --[B3Vs-x/-r]

   else -- [B3Vns]  spostamento residuo di V non significativo
      EmitComment( vCmd, '[B3Vns]')
   end   -- [B3Vs/ns]

   -- calcolo i nuovi parametri di aggancio
   dYDeltaA = dX1PosA - dTPosA
   dVDeltaA = dX2PosA - dTPosA
   -- se non emessi movimenti, imposto posizione Y
   if not bZmaxOk then
      -- risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
      table.insert( vCmd, { 1, 'X1', dX1PosA})
   end
   -- imposto stato carrelli, per eventuale uso pressori (sempre effettuato movimento)
   table.insert( vCmd, { 11, CalcCharStatus( 'X1', dYDeltaA)})
   table.insert( vCmd, { 12, CalcCharStatus( 'X2', dVDeltaA)})
   -- imposto i nuovi parametri di aggancio
   table.insert( vCmd, { 21, dYDeltaA, dVDeltaA})

   EgtOutLog( '  YDeltaA =' .. EgtNumToString( dYDeltaA) .. ' VDeltaA =' .. EgtNumToString( dVDeltaA), 1)
   -- reset contatore
   EMC.CNT = nil

   SpecOutputCNT()
   return vCmd
end -- SpecAdjustCarrB3

---------------------------------------------------------------------
--   *** [C1]  da carrello X1 a X2 : X1 -> X2 ***
---------------------------------------------------------------------
function  SpecAdjustCarrC1( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
   EgtOutLog( '  *[C1] = X1 -> X2 ', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X1 -> X2'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   -- recupero le posizioni correnti
   --local dCorsaY = MaxX1 - MyMinX1
   --local dCorsaV = MaxX2 - MinX2
   local dX1PosA = dTPosI + dYDeltaI
   local dX2PosA = ParkX2
   local dTPosA = dTPosI
   local dYDeltaA = dYDeltaI
   local dVDeltaA = dX2PosA - dTPosA
   local dNewVDelta -- = dVDeltaF
   -- tolleranza
   local dVDeltaTol = GetDeltaTol( dVDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, 'X2', bFixedDelta)
   local bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or  dVDeltaF < dVDeltaA - DELTA_SIC)
   -- reimposto i delta finali in caso di spostamenti richiesti significativi, tenuto conto delle tolleranze
   if bVDeltaS then 
      dNewVDelta = dVDeltaF - dVDeltaTol / 4
   else
      dNewVDelta = dVDeltaA
   end
   EgtOutLog('  YDeltaI=' .. EgtNumToString( dYDeltaA) .. ' VDeltaI(Park)='.. EgtNumToString( dVDeltaA)..
             ' TPosI=' .. EgtNumToString( dTPosI)  , 1)
   EgtOutLog('  VDeltaF=' .. EgtNumToString( dVDeltaF) .. ' NewVDelta=' .. EgtNumToString( dNewVDelta) , 1)
   
   -- risalita testa a Zmax (da effettuare comunque, dato lo scambio di carrelli)
   local bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
   
   -- calcoli preliminari in caso di spostamento richiesto per V significativo
   local dX1Pos, dX2Pos, dTPos
   local dNewV, dExtraX2, bVxs
   local dCorsaYfc, dCorsaVfc, dDistFrN, dCorsaYTd, dCorsaVd, dCorsaVTr, dCorsaYr, bXsw
   if bVDeltaS then
      -- definisco 'ExtraX2' con (Y+T) e V accentrati q.b. per la presa di V
      dX1Pos, dX2Pos, dTPos = PosxExtraYV( dX1PosA, dX2PosA, dTPosA, MyMinX1, MaxX2, 'X2')
      dNewV = dTPos + dNewVDelta
      dExtraX2 = dNewV - MaxX2
      bVxs = dExtraX2 > dVDeltaTol/2     -- ExtraX2 'significativo'
      if bVxs then
         -- calcolo le **corse disponibili dei carrelli a partire da Y e V c.s.** per allontanare q.p. Y e (V+T)
         dCorsaYfc = MaxX1 - dX1Pos
         dCorsaVfc = dX2Pos - MinX2
         --local dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
         dDistBkN = EMC.LB - (dX1Pos-dTPos) - MinJoin - EMC.TCING    -- DistBack 'netta'
         dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
         -- valuto le corse di 'recupero' dai due carrelli possibili riaccentrando (Y+T) e V
         dCorsaYTr = dCorsaYd + (dX1Pos -MyMinX1)
         dCorsaVr = dCorsaVTd
         bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaVr + dCorsaYTr
      end
      -- inizializzo
      dX1PosA = dX1Pos
      dX2PosA = dX2Pos
      dTPosA = dTPos
   end

   -- **[C1Vs-xsw]** posizione finale di V non raggiungibile, con |dExtraX2 > CorsaVr + CorsaYr|
   --            con spostamento richiesto significativo e ExtraX2 significativo
   while bXsw do
      EmitComment( vCmd, '[C1Vs-xsw]')
      -- **1:** posiziono (Y+T) e V come calcolato sopra
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})  
      
      -- chiudo ev' V e apro Y
      table.insert( vCmd, { 12, 1})
      table.insert( vCmd, { 11, 0})
      -- **2:** allontano Y e (V+T)  quanto possibile
      dX1PosA = dX1PosA + dCorsaYd
      dX2PosA = dX2PosA - dCorsaVTd
      dTPosA = dTPosA - dCorsaVTd
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- chiudo il carrello Y e apro V
      table.insert( vCmd, { 11, 1})
      table.insert( vCmd, { 12, 0})
      -- **3:** accentro (Y+T) e V
      dX1PosA = dX1PosA - dCorsaYTr   -- = MyMinX1
      dTPosA = dTPosA - dCorsaYTr 
      dX2PosA = MaxX2
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
      
      -- valuto i Delta ottenuti
      dYDeltaA = dX1PosA - dTPosA  
      dVDeltaA = dX2PosA - dTPosA
      -- aggiorno la verifica di spostamento significativo
      bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
      -- **aggiorno ExtraX2**
      dExtraX2 = dExtraX2 - dCorsaYTr - dCorsaVr
      -- **aggiorno la valutazione di ulteriori corse disponibili**
      -- a partire da Y e V c.s. per allontanare q.p. Y e (V+T)
      dCorsaYfc = MaxX1 - dX1PosA
      dCorsaVfc = dX2PosA - MinX2
      dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
      dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
      -- aggiorno i prossimi recuperi disponibili
      dCorsaYTr = dCorsaYd
      dCorsaVr = dCorsaVTd
      -- aggiorno verifica per ripetizione del ciclo
      bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaVr + dCorsaYTr
   end --[C1Vs-xw]
   
   -- **[C1Vs]** |spostamento| richiesto (ev' residuo) |di V| |'significativo'|
   if bVDeltaS then
      -- **[C1Vs-x]** posizione  di |V non raggiungibile|  
      if dExtraX2 > 0 then
         -- (dExtraX2 <= (CorsaYr+CorsaVr) da ciclo precedente)
         -- **[C1Vs-xs]** posizione di V non raggiungibile, |con ExtraX2 'significativo'|
         if bVxs then
            local dCorsaYTrA = dX1PosA - MyMinX1
            -- **[C1Vs-xs1]** se posso recuperare dExtraX2 solo accentrando V e (Y+T)
            if dCorsaYTrA >= dExtraX2 then
               EmitComment( vCmd, '[C1Vs-xs1]')

               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **1:** accentro (Y+T) q.b. e V 
               dX1PosA = dX1PosA - dExtraX2
               dTPosA = dTPosA - dExtraX2
               dX2PosA = MaxX2     -- (pos. finale)
               dVDeltaA = dX2PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})

            -- **[C1Vs-xs2]** dCorsaYTrA < dExtraX2 
            else  
               EmitComment( vCmd, '[C1Vs-xs2]')
               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **1: accentro** ev' (Y+T) e V alle posizioni impostate 
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
               dVDeltaA = dX2PosA - dTPosA
               -- chiudo (ev') il carrello V e apro Y
               table.insert( vCmd, { 12, 1})
               table.insert( vCmd, { 11, 0})
               -- **2:allontano** (V+T) q.b./q.p. e Y per preparare il recupero di ExtraX2
               dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX2, dCorsaYTrA, dCorsaYd, dCorsaVTd, 'X2')
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
               dYDeltaA = dX1PosA - dTPosA

               -- chiudo ev' Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **3: accentro**(Y+T) e V (pos. finale)
               dX1PosA = MyMinX1
               dTPosA = dX1PosA - dYDeltaA
               dX2PosA = MaxX2
               dVDeltaA = dX2PosA - dTPosA  
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
            end   -- [C1Vs-xs1/xs2]

         -- **[C1Vs-xn]** |ExtraX2 ancora > 0 , ma non 'significativo'| (<= DeltaTol/2)
         else  
            EmitComment( vCmd, '[C1Vs-xn]')
            -- chiudo ev' Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0})
            -- 1: porto ev' (Y+T) alla posizione impostata sopra e accentro V
            dExtraX2 = 0
            dX2PosA = MaxX2 -- pos. finale
            table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
             -- chiudo V (?)
            table.insert( vCmd, { 12, 1})
         end -- [C1Vs-xs/xn]
                  
      -- **[C1Vs-r]** posizione di V 'raggiungibile' (ExtraX2 <= 0)
      else 
         EmitComment( vCmd, '[C1Vs-r]')
         -- chiudo ev' Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- posizione ev' (Y+T) come impostato sopra e V in posizione finale
         dX2PosA = dTPosA + dNewVDelta
         table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA}) 
      end  -- [C1V-s] 

      -- **[C1Vns]** |spostamento| richIesto per V |non 'significativo'|
   else
      EmitComment( vCmd, '[C1Vns]')
   end

   -- calcolo i nuovi parametri di aggancio
   dVDeltaA = dX2PosA - dTPosA  
   -- chiudo ev' V e apro Y
   table.insert( vCmd, { 12, CalcCharStatus( 'X2', dVDeltaA)})
   table.insert( vCmd, { 11, 0})
   -- sposto il carrello Y in parcheggio
   table.insert( vCmd, { 1, 'X1', ParkX1})
   -- imposto i nuovi parametri di aggancio
   table.insert( vCmd, { 21, 0, dVDeltaA})

   EgtOutLog( '  VDeltaA =' .. EgtNumToString( dVDeltaA), 1)
   -- reset contatore
   EMC.CNT = nil

   SpecOutputCNT()
   return vCmd
end --SpecAdjustCarrC1   

---------------------------------------------------------------------
--   *** [C2]  da entrambi i carrelli a X2 : X1+X2 -> X2 ***
---------------------------------------------------------------------
function SpecAdjustCarrC2( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta)
   EgtOutLog( '  *[C2] = X1+X2 -> X2', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X1+X2 -> X2'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   local dCorsaY = MaxX1 - MyMinX1
   local dCorsaV = MaxX2 - MinX2
   -- recupero le posizioni correnti dei carrelli
   local dX1PosA = dTPosI + dYDeltaI
   local dX2PosA = dTPosI + dVDeltaI
   local dTPosA = dTPosI
   local dYDeltaA = dYDeltaI
   local dVDeltaA = dVDeltaI
   local dNewVDelta -- = dVDeltaF
   -- tolleranze
   local dVDeltaTol = GetDeltaTol( dVDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, 'X2', bFixedDelta) 
   -- definisco spostamento 'significativo' in base alle tolleranze
   local bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
   -- reimposto ev' il delta finale
   if bVDeltaS then
      dNewVDelta = dVDeltaF - dVDeltaTol / 4 
   else
      dNewVDelta = dVDeltaA
   end
   -- definisco 'ExtraX2'
   local dNewV = dTPosI + dNewVDelta
   local dExtraX2 = dNewV - MaxX2   -- > 0 se nuova pos. di X2 'non raggiungibile' (= oltre MaxX2)
   local bVxs = dExtraX2 > dVDeltaTol/2    -- 'ExtraX2 significativo'
   
   EgtOutLog( '  YDeltaI=' .. EgtNumToString( dYDeltaI) .. ' VDeltaI=' .. EgtNumToString( dVDeltaI) ..
              ' VDeltaF=' .. EgtNumToString( dVDeltaF) ,1)
   EgtOutLog( '  NewVDelta=' .. EgtNumToString( dNewVDelta), 1)

   -- risalita testa a Zmax (da effettuare comunque, per il parcheggio di Y)
   local bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
   
   -- calcoli preliminari in caso di spostamento richiesto per YV significativo
   local dCorsaYfc, dCorsaVfc, dDistBkN, dCorsaYd, dCorsaVd, dCorsaVTd, dCorsaYTr, dCorsaVr, bXsw
   if bVDeltaS  then
      if bVxs then
         -- calcolo le corse disponibili dei carrelli dalle posizioni attuali (=iniziali)
         -- per allontanare Y e (V+T) q.p.
         dCorsaYfc = MaxX1 - dX1PosA
         dCorsaVfc = dX2PosA - MinX2
         dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
         dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
         -- valuto le corse di 'recupero' dai due carrelli possibili riaccentrando (Y+T) e V
         dCorsaYTr = dCorsaYd + (dX1PosA - MyMinX1)
         dCorsaVr = dCorsaVTd
         bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaYTr + dCorsaVr
      end
   end

   -- **[C2Vs-xsw]** posizione finale di V non raggiungibile, |con dExtraX2 > CorsaYTr + CorsaVr|
   while  bXsw do
      EmitComment( vCmd, '[C2Vs-xsw]')

      -- chiudo eventualmente il carrello V e apro Y
      table.insert( vCmd, { 12, 1})
      table.insert( vCmd, { 11, 0})
      -- |1:| allontano Y e (V+T)  quanto possibile
      dX1PosA = dX1PosA + dCorsaYd
      dX2PosA = dX2PosA - dCorsaVTd
      dTPosA = dTPosA - dCorsaVTd
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})

      -- chiudo il carrello Y e apro V
      table.insert( vCmd, { 11, 1})
      table.insert( vCmd, { 12, 0})
      -- |3:| accentro (Y+T) e V
      dX1PosA = dX1PosA - dCorsaYTr 
      dTPosA = dTPosA - dCorsaYTr 
      dX2PosA = MaxX2
      table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})

      -- valuto i Delta ottenuti
      dYDeltaA = dX1PosA - dTPosA  
      dVDeltaA = dX2PosA - dTPosA
      -- aggiorno la verifica di spostamento significativo
      bVDeltaS = ( dVDeltaF > dVDeltaA + dVDeltaTol or dVDeltaF < dVDeltaA - DELTA_SIC)
      -- **aggiorno ExtraX2**
      dExtraX2 = dExtraX2 - dCorsaYTr - dCorsaVr
      -- **aggiorno la valutazione di ulteriori corse disponibili**
      dCorsaYfc = MaxX1 - dX1PosA
      dCorsaVfc = dX2PosA - MinX2
      dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
      dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
      -- aggiorno i prossimi recuperi disponibili
      dCorsaYTr = dCorsaYd + (dX1PosA - MyMinX1)
      dCorsaVr = dCorsaVTd
      -- aggiorno verifica per ripetizione del ciclo
      bXsw = bVDeltaS and bVxs and dExtraX2 > dCorsaYTr + dCorsaVr 
   end -- [C2V-xsw]

   -- **[C2Vs]** se lo |spostamento richiesto per V è significativo|
   if bVDeltaS then
      -- **[C2Vs-x]** posizione  di |V non raggiungibile| (oltre MaxX2)
      if dExtraX2 > 0 then
         -- **[C2Vs-xs]** |ExtraX2 'significativo'|
         if bVxs then
         local dCorsaYTrA = dX1PosA - MyMinX1
         -- **[C2Vs-xs1]** se posso recuperare ExtraX2 semplicem' accentrando V e (Y+T)
         if dCorsaYTrA >= dExtraX2 then
            EmitComment( vCmd, '[C2Vs-xs1]')

            -- chiudo ev' Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0})
            -- **1: accentro** (Y+T) e V 
            dX1PosA = dX1PosA - dExtraX2
            dTPosA = dTPosA - dExtraX2
            dX2PosA = MaxX2     -- (pos. finale)
            dVDeltaA = dX2PosA - dTPosA  
            table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})

         else -- **[C2Vs-xs2]**
            EmitComment( vCmd, '[C2Vs-xs2]')

            -- chiudo ev' V e apro Y
            table.insert( vCmd, { 12, 1})
            table.insert( vCmd, { 11, 0})
            -- **1: allontano** (V+T) e Y 
            dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX2, dCorsaYTrA, dCorsaYd, dCorsaVTd, 'X2')
            table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
            dYDeltaA = dX1PosA - dTPosA

            -- chiudo il carrello Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0})
            -- **2: accentro** (Y+T)  e V 
            dX1PosA = MyMinX1
            dTPosA = dX1PosA - dYDeltaA 
            dX2PosA = MaxX2     -- (pos. finale)
            table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
         end  -- [C2Vs-xs1/-xs2]

      -- **[C2Vs-xn]**  |dExtraX2 ancora > 0 ma non 'significativo')|     
      else --if dExtraX2 < DeltaToll/2
         EmitComment( vCmd, '[B1Vs-xn]')
         -- ev' chiudo Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- **1:** accentro V
         dExtraX2 = 0 
         dX2PosA = MaxX2     -- (pos. finale)
         table.insert( vCmd, { 1, 'X2', dX2PosA})
      end  -- [C2s-xs/xn]

      -- **[C2Vs-r]** |dExtraX2 < 0 (pos. V raggiungibile)|     
      else
         EmitComment( vCmd, '[C2Vs-r]')
         -- chiudo ev' Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- porto V in posizione finale
         dX2PosA = dTPosA + dNewVDelta      
         table.insert( vCmd, { 1, 'X2', dX2PosA}) 
      end --[C2Vs]

   -- **[C2Vns]** = |spostamento di V non significativo|
   else
      EmitComment( vCmd, '[C2Vns]')
   end  --[C2Vs/n]

   -- calcolo il nuovo parametro di aggancio
   dVDeltaA = dX2PosA - dTPosA
   -- chiudo eventualmente il carrello V e apro Y
   table.insert( vCmd, { 12, CalcCharStatus( 'X2', dVDeltaA)})
   table.insert( vCmd, { 11, 0})
   -- sposto il carrello Y in parcheggio
   table.insert( vCmd, { 1, 'X1', ParkX1})
   -- imposto il nuovo parametro di aggancio
   table.insert( vCmd, { 21, 0, dVDeltaA})

   EgtOutLog( '  VDeltaA =' .. EgtNumToString( dVDeltaA), 1)
   -- reset contatore
   EMC.CNT = nil

   SpecOutputCNT()
   return vCmd
end --SpecAdjustCarrC2

---------------------------------------------------------------------
--   *** [C3]  da carrello X2 a X2 : X2 -> X2 ***
---------------------------------------------------------------------
function SpecAdjustCarrC3( dTPosI, dYDeltaI, dVDeltaI, dTPosF, dYDeltaF, dVDeltaF, bFixedDelta, bFixedPos)
   EgtOutLog( '  *[C3] = X2 -> X2', 1)
   -- elenco comandi
   local vCmd = {}
   -- Commento
   table.insert( vCmd, { 0, 'X2 -> X2'})
   -- se primo scambio
   local MyMinX1 = EgtIf( EMC.CNT == 1, MinX1 + AGG_LOAD, MinX1)
   -- recupero le posizioni correnti dei carrelli 
   local dX2PosA = dTPosI + dVDeltaI
   local dX1PosA = ParkX1
   local dTPosA = dTPosI
   local dVDeltaA = dVDeltaI  
   local dYDeltaA = dX1PosA - dTPosA
   local dNewVDelta
   -- tolleranza
   local dVDeltaTol = GetDeltaTol( dVDeltaF, EMC.TCING, EMC.HCING, EMC.HOVM, 'X2', bFixedDelta)
   local bVDeltaS = (( dVDeltaF > dVDeltaA + dVDeltaTol and not bFixedPos) or  dVDeltaF < dVDeltaA - DELTA_SIC)
   if bVDeltaS then
      dNewVDelta = dVDeltaF - dVDeltaTol / 4
   else
      dNewVDelta = dVDeltaA
   end
   EgtOutLog( '  VDeltaI=' .. EgtNumToString( dVDeltaI) .. ' TPosI=' .. EgtNumToString( dTPosI), 1)
   EgtOutLog( '  VDeltaF=' .. EgtNumToString( dVDeltaF) .. ' NewVDelta=' .. EgtNumToString( dNewVDelta), 1)  
   -- flag per risalita testa a Zmax
   local bZmaxOk = false

   -- **[C3Vs]** |pos. di V cambia in modo significativo| 
   if bVDeltaS then
      EmitComment( vCmd, '[C3Vs]')
      -- eventuale risalita testa a Zmax
      bZmaxOk = EnsureZmax( bZmaxOk, vCmd)
      -- definisco 'ExtraX2' con (V+T) e Y accentrati q.b. per la presa con Y
      local dX1Pos, dX2Pos, dTPos = PosxExtraYV( dX1PosA, dX2PosA, dTPosA, MyMinX1, MaxX2, 'X1')
      local dNewV = dTPos + dNewVDelta
      local dExtraX2 = dNewV - MaxX2
      -- effettuo spostamenti per predisporre all'aggancio di T con Y
      dX1PosA = dX1Pos
      dX2PosA = dX2Pos
      dTPosA = dTPos
      table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
      dYDeltaA = dX1PosA - dTPosA  

      -- **[C3Vs-x]** posizione di |V non raggiungibile| (oltre MaxX2)
      if dExtraX2 > 0 then
         -- **[C3Vs-xs]** posizione di V non raggiungibile, |con ExtraX2 'significativo'|
         if dExtraX2 > dVDeltaTol /2 then
            -- calcolo le **corse disponibili dei carrelli a partire da Y e V c.s.** per allontanare q.p. (V+T) e Y
            local dCorsaYfc = MaxX1 - dX1Pos
            local dCorsaVfc = dX2Pos - MinX2
            local dDistBkN = EMC.LB - dYDeltaA - MinJoin - EMC.TCING    -- DistBack 'netta'
            local dCorsaYd, dCorsaVTd = GetCorseDisp( dCorsaYfc, dCorsaVfc, dDistBkN, 'Bk')
            -- valuto **le corse di 'recupero'** dai due carrelli possibili riaccentrando (Y+T) e V
            local dCorsaYTr = dCorsaYd + (dX1Pos - MyMinX1)
            local dCorsaVr = dCorsaVTd

            -- **[C3Vs-xsw]** posizione finale dNewV non raggiungibile, con |dExtraX2 > CorsaVr + CorsaYr|
            if dExtraX2 > ( dCorsaYTr + dCorsaVr) and bVDeltaS then
               EmitComment( vCmd, '[C3Vs-xsw]' .. 'CASO NON GESTITO')
               return
            end

            local dCorsaYTrA = dX1Pos - MyMinX1    -- !! att.ne: non dX1PosA !!
            -- **[C3Vs-xs1]** se posso recuperare ExtraX2 semplicem' accentrando (Y+T) e V
            -- (dalle posizione impostate sopra per l'aggancio di Y)
            if dCorsaYTrA >= dExtraX2 then
               EmitComment( vCmd, '[C3Vs-xs1]')
               -- chiudo Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})                     
               -- **1: accentro** (Y+T) e V 
               dX1PosA = dX1PosA - dExtraX2
               dTPosA = dTPosA - dExtraX2
               dX2PosA = MaxX2   -- (pos. finale)
               table.insert( vCmd, { 3, 'X1', dX1PosA , 'T', dTPosA, 'X2', dX2PosA})
               dVDeltaA = dX2PosA - dTPosA  

            else -- **[C3Vs-xs2]**
               -- ci sarebbe un doppio movimento di V ? => caso impossibile ?
               EmitComment( vCmd, '[C3Vs-xs2]')
               -- **1:** posiziono (ulteriormente!) Y e (V+T) 
               dX1PosA, dX2PosA, dTPosA = PosXs2Enl( dX1PosA, dX2PosA, dTPosA, dExtraX2, dCorsaYTrA, dCorsaYd, dCorsaVTd, 'X2')
               table.remove( vCmd)
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA })
               dYDeltaA = dX1PosA - dTPosA
               -- chiudo Y e apro V
               table.insert( vCmd, { 11, 1})
               table.insert( vCmd, { 12, 0})
               -- **2: accentro** (Y+T) e V 
               dX1PosA = MyMinX1 
               dX2PosA = MaxX2     -- (pos. finale)   
               dTPosA = dX1PosA - dYDeltaA 
               table.insert( vCmd, { 3, 'X1', dX1PosA, 'T', dTPosA, 'X2', dX2PosA})
            end  -- [C3Vs-xs1/-xs2]

         -- **[C3Vs-xn]**  |dExtraX2 > 0 ma 'non significativo')|     
         else --if dExtraX2 < DeltaToll/2
            EmitComment( vCmd, '[C3Vs-xn]')                   
            -- chiudo Y e apro V
            table.insert( vCmd, { 11, 1})
            table.insert( vCmd, { 12, 0}) 
            -- **1:** accentro V
            dExtraX2 = 0
            dX2PosA = MaxX2     -- (pos. finale)
            table.insert( vCmd, { 1, 'X2', dX2PosA})
         end  --[C3Vs-xs/xn]

      -- **[C3Vs-r]**  |posizione di Y raggiungibile|  (ExtraX1 >=0) 
      --           (si esclude la possibilità di extra corsa oltre maxX1)
      else
         EmitComment( vCmd, '[C3Vs-r]')                  
         -- chiudo Y e apro V
         table.insert( vCmd, { 11, 1})
         table.insert( vCmd, { 12, 0})
         -- **2:** posiziono V alla posizione richiesta
         dX2PosA = dNewV
         table.insert( vCmd, { 1, 'X2', dX2PosA})
      end -- [C3Vs]

      -- calcolo il nuovo parametro di aggancio
      dVDeltaA = dX2PosA - dTPosA 
      -- chiudo ev' V e apro Y
      table.insert( vCmd, { 12, CalcCharStatus( 'X2', dVDeltaA)})
      table.insert( vCmd, { 11, 0})
      -- sposto il carrello Y in parcheggio
      table.insert( vCmd, { 1, 'X1', ParkX1})
      -- imposto il nuovo parametro di aggancio
      table.insert( vCmd, { 21, 0, dVDeltaA})

      -- reset contatore
      EMC.CNT = nil

   else -- **[C3Vns]** |spostamento| finale richiesto (ev' residuo) di |V non 'significativo'|
      EmitComment( vCmd, '[C3Vns]')
   end   --[C3Vs/ns]   
  
   EgtOutLog( '  VDeltaA =' .. EgtNumToString( dVDeltaA), 1)
   
   SpecOutputCNT()
   return vCmd
end --SpecAdjustCarrC3

---------------------------------------------------------------------
function SpecOutputCNT()
   if EMC.CNT == 1 then
      EgtSetInfo( EMC.PATHID, 'CNT', 1)
   else
      EgtRemoveInfo( EMC.PATHID, 'CNT')
   end
end

---------------------------------------------------------------------
function SpecOutputCmds( vCmd, bEnd)

   local sRoot = EgtIf( not bEnd, 'AS', 'AE')

   -- Registro il numero di comandi
   if #vCmd > 0 then
      EgtSetInfo( EMC.PATHID, sRoot..'#', #vCmd)
   else
      EgtRemoveInfo( EMC.PATHID, sRoot..'#')
   end
   -- Registro i comandi
   for i = 1, #vCmd do
      local Cmd = vCmd[i]
      local sKey = sRoot..tostring( i)
      -- commento
      if Cmd[1] == 0 then
         local sInfo = '0,'..Cmd[2]
         if Cmd[3] then sInfo = sInfo..','..Cmd[3] end
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
      -- movimento di 1 asse
      elseif Cmd[1] == 1 then
         local sInfo = '1,'..Cmd[2]..','..EgtNumToString( Cmd[3],3)
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
      -- movimento di 2 assi
      elseif Cmd[1] == 2 then
         local sInfo = '2,'..Cmd[2]..','..EgtNumToString( Cmd[3],3)..','..Cmd[4]..','..EgtNumToString( Cmd[5],3)
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
      -- movimento di 3 assi
      elseif Cmd[1] == 3 then
         local sInfo = '3,'..Cmd[2]..','..EgtNumToString( Cmd[3],3)..','..Cmd[4]..','..EgtNumToString( Cmd[5],3)..','..
                             Cmd[6]..','..EgtNumToString( Cmd[7],3)
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
      -- apertura/chiusura morsa Y
      elseif Cmd[1] == 11 then
         local sInfo = '11,'..EgtNumToString( Cmd[2],0)
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
         if Cmd[2] == 0 then EMC.YDELTA = nil end
      -- apertura/chiusura morsa V
      elseif Cmd[1] == 12 then
         local sInfo = '12,'..EgtNumToString( Cmd[2],0)
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
         if Cmd[2] == 0 then EMC.VDELTA = nil end
      -- impostazione nuovo stato dei carrelli
      elseif Cmd[1] == 21 then
         local sInfo = '21,'..EgtNumToString( Cmd[2],3)..','..EgtNumToString( Cmd[3],3)
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
         if Cmd[2] > 0 and Cmd[3] > 0 then
            EMC.YDELTA = Cmd[2]
            EMC.VDELTA = Cmd[3]
         elseif Cmd[2] > 0 then
            EMC.YDELTA = Cmd[2]
            EMC.VDELTA = nil
         elseif Cmd[3] > 0 then
            EMC.YDELTA = nil
            EMC.VDELTA = Cmd[3]
         end
      -- aggancio grezzo a carrello
      elseif Cmd[1] == 31 then
         local sInfo = '31,'..EgtNumToString( Cmd[2],0)..','..Cmd[3]
         EgtSetInfo( EMC.PATHID, sKey, sInfo)
      end
   end

   -- Salvo i nuovi delta dei carrelli
   if EMC.YDELTA then
      EgtSetInfo( EMC.PATHID, 'YDELTA', EMC.YDELTA)
   else
      EgtRemoveInfo( EMC.PATHID, 'YDELTA')
   end
   if EMC.VDELTA then
      EgtSetInfo( EMC.PATHID, 'VDELTA', EMC.VDELTA)
   else
      EgtRemoveInfo( EMC.PATHID, 'VDELTA')
   end

end

---------------------------------------------------------------------
function SpecSetCarrPosFromCmds( vCmd)
   -- recupero nuova posizione carrelli
   for i = 1, #vCmd do
      local Cmd = vCmd[i]
      if Cmd[1] == 21 then
         if Cmd[2] > 0 and Cmd[3] > 0 then
            EMC.YDELTA = Cmd[2]
            EMC.VDELTA = Cmd[3]
         elseif Cmd[2] > 0 then
            EMC.YDELTA = Cmd[2]
            EMC.VDELTA = nil
         elseif Cmd[3] > 0 then
            EMC.YDELTA = nil
            EMC.VDELTA = Cmd[3]
         end
      end
   end
end

---------------------------------------------------------------------
function SpecTestOnlyRemarkInCmds( vCmd)
   if not vCmd then return true end
   -- verifico se nella lista dei comandi ci sono solo commenti   
   for i = 1, #vCmd do
      if vCmd[i][1] ~= 0 then
         return false
      end   
   end 
   return true
end
