esrf

Beamline Instrument Software Support
SPEC Macro documentation: [ Macro Index | BCU Home ]

#%TITLE% NATT.MAC
#%NAME%
#%B%natt.mac%B% - move the new attenuators
#
#%CATEGORY% Positioning
#
#%DESCRIPTION% 
# This macros control the standard ESRF filters. This attenuator consists of
# a motor controlled directly from SPEC, associated limit switches, and a 
# home switch for every filter position. All the home switches are connected 
# together to use the single home switch entry on the motor controller card.
# %BR%
# The macros attmv, attref, and attshow are the macros to operate these
# attenuators:
# %UL%
#   %LI% attmv to move it from the command line
#   %LI% attref to recover if ever the position of the motor is lost.
#   %LI% attshow to use the macros with a simple text menu
#   %LI% A graphical user interface which can be installed seperately
# %XUL%
# There is also a set of macros which help in setting up the attenuators:
# %UL%
#   %LI% attdiag, attsetup, attsavesetup, attref, attlimits, attsetpos,
#        attrefall, att_getxml, att_writexml.
# %XUL%
# These macros are used as follows:
# %UL%
# %LI% Call "attdiag <mne>" in order to have a complete and precise map
#      (steps between filters, position and length of switches, backlash -
#      direction and values) of the %B%mne%B% axis. The macro is not compulsory
#      when configure an axis but is highly recommended to use if controlling
#      the axis for the first time or in case of an abnormal behaviour. The
#      execution might take 10-20 minutes.
# %LI% Call "attsetup [<filename>]" to define motor mnemonics, filter name,
#      attenuators type and security, The filter name is a string with no
#      spaces. With no parameters you will answer to questions. With the
#      %B%filemane%B% (full path) parameter, all the setup information will be
#      retrieved from it. The file should be by default in the
#      /users/blissadm/local/spec/userconf directory. 
# %LI% Call "attsavesetup <filename>" to save your current attenuator 
#      configuration so it can be used by attsetup. You should by default save
#      the file to the /users/blissadm/local/spec/userconf directory
#      (e.g. attsavesetup /users/blissadm/local/spec/userconf/att.save).
# %LI% Call "attref <mne> [calibration_step [sign]]" if you have lost the
#      filter positions of the axis. The motor will move to a hardware limit
#      switch and then try to find the first filter position with the help of
#      the home switches. The precision of the search and the direction can be
#      specified. If not the macro will use the default values which are 
#      positive direction and 0.004 mm precision.
# %LI% Call "attrefall <mne> [calibration_step [sign]]" if you have lost the
#      filter positions of the axis and you have unequal distances between the
#      fiters. The motor will move to a hardware limit switch and then try to
#      find all the filter position with the help of the home switches. At the
#      end the motor is moved to the second filter. The precision of the
#      search and the direction can be specified. If not the macro will use
#      the dafault values which are positive direction and 0.004 mm precision.
# %LI% Call "attlimits <mne>" to get the positions of hardware limits and set 
#      them as software limits. Any subsequent move of the axis will make the
#      software move the motor an ATT_DELTAMV mm before the limits, only if the
#      difference between the position asked and the corrected position is
#      below a certain value (ATT_DELTAMAX). Otherwise, the normal limits
#      reached message will appear.
# %LI% Use the standard SPEC set command - "set <mne> pos" when there is an
#      offset between the home switches and the desired beam position. You can
#      simply move the center of a filter in the beam and set this position to
#      your new filter number. For example, if your second filter is currently
#      in the center of thebeam, just type "set att1 2". This will result in
#      an offset between your dial and user position. All other filters will
#      shift automatically too. If you need to shift individual filter position
#      (normally should not be necessary if everything works fine!), use the
#      attsetpos macro for every filter position.
# %LI% Use "attmv <mne> pos .. <mne> pos" to move the attenuators to the filter
#      pos. The advantage over moving the motor with the normal mv command is
#      a check if the motor really moved and if the home switches are active.
#      You can also use the filter name (i.e. 0.18mm_Al) as a position. 
# %LI% Use "attshow" to see position of any axis and to move them
# %LI% Use "_attmv(nb, mne[], pos[])" if you want to move your axis from a 
#      macro
#         nb:    Number of axis to move
#         mne[]: Associative array containing the mnemonic of axis to move 
#         pos[]: Associative array containing the position where to move axis
# %LI% Use "attsetpos <mne> <filter name> <pos>" to set position of a 
#         particular filter. There is a value associated with every filter 
#         name.This position is set by default to the position of the filter 
#         (e.g. 1 2 3 4 5 or 6). If after a scan, one filter position is not 
#         exactly 1, 2..., you can use this macro to set a new position for 
#         this filter. Afterwards, using filter name in attmv will move the
#         axis to the new position. Filter values are used in attshow to move 
#         axis and saved with attsavesetup.
# 
# %XUL%
#
#%END%

#%UU%
#%MDESC% Initialise the table of parameters for the existing attenuator
#types.
def _attinittable '{
global ATTTABLE ATTNBTABLE
  
  ATTNBTABLE = 11
  
  ATTTABLE[1]["nbfilt"] = 4
  ATTTABLE[1]["step_size"] = 10560  
  ATTTABLE[1]["filter_distance_mm"] = 33  
  ATTTABLE[1]["name"] = "Oxford 4 filter positions"
  ATTTABLE[1]["mne"] = "oxford4"

  ATTTABLE[2]["nbfilt"] = 5
  ATTTABLE[2]["step_size"] = 7840
  ATTTABLE[2]["filter_distance_mm"] = 24.5
  ATTTABLE[2]["name"] = "Oxford 5 filter positions"
  ATTTABLE[2]["mne"] = "oxford5"

  ATTTABLE[3]["nbfilt"] = 4
  ATTTABLE[3]["step_size"] = 10560  
  ATTTABLE[3]["filter_distance_mm"] = 33  
  ATTTABLE[3]["name"] = "Rial 4 filter positions"
  ATTTABLE[3]["mne"] = "rial4"

  ATTTABLE[4]["nbfilt"] = 5
  ATTTABLE[4]["step_size"] = 12250
  ATTTABLE[4]["filter_distance_mm"] = 24.5
  ATTTABLE[4]["name"] = "Rial 5 filter positions"
  ATTTABLE[4]["mne"] = "rial5"

  ATTTABLE[5]["nbfilt"] = 4
  ATTTABLE[5]["step_size"] = 15000
  ATTTABLE[5]["filter_distance_mm"] = 30
  ATTTABLE[5]["name"] = "Rial 4 filter positions 30 mm"
  ATTTABLE[5]["mne"] = "rial4_30"

  ATTTABLE[6]["nbfilt"] = 4
  ATTTABLE[6]["step_size"] = 11200
  ATTTABLE[6]["filter_distance_mm"] = 35
  ATTTABLE[6]["name"] = "Oxford 4 filter positions 35 mm"
  ATTTABLE[6]["mne"] = "oxford4_35"

  ATTTABLE[7]["nbfilt"] = 4
  ATTTABLE[7]["step_size"] = 9600
  ATTTABLE[7]["filter_distance_mm"] = 30
  ATTTABLE[7]["name"] = "Spline filter positions 30 mm"
  ATTTABLE[7]["mne"] = "spline_30"

  ATTTABLE[8]["nbfilt"] = 4
  ATTTABLE[8]["step_size"] = 16500
  ATTTABLE[8]["filter_distance_mm"] = 30
  ATTTABLE[8]["name"] = "ID14 attenuators"
  ATTTABLE[8]["mne"] = "id14att"

  ATTTABLE[9]["nbfilt"] = 4
  ATTTABLE[9]["step_size"] = -1
  ATTTABLE[9]["filter_distance_mm"] = -1
  ATTTABLE[9]["name"] = "No switch filters"
  ATTTABLE[9]["mne"] = "NoSwitch"

  ATTTABLE[10]["nbfilt"] = 4
  ATTTABLE[10]["step_size"] = 4000*35
  ATTTABLE[10]["filter_distance_mm"] = 35
  ATTTABLE[10]["name"] = "Oxford 4 filters Icepap attenuator 35 mm"
  ATTTABLE[10]["mne"] = "id24att"

  ATTTABLE[11]["nbfilt"] = 5
  ATTTABLE[11]["step_size"] = 2500*24.5
  ATTTABLE[11]["filter_distance_mm"] = 24.5
  ATTTABLE[11]["name"] = "Rial 5 filter Icepap attenuator 24.5 mm"
  ATTTABLE[11]["mne"] = "rial5ice"

}'

#%UU% [filename]
#%MDESC% Initialise the attenuator motor, answering questions or get the
#information from %B%filename%B%.
def attsetup '{
global ATTNUM[]
global ATTTYPE[]
global ATTSEC[]
global ATTFILTNAME[]
global ATTFILTPOS[]
global _CALIB_STEP _ATTMOT ATTNB ATT_DELTA ATTFIRSTTIME
global ATT_LOCKED_MSG
global ATT_DELTAMV ATT_DELTAMAX
global ATT_MOVED_ON ATT_MOVED_NB ATT_MOVED_MOT[] ATT_MOVED_POS[]
global ATT_HO[]
global ATT_CHECK
global ATT_MENU_TIMEOUT

