esrf

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

#%TITLE% ELMETER_MH.MAC
#%NAME% %B%elmeter_mh.mac%B% - Electrometer Macro Counter
#%DESCRIPTION%
# The macro set offers the possibility to set-up the electrometer TANGO
# attributes as macro counter.%BR%
# The macro counter controller defined in spec config is called %B%elmeter%B%
# and has as the ADDR the device server device (e.g. d29/elmeter/all)
# For each controller the %B%type%B% additional parameter shoud be
# configured (e.g.CONPAR:type = K_6485). Each counter should have a
# parameter %B%device%B% (e,g, CNTPAR:device = d29/keithley/wb) which is the
# one used to controll one counter only (for start, gain setting etc.) and
# an optional parameter %B%autorange%B% (e.g. CNTPAR:autorange = 1) which
# indicates if the automatic range should be set.
#%END%

need spec_utils.mac

#%IU% (hwn, type, unit, module, chan)
#%MDESC% Called after reading the config file. If return the string ".error.",
#spec will consider the channel unusable.
def elmeter_config(hwn, type, unit, module, chan) '{
global ELMETER_PARAMS ELMETER_DATA
global ELMETER_CH[]
global ELMETER_CTRL[] ELMETER_IDX[]

  if (ELMETER_PARAMS["debug"] != 0) {
    tty_cntl("md");printf ("elmeter_config type -> %s\n", type);tty_cntl("me")
    tty_cntl("md");printf ("elmeter_config hardware %s\n", hwn);tty_cntl("me")
    printf("unit %d, module %d, chan %d\n", unit, module, chan)
  }

  if (type == "ctrl") {
    unglobal ELMETER_CH
    global ELMETER_CH []
    if (get_index(elmeter_ADDR,unit) == -1)
      return ".error."

    ELMETER_CTRL[unit] = elmeter_ADDR
    return
  }

  if (type == "mot") {
    return
  }

  if (type == "cnt") {
    if (counter_par(hwn,"device_id") != "elmeter")
      return ".error."
    if (elmeter_getpropsc(hwn) == -1)
      return ".error."
    elm_cdefs(hwn)
  }
}'

#%IU% (hwn, key, p1, p2)
#%MDESC% Execute the command %B%key%B% for the counter %B%hwn%B%, with
#parameters %B%p1%B% and %B%p2%B% if needed. The keys used are as follows:%BR%
#%B%"start_one"%B% and %B%"prestart_all"%B% - start the counting
#Parameters: p1 - counting time.%BR%
#Parameters: p2 - 2 (count to a time preset - tcount()).%BR%
#%B%"counts"%B% - return the current counts.
#Parameters:none.%BR%
#%B%"get_status%B%" - only used for polling counter
#Parameters:none.%BR%
#%B%"halt_all%B%" - stop the counting and read the controller (all the
#counters in one go)
#Parameters: p1 - controller unit number.%BR%
#Return the string ".error.", spec will consider the channel unusable.
def elmeter_cmd(hwn, key, p1, p2, unit) '{
local dev attr ret _pos add_attr l_data nb_data
global ESRF_ERR

  if (ELMETER_PARAMS["debug"] != 0) {
    tty_cntl("md");printf ("elmeter_cmd %s\n", key);tty_cntl("me")
  }

  if (key == "prestart_all") {
    if (ELMETER_PARAMS["debug"] != 0) {
      printf (" --------> mne %s, cnt_time %f, cnt_mode %d, unit %d\n",\
	hwn, p1, p2, unit)
    }
    if (hwn == "..") {
      for (i in ELMETER_CH) {
          ret = elm_start(i,p1)
        if (ret == -1) {
          return ".error."
        }
      }
    }
  } else if (key == "start_one") {
    if (ELMETER_PARAMS["debug"] != 0)
      printf (" --------> start for device %d\n", hwn)
    if (p2 == 0) {
      #do nothing if counter
      return
    }
  } else if (key == "halt_all") {

    if (ELMETER_PARAMS["debug"] != 0){
        printf(" --------> halt all ctrl %d\n", p1)
    }

    elm_stopall()

    ret = elm_read(p1) # p1 = controller number
    if (ret == -1) {
      return ".error."
    }
    else {
      split(ret, l_data)
      for (nb_data in l_data)
         ELMETER_DATA[p1][nb_data] = l_data[nb_data]
    }
  } else if (key == "halt_one") {
    if (ELMETER_PARAMS["debug"] != 0)
      printf(" --------> halt all counter %d\n", hwn)
  } else if (key == "counts") {
      if (ELMETER_PARAMS["debug"] != 0) {
          printf(" --------> read counter %d (%s)\n", hwn, cnt_mne(hwn))
          printf(" -------->    unit:%d index: %d ", counter_par(hwn,"unit"), counter_par(hwn,"index"))
          print " --> ELMETER_DATA="
          print ELMETER_DATA
      }
      if (counter_par(hwn,"type") == "K_6482"){
          ret = ELMETER_DATA[counter_par(hwn,"unit")][counter_par(hwn,"channel")]
      }
      else{
          ret = ELMETER_DATA[counter_par(hwn,"unit")][counter_par(hwn,"index")]
      }
      return ret
  }
}'

