!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_pipe_command(line,error)
  use mrtcal_messaging
  use gkernel_types
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_pipe_command
  use mrtcal_parameters
  use mrtcal_buffers
  use mrtcal_index_vars
  !---------------------------------------------------------------------
  ! @ private
  ! PIPELINE
  ! 1. /SHOW [*|NONE|DONE|FAILED]
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line
  logical,          intent(inout) :: error
  ! Local
  logical :: myerror,show,dupl
  integer(kind=4), parameter :: ishow = 1
  integer(kind=entry_length) :: icaluser,ientuser,cent,ient,num
  integer(kind=4) :: nerror,nsuccess
  type(user_calib_t) :: select
  character(len=message_length) :: mess
  type(cputime_t) :: time
  integer(kind=4), allocatable :: lastversion(:)
  character(len=*), parameter :: rname='PIPE>COMMAND'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Parsing first
  show = sic_present(ishow,0)
  if (show) then
     call mrtcal_pipe_parse(line,ishow,select,error)
     if (error) return
  endif
  !
  if (cx%next.le.1) then
     call mrtcal_message(seve%e,rname,'Current index is empty')
     error = .true.
     return
  endif
  !
  call gag_cputime_init(time)
  !
  call mrtcal_separator(seve%r,rname,2,error)
  if (error) return
  call mrtcal_message(seve%r,rname,'Welcome to the MRTCAL pipeline!')
  call mrtcal_separator(seve%r,rname,2,error)
  if (error) return
  call mrtcal_setup_print(rsetup,error)
  if (error) return
  call mrtcal_separator(seve%r,rname,2,error)
  if (error) return
  call mrtcal_message(seve%r,rname,'Working on current index')
  call mrtindex_list(cx,'UPDATED IDX',(/0/),6,.false.,error)
  if (error) return
  write(mess,'(I0,A)') cx%next-1,' entries in selection, iterating'
  call mrtcal_message(seve%r,rname,mess)
  !
  ! Save the latest known version in IX of all entries in CX
  allocate(lastversion(cx%next-1))
  do cent=1,cx%next-1
    num = cx%num(cent)
    call mrtindex_numver2ent(rname,ix,num,0,ient,dupl,error)
    if (error)  return
    lastversion(cent) = ix%version(ient)
  enddo
  !
  ! Loop on entries
  myerror = .false.
  nerror = 0
  nsuccess = 0
  do cent=1,cx%next-1
     ! User can interrupt the pipeline
     if (sic_ctrlc_status()) exit
     !
     ! Skip entries which were implicitly calibrated by a previous entry within
     ! the same PIPELINE call. Typical sequence is:
     !   1 SCIENCE
     !   2 CALIBRATION
     !   3 SCIENCE
     ! Iteration #1 calibrates both 1 SCIENCE with implicit new 2 CALIBRATION
     ! Iteration #2 calibrates 2 CALIBRATION (again!)
     ! Iteration #3 calibrates 3 SCIENCE with old 2 CALIBRATION
     ! We have to avoid calibrating 2 CALIBRATION twice with the same PIPELINE
     ! command. But be careful it is valid to calibrate several times an entry
     ! (several calls to CALIBRATE or PIPELINE commands) with e.g. different
     ! MSET tuning. The proper solution is to check if a new version has
     ! appeared in the meantime.
     num = cx%num(cent)
     call mrtindex_numver2ent(rname,ix,num,0,ient,dupl,error)
     if (error)  return
     if (lastversion(cent).ne.ix%version(ient)) then
       call mrtcal_separator(seve%r,rname,2,error)
       if (error) return
       call mrtindex_list_one_default(cx,cent,mess,error)
       if (error) return
       mess = 'Skip already calibrated '//mess
       call mrtcal_message(seve%w,rname,mess)  ! Or debug?
       cycle
     endif
     !
     ! Calibrate next entry
     icaluser = 0
     ientuser = cx%mnum(cent)
     call mrtcal_calib_or_solve_ix_entry(&
          code_command_pipe,&
          rsetup,ix,ientuser,icaluser,&
          rfile,rcalib,rscience,myerror)
     if (myerror) then
       nerror = nerror+1
     else
       nsuccess = nsuccess+1
     endif
     if (myerror .and. .not.rsetup%pipeline) then
        ! Interactive mode => Stop as soon as possible
        error = .true.
        return
     else
        ! True pipeline mode => Continue as long as possible
        myerror = .false.
        continue 
     endif
  enddo ! cent
  deallocate(lastversion)
  !
  call gag_cputime_get(time)
  !
  if (show) then
     call mrtcal_pipe_feedback(select,ix,cx,error)
     if (error) return
  endif
  call mrtcal_separator(seve%r,rname,2,error)
  if (error) return
  write(mess,'(A,I0,A,I0,A,I0,A)')  'Successfully processed ',nsuccess,  &
    ' entries, ',nerror,' errors (total ',cx%next-1,' in index)'
  call mrtcal_message(seve%r,rname,mess)
  if (time%curr%elapsed.lt.6.d1) then
    write(mess,'(A,F0.2,A)')  'Pipeline executed in ',time%curr%elapsed,' seconds'
  elseif (time%curr%elapsed.lt.3.6d3) then
    write(mess,'(A,F0.2,A)')  'Pipeline executed in ',time%curr%elapsed/6.d1,' minutes'
  else
    write(mess,'(A,F0.2,A)')  'Pipeline executed in ',time%curr%elapsed/3.6d3,' hours'
  endif
  call mrtcal_message(seve%r,rname,mess)
  call mrtcal_message(seve%r,rname,'The MRTCAL pipeline wishes you a pleasant day!')
  call mrtcal_separator(seve%r,rname,2,error)
  if (error) return
  !