local i s1 s2 str fname found

  ATT_MENU_TIMEOUT = 0

  ATT_MOVED_ON = 0
  ATT_DELTAMV = 0.001
  ATT_DELTAMAX = 0.03
  ATT_DELTA = 0.1

  _attinittable

  if ($# == 0) {
    # Axis Menmonic
    _ATTMOT = getval("Attenuator motor mnemonic: ",_ATTMOT)
    _check_mne(_ATTMOT)

    # Axis Type
    printf("  Attenuator types:\n")
    for (i=1; i<= ATTNBTABLE; i++)
      printf("%8d: %s (including empty filter)\n", i, ATTTABLE[i]["name"])
    ATTTYPE[_ATTMOT] = getval("Attenuator type", ATTTYPE[_ATTMOT])
    nbfilt = ATTTABLE[ATTTYPE[_ATTMOT]]["nbfilt"]

    # Axis Security
    str = sprintf("Security filter number [1-%d] :", nbfilt)
    ATTSEC[_ATTMOT] = getval(str, ATTSEC[_ATTMOT])

    # Filter Names & Pos
    for (i=1;i<=nbfilt;i++) {
      str = sprintf("Name for filter #%d :", i)
      ATTFILTNAME[_ATTMOT][i] = getval(str, ATTFILTNAME[_ATTMOT][i])
      if (ATTFILTNAME[_ATTMOT][i] == "")
        ATTFILTNAME[_ATTMOT][i] = sprintf("filt%d", i)

      if (ATTTYPE[_ATTMOT] != 9) {
        ATTFILTPOS[_ATTMOT][i]  = i
      } else {
        aux_str = sprintf("Filter \"%s\" position", ATTFILTNAME[_ATTMOT][i])
        ATTFILTPOS[_ATTMOT][i] = getval(aux_str, ATTFILTPOS[_ATTMOT][i])+0
      }
    }

    for (i=0,found=0;i<ATTNB;i++) {
      if (ATTNUM[i] == _ATTMOT) {
        found = 1
        break
      }
    }
    if (!found)
      ATTNUM[ATTNB++] = _ATTMOT

    if (ATT_CHECK) {
      if (ATTTABLE[ATTTYPE[_ATTMOT]]["step_size"] != -1) {
        if ((s1 = ATTTABLE[ATTTYPE[_ATTMOT]]["step_size"]) != \
	      (s2 = fabs(motor_par(motor_num(_ATTMOT),"step_size"))))
        printf ("Warning: motor %s should have %d step/mm in config, but has %d\n",\
			     _ATTMOT, s1, s2)
      }
    }
  } else if ($# >= 1) {
    fn = "$1"
    printf("***** Getting attenuator configuration from %s *****\n", fn)
    if (open(fn) == -1) {
      printf("Cannot open file \"%s\" for attenuator(s) setup, exit\n", fn)
      exit
    }

    ATTFIRSTTIME = 1
    for (;;) {
      # look for a mnemonic
      if ((str=_att_find_key(fn, "mne")) == "error") {
        break 
      }

      if (motor_mne(motor_num(str)) == str) {
        _ATTMOT = str
      } else {
        printf("motor \"%s\" not configure, exit\n", str)
        exit
      }
      printf("*** configure attenuator %d : \"%s\" \n", ATTNB+1, _ATTMOT)

      # looking for axis type
      if ((str=_att_find_key(fn, "type")) == "error") {
        printf("configuration error, exit\n")
        exit
      }

      if ((str!="1") && (str!="2") && (str!="3") && \
	  (str!="4") && (str!="5") && (str!="6") && \
	  (str!="7") && (str!="8") && (str!="9"))
        str = 1
      else
        str = str + 0

      ATTTYPE[_ATTMOT] = str

      # looking for axis security
      if ((str=_att_find_key(fn, "security")) == "error") {
        printf("configuration error, exit\n")
        exit
      }
      ATTSEC[_ATTMOT] = str

      nbfilt = ATTTABLE[ATTTYPE[_ATTMOT]]["nbfilt"]
      for (i=1;i<=nbfilt;i++) {
        # filter name
        if ((str=_att_find_key(fn, "name")) == "error") {
          printf("configuration error, exit\n")
          exit
        }
        # filter position
        ATTFILTNAME[_ATTMOT][i] = str
        if ((str=_att_find_key(fn, "pos")) == "error") {
          printf("configuration error, exit\n")
          exit
        }
        ATTFILTPOS[_ATTMOT][i]  = str + 0
      }

      for (i=0,found=0;i<ATTNB;i++)
        if (ATTNUM[i] == _ATTMOT) {
          found = 1
          break
        }

      if (!found)
        ATTNUM[ATTNB++] = _ATTMOT
    }

    close(fn)
    ATT_HO[_ATTMOT]["filename"] = "$2"
    ATT_HO[_ATTMOT]["root"] = "$3"
    ATT_HO[_ATTMOT]["name"] = "$4"
    ATT_HO[_ATTMOT]["offset"] = "$5"
  } else {
    printf("Usage : attsetup <no parameter> ==> one axis configuration\n")
    printf("        attsetup [filename]     ==> axis setup from file\n")
  }

  if (whatis("user_att_locked") == 0)
    eval("def user_att_locked\'{ }\'")

}'

#%UU% [filename]
#%MDESC% Write current attenuator configuration (all axis) in a %B%file%B%.
def attsavesetup '{
  local i fn nbfilt j
  
  if ($# != 1) {
    printf ("Usage: attsavesetup [filename]\n")
    exit
  }
  
  fn = "$1"
  
  if (!unix(sprintf("test -w %s", fn))) {
    str = sprintf("File \"%s\" allready exists, overwrite", fn)
    if (yesno(str,0)) {
      unix(sprintf("/bin/rm %s", fn))
      sleep(1)
    } else {
      exit
    }
  }

  open(fn)
  for (i=0;i<ATTNB;i++) {
    fprintf(fn, "mne      = %s\n", ATTNUM[i])
    fprintf(fn, "type     = %d\n", ATTTYPE[ATTNUM[i]])
    fprintf(fn, "security = %d\n", ATTSEC[ATTNUM[i]])
    nbfilt = ATTTABLE[ATTTYPE[ATTNUM[i]]]["nbfilt"]
    for (j=1;j<=nbfilt;j++) {
      fprintf(fn, "  filter %d\n", j)
      fprintf(fn, "    name = %s\n", ATTFILTNAME[ATTNUM[i]][j])
      fprintf(fn, "    pos  = %g\n", ATTFILTPOS[ATTNUM[i]][j])
    }
    fprintf(fn, "\n")
  }
  close(fn)
}'

#%IU% (filename, key)
#%MDESC% Find a keyword %B%key%B% in the attenuator config file %B%filename%B%.
def _att_find_key(fn, key) '{
  local str n
  
  mytab[0] = 0
  if (ATTFIRSTTIME)  {
    str = getline(fn, 0)
    ATTFIRSTTIME = 0
  } else {
    str = getline(fn)
  }
  
  for (;(str!=-1) && (index(str,key)==0);str=getline(fn)) ;
  if (str != -1) {
    n = split(str, mytab)
    str = mytab[n-1]
    while (index(str, "\n") != 0) {
      str = substr(str, 1, index(str, "\n")-1)
    }
      
  } else {
    str = "error"
  }
  return(str)
}'

#%UU% <motor_mnemonic [calibration_step [sign]]>
#%MDESC% Find the "second filter". Set it as reference position (2) and step
#on it. The motor will move to a  hardware limit switch and then try to find
#the second filter position with the help of the home switches. The direction
#of the movement %B%sign%B% is defined as 1 (from negative limit in positive
#direction) or -1 (positive limit in negative direction). The precision for
#the home switch search %B%calibration_step%B% [mm] could be defined - default
#value is 0.004 mm.
def attref '{
local mnum nbfilt
global _CALIB_STEP _SIGN _ATTDEBUG

  if (_att_locked()) {
    printf("%s\n", ATT_LOCKED_MSG)
    exit
  }

  if ($# < 1) {
    printf ("Usage: attref motor_mnemonic [ calib_step [direction]]\n")
    _ATTMOT = getval("Attenuator motor mnemonic: ",_ATTMOT)
    #_CALIB_STEP = fabs(getval("Attenuator calibration step [mm or step]: ", _CALIB_STEP))
    #_SIGN = ("Direction to scan - 1(positive)/-1(negative)",  _SIGN)
    mnum =_check_mne(_ATTMOT)
  } else {
    _check0 "$1"
    _ATTMOT ="$1"
    mnum = $1
    _CALIB_STEP = fabs($2)
    _SIGN = $3
  }
 
  if (!_CALIB_STEP)
    _CALIB_STEP = 0.004

  if (!_SIGN)
    _SIGN = 1

  if (ATTTABLE[ATTTYPE[_ATTMOT]]["filter_distance_mm"] == -1) {
    printf("Axis without switches! Cannot get reference, exit.\n")
    exit
  }

  if (ATTTYPE[_ATTMOT] == 0 || \
	(nbfilt = ATTTABLE[ATTTYPE[_ATTMOT]]["nbfilt"]) == 0) {
    printf("Please, run attsetup first, exit\n")
    exit
 } 

  printf("**** Searching reference position of %s ****\n", _ATTMOT)
  _attref (mnum)
}'

#%UU% <motor_mnemonic>
#%MDESC% Find both limit switches position and set them as software limits for
#the motor%BR%
#%B%attref%B% has to be called already. Otherwise the hardware limit position
#will not be correct.
def attlimits '{
local mnum tomm neg_limit pos_limit
  
  if (_att_locked()) {
    printf("%s\n", ATT_LOCKED_MSG)
    exit
  }

  if ($# != 1) {
    printf("Usage: attlimits att_mnemonic\n")
    exit
  }
  
  mnum = $1
  
  tomm = motor_par(mnum,"step_size")

  printf("*** Moving to negative limit\n")
  neg_limit =_att_gotolim(mnum, -1)
	
  printf( "*** Moving to positive limit\n")
  pos_limit = _att_gotolim(mnum, +1)

  printf("*** Harware Limits found at [%g %g] (%d steps, %d steps)\n", \
	neg_limit, pos_limit, int(neg_limit * tomm), int(pos_limit * tomm)) 

  printf("*** Setting previous values as software limits\n")
  set_lim(mnum, dial(mnum, neg_limit), dial(mnum, pos_limit))
}'