#%IU% (hwn, key, action, p1)
#%MDESC% Get/set the corresponding electrometer counter par
def elmeter_par(hwn, key, action, p1) '{
  local ret elm_dev

  if (key == "device")
    return ""

  elm_dev = counter_par(hwn, "device")
  if (key == "acq_afap") {
    TANGO_ERR = "-1"
    if (action == "get") {
      ret = tango_get(elm_dev, key)
    } else {
      ret = tango_put(elm_dev, key, p1)
    }
    if (TANGO_ERR != "0") {
      tty_cntl("md")
      print_tango_err()
      tty_cntl("me")
      return(-1)
    } else {
      return ret
    }
  }

}'

#%IU% (ctrl,unit)
#%MDESC% Get the index in the list of devices, served by the %B%ctrl%B%
# controller device server.
def get_index(ctrl,unit) '{
global ELMETER_IDX[]
local idx[] i j

  if (ELMETER_PARAMS["debug"] != 0)
    printf ("Calling get index for controller %s, unit %d\n", ctrl, unit)

  TANGO_ERR = "-1"
  tango_io(ctrl,"timeout",10)
  tango_io(ctrl, "GetDevicesList", idx)
  if (TANGO_ERR != "0") {
    print_tango_err()
    return(-1)
  }

  for (i in idx) {
    ELMETER_IDX[unit][i] = tolower(idx[i])
  }

}'

#%IU% (ctn)
#%MDESC% Get the counter properties. Save it as a index counter parameter.
def elmeter_getpropsc(ctn) '{
local dev key i unit
global ELMETER_PARAMS

  if (ELMETER_PARAMS["debug"] != 0) {
    tty_cntl("md");printf ("elmeter_getpropsc %d\n", ctn);tty_cntl("me")
  }

  if (counter_par(ctn,"controller") != "MAC_CNT") {
    counter_par(ctn,"disable",1)
    return(-1)
  }

  unit = counter_par(ctn,"unit")
  dev = counter_par(ctn,"device")

  for (i in ELMETER_IDX[unit]) {
    if (tolower(dev) == ELMETER_IDX[unit][i]) {
      counter_par(ctn,"index", i, "add")
      if (counter_par(ctn,"type") == "K_6482")
        ELMETER_CTRL[unit][dev] = 0
      S[ctn] = 0
      break
    }
  }

  ELMETER_CH[ctn] = ctn
  counter_par(ctn,"counting",0,"add")

  TANGO_ERR = "-1"
  tango_io(dev,"timeout",10)
  if (TANGO_ERR != "0") {
    print_tango_err()
    counter_par(ctn,"disable",1)
    return(-1)
  }
  return(elm_autorange_status(ctn))
}'