end subroutine mrtcal_pipe_command
!
subroutine mrtcal_pipe_parse(line,iopt,select,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this => mrtcal_pipe_parse
  use mrtcal_calib_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  character(len=*),   intent(in)    :: line
  integer(kind=4),    intent(in)    :: iopt
  type(user_calib_t), intent(out)   :: select
  logical,            intent(inout) :: error
  ! Local
  integer(kind=4) :: nchar
  character(len=*), parameter :: rname='PIPE>PARSE'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  select%cback = '*'
  select%cobst = '*'
  select%cswit = '*'
  select%csour = '*'
  select%cproj = '*'
  select%cfron = '*'
  select%ccomp = 'READABLE'
  select%ccali = '*'
  call sic_ke(line,iopt,1,select%ccali,nchar,.false.,error)
  if (error)  return
  !
end subroutine mrtcal_pipe_parse
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_pipe_feedback(select,ix,cx,error)
  use mrtcal_messaging
  use mrtindex_types
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_pipe_feedback
  use mrtcal_calib_types
  !---------------------------------------------------------------------
  ! @ private
  ! User feedback: Update CX according to user choice
  !---------------------------------------------------------------------
  type(user_calib_t),        intent(in)    :: select
  type(mrtindex_optimize_t), intent(in)    :: ix
  type(mrtindex_optimize_t), intent(inout) :: cx
  logical,                   intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='PIPE>FEEDBACK'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_pipe_update_cx(ix,cx,error)
  if (error) return
  call mrtcal_pipe_filter_cx(select,cx,error)
  if (error) return
  if (cx%next.gt.1) then
     call mrtcal_separator(seve%r,rname,2,error)
     if (error) return
     call mrtcal_message(seve%r,rname,'Results:')
     call mrtindex_list(cx,'UPDATED IDX',(/0/),6,.false.,error)
     if (error) return
  endif
  !
end subroutine mrtcal_pipe_feedback
!
subroutine mrtcal_pipe_update_cx(ix,cx,error)
  use mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_pipe_update_cx
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  ! Update CX with latest version of the IX entries
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t), intent(in)    :: ix
  type(mrtindex_optimize_t), intent(inout) :: cx
  logical,                   intent(inout) :: error
  ! Local
  logical :: duplicated
  integer(kind=4), parameter :: newest = 0
  integer(kind=entry_length) :: cent,ientuser,ientnewest
  character(len=*), parameter :: rname='PIPE>UPDATE>CX'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  do cent=1,cx%next-1
     ientuser = cx%mnum(cent)
     call mrtindex_numver2ent(rname,ix,ix%num(ientuser),newest,ientnewest,&
          duplicated,error)
     if (error) return
     call mrtindex_optimize_to_optimize(ix,ientnewest,cx,cent,error)
     if (error) return
  enddo ! ient
  !
end subroutine mrtcal_pipe_update_cx
!
subroutine mrtcal_pipe_filter_cx(select,cx,error)
  use mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_pipe_filter_cx
  use mrtindex_types
  use mrtcal_calib_types
  !---------------------------------------------------------------------
  ! @ private
  ! Filter CX according to user choice
  !---------------------------------------------------------------------
  type(user_calib_t),        intent(in)    :: select
  type(mrtindex_optimize_t), intent(inout) :: cx
  logical,                   intent(inout) :: error
  ! Local
  type(mrtindex_optimize_t) :: subidx
  character(len=*), parameter :: rname='PIPE>FILTER>CX'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtindex_find(select,cx,subidx,error)
  if (error) goto 100
  call mrtindex_optimize_to_optimize(subidx,cx,error)
  if (error) goto 100
  !
100 continue
  call deallocate_mrtoptimize(subidx,error)
  !
end subroutine mrtcal_pipe_filter_cx
!
subroutine mrtcal_separator(severity,rname,nline,error)
  use mrtcal_messaging
  use mrtcal_interfaces, except_this => mrtcal_separator
  !---------------------------------------------------------------------
  ! @ private
  ! Print nline white lines as entry separators
  !---------------------------------------------------------------------
  integer(kind=4),  intent(in)    :: severity
  character(len=*), intent(in)    :: rname
  integer(kind=4),  intent(in)    :: nline
  logical,          intent(inout) :: error
  ! Local
  integer(kind=4) :: iline
  !
  if (nline.le.0) then
     call mrtcal_message(seve%e,rname,'Negative number of white lines!')
     error = .true.
     return
  else
     do iline=1,nline
        call mrtcal_message(severity,rname,'')
     enddo ! iline
  endif
  !
end subroutine mrtcal_separator
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