#%IU% (mnum)
#%MDESC% Internal function to find the "zero reference filter". The algorithm
#is based on the fact that the first home switch is passed when we move 
#0.5 filter distances (i.e.33mm/2 = 16mm) from the limit. The %B%mnum%B% is
#the axis ordinal number in the motors list.
def _attref (mnum)'{
local a0 a1 dist step mid[] dist_mm
global ATTFILTPARS[]

  a0 = _att_gotolim(mnum,-1* _SIGN)
  printf ("Limit switch reacheat at position %.2f\n", a0)

  step = _CALIB_STEP
  sleep(.1)

  dist_mm = ATTTABLE[ATTTYPE[motor_mne(mnum)]]["filter_distance_mm"]

  #check if home switch active
  if (_attswitchf (mnum, 0, 99) == 0) {
    printf ("\t - both limit and home switches active\n")
    a0 = A[mnum]
  } else {
    printf("\t - move until the home switch of the first filter reached...\n")
    a0 = _attswitchf (mnum,step,4)
  }
  printf ("Home switch reached at position %.2f\n", a0)
  printf("\t - move until the home switch of the first filter left...\n")
  a1 = _attswitchf (mnum,step,0)
  printf ("Home switch left at position %.2f\n", a1)
  mid[0] = a1 - _SIGN * fabs(a1-a0)/2
  printf ("Home switch size %.2f mm\n", fabs((a1-a0) * dist_mm))

  printf ("\t - go to a position between first and second filter...\n")
  waitmove ; get_angles
  A[mnum] += 0.5 * _SIGN
  move_em
  _show_waitf()

  printf("\t - move until the home switch of the second filter reached...\n")
  a0 = _attswitchf (mnum,step,4)
  printf ("Home switch reached at position %.2f\n", a0)
  printf("\t - move until the home switch of the second filter left...\n")
  a1 = _attswitchf (mnum,step,0)
  printf ("Home switch left at position %.2f\n", a0)
  mid[1] = a1 - _SIGN * fabs(a1-a0)/2
  printf ("Home switch size %.2f mm\n", fabs(a1-a0))

  dist = fabs(mid[1] - mid[0])
  printf ("\t - Filter distance: %.2f mm. Filter (switch) size: %.2f mm\n", \
	dist*dist_mm, fabs((a1-a0)*dist_mm))
  printf ("Moving to the centre of the second filter (%.2f)\n", mid[1])
  A[mnum] = mid[1]
  move_all; waitmove

  chg_dial(mnum,2)
  ATTFILTPOS[motor_mne(mnum)][2] = 2
  ATTFILTPOS[motor_mne(mnum)][1] = 2 - (dist*_SIGN)
  ATTFILTPARS[motor_mne(mnum)]["filtpos2"] = 2
  ATTFILTPARS[motor_mne(mnum)]["filtpos1"] = 2 - (dist*_SIGN)
}'

#%IU% (mnum, sign)
#%MDESC% Go to positive or negative limit (positive means same sign as the
#step_size (positive motor movement). Show some animation during the move.
#Return the current motor position. 
def _att_gotolim(mnum, sign) '{
local stepsign way
  stepsign = fabs(motor_par(mnum, "step_size")) / motor_par(mnum, "step_size")
  if (stepsign * sign < 0) {
    way = "negative"
    chg_dial(mnum, "lim-")
  } else {
    way = "positive"
    chg_dial(mnum, "lim+")
  }
  printf ("\t- go to %s limit, please wait...\n", way)
  _show_waitf()
  return(A[mnum])
}'

#%IU% ()
#%MDESC% Draw a bar while action goin on
def _show_waitf() '{
local m
  printf(" ")
  while(wait(0x21)){
    printf("\b%s", substr("-\\|/", (m=++m%4)+1, 1))
    sleep(.05)
  }
  printf("\b")
  getangles
}'

#%IU% (motor_mnemonic, motor_steps, parameter)
#%MDESC% If par=99, return the home switch status (0-active, 4-nonactive).
#Otherwise check if home switch signal is active (par=0) or not (par=4) and
#move the motor untill the desired status reached. Retunt the current motor
#position.
def _attswitchf (mnum, mstep, par) '{
local chnum device pos

  device = motor_par(mnum,"device_id")
  if (device == "icepap") {
    pos = _attswitchicepap(mnum, mstep, par)
  } else {
    pos = _attswitchmaxe(mnum, mstep, par)
  }
  return(pos)
}'

#%IU% (motor_number, sign)
#%MDESC% Return the limit switch status (0-nonactive, 1=-active). Check low or
#high  limit switch according to the sign.
#position.
def _attlimitswitchf (mnum, sign) '{
local device ret

  ret = 0
  device = motor_par(mnum,"device_id")
  if (device == "icepap") {
    if (sign > 0) {
      ret = motor_par(mnum,"high_lim_set")
    } else {
      ret = motor_par(mnum,"low_lim_set")
    }
  } else {
    local chnum
    chnum = motor_par(mnum,"channel")
    ret = esrf_io(device, "DevReadSwitches", chnum) 
    if (ret == -1) {
      eprintf("Cannot read limit switche status, exiting...\n")
      exit
    } else {
      if (sign > 0) {
        if (ret == 2)
          return(1)
        else
         return(0)
      } else {
        if (ret == 1)
          return(1)
        else
         return(0)
      }
    }
  }
  if (ret != 0)
    ret = 1
  return(ret)
}'

#%IU% (motor_number, motor_steps, parameter)
#%MDESC% If par=99, return the home switch status (0-active, 4-nonactive).
#Otherwise check if home switch signal is active (par=0) or not (par=4) and
#move the motor %B%motor_steps%B% untill the desired status reached.
#Retunt the current motor position.
def _attswitchicepap(mnum, mstep, par) '{
local stat ret cmd sign

  if (par == 99) {
    stat = motor_par(mnum,"home_active")
    stat==0?(ret = 4 ):(ret = 0)
    return(ret)
  }

  sign = 1
  if (motor_par(mnum, "step_size") < 0)
    sign = -1

  (_SIGN*sign > 0) ? (cmd = sprintf ("motor_par(%d,\"high_lim_set\")",mnum)):\
	(cmd = sprintf ("motor_par(%d,\"low_lim_set\")",mnum))
  if (par == 0) {
    while(motor_par(mnum,"home_active") != 0) {
      stat = eval(cmd)
      if (stat == 0) {
        A[mnum] += mstep * _SIGN
        move_all
        _show_waitf()
      } else {
        return A[mnum]
      }
    }
  } else {
    while(motor_par(mnum,"home_active") == 0) {
      stat = eval(cmd)
      if (stat == 0) {
        A[mnum] += mstep * _SIGN
        move_all
        _show_waitf()
      } else {
        return A[mnum]
      }
    }
  }
  return A[mnum]
}'

#%IU% (motor_number, motor_steps, parameter)
#%MDESC% If par=99, return the home switch status (0-active, 4-nonactive).
#Otherwise check if home switch signal is active (par=0) or not (par=4) and
#move the motor %B%motor_steps%B% untill the desired status reached.
#Retunt the current motor position.
def _attswitchmaxe(mnum, mstep, par)'{
local chnum device stat

 device = motor_par(mnum,"device_id")
 chnum = motor_par(mnum,"channel")
 if (par == 99) {
   return(esrf_io (device,"DevReadHardStatus", chnum) & 0x04)
 }
 while ((esrf_io (device,"DevReadHardStatus", chnum) & 0x04) == par) {
   stat = _attlimitswitchf (mnum, _SIGN)
   if (stat == 1)
     break
   getangles
   A[mnum] += mstep * _SIGN
   move_all
   _show_waitf()
   }
 return A[mnum]
}'

#%IU% (motor_mnemonic)
#%MDESC% Check if the motor exists in the configuration.
def _check_mne (motor)'{
local mnum
  mnum = motor_num(motor)
  if (motor_mne(mnum) != motor) {
    print "Invalid motor mnemonic: "motor
    exit
  }
  return(mnum)
}'

#%IU% (mnum)
#%MDESC% Return the home switch signal value - activ (0) or not (4).
def _att_checkhomef (mnum) '{

  return(_attswitchf(mnum, 0, 99))
}'

#%UU% [<motor_mnemonic>] [<filter position>/<filter name>] .....
#%MDESC% Move the axis <motor_mnemonic> to the given position <filter_number>
#Move to an intermediate position, check the switch, move to the final
#position and check again that the switch is on.
def attmv '{

  local tmot[] tpos[]
  local nbfilt i j mot_nb par_nb findmot findfilt

  if (_att_locked()) {
    printf("%s\n", ATT_LOCKED_MSG)
    exit
  }

  npar = $#
  if ((npar == 0) && (npar/2.0 != int(npar/2.0))) {
    p "Usage: attmv motor_mnemonic filter_number [motor_mnemonic filter_number]"
    p "                      OR"
    p "       attmv motor_mnemonic filter_name [motor_mnemonic filter_name]"
    exit
  }
 
  par_nb = split ("$*",inp)

  # first construct 2 associative array containing for the first one the
  # mnemonic of the axis to move and for the second, the position where to move
  for (i=0, mot_nb=0; i<par_nb ; i=i+2) {
    if (motor_mne(motor_num(inp[i])) != inp[i]) {
      printf("Motor \"%s\" does not exist\n", inp[i])
    } else {
      # inp[i] is a real motor, check if it is an attenuator
      for (findmot=0,j=0;j<ATTNB;j++)
        if (ATTNUM[j] == inp[i])
          findmot = 1

      if (findmot == 0) {
        printf("Motor \"%s\" is not configured as an attenuator axis\n", \
                inp[i])
      } else {
        tmot[mot_nb] = motor_num(inp[i])

        # check if this a position or a filter name
        nbfilt = ATTTABLE[ATTTYPE[inp[i]]]["nbfilt"]
        for (j=1,findfilt=0;j<=nbfilt;j++)
          if (ATTFILTNAME[inp[i]][j] == inp[i+1])
            findfilt = j

        if (findfilt == 0)
          tpos[mot_nb++] = inp[i+1] + 0
        else
          tpos[mot_nb++] = ATTFILTPOS[inp[i]][findfilt]
      }
    }
  }
  
  _attmv(mot_nb, tmot, tpos)
}'