#%IU% (hwn,p1)
#%MDESC% Start the %B%hwn%B% counting for %B%p1%B% seconds.
def elm_start(hwn,p1) '{
local ret dev unit type

  if (counter_par(hwn,"disable") != 0)
    return(0)

  if (counter_par(hwn,"counting") == 1)
      return(0)

  dev = counter_par(hwn,"device")
  unit = counter_par(hwn,"unit")
  type = counter_par(hwn,"type")

  if ((type == "K_6482") && (ELMETER_CTRL[unit][dev] == 1))
    return(0)

  TANGO_ERR = "-1"
  ret = tango_io(dev,"StartAcq",p1)
  if (TANGO_ERR != "0") {
    tty_cntl("md")
    printf ("counter %s:",cnt_mne(hwn))
    print_tango_err()
    tty_cntl("me")
    counter_par(hwn,"counting",0)
    return(-1)
  }
  counter_par(hwn,"counting",1)
  if (type == "K_6482")
    ELMETER_CTRL[unit][dev] = 1
  return(ret)
}'

#%IU% ()
#%MDESC% Stop all the configured counters.
def elm_stopall() '{
  local i
  for (i in ELMETER_CH) {
    elm_stop(i)
  }
}'

#%IU% (hwn)
#%MDESC% Stop the %B%hwn%B% counting.
def elm_stop(hwn)  '{
  local dev

  dev = counter_par(hwn,"device")
  if (counter_par(hwn,"counting") == 1) {
    tango_io(dev,"WaitAcq")
    counter_par(hwn,"counting",0)
    if (counter_par(hwn,"type") == "K_6482")
      ELMETER_CTRL[counter_par(hwn,"unit")][dev] = 0
  }
}'

#%IU% (p1)
#%MDESC% Read the %B%p1%B% controller. Mark all the read counters as not counting.
def elm_read(p1) '{
  local ret vals[] ii

  if (ELMETER_CTRL[p1] == 0)
    return(-1)

  TANGO_ERR = "-1"
  tango_io(ELMETER_CTRL[p1], "Read", vals)

  if (TANGO_ERR != "0") {
    tty_cntl("md")
    print_tango_err()
    tty_cntl("me")
    return(-1)
  }

  if (ELMETER_PARAMS["debug"] != 0) {
      print(" ----> READ, vals=")
      print(vals)
  }

  # Build a string containing read values.
  ret = ""
  for (ii in vals){
    # Now spec is iterating in the good order...
    ret = sprintf ("%s %g", ret, vals[ii])
  }

  for (ii in ELMETER_CH) {
    if (ELMETER_CTRL[p1] == elmeter_ADDR) {
      counter_par(ii, "counting", 0)
    }
  }

  if (ELMETER_PARAMS["debug"] != 0) {
      printf(" -----> read ret value = %s\n", ret)
  }

  return (ret)
}'

#### Autorange ####
#%IU% (hwn)
#%MDESC% Stop the %B%hwn%B% counter autorange.
def elm_autorange_off(hwn) '{

  return(elm_autorange_set(hwn, 0))
}'

#%IU% (hwn)
#%MDESC% Start the %B%hwn%B% counter autorange.
def elm_autorange_on(hwn) '{

  return(elm_autorange_set(hwn, 1))
}'

#%IU% (hwn)
#%MDESC% Get the %B%hwn%B% counter autorange status (1 if autorange, 0 if not)
def elm_autorange_status(hwn) '{
  local ret

  TANGO_ERR = "-1"
  ret = tango_get(counter_par(hwn,"device"), "autorange")
  ELMETER_PARAMS[hwn]["autorange"] = ret

  if (TANGO_ERR != "0") {
    tty_cntl("md")
    print_tango_err()
    tty_cntl("me")
    #disable the counter if error
    counter_par(hwn,"disable",1)
    return(-1)
  }

  counter_par(hwn, "disable", 0)

  return(ret)
}'

#%IU% (hwn,stat)
#%MDESC% Set the %B%hwn%B% counter autorange on (%B%stat%B%=1) or off (stat = 0).
def elm_autorange_set(hwn, stat) '{
  local ret autor

  if (counter_par(hwn,"disable") != 0)
    return(stat)

  autor = ELMETER_PARAMS[hwn]["autorange"]
  ELMETER_PARAMS[hwn]["saved_autorange"] = autor
  ELMETER_PARAMS[hwn]["autorange"] = stat
  TANGO_ERR = "-1"
  ret = tango_put(counter_par(hwn,"device"),"autorange",stat)
  if (TANGO_ERR != "0") {
    tty_cntl("md")
    ELMETER_PARAMS[hwn]["autorange"] = autor
    print_tango_err()
    tty_cntl("me")
    return(-1)
  }
  elm_autorange_status(hwn)
}'