cdef("user_att_endmove")
#%IU% (<nb. motor>, <motor num array>, <motor pos array>)
#%MDESC%Function to test the limits, correct the position, send to
#intermediate and then to final postion the motors specified in the second
#parameter (an associative array). The first parameter is the number of motors
#to move. The third parameter is an associative array containing the final
#positions for the motors to move.
def _attmv(n, tmot, tpos) '{
  local i dpos limpos limneg rpos nbmot backlash
  local new_pos[] new_mot[] init_pos[] mid_pos[]
  
  ATT_MOVED_ON = 1
  ATT_MOVED_NB = n
  ATT_MOVED_MOT = tmot
  ATT_MOVED_POS = tpos
  cdef("cleanup_once", "ATT_MOVED_ON = 0\n", "attmove")
  
  if (_att_locked()) {
    printf("%s\n", ATT_LOCKED_MSG)
    exit
  }

  waitmove ; get_angles

  # check limits
  p "*** check axis limits"
  for (i=0,nbmot=0 ; i<n ; i++) {
    backlash = motor_par(tmot[i], "backlash")
    dpos    = dial(tmot[i], tpos[i])
    toadd = 0
    if (((backlash < 0) && (dial(tmot[i], A[tmot[i]]) > dpos)) || \
        ((backlash > 0) && (dial(tmot[i], A[tmot[i]]) < dpos)))
      toadd = backlash / motor_par(tmot[i], "step_size")
    dpos += toadd
    limpos  = get_lim(tmot[i],  1)
    limneg  = get_lim(tmot[i], -1)

    if ((dpos < limneg) || (dpos > limpos) ) {
      if (dpos < limneg)
        rpos = user(tmot[i], get_lim(tmot[i], -1)) + ATT_DELTAMV - toadd
      else
        rpos = user(tmot[i], get_lim(tmot[i],  1)) - ATT_DELTAMV - toadd

      if (fabs(rpos-tpos[i]) > ATT_DELTAMAX) {
        printf("\"%s\": %g is outside soft. lim. and cannot be corrected\n",\
               motor_mne(tmot[i]), tpos[i])
      } else {
        printf("\"%s\": %g is outside soft. lim., corrected in %g\n", \
               motor_mne(tmot[i]), tpos[i], rpos)
        new_mot[nbmot]   = tmot[i]
        new_pos[nbmot++] = rpos
      }        
    } else {
      new_mot[nbmot]   = tmot[i]
      new_pos[nbmot++] = tpos[i]
    }
  }

  if (nbmot < 1)
    exit

  # calculate sign
  for (i=0; i<nbmot; i++) {
    init_pos[i] = A[new_mot[i]]

    # calulate if we will move up (mv_sign == +1) or down (mv_sign = -1)
    if (new_pos[i] == init_pos[i])
      mv_sign[i] = 0  
    else if (new_pos[i] > init_pos[i])
      mv_sign[i] = +1
    else
      mv_sign[i] = -1
  }

  # calculate intermediate position  
  for (aux_mot2move=0,i=0; i<nbmot; i++) {
    if (ATTTABLE[ATTTYPE[motor_mne(new_mot[i])]]["step_size"] != -1) {
      aux_mot2move += 1
      nbfilt = ATTTABLE[ATTTYPE[motor_mne(new_mot[i])]]["nbfilt"]

      # move to an intermed. position to see switch change (which is on the
      # way to the filter but only if between the first and the last filter)
      if (mv_sign[i]) {
         new_filt[i] = int(dial(new_mot[i], new_pos[i])+0.5)
         if (new_filt[i] <= 1) {
            midpos[i] = user(new_mot[i], 1.5)
         } else if (new_filt[i] >= nbfilt) {
            midpos[i] = user(new_mot[i], nbfilt - 0.5)
         } else {
            midpos[i] = user(new_mot[i], new_pos[i] - mv_sign[i] * 0.5)
         }
         A[new_mot[i]] = midpos[i]
      }
    }
  }

  if (aux_mot2move != 0){
    # move all the axis to intermediate position
    p "*** Move axis to intermediate position"
    move_em
    waitmove ; get_angles
  }

  for (i=0; i<nbmot ; i++) {
     if (mv_sign[i]) {
       if (ATTTABLE[ATTTYPE[motor_mne(new_mot[i])]]["step_size"] != -1)
         if (_att_checkhomef(new_mot[i]) == 0) {
            p "WARNING: " motor_mne(new_mot[i]) " position switch not changing, check the hardware"
         }

       # set final position
       A[new_mot[i]] = new_pos[i]
     }
   }
  # move all the axis to final position
  p "*** Move axis to final position"
  move_em
  waitmove; get_angles

  for (i=0; i<nbmot; i++)
     if (mv_sign[i])
       if (ATTTABLE[ATTTYPE[motor_mne(new_mot[i])]]["step_size"] != -1)
         if (_att_checkhomef(new_mot[i]) != 0)
           p "WARNING: " motor_mne(new_mot[i])" position switch not reached"

  user_att_endmove
  ATT_MOVED_ON = 0
}'

#%UU% <axis mnemonic> <filter num> <pos>
#%MDESC% Set the position for filter #n of attenuator axis.
def attsetpos '{
local motmne filtname filtpos nbfilt i findfilt
  
  if ($# != 3) {
    printf ("Usage: attsetpos mne filter_name position\n")
    exit
  }

  motmne   ="$1"
  filtname = "$2"
  filtpos  = $3

  if (motmne in ATTTYPE) {
    nbfilt = ATTTABLE[ATTTYPE[motmne]]["nbfilt"]
    for (i=1,findfilt=0;i<=nbfilt;i++)
      if (ATTFILTNAME[motmne][i] == filtname)
        findfilt = i

    if (findfilt != 0) {
      ATTFILTPOS[motmne][findfilt] = filtpos
    } else {
      printf("Filter \"%s\" does not exist for attenuator \"%s\", exit !!\n", \
             filtname, motmne)
    }
  } else {
    printf("motor \"%s\" is not configured as attenuator, exit !!\n", motmne)
    exit
  }
}'

#%UU%
#%MDESC% Show selected filters on every axis.
def attwa '{
  local i motmne
  
  waitmove ; get_angles
  for (i=0;i<ATTNB;i++) {
    motmne = ATTNUM[i]
    printf("%s\t--->\t%s\n", motmne, _attwm_f(motmne))
  }
}'
#%IU% (motmne)
#%MDESC% Return the selected filter for motor %B%motmne%B%.
def _attwm_f(motmne) '{
  local mnum j filtsel
  mnum = motor_num(motmne)

  nbfilt = ATTTABLE[ATTTYPE[motmne]]["nbfilt"]
  for (filtsel="Unknown",j=1;j<=nbfilt;j++) {
    if ((A[mnum] < (ATTFILTPOS[motmne][j]+ATT_DELTA)) && \
        (A[mnum] > (ATTFILTPOS[motmne][j]-ATT_DELTA))) {
      filtsel = ATTFILTNAME[motmne][j]
    }
  }
  return filtsel
}'

def attshow '{
  attshow_ascii
}'

#%IU%
#%MDESC% Start the interactive ASCII menu.
def attshow_ascii '{
  local  _atteopt 
  local  _attemaxe _attemfilt
  local  attereading

  ATTE_ERROR=""

  for(;;) {
    _attshowmenu

    if (_att_locked()) {
      printf("\n\n%s\n\n", ATT_LOCKED_MSG)
      _atteopt = _getval_timeout("0 to exit", 0, ATT_MENU_TIMEOUT)
      if (_atteopt == "0" ) break
    } else {
      printf("\n\n\n\n")
      _atteopt = _getval_timeout("Choose filter number or 0 to exit", 0, ATT_MENU_TIMEOUT)

      printf("\n\n")

      if (_atteopt == "0" ) break

      if (_atteopt in ATTAXISSEL) {
        tmot[0] = ATTAXISSEL[_atteopt] + 0
        tpos[0] = ATTFILTERSEL[_atteopt] + 0
        _attmv(1, tmot, tpos)
      }
    }
  } 
}'

def _getval_timeout(msg, default, tout) '{
  local _s _c _t0
  if (!tout) {
    return getval(msg, default)
  }
  printf("%s (%s)? ", msg, default)
  _s= ""
  _t0= time()
  while (1) {
    sleep(0.01)
    _c= input(-1)
    if (_c) {
      _t0= time()
    }
    if (_c=="\n") {
      break
    } else if ((_c=="\b" || _c=="\177") && _s) {
      _s= substr(_s, 0, length(_s)-1)
      printf("\b \b")
    } else if (_c>=" " && _c<="z") {
      _s= _s _c
      printf(_c)
    }
    if (_c) {
      _t0= time()
    } else if ((time()-_t0)>tout) {
      break
    }
  }
  if (!_s) return (default)
  else return (_s)
}'
  

  
#%IU%
#%MDESC% Prepare and show the menu on the screen.
def _attshowmenu '{
  global ATTAXISSEL[]
  global ATTFILTERSEL[]
  local xoff yoff attex attey axmax nfil str newstr
  local att_active att_label att_axeperline i j

  att_axeperline=ATTNB>3?3:ATTNB
  
  if (ATTNB==2) {
    xdist = 22
  } else {
    xdist = 30
  }
  axmax=0
  yoff = 0 
  axis  = 0

  clscreen()
  printf("\n              *********** ")
  tty_cntl("us")
  printf(" Attenuator MENU " )
  tty_cntl("ue")
  printf(" ***********\n")

  for (i=0;i<ATTNB;i++) {
    motmne = ATTNUM[i]
    axis++;
    # 
    # Axis Header.
    attey = 3 + yoff 
    xoff = 5 
    if (ATTNB>2) { 
      mul = axis - 2
    } else if (ATTNB==2) {
      if (axis==1) {
        mul=-1
      } else {
        mul=1
      }
    } else {
      mul = 0
    }
    attex = 40 + xdist*mul - xoff
    
    if ((ATTTABLE[ATTTYPE[motmne]]["step_size"] != -1) && \
        (_att_checkhomef (motor_num(motmne)) == 4)) {
      newstr = sprintf("!!%s!!", motmne)
      str= sprintf(" %-12s",newstr)
      tty_cntl("md")
      tty_move(attex,attey,str)
      tty_cntl("me")
    } else {
      str= sprintf(" %-8s",motmne)
      tty_cntl("us")
      tty_move(attex,attey,str)
      tty_cntl("ue")
    }

    #
    # Filters
    nfil = ATTTABLE[ATTTYPE[motmne]]["nbfilt"]
    if (nfil>axmax) {
      axmax=nfil
      ATTE_Y= yoff + axmax + 5
    }
    waitmove ; get_angles

    for (j=0;j<nfil;j++) {
      att_label  = sprintf("%s", ATTFILTNAME[motmne][j+1])
      xoff=10
      attey = 4 + yoff + j
      attex = 40 + xdist*mul - xoff

      str= sprintf("%s",100*(i+1)+j+1)
      ATTAXISSEL[str] = motor_num(motmne)
      ATTFILTERSEL[str] = ATTFILTPOS[motmne][j+1]+0
      str= sprintf(" %s ",str)
      tty_cntl("md")
      tty_move(attex,attey,str)
      str= sprintf("%s", att_label)
      if ((A[motor_num(motmne)]<(ATTFILTPOS[motmne][j+1]+ATT_DELTAMAX)) && \
           (A[motor_num(motmne)]>(ATTFILTPOS[motmne][j+1]-ATT_DELTAMAX))) {
        tty_cntl("mr")
        tty_move(attex+5,attey,str)
        tty_cntl("me")
      } else {
        tty_cntl("me")
        tty_move(attex+5,attey,str)
      }
    }

    if (axis%att_axeperline==0) {
      yoff += axmax + 2
      axmax=0
      axis=0
    }
  }
}'

#%IU% <motor_mnemonic>
#%MDESC% Calculate [steps] the size  between the filters as well as the
#backlash. The procedure is: send the motor to a hardware limit, start moving
#(in fine steps) in the other direction to find all the home switches and
#store their positions. Repete the procedure in the oposite direction to
#estimate the backlash.
def attdiag '{
local motmne mnum stepsizeold stepsizenew course_neg course_pos
local backlashold limp limm i
local diffarr firstauxpos firstauxneg lastauxpos lastauxneg aux
global ATTDIAGPOS[]
global ATTDIAGNEG[]
local device

  if ($# < 1) {
    printf ("Usage: attdiag attmne [direction]\n")
    exit
  }
  
  if (_att_locked()) {
    printf("%s\n", ATT_LOCKED_MSG)
    exit
  }
  motmne = "$1"
  mnum = motor_num(motmne)
  if (mnum == -1) {
    printf("Motor %s is not configured, exit...\n", motmne)
    exit
  }

  device = motor_par(mnum,"device_id")

  # as we do not know the attenuator type, we work in steps
  stepsizeold = motor_par(mnum, "step_size")
  backlashold = motor_par(mnum, "backlash")
  limp        = get_lim(mnum, +1)
  limm        = get_lim(mnum, -1)
  if (stepsizeold < 0)
    stepsizenew = -1
  else
    stepsizenew = 1
  printf("Change step size from %d to %d\n", stepsizeold, stepsizenew)
  spec_par("modify_step_size", 1)
  motor_par(mnum, "step_size", stepsizenew)
  printf("Change backlash from %d to 0\n", backlashold)
  motor_par(mnum, "backlash", 0)
  printf("Change limits from (%g,%g) to (-1000000,1000000)\n", limm, limp)
  set_lim(mnum, -1000000, 1000000)

  printf("Go to both limits. Calculate total movement allowed\n")
  _att_gotolim(mnum, -1)

  chg_dial(mnum, 0)
  chg_offset(mnum, 0)

  course_pos = fabs(_att_gotolim(mnum, 1))
  chg_dial(mnum, 0)
  chg_offset(mnum, 0)
  printf("Maximum movement from NEG to POS is %d steps\n\n", course_pos)

  printf("Go to both limits in opposite direction. ")
  printf ("Calculate total movement allowed\n")
  #printf("Go to NEGATIVE limit to calculate total movement allowed\n")
  course_neg = fabs(_att_gotolim(mnum, -1))
  chg_dial(mnum, 0)
  chg_offset(mnum, 0)
  printf("Maximum movement from POS to NEG is %d steps\n\n", course_neg)

  printf("Diagnostic from NEGATIVE to POSITIVE switches\n")
  if (device == "icepap")
     nbfilt = _attrefallinit(mnum,1,50)
  else
    nbfilt = _attdiag(mnum, 1, course_pos)
  _attdiagprint(mnum, 1, course_pos, nbfilt)

  printf("Diagnostic from POSITIVE to NEGATIVE switches\n")
  if (device == "icepap")
     nbfilt = _attrefallinit(mnum,-1,50)
  else
    nbfilt = _attdiag(mnum, -1, course_pos)

  _attdiagprint(mnum, -1, course_neg, nbfilt)
  
  for (diffarr=0,i=2;i<=nbfilt-1;i++) {
    diffarr += (ATTDIAGPOS[motmne][sprintf("swstart%d",i)] - \
	ATTDIAGNEG[motmne][sprintf("swstart%d",i)])
  }
  diffarr /= nbfilt-2
  
  firstauxpos = ATTDIAGPOS[motmne]["swend1"]
  firstauxneg = ATTDIAGNEG[motmne]["swend1"]
  lastauxpos = ATTDIAGPOS[motmne][sprintf("swstart%d",nbfilt)]
  lastauxneg = ATTDIAGNEG[motmne][sprintf("swstart%d",nbfilt)]
  aux = ((firstauxpos+firstauxneg) > \
	(course_pos-lastauxpos+course_neg-lastauxneg)?-1:1)

  printf("***** Estimated backlash          : %d (steps)\n", int(diffarr))
  printf("***** Adviced backlash correction : %d (steps)\n", \
	aux*int(diffarr*1.5))
  
  motor_par(mnum, "step_size", stepsizeold)
  motor_par(mnum, "backlash",  backlashold)
  set_lim(mnum, limm, limp)
  spec_par("modify_step_size", 0)

}'

#%IU% (mnum, way, course, nbfilt)
#%MDESC% Print the information gathered during the diagnostic scan.
def _attdiagprint(mnum, way, course, nbfilt) '{
local infoarr motmne from to leng lengstr pos posstr pos_prec
local diff diffstr i fromstr
  
  infoarr  = (way==1?"ATTDIAGPOS":"ATTDIAGNEG")
  motmne = motor_mne(mnum)
  
  printf("\n***** Negative Hardware Limit at 0\n")
  printf("***** filter #  |    pos    (diff)    |   from   |    to    |  length  |\n")
  printf("***** ------------------------------------------------------------------\n")
  
  from = @infoarr[motmne]["swstart1"]
  to   = @infoarr[motmne]["swend1"]
  if (from == -1) {
    fromstr = "(     0)"
    lengstr = sprintf(">%7d", to)
    posstr  = " Unknown"
    pos_prec = -1
  } else {
    fromstr = sprintf("%8d", from)
    leng = fabs(from-to)
    lengstr = sprintf("%8d", leng)
    pos_prec = to - leng / 2
    posstr  = sprintf("%8d", pos_prec)
  }
  printf("*****     1     | %s            | %s | %8d | %s |\n",posstr,fromstr,to,lengstr)
  pos_prec = -1
  
  for (i=2;i<nbfilt;i++) {
    from = @infoarr[motmne][sprintf("swstart%d",i)]
    to   = @infoarr[motmne][sprintf("swend%d", i)]
    leng = fabs(from-to)
    pos  = to - leng / 2
    if (pos_prec > 0) {
      diff = pos - pos_prec
      printf("*****    %2d     | %8d (%8d) | %8d | %8d | %8d |\n",i,pos,diff,from,to,leng)
    } else {
      printf("*****    %2d     | %8d            | %8d | %8d | %8d |\n",i,pos,from,to,leng)
    }
    pos_prec = pos
  }
  
  from = @infoarr[motmne][sprintf("swstart%d",nbfilt)]
  to   = @infoarr[motmne][sprintf("swend%d", nbfilt)]
  if (to == -1) {
    tostr = sprintf("(%6d)", course)
    lengstr = sprintf(">%7d", course-from)
    posstr  = " Unknown"
    pos_prec = -1
    diffstr = "          "
  } else {
    tostr = sprintf("%8d", to)
    leng = fabs(from-to)
    lengstr = sprintf("%8d", leng)
    pos = to - leng / 2
    posstr  = sprintf("%8d", pos)
    diffstr = sprintf("(%8d)", pos - pos_prec)
  }
  printf("*****    %2d     | %s %s | %8d | %s | %s |\n",nbfilt,posstr,diffstr,from,tostr,lengstr)

  printf("***** positive Hardware Limit at %g\n\n", course)
}'

#%IU% (mnum, way, course)
#%MDESC% Internal function to scan the blade %B%mnum%B% in the direction of the
#scan %B%way%B% defined in advance as 1 (from negative to positive limit) or
#-1 (positive to negative limit). The algorithm is based on the home
#switchsignal being active or not moving in fine steps for at most %B%course%B%
#position. Returns the number of filters (home switches) found.
def _attdiag(mnum, way, course) '{
local step startpos endpos i nbfilt motmne nextpos
local prev_status new_status
local auxarr[]
  
  step     = way * 50
  startpos = (way==1?0:course)
  endpos   = (way==1?course:0)
  motmne   = motor_mne(mnum)

  auxarr[motmne]["HarLimNeg"] = 0  
  auxarr[motmne]["HarLimPos"] = course

  _att_gotolim(mnum, -way)
  waitmove ; get_angles
  
  printf("*** check filter #1\n")
  if (_att_checkhomef(mnum) == 0) {
    if (way == 1)
      auxarr[motmne]["swstart1"] = -1
    else
      auxarr[motmne]["swend1"] = -1
  } else {
    while (_att_checkhomef(mnum) == 4)  {
      A[mnum] += step
      move_em
      waitmove ; get_angles
    }
    if (way == 1)
      auxarr[motmne]["swstart1"] = A[mnum]
    else
      auxarr[motmne]["swend1"] = A[mnum]
  }
  while (_att_checkhomef(mnum) == 0)  {
    A[mnum] += step
    move_em
    waitmove ; get_angles
  }
  if (way == 1)
    auxarr[motmne]["swend1"] = A[mnum]
  else
    auxarr[motmne]["swstart1"] = A[mnum]
  
  printf("*** check filter #2\n")
  nbfilt = 1
  nextpos = A[mnum]+step
  test = (way==1?(nextpos<course):(nextpos>0))
  prev_status = _att_checkhomef(mnum)
  while (test) {
    A[mnum] = nextpos
    move_em
    waitmove ; get_angles

    new_status = _att_checkhomef(mnum)

    if ((prev_status == 4) && (new_status == 0)) {
      nbfilt++
      if (way == 1)
        auxarr[motmne][sprintf("swstart%d",nbfilt)] = A[mnum]
      else
        auxarr[motmne][sprintf("swend%d",nbfilt)] = A[mnum]
    } 
    
    if ((prev_status == 0) && (new_status == 4)) {
      if (way == 1)
        auxarr[motmne][sprintf("swend%d", nbfilt)] = A[mnum]
      else
        auxarr[motmne][sprintf("swstart%d", nbfilt)] = A[mnum]
      printf("*** check filter #%d\n", nbfilt+1)
    }
    
    prev_status = new_status
    nextpos = A[mnum] + step
    test = (way==1?(nextpos<course):(nextpos>0))
  }
  
  if (_att_checkhomef(mnum) == 0) {
    if (way == 1)
      auxarr[motmne][sprintf("swend%d", nbfilt)] = -1
    else
      auxarr[motmne][sprintf("swstart%d", nbfilt)] = -1
  }
  
  if (way == -1) {
    ATTDIAGNEG[motmne]["HarLimNeg"] = 0  
    ATTDIAGNEG[motmne]["HarLimPos"] = course
    for (i=1;i<=nbfilt;i++) {
      ATTDIAGNEG[motmne][sprintf("swstart%d",i)] = \
	auxarr[motmne][sprintf("swstart%d",nbfilt-i+1)]
      ATTDIAGNEG[motmne][sprintf("swend%d",i)] = \
	auxarr[motmne][sprintf("swend%d",nbfilt-i+1)]
    }
  } else {
    ATTDIAGPOS[motmne]["HarLimNeg"] = 0  
    ATTDIAGPOS[motmne]["HarLimPos"] = course
    for (i=1;i<=nbfilt;i++) {
      ATTDIAGPOS[motmne][sprintf("swstart%d",i)] = \
	auxarr[motmne][sprintf("swstart%d",i)]
      ATTDIAGPOS[motmne][sprintf("swend%d",i)] = \
	auxarr[motmne][sprintf("swend%d",i)]
    }
 }
  
  return(nbfilt)
}'