def elm_autorange_restore(hwn) '{
  return(elm_autorange_set(hwn, ELMETER_PARAMS[hwn]["saved_autorange"]))
}'

#### Autorange end ####

#### Range get/set ####
#%IU% (value)
#%MDESC% Convert 2e-xxx to xxx
def _pow2int(value) '{
local ii res

  ii = 0
  res = value
  while(res < 1) {
    ii++
    res *= 10
  }
  return(ii)
}'

#%UU% <[c_mne]>
#%MDESC% Get the %B%c_mne%B% counter range.
def elm_getrange '{
  local c_mne rr val jj

  val = 0
  c_mne = "$1"
  if (!c_mne) {
    # Read ALL ranges
    for (jj in ELMETER_CH) {
      rr = _elm_getrange(jj)
      if (rr != -1) {
        val =  _pow2int(rr)
      }
      if (elm_autorange_status(jj) == 1) {
        printf ("\n%s autorange set, current range is %g\n", cnt_mne(jj), rr)
      } else {
        printf ("\n%s range is set to: %d (max input current %g A)\n", \
		cnt_mne(jj), val, rr)
      }
    }
  } else {
    cntnum = cnt_num(c_mne)
    if (cntnum == -1) {
      eprintf ("Invalid counter mnemonic %s, exiting...\n", "$1")
      exit
    }
    rr = _elm_getrange(cntnum)
    if (rr != -1) {
      val =  _pow2int(rr)
    }
    if (elm_autorange_status(cntnum) == 1) {
      printf ("\n%s autorange set, current range is %g\n", c_mne, rr)
    } else
      printf ("\n%s range is set to: %d (max input current %g A)\n", \
      	     c_mne, val, rr)
  }
}'

#%IU% (hwn)
#%MDESC% Get the %B%hwn%B% counter range.
def _elm_getrange(hwn) '{
local ret

  TANGO_ERR = "-1"
  ret = tango_get(counter_par(hwn,"device"), "range")

  if (TANGO_ERR != "0") {
    tty_cntl("md")
    print_tango_err()
    tty_cntl("me")
    # Disable the counter in case of error.
    counter_par(hwn, "disable", 1)
    return(-1)
  }

  counter_par(hwn, "disable", 0)

  return(ret)
}'

#%UU% [cntmne range]
#%MDESC% Set the %B%cntmne%B% counter %B%range%B% - allowed values:
#2 (corersponds to 20mA + 5% = 21mA maximum input current)
#to 9 (2nA + 5% = 2.1nA maximum input current). Note that setting the
#range stops the autorange mode!
def elm_setrange '{
local cntmne rr

  cntnum = cnt_num("$1")
  if (cntnum == -1) {
    eprintf ("Invalid counter mnemonic %s, exiting...\n", "$1")
    exit
  }
  rr = int($2)
  if ((rr < 2) || (rr > 9)) {
    eprintf ("Invalid range number. Please give new value (2 to 9)")
    rr = int(input(": "))
  }
  rr = sprintf ("2e-%d", rr)
  if (_elm_setrange(cntnum, rr) == 0)
    printf ("\nRange set to: %d (max input current %g A)\n", $2, (rr+rr*0.05))
  else
    eprintf ("Attention! Range not set and counter %s disabled\n", "$1")
}'

#%IU% (hwn, range)
#%MDESC% Set the %B%hwn%B% counter %B%range%B% - allowed values: 2e-2 to 2e-9
#Note that setting the range stops the autorange mode!
def _elm_setrange(hwn, rr) '{

  TANGO_ERR = "-1"
  tango_put(counter_par(hwn,"device"),"range", rr)
  if (TANGO_ERR != "0") {
    tty_cntl("md")
    print_tango_err()
    tty_cntl("me")
    #disable the counter if error
    counter_par(hwn,"disable",1)
    return(-1)
  }
  ELMETER_PARAMS[hwn]["autorange"] = 0
  counter_par(hwn,"disable",0)
  return(0)
}'
#### Range get/set end ####

#### Rate get/set ####
#%UU% <[counter_mnemonic]>
#%MDESC% Get the %B%c_mne%B% counter rate.
def elm_getrate '{
  local c_mne rr val jj

  val = 0
  c_mne = "$1"
  if (!c_mne) {
    # Read ALL rates
    for (jj in ELMETER_CH) {
      rr = _elm_getrate(jj)
      if (rr != -1) {
        printf ("\n %s rate is set to:  %g (sampling time : %gms)\n", cnt_mne(jj), rr, rr*20)
      }
    }
  } else {
    cntnum = cnt_num(c_mne)
    if (cntnum == -1) {
      eprintf ("Invalid counter mnemonic %s, exiting...\n", "$1")
      exit
    }
    rr = _elm_getrate(cntnum)
    if (rr != -1) {
      printf ("\nRate is set to: %g (sampling time : %gms)\n", rr, rr*20)
    }
  }
}'

#%IU% (hwn)
#%MDESC% Read the %B%hwn%B% counter rate.
def _elm_getrate(hwn) '{
  local ret

  TANGO_ERR = "-1"
  ret = tango_get(counter_par(hwn, "device"), "rate")

  if (TANGO_ERR != "0") {
    tty_cntl("md")
    print_tango_err()
    tty_cntl("me")
    return(-1)
  }

  return(ret)
}'

#%UU% [cntmne rate]
#%MDESC% Set the %B%cntmne%B% counter %B%rate%B% - allowed values:
#0.01 to 5 (corresponding to 0.2ms to 100ms sampling time).
def elm_setrate '{
local cntnum rr

  cntnum = cnt_num("$1")
  if (cntnum == -1) {
    eprintf ("Invalid counter mnemonic %s, exiting...\n", "$1")
    exit
  }
  rr = $2
  if ((rr < 0.01) || (rr > 5)) {
    eprintf ("Invalid rate number. Please give new value (0.01 to 5)")
    rr = input(": ")
  }
  if (_elm_setrate(cntnum, rr) == 0)
    printf ("\nCounter %s rate set to: %g (sampling time:%gms)\n", "$1", rr, rr*20)
  else
    eprintf ("Attention! Cannot set counter %s rate!\n", "$1")
}'

#%IU% (hwn, rate)
#%MDESC% Set the %B%hwn%B% counter %B%rate%B% - allowed values: 0.01 to 5
def _elm_setrate(hwn, rr) '{

  TANGO_ERR = "-1"
  tango_put(counter_par(hwn,"device"),"rate", rr)
  if (TANGO_ERR != "0") {
    tty_cntl("md")
    print_tango_err()
    tty_cntl("me")
    return(-1)
  }
  return(0)
}'

#### Rate get/set end ####

#%IU% (hwn)
#%MDESC% Set some %B%hwn%B% counter defs to be used whith scans.
def elm_cdefs(hwn) '{
local cnt

  #cnt = counter_par(hwn,"unit")
  if (counter_par(hwn,"autorange") == 1) {
    cnt = hwn
    cdef ("user_prescan_head", sprintf("elm_autorange_off(%d);",cnt),sprintf ("_elm%d_",cnt),0x20)
    cdef ("user_scan_tail", sprintf("elm_autorange_restore(%d);",cnt),sprintf ("_elm%d_",cnt),0x20)
    cdef ("_cleanup3", sprintf("elm_autorange_restore(%s);",cnt),sprintf ("_elm%d_",cnt),0x20)
  }
}'

#%UU%
#%MDESC% Removes all cdef and globals for all the electrometer macro counters.
def elm_unsetup '{
local jj

  for (jj in ELMETER_CH) {
    _elm_unsetup(jj)
  }

  unglobal ELMETER_PARAMS ELMETER_DATA
  unglobal ELMETER_CH
  unglobal ELMETER_CTRL ELMETER_IDX
}'