#%IU% (mnum)
#%MDESC% Internal function to scan the blade %B%mnum%B%. The direction of the
#scan (_SIGN) could be defined in advance as 1 (from negative to positive
#limit) or -1 (positive to negative limit). The algorithm is based on the home
#switch signal being active or not moving in fine steps, which absolute value
#(_CALIB_STEP) could be set as well. This is the procedure used with icepep
#controller. Returns the number of filters (home switches) found.
def _attrefallinit(mnum, way, step) '{
local a0 a1 mid dist cmd stat motmne
global ATTDIAGNEG ATTDIAGPOS ATTFILTPARS[]
local step_mm label device
local i ii

  motmne = motor_mne(mnum)
  step_mm = motor_par(mnum,"step_size")

  if (way)
    _SIGN = way
  if (step)
    _CALIB_STEP = step

  (_CALIB_STEP > 1) ? (label = "steps") : (label = "mm")

  a0 = _att_gotolim(mnum,-1* _SIGN)
  if (_ATTDEBUG)
    printf ("Limit switch reacheat at position %.2f\n", a0)

  sleep(.5)

  #check if home switch active
  if (_attswitchf (mnum, 0, 99) == 0) {
    printf ("\t - both limit and home switches active\n")
    a0 = A[mnum]
  } else {
    printf("\t - move until the home sitch of the first filter reached...\n")
    a0 = _attswitchf (mnum,_CALIB_STEP,4)
  }
  if (_ATTDEBUG)
    printf ("Home switch reached at position %.2f\n", a0)

  printf("\t - move until the home switch of the first filter left...\n")
  a1 = _attswitchf (mnum,_CALIB_STEP,0)
  mid[0] = a1 - _SIGN * fabs(a1-a0)/2

  if (_ATTDEBUG) {
    printf ("Home switch left at position %.2f\n", a1)
    printf ("Home switch size %.2f %s\n", fabs(a1-a0), label)
    if (fabs(step_mm) < 1)
      printf ("Home switch size %.2f mm\n", fabs((a1-a0)/step_mm))
  }

  if (_SIGN < 0) {
     ATTDIAGNEG[motmne]["swstart1"] = a0
     ATTDIAGNEG[motmne]["swend1"] = a1
  } else {
     ATTDIAGPOS[motmne]["swstart1"] = a0
     ATTDIAGPOS[motmne]["swend1"] = a1
  }

  i = 2
  while(1) {
    printf("\t - move until the home switch of filter #%1d reached...\n", i)
    a0 = _attswitchf (mnum,_CALIB_STEP,4)
    if (_ATTDEBUG)
      printf ("Home switch reached at position %.2f\n", a0)

    printf("\t - move until the home switch of filter #%1d left...\n", i)
    a1 = _attswitchf (mnum,_CALIB_STEP,0)
    if (_ATTDEBUG)
      printf ("Home switch left at position %.2f\n", a1)

    if (fabs(a1-a0) > 0.05) {
      if (_SIGN < 0) {
        ATTDIAGNEG[motmne][sprintf("swstart%d",i)] = a0
        ATTDIAGNEG[motmne][sprintf("swend%d",i)] = a1
      } else {
        ATTDIAGPOS[motmne][sprintf("swstart%d",i)] = a0
        ATTDIAGPOS[motmne][sprintf("swend%d",i)] = a1
      }
      mid[i-1] = a1 - _SIGN * fabs(a1-a0)/2
      dist[i-1] = fabs(mid[i-1] - mid[i-2])

     if (_ATTDEBUG) {
        printf ("Home switch size %.2f %s\n", fabs(a1-a0), label)
        printf ("\t - Filter distance: %.2f %s. ", dist[i-1], label)
        printf ("Filter (switch) size: %.2f %s\n", fabs(a1-a0), label)
        if (fabs(step_mm) < 1) {
          printf ("\t - Filter distance: %.2f mm. ", fabs(dist[i-1]/step_mm))
          printf ("Filter (switch) size: %.2f mm\n", fabs((a1-a0)/step_mm))
        }
      }
      stat = _attlimitswitchf (mnum, _SIGN)
      if (stat == 0)
        i++
      else
        break
    } else {
      printf ("No more filters, exit...\n")
      i--
      break
    }
  }
  printf ("%d filters found\n",i)
  if (fabs(_CALIB_STEP) < 1) {
    ATTFILTPARS[motmne]["filtpos1"] = 0
    #ATTFILTPARS[motmne]["filtpos1"] = mid[0]
    for (ii=2; ii<=i; ii++) {
      ATTFILTPARS[motmne][sprintf("filtpos%d",ii)] = \
	(mid[ii-2] + dist[ii-1])*_SIGN
    }
  }
  return(i)
}'

#%IU% ()
#%MDESC% Define a user macro to lock the attenuators.
def _att_locked() '{
  global ATT_LOCKED
  
  ATT_LOCKED = 0
  
  user_att_locked
  
  return(ATT_LOCKED)
}'

#%UU%
#%MDESC% Get the filter names and corresponding motor positions for the
#current axe from the default hardware object - xml file.
def att_getxml '{
local keys

  if (!ATT_HO[_ATTMOT]["filename"])
    ATT_HO[_ATTMOT]["filename"] = \
	getval("Attenuator axe hardware object", ATT_HO[_ATTMOT]["filename"])
  if (!ATT_HO[_ATTMOT]["root"])
    ATT_HO[_ATTMOT]["root"] = \
	getval("Attenuator axe hardware object root", ATT_HO[_ATTMOT]["root"])
  if (!ATT_HO[_ATTMOT]["name"])
    ATT_HO[_ATTMOT]["name"] = \
	getval("Attenuator axe hardware object username keyword", \
		ATT_HO[_ATTMOT]["name"])
  if (!ATT_HO[_ATTMOT]["offset"])
    ATT_HO[_ATTMOT]["offset"] = \
	getval("Attenuator axe hardware object offset keyword", \
		ATT_HO[_ATTMOT]["offset"])

  keys["name"] = ATT_HO[_ATTMOT]["name"]
  keys["offset"] = ATT_HO[_ATTMOT]["offset"]
  _att_getxml(ATT_HO[_ATTMOT]["filename"], _ATTMOT, \
	ATT_HO[_ATTMOT]["root"], keys)
}'

#%IU% (ho, motmne, keys_root, keys)
#%MDESC% Get the filter names and corresponding motor positions for the
#%B%motnum%B% axis from the %B%ho%B% hardware object - xml file.
def _att_getxml(ho, motmne, keys_root, keys) '{
local key name
global ATTFILTERS

  key = sprintf ("%s/%s", keys_root, keys["name"])
  if (xml_read(ho,key) == -1) {
    eprintf ("Cannot read the axis XML file, names not set\n")
    return(-1)
  }
  for (name in XML_tmp[]["__value__"]) {
    ATTFILTERS[motmne][sprintf ("name%d", name+1)] = XML_tmp[name]["__value__"]
  }

  key = sprintf ("%s/%s", keys_root, keys["offset"])
  if (xml_read(ho, key) == -1) {
    eprintf ("Cannot read the axis XML file, names not set\n")
    return(-1)
  }
  for (name in XML_tmp[]["__value__"]) {
    ATTFILTERS[motmne][sprintf ("pos%d", name+1)] = XML_tmp[name]["__value__"]
  }
  return(0)

}'

#%UU%
#%MDESC% Write the filter names and corresponding motor positions for the
#current axe from the default hardware object - xml file.  The information
#to write is taken from the ATTFILTERS arary.
def att_writexml '{
local keys
  if (!ATT_HO[_ATTMOT]["filename"])
    ATT_HO[_ATTMOT]["filename"] = \
	getval("Attenuator axe hardware object", ATT_HO[_ATTMOT]["filename"])
  if (!ATT_HO[_ATTMOT]["root"])
    ATT_HO[_ATTMOT]["root"] = \
	getval("Attenuator axe hardware object root", ATT_HO[_ATTMOT]["root"])
  if (!ATT_HO[_ATTMOT]["name"])
    ATT_HO[_ATTMOT]["name"] = \
	getval("Attenuator axe hardware object username keyword", \
		ATT_HO[_ATTMOT]["name"])
  if (!ATT_HO[_ATTMOT]["offset"])
    ATT_HO[_ATTMOT]["offset"] = \
	getval("Attenuator axe hardware object offset keyword", \
		ATT_HO[_ATTMOT]["offset"])

  keys["name"] = ATT_HO[_ATTMOT]["name"]
  keys["offset"] = ATT_HO[_ATTMOT]["offset"]
  _att_writexml(ATT_HO[_ATTMOT]["filename"], _ATTMOT, \
	ATT_HO[_ATTMOT]["root"], keys)
}'

#%IU% (ho, motmne, keys_root, keys)
#%MDESC% Write the filter names and corresponding motor positions for the
#%B%motnum%B% axis in the %B%ho%B% hardware object - xml file. The information
#to write is taken from the ATTFILTERS arary.
def _att_writexml(ho, motmne, keys_root, keys) '{
local i n newname newpos
local key

  n = ATTTABLE[ATTTYPE[motmne]]["nbfilt"]

  for (i=1;i<=n;i++) {
    key = sprintf("%s[%d]/%s",keys_root,i, keys["name"])
    newname = sprintf("%s", ATTFILTERS[motmne][sprintf("name%d", i)])
    xml_cache_write(ho, key, newname)
    #key = sprintf("/equipment/positions/position[%d]/motor",i)
    key = sprintf("%s[%d]/%s",keys_root,i, keys["offset"])
    newpos = sprintf("%g", ATTFILTERS[motmne][sprintf("pos%d", i)])
    xml_cache_write(ho, key, newpos)
  }  
  xml_do_write()
}'

#%UU% <motor_mnemonic [calibration_step [sign]]>
#%MDESC% Scan the blade %B%motor_mnemonic%B%. The direction of the movement
#%B%sign%B% is defined as 1 (from negative limit in positive direction) or -1
#(positive limit in negative direction) - default value is 1. The algorithm is
#based on the home switch signal being active or not, moving in fine steps,
#which absolute [mm] value %B%calibration_step%B% could be set - default value
#is 0.004 mm.
def attrefall '{
local mnum nbfilt olddebug
global _CALIB_STEP _SIGN _ATTDEBUG

  if (_att_locked()) {
    printf("%s\n", ATT_LOCKED_MSG)
    exit
  }

  if ($# < 1) {
    printf ("Usage: attrefall motor_mnemonic [calib_step [direction]]\n")
    _ATTMOT = getval("Attenuator motor mnemonic: ",_ATTMOT)
    _CALIB_STEP = fabs(getval("Attenuator calibration step [mm or step]: ", _CALIB_STEP))
    _SIGN = getval("Direction to scan - 1(positive)/-1(negative)", _SIGN)
    mnum =_check_mne(_ATTMOT)
  } else {
    _check0 "$1"
    _ATTMOT ="$1"
    mnum = $1
    _CALIB_STEP = fabs($2)
    _SIGN = $3
  }
 
  if (!_CALIB_STEP)
    _CALIB_STEP = 0.004

  if (!_SIGN)
    _SIGN = 1

  if (ATTTABLE[ATTTYPE[_ATTMOT]]["filter_distance_mm"] == -1) {
    printf("Axis without switches! Cannot get reference, exit.\n")
    exit
  }

  if (ATTTYPE[_ATTMOT] == 0 || \
	(nbfilt = ATTTABLE[ATTTYPE[_ATTMOT]]["nbfilt"]) == 0) {
    printf("Please, run attsetup first, exit\n")
    exit
 } 

  printf("**** Scanning filter positions of %s ****\n", _ATTMOT)
  olddebug = _ATTDEBUG
  _ATTDEBUG = 1
  _attrefall(mnum)

  printf ("Moving to filter #2...\n")
  A[mnum] = ATTFILTPOS[_ATTMOT][2]
  move_em; _show_waitf()
  _ATTDEBUG = olddebug
}'

#%IU% (mnum)
#%MDESC% Internal function to scan the blade %B%mnum%B%. The direction of the
#scan (_SIGN) should be defined in advance as 1 (from negative to positive
#limit) or -1 (positive to negative limit). The algorithm is based on the home
#switchsignal being active or not moving in fine steps, which absolute value
#(_CALIB_STEP) should be set.
#Returns the number of filters (home switches) found.
def _attrefall(mnum) '{
local a0 a1 mid dist cmd stat motmne
global ATTDIAGNEG ATTDIAGPOS ATTFILTPARS[]
local dist_mm label
local i ii

  motmne = motor_mne(mnum)

  if (_SIGN < 0) {
    cmd = sprintf ("motor_par(%d,\"low_lim_set\")",mnum)
  } else {
    cmd = sprintf ("motor_par(%d,\"high_lim_set\")",mnum)
  }

  if (_CALIB_STEP > 1) {
    label = "steps"
    dist_mm = 1
  } else {
    label = "mm"
    dist_mm = ATTTABLE[ATTTYPE[motmne]]["filter_distance_mm"]
  }

  a0 = _att_gotolim(mnum,-1* _SIGN)
  if (_ATTDEBUG)
    printf ("Limit switch reacheat at position %.2f\n", a0)

  step = _CALIB_STEP

  sleep(.5)

  #check if home switch active
  if (_attswitchf (mnum, 0, 99) == 0) {
    printf ("\t - both limit and home switches active\n")
    a0 = A[mnum]
  } else {
    printf("\t - move until the home sitch of the first filter reached...\n")
    a0 = _attswitchf (mnum,step,4)
  }
  if (_ATTDEBUG)
    printf ("Home switch reached at position %.3f\n", a0)

  printf("\t - move until the home switch of the first filter left...\n")
  a1 = _attswitchf (mnum,step,0)
  mid[0] = a1 - _SIGN * fabs(a1-a0)/2

  if (_ATTDEBUG) {
    printf ("Home switch left at position %.3f\n", a1)
    printf ("Home switch size %.2f %s\n", fabs((a1-a0)*dist_mm), label)
  }

  printf ("\t - move to a position between the first and second filters...\n")
  waitmove ; get_angles
  A[mnum] += 0.5 * _SIGN
  move_em
  _show_waitf()
  waitmove ; get_angles

  i = 2
  while(1) {
    printf("\t - move until the home switch of filter #%1d reached...\n", i)
    a0 = _attswitchf (mnum,step,4)
    if (_ATTDEBUG)
      printf ("Home switch reached at position %.3f\n", a0)

    printf("\t - move until the home switch of filter #%1d left...\n", i)
    a1 = _attswitchf (mnum,step,0)
    if (_ATTDEBUG)
      printf ("Home switch left at position %.3f\n", a1)

    if (fabs(a1-a0) > 0.05) {
      mid[i-1] = a1 - _SIGN * fabs(a1-a0)/2
      dist[i-1] = fabs(mid[i-1] - mid[i-2])

      if (_ATTDEBUG) {
        printf ("Home switch size %.2f %s\n", fabs((a1-a0)*dist_mm), label)
        printf ("\t - Filter distance: %.2f %s. ", \
		fabs(dist[i-1]*dist_mm), label)
        printf ("Filter (switch) size: %.2f %s\n", \
		fabs((a1-a0)*dist_mm), label)
      }
      stat = eval(cmd)
      if (stat == 0) {
        i++
        printf ("\t - move to a position between filter #%d and #%d ...\n", \
		(i-1), i)
        waitmove ; get_angles
        A[mnum] += 0.5 * _SIGN
        move_em
        _show_waitf()
      } else
        break
    } else {
      i--
      break
    }
  }

  tty_cntl("md")
  printf ("%d filter positions found:\n",i)
  if (_CALIB_STEP < 1) {
    ATTFILTPARS[motmne]["filtpos1"] = mid[0]
    printf ("\tFilter #1: %.2f\n", ATTFILTPARS[motmne]["filtpos1"])
    ATTFILTPOS[motmne][1] = mid[0]
    for (ii=2; ii<=i; ii++) {
      ATTFILTPARS[motmne][sprintf("filtpos%d",ii)] = \
	(mid[ii-2] + dist[ii-1])*_SIGN
      ATTFILTPOS[motmne][sprintf("%d",ii)] = \
	(mid[ii-2] + dist[ii-1])*_SIGN
      printf ("\tFilter #%d: %.2f\n", ii, \
	ATTFILTPARS[motmne][sprintf("filtpos%d",ii)])
    }
  }
  tty_cntl("me")
  return(i)
}'

#%IU% (mnum)
#%MDESC% Internal function to find the "zero reference filter". The algorithm
# is based on the spec home mechanism. The direction of the movement (_SIGN)
#should be defined in advance as 1 (from negative limit in positive
#direction) or -1 (positive limit in negative direction). The absolute value
#of the step for the fine move when the home switch is active (_CALIB_STEP)
#should be set. At the end move to the centre of the filter and set it as
#position 2.
def _attrefhome(mnum) '{
local a0 a1 dist mid[] step
global ATTFILTPARS[]

  a0 = _att_gotolim(mnum,-1* _SIGN)
  printf ("Limit switch reacheat at position %.2f\n", a0)

  step =  _CALIB_STEP

  sleep(.5)

  #check if home switch active
  if (_attswitchf (mnum, 0, 99) == 0) {
    printf ("\t - both limit and home switches active\n")
    a0 = A[mnum]
  } else {
    printf("\t - move until the home switch of the first filter reached...\n")
    if ( _SIGN > 0)
      chg_dial(mnum,"home+")
    else
      chg_dial(mnum,"home-")
    _show_waitf()
    a0 = A[mnum]
  }
  printf ("Home switch reached at position %.2f\n", a0)
  printf("\t - move until the home switch of the first filter left...\n")
  a1 = _attswitchf (mnum,step,0)
  printf ("Home switch left at position %.2f\n", a1)
  mid[0] = a1 -  _SIGN * fabs(a1-a0)/2
  printf ("Home switch size %.2f mm\n", fabs(a1-a0))

  printf("\t - move until the home switch of the second filter reached...\n")
    if ( _SIGN > 0)
      chg_dial(mnum,"home+")
    else
      chg_dial(mnum,"home-")
  _show_waitf()
  a0 = A[mnum]
  printf ("Home switch reached at position %.2f\n", a0)
  printf("\t - move until the home switch of the second filter left...\n")
  a1 = _attswitchf (mnum,step,0)
  printf ("Home switch left at position %.2f\n", a1)
  mid[1] = a1 - _SIGN * fabs(a1-a0)/2
  printf ("Home switch size %.2f mm\n", fabs(a1-a0))

  dist = fabs(mid[1] - mid[0])
  printf ("\t - Filter distance: %.2f. Filter (switch) size: %.2f\n", \
	dist, fabs(a1-a0))

  printf ("Moving to the centre of the first filter (%.2f)\n", mid[0])
  A[mnum] = mid[0]
  move_all; waitmove
  printf ("Set the position to 0\n")
  chg_dial(mnum,0)
  chg_offset(mnum,0)
  ATTFILTPARS[motor_mne(mnum)]["filtpos1"] = 0
  ATTFILTPARS[motor_mne(mnum)]["filtpos2"] = dist*_SIGN

}'