#%IU% (cnt)
#%MDESC% removes cdef and globals for counter number %B%cnt%B%.
def _elm_unsetup(cnt) '{

  cdef ("user_prescan_head","", sprintf ("_elm%d_", cnt), "delete")
  cdef ("user_scan_tail",   "", sprintf ("_elm%d_", cnt), "delete")
  cdef ("_cleanup3",        "", sprintf ("_elm%d_", cnt), "delete")

  delete ELMETER_CH[cnt]
  delete ELMETER_PARAMS[cnt]["autorange"]
}'

######## bench macros ########
#%IU% (cmd, nb, tt)
#%MDESC% Do a bench of the command %B%cmd%B%, %B%nb%B% times. Subtracs the
#count time %B%tt%B% if needed. Return the mean execution time value.
def bench_ct(cmd,nb,tt) '{
global float array TT[nb]
local i
local oldtime tunits tlabel

  for (i = 0; i<nb; i++) {
    oldtime=time()
    eval(cmd)
      oldtime = time()-oldtime
    if (oldtime > 60) {
      tunits = 60; tlabel="min."
      munits = 1e5
    } else if (oldtime > 1) {
      tunits = 1; tlabel="sec."
      munits = 1e3
    } else {
      tunits = 0.001; tlabel="msec."
      munits = 1e0
    }
    if (tt)
      tt = S[0]
    TT[i] = ((oldtime - tt)/tunits) * munits
  }
  return(array_op("sum", TT)/nb)
}'

######## bench macros END ########
#%IMACROS%
#%TOC%
#%DEPENDENCIES% spec_utils.mac %BR%
#%AUTHOR% A.Beteva%BR%
#$Revision: 2.9 $ $Date: 2020/03/10 17:50:10 $
#%END%
#%LOG%
#$Log: elmeter_mh.mac,v $
#Revision 2.9  2020/03/10 17:50:10  guilloud
#fix order of index list of devices (l. 311) + debug
#(+ quote typo)
#
#Revision 2.7  2020/03/08 15:10:14  guilloud
#formating + fix TANGO_ERR
#
#Revision 2.6  2016/05/10 09:21:15  ahoms
#Add As-Fast-As-Possible "acq_afap" mode Tango attr. control as counter_par
#
#Revision 2.5  2015/09/01 12:54:57  beteva
#changed the type parameter from controller to device to be able to mix different Keithleys in the same device
#
#Revision 2.3  2014/05/15 14:13:40  beteva
#Added elm_unsetup (remove configured electrometer(s)).
#Changed getrange to read the autorange state systematically.
#
#Revision 2.2  2014/05/12 12:08:02  beteva
#Added elm_autorange_set and elm_autorange_restore commands.
#Add cdefs only if the counter has autorange property set to 1.
#
#Revision 2.1  2014/05/06 12:41:55  beteva
#Added Set/Get rate commands. Corrected typo in setrange.
#
#Revision 2.0  2014/04/30 12:53:38  beteva
#Changed range and autorange handling from commands to attributes
#(as from v1.5 of the device server).
#Now elm_getrange reads all availble counter with no input parameter.
#
#Revision 1.8  2014/04/02 11:50:39  guijarro
#replaced StopAcq by WaitAcq
#
#Revision 1.7  2013/10/08 13:52:33  beteva
#changed TANGO_ERR = "" to TANGO_ERR = "-1"
#
#Revision 1.6  2013/05/16 11:16:22  lagier
#fixed ELMETER_CH global declaration as an array.
#
#Revision 1.5  2013/04/24 12:27:04  beteva
#added elm_getrange/elm_setrange macros
#
#Revision 1.4  2012/07/30 11:59:55  beteva
#moved the search for index from the controller to the counter config;
#changed autorange_* to elm_autorange_*.
#
#Revision 1.3  2012/05/09 11:59:29  beteva
#Add an extra check in the get_index to avoid calls to otehr macro counters
#
#Revision 1.2  2012/04/25 12:31:41  beteva
# Disable a counter if tango error at setup. Corrected a typo.
#
#Revision 1.1  2012/01/26 14:53:26  beteva
#Initial revision
#