#%IU% (mnum)
#%MDESC% Internal function to scan the blade %B%mnum%B%. The direction of the
#scan (_SIGN) should be defined in advance as 1 (from negative to positive
#limit) or -1 (positive to negative limit). The algorithm is based on the home
#switch being hardware configured the same way for all the filters and uses
#the spec home mechanism. The absolute value of the step for the fine move
#when the home switch is active (_CALIB_STEP) should be set.
#Returns the number of filters (home switches) found.
def _attrefhomeall(mnum) '{
local a0 a1 dist step cmd stat motmne way
global ATTDIAGNEG ATTDIAGPOS ATTFILTPARS[]
local step_mm label
local i ii

  motmne = motor_mne(mnum)
  step_mm = motor_par(mnum,"step_size")

  if (_SIGN < 0) {
    cmd = sprintf ("motor_par(%d,\"low_lim_set\")",mnum)
  } else {
    cmd = sprintf ("motor_par(%d,\"high_lim_set\")",mnum)
  }

  (_CALIB_STEP > 1) ? (label = "steps") : (label = "mm")

  a0 = _att_gotolim(mnum,-1* _SIGN)
  if (_ATTDEBUG)
    printf ("Limit switch reacheat at position %.2f\n", a0)
  
  step = _CALIB_STEP * _SIGN

  sleep(.5)

  #check if home switch active
  if (_attswitchf (mnum, 0, 99) == 0) {
    printf ("\t - both limit and home switches active\n")
    a0 = A[mnum]
  } else {
    printf("\t - move until the home switch of the first filter reached...\n")
    if ( _SIGN > 0)
      chg_dial(mnum,"home+")
    else
      chg_dial(mnum,"home-")
    _show_waitf()
    a0 = A[mnum]
  }
  if (_ATTDEBUG)
    printf ("Home switch reached at position %.2f\n", a0)

  printf("\t - move until the home switch of the first filter left...\n")
  a1 = _attswitchf (mnum,step,0)
  if (fabs(a1-a0) > 0.05) {
    mid[0] = a1 - _SIGN * fabs(a1-a0)/2

    if (_ATTDEBUG) {
      printf ("Home switch left at position %.2f\n", a1)
      printf ("Home switch size %.2f %s\n", fabs(a1-a0), label)
      if (fabs(step_mm) < 1)
        printf ("Home switch size %.2f mm\n", fabs((a1-a0)/step_mm))
    }

    if (_SIGN < 0) {
      ATTDIAGNEG[motmne]["swstart1"] = a0
      ATTDIAGNEG[motmne]["swend1"] = a1
    } else {
      ATTDIAGPOS[motmne]["swstart1"] = a0
      ATTDIAGPOS[motmne]["swend1"] = a1
    }
    i = 2
  } else
    i = 1

  while(1) {
    printf("\t - move until the home switch of filter #%1d reached...\n", i)
    if ( _SIGN > 0)
      chg_dial(mnum,"home+")
    else
      chg_dial(mnum,"home-")
    _show_waitf()
    a0 = A[mnum]
    if (_ATTDEBUG)
      printf ("Home switch reached at position %.2f\n", a0)

    printf("\t - move until the home switch of the filter #%1d left...\n", i)
    a1 = _attswitchf (mnum,step,0)
    if (_ATTDEBUG)
      printf ("Home switch left at position %.2f\n", a1)

    if (fabs(a1-a0) > 0.05) {
      if (_SIGN < 0) {
        ATTDIAGNEG[motmne][sprintf("swstart%d",i)] = a0
        ATTDIAGNEG[motmne][sprintf("swend%d",i)] = a1
      } else {
        ATTDIAGPOS[motmne][sprintf("swstart%d",i)] = a0
        ATTDIAGPOS[motmne][sprintf("swend%d",i)] = a1
      }
      mid[i-1] = a1 - _SIGN * fabs(a1-a0)/2
      dist[i-1] = fabs(mid[i-1] - mid[i-2])

      if (_ATTDEBUG) {
        printf ("Home switch size %.2f %s\n", fabs(a1-a0), label)
        printf ("\t - Filter distance: %.2f %s. ", dist[i-1], label)
        printf ("Filter (switch) size: %.2f %s\n", fabs(a1-a0), label)
        if (fabs(step_mm) < 1) {
          printf ("\t - Filter distance: %.2f mm. ", fabs(dist[i-1]/step_mm))
          printf ("Filter (switch) size: %.2f mm\n", fabs((a1-a0)/step_mm))
        }
      }
      stat = eval(cmd)
      if (stat == 0)
        i++
      else
        break
    } else {
      printf ("No more filters, exit...\n")
      i--
      break
    }
  }
  printf ("%d filters found\n",i)
  ATTFILTPARS[motmne]["filtpos1"] = mid[0]
  for (ii=2; ii<=i; ii++)
    ATTFILTPARS[motmne][sprintf("filtpos%d",ii)] = \
	(mid[ii-2] + dist[ii-1])*_SIGN

  return(i)
}'

#### Obsolete macros - SpecGUI ####

#%IU% [<where to see atenuators (gui/ascii)]
#%MDESC% Starts the interactive menu. 
#If no parameter is set, choose gui if SpecGUI is running 0n the SPEC version.
#Otherewise take the parameter. 
def attshow_old '{
  if ( (($# != 1) && (!GUI_RUN)) || (($# == 1) && ("$1" != "gui")))
    attshow_ascii
  else
    attshow_gui

}'

#%IU%
#%MDESC% Starts the interactive ASCII menu.
def attshow_gui '{
  global ATT_PID
  local file

  if (\!(ATT_PID && file_info(ATT_PID,"alive"))) {
    file    = sprintf("/tmp/attgui_%s_%s.pid",USER,SPEC)
    attcmd  = "att_gui"
    unix(sprintf("/bin/rm -f %s",file))
    unix(sprintf("%s >/dev/null 2>&1 & echo \$! > %s",attcmd,file))
    ATT_PID = getline(file)
    getline(file,"close")  
  }
}'

#%IU%
#%MDESC%
# setup GUI use of attenuator, chains Xattwa with user_getpangles
def Xattsetup '{
  if (GUI_RUN) cdef("user_getpangles","Xattwa\n","_Xatt_",0x20)
  Xattwa 0
}'
 
#%IU%
#%MDESC%
# update GUI att filter.
# GUI outputfield name must be "att-mnemonic"_name, e.g. att1 -> att1_name
# This macro can be chained with user_getpangles.
def Xattwa '{
  local i name motmne moti
  global XOLDATT_A[]
  
  for (i=0;i<ATTNB;i++) {
    motmne = ATTNUM[i]
    moti = motor_num(motmne)
    if(A[moti] != XOLDATT_A[moti] || $#) {
      Xval(name=motmne"_name", _attwm_f(motmne))
      XOLDATT_A[moti]=A[moti]
    }
  }
}'

#%IU%
#%MDESC%
#  Send configuration to graphical application
def Xattconfig '{
  local i j str
  
  str = sprintf("%d %g", ATTNB, ATT_DELTA)
  for (i=0;i<ATTNB;i++) {
    str = sprintf(" %s %s", str, ATTNUM[i])
    str = sprintf(" %s %d", str, motor_num(ATTNUM[i]))
    nbfilt = ATTTABLE[ATTTYPE[ATTNUM[i]]]["nbfilt"]
    str = sprintf(" %s %d", str, nbfilt)
    str = sprintf(" %s %d", str, ATTSEC[ATTNUM[i]])
    for (j=1;j<=nbfilt;j++) {
      str = sprintf(" %s %s", str, ATTFILTNAME[ATTNUM[i]][j])
      str = sprintf(" %s %g", str, ATTFILTPOS[ATTNUM[i]][j])
    }
  }  
  bpipe attconfig
  fprintf("pipe", " %s ", str)
  epipe
}'

#### Enf obsolete macros ####
#%MACROS%
#%IMACROS%
#%AUTHOR% %BR%
#%TOC%
#$Revision: 1.17 $, $Date: 2014/10/14 13:27:32 $
#%END%
#%LOG%
#$Log: natt.mac,v $
#Revision 1.17  2014/10/14 13:27:32  papillon
#* added a global variable ATT_MENU_TIMEOUT : if set attshow
#  will silently exit after ATT_MENU_TIMEOUT sec without
#  any user input in order to not block spec session
#  for guis, spec server, ...
#  This variable is reset to 0 on attsetup to not disturb
#  current installation.
#
#Revision 1.16  2013/08/23 10:18:41  papillon
#* add a user hook macro on end of att move
#
#Revision 1.15  2013/04/16 13:48:42  beteva
#print warning for wrong configuration only if needed (ATT_CHECK!=0)
#
#Revision 1.14  2012/09/18 14:09:32  papillon
#* correct attwa / _attwm_f
#
#Revision 1.13  2012/03/07 16:28:38  beteva
#Commands to control the attenuator, using ICEPAP motors.
#Two new types (from ID24).
#
#Revision 1.12  2008/08/12 14:36:19  rey
#documentaiton changes
#
#Revision 1.11  2006/06/21 14:59:37  berruyer
#* add a new attenuator type: no switches
#their position without checks
#* add global variables to store motors and positions during a _attmv. To be used in user_att_lock for attenuators checking
#
#Revision 1.10  2006/02/06 08:52:24  rey
#Added support for ID14 attenuators
#
#Revision 1.9  2006/02/06 08:42:19  rey
#ID14 attenuators type added.
#
#Revision 1.8  2005/09/21 12:44:34  pepellin
#Use clscreen()
#
#Revision 1.7  2003/02/27 13:55:31  klora
#Changed documentation and WARNING message in attmv (motor number was given
#and not the mnemonic)
#
#Revision 1.6  2003/01/27 13:25:26  claustre
#move ATT_DELTAMV and ATT_DELTAMAX globals to attsetup macro to get them
#initialized right now.
#
#Revision 1.5  2003/01/10 15:55:45  berruyer
#add attenuator set for Spline beamline
#
#Revision 1.4  2002/09/11 08:13:32  berruyer
#correct sel_lm dans attdiag
#change display policy in attshow: add delta to be n filter and show if or not on switches
#add new id17 attenuator type (oxford 4filters 35mm)
#
#Revision 1.3  2002/04/30  10:38:00  10:38:00  berruyer (Gilles Berruyer)
#add parameters number ceck in attlimits
#
#Revision 1.2  2002/03/07  15:55:00  15:55:00  berruyer (Gilles Berruyer)
#add saving in files
#remove database support
#direct access via builder to natt_appli
#attlimits added
#attdiag added
#last correction: syntax error in Xattconfig, _attmv, added change of limits in attdiag and parameter presence in the same macros
#
#Revision 1.1  2001/03/19  14:40:55  14:40:55  beteva (Antonia Beteva)
#Initial revision
#