esrf

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

#%TITLE% TFOC.MAC
#
#%NAME%
#  TFOC.MAC - Manages the transfocator device through WAGO DAC and I/O
#
#%DESCRIPTION%
#
#%END%
#



constant TFFILE    BLISSADM"/local/spec/userconf/tfoc.dat"
constant TFDAC      "dac_ch"
constant TFLIM     "lim_ch"
constant TFENA     "ena_ch"
constant TFLSON    0
constant TFLSOFF   1
constant TFTIMEOUT 40





#%UU% <edit|show|update>
#%MDESC%
# edit  : create or modify a configuration%BR%
# show  : print current configuration loaded%BR%
# update: load configuration from file%BR%
#
def tfsetup '{
   local usage action

   if (!$#) {
      _tfsetup_usage("$0")
      exit
   }

   action = "$1"
   if (action == "show")   { _tfsetup_show(); return }
   if (action == "new")    { _tfsetup_new();  return }
   if (action == "edit")   { _tfsetup_edit(); return }
   if (action == "update") { _tfsetup_read(); return }

   _tfsetup_usage("$0")
}'


#%IU%
#%MDESC%
#
def _tfsetup_usage(arg) '{
  print
  print "Usage    : ",arg," <edit|show|update>"
  print "   edit  : create or modify a configuration"
  print "   show  : print current configuration loaded"
  print "   update: load configuration from file"
}'

#%IU%()
#%MDESC%
#
def _tfsetup_cfgfile() '{
  local dirs[]
  local n

  n     = split(TFFILE, dirs, "/")
  return(dirs[n-1])
}'


#%IU%()
#%MDESC%
#
def _tfsetup_edit() '{
  local fname

  fname = _tfsetup_cfgfile()
  if (!file_info(TFFILE, "isreg")) {
    print "WARNING: configuration file \""fname"\" is missing, creating it"
    _tfsetup_new()
  }

  print "Editing \""fname"\" ... "
  unix(sprintf("%s %s", SEDITOR, TFFILE))

  _tfsetup_read()

}'


#%IU%()
#%MDESC%
#
def _tfsetup_new() '{
  local blabla

  if (file_info(TFFILE, "isreg")) {
    if (yesno("Configuration file alredy exists. Do you want to delete it",0)) {
      unix(sprintf("\\rm -i %s", TFFILE))
    }
  }

  blabla = \
   "# Configuration file for Transfocator control through Wago box\n" \
   "# A single instance can be declared in this file.\n" \
   "#\n" \
   "# Syntax: \n" \
   "#   wagoname <wcidxxx>\n"  \
   "#   vin      <+volts>\n"  \
   "#   vout     <-volts>\n"  \
   "#   vstop    <+volts>\n"  \
   "#   naxes    <number>\n"  \
   "#   "TFDAC"   <string>\n"  \
   "#   "TFLIM"   <string>\n"  \
   "#   "TFENA"   <string>\n"  \
   "#\n" \
   "#   <actuator1> <desc_text> <N/R>\n" \
   "#   <actuator2> <desc_text> <N/R>\n" \
   "#   ....\n" \
   "#   <actuatorN> <desc_text> <N/R>\n" \
   "#\n" \
   "# Notes: \n" \
   "#   <wcidxxx>   is the WAGO box hostname\n"  \
   "#   vin         is positive voltage to apply to move piezo in  beam\n"  \
   "#   vout        is negative voltage to apply to move piezo out beam\n"  \
   "#               the higher these values are, the faster the piezo moves\n"\
   "#   naxes       is the number of piezos controlled by the WAGO box\n" \
   "#   "TFDAC"      WAGO logical channel configured for DACs\n" \
   "#   "TFLIM"      WAGO logical channel configured for switches inputs\n" \
   "#   "TFENA"      WAGO logical channel configured for enable ouput\n" \
   "#   <actuatorN> is the actuator position, from 1 to <naxes>\n" \
   "#               a position not given is considered as empty\n" \
   "#   <desc_text> is description text of the actuator, within quotes\n" \
   "#   <N/R>       is the N/R ratio of the actuator, ex: 4/0.2\n" \
   "#\n" \
   "wagoname wcidxxx\n"  \
   "vin      +8\n" \
   "vout     -8\n" \
   "naxes    20\n" \
   TFDAC"   xxxx\n" \
   TFLIM"   xxxx\n" \
   TFENA"   xxxx\n" \
   "\n" \
   "#Actuator     \"Description Text\"      N/R\n" \
   "1             \"alignement pinhole\"    0\n" \
   "3             \"2D 1 x R=1.5mm\"        1/1.5\n" \
   "4             \"2D 1 x R=1.0mm\"        1/1.0\n" \


  fprintf(TFFILE, "%s", blabla)
  close(TFFILE)
  if (!file_info(TFFILE, "isreg")) {
    print "ERROR: Can\'t create file: " TFFILE
    exit
  }

}'



#%IU%()
#%MDESC%
#
def _tfsetup_show() '{
  tfshow
}'


#%IU%()
#%MDESC%
#
def _tfsetup_read() '{
  global TFMAIN[]
  global TFDESC[]
  global TFN[]
  global TFR[]
  local  line
  local  lineraw
  local  arg
  local  wcname
  local  vin 
  local  vout
  local  vstop
  local  naxes
  local  fname
  local  dac_ch
  local  lim_ch
  local  ena_ch
  local  i
  local  c



  #
  # Minimum check on configuration file
  #
  fname = _tfsetup_cfgfile()
  if (!file_info(TFFILE, "isreg")) {
    print "ERROR: configuration file \""fname"\" is missing"
    print "HINT : use \"fsetup edit\" to create it"
    return(-1)
  }

  #
  # Configuration parameters initialization
  #
  for(i in TFMAIN) { delete TFMAIN[i] }
  for(i in TFDESC) { delete TFDESC[i] }
  for(i in TFN)    { delete TFN[i]    }
  for(i in TFR)    { delete TFR[i]    }
  wcname = ""
  vin    = 0
  vout   = 0
  vstop  = 0
  naxes  = 0
  dac_ch = ""
  lim_ch = ""
  ena_ch = ""


  #
  # Configuration file parsing 
  #
  print "Reading \""fname"\" ..."
  getline(TFFILE, "close")
  while ((line = getline(TFFILE)) != -1) {
    lineraw = ""
    sscanf(line, "%[^\n\r]", lineraw)
    line    = ""
    if (sscanf(lineraw, " %[^#]", line) != 1)
      continue
      
    sscanf(line, " %s", c)
    if (c == "wagoname") {
      arg = substr(line,length(c)+1)
      sscanf(arg,"%s",wcname)
      if(!length(wcname)) {
        print "ERROR: missing WAGO box name"
        return(-1)
      }

    } else if (c == "vin") {
      arg = substr(line,length(c)+1)
      if(!sscanf(arg,"%d",vin)) {
        print "ERROR: invalid \""c"\" parameter"
        return(-1)
      }

    } else if (c == "vout") {
      arg = substr(line,length(c)+1)
      if(!sscanf(arg,"%d",vout)) {
        print "ERROR: invalid \""c"\" parameter"
        return(-1)
      }

    } else if (c == "vstop") {
      arg = substr(line,length(c)+1)
      if(!sscanf(arg,"%d",vstop)) {
        print "ERROR: invalid \""c"\" parameter"
        return(-1)
      }

    } else if (c == "naxes") {
      arg = substr(line,length(c)+1)
      if(!sscanf(arg,"%d",naxes)) {
        print "ERROR: invalid \""c"\" parameter"
        return(-1)
      }

    } else if (c == TFDAC) {
      arg = substr(line,length(c)+1)
      sscanf(arg,"%s",dac_ch)
      if(!length(dac_ch)) {
        print "ERROR: missing WAGO channel name"
        return(-1)
      }

    } else if (c == TFLIM) {
      arg = substr(line,length(c)+1)
      sscanf(arg,"%s",lim_ch)
      if(!length(lim_ch)) {
        print "ERROR: missing WAGO channel name"
        return(-1)
      }

    } else if (c == TFENA) {
      arg = substr(line,length(c)+1)
      sscanf(arg,"%s",ena_ch)
      if(!length(ena_ch)) {
        print "ERROR: missing WAGO channel name"
        return(-1)
      }

    } else {
      local desc
      local nr_str
      local beg end
      local n r

      beg = index(line,"\"")+1
      end = index(substr(line,beg),"\"")+beg

      sscanf(substr(line,0,beg-1),"%d",i)
      desc     = substr(line,beg,end-beg-1)
      nr_str   = substr(line,end+1)
      sscanf(nr_str,"%f/%f",n,r) 

      # TODO: add some checks on values here

      TFDESC[i] = desc
      TFN[i]    = n
      TFR[i]    = r
    }

  } # end of while(getline)


  #
  # Configuraion parameters verification
  #
  if(!length(wcname)) {
    print "ERROR: missing WAGO box name"
    print "HINT : add field \"wagoname\" to configuration"
    return(-1)
  }

  if(!(wcname in WAGO)) {
    print "ERROR: invalid or unconfigured WAGO box name"
    print "HINT : use \"wagosetup\" first"
    return(-1)
  }

  if(vin<=0) {
    print "ERROR: invalid voltage for piezo in beam motion"
    print "HINT : give a positive value from 0 to +10"
    return(-1)
  }

  if(vout>=0) {
    print "ERROR: invalid voltage for piezo out beam motion"
    print "HINT : give a positive value from -10 to 0"
    return(-1)
  }

  if(naxes<=0) {
    print "ERROR: invalid number of piezos controlled by the WAGO box"
    print "HINT : add field \"naxes\" to configuration"
    return(-1)
  }

  if(!length(lim_ch)) {
    print "ERROR: missing WAGO channel name \""TFLIM"\""
    print "HINT : add field to configuration"
    return(-1)
  }

  if(!length(dac_ch)) {
    print "ERROR: missing WAGO channel name \""TFDAC"\""
    print "HINT : add field to configuration"
    return(-1)
  }

  if(!length(ena_ch)) {
    print "ERROR: missing WAGO channel name \""TFENA"\""
    print "HINT : add field to configuration"
    return(-1)
  }

  if(!index(WAGO[wcname]["ldevs"],dac_ch)) {
    print "ERROR: invalid WAGO channel name \""dac_ch"\""
    print "HINT : check WAGO Devise Server resources"
    return(-1)
  }

  if(!index(WAGO[wcname]["ldevs"],lim_ch)) {
    print "ERROR: invalid WAGO channel name \""lim_ch"\""
    print "HINT : check WAGO Devise Server resources"
    return(-1)
  }

  if(!index(WAGO[wcname]["ldevs"],ena_ch)) {
    print "ERROR: invalid WAGO channel name \""ena_ch"\""
    print "HINT : check WAGO Devise Server resources"
    return(-1)
  }



  #
  # Store new configuration
  #
  TFMAIN["wcname"] = wcname
  TFMAIN["vin"]    = vin
  TFMAIN["vout"]   = vout
  TFMAIN["vstop"]  = vstop
  TFMAIN["naxes"]  = naxes
  TFMAIN["dac_ch"] = dac_ch
  TFMAIN["lim_ch"] = lim_ch
  TFMAIN["ena_ch"] = ena_ch
}'




#%UU% ["all"|"in"|"out"]
#%MDESC%
# Without argument, it shows the position of configured actuators.%BR%
# With "all", it shows even the unconfigured actuators.%BR%
# Finally only the actuationrs "in" the beam or "out" can be displayed.
#
def tfshow '{
  local i
  local todo
  local lim[]
  local naxes
  local axis
  local pos

  todo = "min"
  if($#>=1) { todo = "$1" }

  if((todo!="min") && (todo!="all") && (todo!="in") && (todo!="out")) {
    print "Usage    : $0 [\"all\"|\"in\"|\"out\"]"
    exit
  }
 
  # get all limitswitches values
  if(_tfgetls(lim) == -1) { exit }

  printf("%-15s %-20s %-10s %-3s\n",\
    "Actuator", \
    "Description", \
    "N/R", \
    "Pos")

  # TODO: add a check on TFMAIN
  naxes=TFMAIN["naxes"]
  for(i=0;i<naxes;i++) {
    axis = i+1
    if((todo!="all") && !(axis in TFDESC))  { continue }
    
    pos="???"
    if(lim[2*i]==TFLSON  && lim[2*i+1]==TFLSOFF) {pos="IN"}
    if(lim[2*i]==TFLSOFF && lim[2*i+1]==TFLSON)  {pos="OUT"}

    if((todo=="in")  && (pos!="IN"))  { continue }
    if((todo=="out") && (pos!="OUT")) { continue }

    printf("%-15s %-20s %-10s %-3s\n",\
      sprintf("%2d 0x%05x",axis,(1<<i)), \
      TFDESC[axis], \
      sprintf("%.0f/%.1f",TFN[axis],TFR[axis]), \
      pos)
  }

}'


#%IU%(array)
#%MDESC%
# Fill the two cells array. The first value is
# a bit pattern value corresponding to the limitswitches values.
# The actuator positive switch is activated, the corresponding bit is set.
# In all other switch value cases (negative on, both on, both off), 
# the bit is clear.
# The second value returned is a bit pattern with bit set when the 
# corresponding actuator limitswitches are in-coherent (both on, both off)
#
def _tfgetls_pattern(bps) '{
  local i
  local lim[]
  local naxes
  local bp
  local bp_sta

  
  # get all limitswitches values
  _tfgetls(lim)

  # TODO: add a check on TFMAIN
  naxes=TFMAIN["naxes"]
  for(i=0,bp=0,bp_sta=0;i<naxes;i++) {
    if(lim[2*i]==TFLSON   && lim[2*i+1]==TFLSOFF) { bp     |= (1<<i) }
    if(lim[2*i]==TFLSON   && lim[2*i+1]==TFLSON)  { bp_sta |= (1<<i) }
    if(lim[2*i]==TFLSOFF  && lim[2*i+1]==TFLSOFF) { bp_sta |= (1<<i) }
  }

  bps[0] = bp
  bps[1] = bp_sta

  return
}'



#%IU%(array)
#%MDESC%
# Fill the array with all limitswitches values.
# Return the array size or -1 in case or error.
#
def _tfgetls(ls) '{
   local n

   if(!("lim_ch" in TFMAIN) || !("naxes" in TFMAIN)) {
     print "ERROR: bad internal array configuration"
     print "HINT : use \"fsetup\" first"
     return(-1)
   }

   n=wago_readch(TFMAIN["lim_ch"], ls)

   if(n != TFMAIN["naxes"]*2) {
     print "ERROR: bad number of WAGO limitswitches channels"
     print "HINT : use \"fsetup\" first"
     return(-1)
   }

   return(n)
}'


#%IU%(array)
#%MDESC%
# Fill the array with all WAGO DACs values.
# Return the array size or -1 in case or error.
#
def _tfgetdacs(dacs) '{
   local n

   if(!("dac_ch" in TFMAIN) || !("naxes" in TFMAIN)) {
     print "ERROR: bad internal array configuration"
     print "HINT : use \"fsetup\" first"
     return(-1)
   }

   n=wago_readch(TFMAIN["dac_ch"], dacs)

   if(n != TFMAIN["naxes"]) {
     print "ERROR: bad number of WAGO DACs channels"
     print "HINT : use \"fsetup\" first"
     return(-1)
   }

   return(n)
}'


#%IU%(array)
#%MDESC%
# Write the array of values to all WAGO DACs.
#
def _tfsetdacs(dacs) '{

   if(!("dac_ch" in TFMAIN) || !("naxes" in TFMAIN)) {
     print "ERROR: bad internal array configuration"
     print "HINT : use \"fsetup\" first"
     return(-1)
   }

   wago_writech(TFMAIN["dac_ch"], dacs)

   return(0)
}'



#%UU% actuator
#%MDESC%
# Move the specified actuator, numbered from 1, in the beam.
#
def tfin '{
  local naxes
  local axis
  local bps[]
  local bp_beg
  local bp_sta

  if($#!=1) {
    print "Usage    : $0 actuator"
    exit
  }
  axis = $1

  # TODO: add a check on TFMAIN
  naxes = TFMAIN["naxes"]

  if((axis<1) || (axis>naxes)) {
    print "ERROR: invalid actuator, must be within [1:"naxes"]"
    exit
  }

  # get the current state of all actuators
  _tfgetls_pattern(bps)
  bp_beg = bps[0]
  bp_sta = bps[1]

  # trigger the motion
  _tfset(bp_beg |= (1<<(axis-1)))

}'


#%UU% actuator|"all"
#%MDESC%
# Move the specified actuator, numbered from 1,  or "all" the
# actuators out of the beam.
#
def tfout '{
  local naxes
  local axis
  local bps[]
  local bp_beg
  local bp_sta

  if($#!=1) {
    print "Usage    : $0 actuator|\"all\""
    exit
  }

  # handle the case of all the actuators have to be moved out
  if("$1"=="all") {
    _tfset(0x00)
    return
  }
 
  # get the axis number
  axis = $1

  # TODO: add a check on TFMAIN
  naxes = TFMAIN["naxes"]

  if((axis<1) || (axis>naxes)) {
    print "ERROR: invalid actuator, must be within [1:"naxes"]"
    exit
  }

  # get the current state of all actuators
  _tfgetls_pattern(bps)
  bp_beg = bps[0]
  bp_sta = bps[1]

  # trigger the motion
  _tfset(bp_beg &= ~(1<<(axis-1)))

}'


#%UU% bit_pattern
#%MDESC%
# Move all the actuators according to the bit pattern passed. 
# If the bit is set then the corresponding actuator is moved in the beam.
# If the bit is clear then the actuator is moved out.
# The bit pattern can be passed as "0xNNNN"
#
def tfset '{
  local bp


  bp = 0
  if($#!=1) {
    print "Usage    : $0 bit_pattern"
    exit
  }

  # get the bit pattern value
  if(!sscanf("$1","0x%x",bp)) {
    if(!sscanf("$1","%d",bp)) {
      print "ERROR: argument must be an integer"
      exit
    }
  }

  # trigger the motions
  _tfset(bp)
}'



#%UU%
#%MDESC%
# Abort any motion on all actuators
#
def tfabort '{
  local dacs[]
  local naxes
  local i

  # TODO: add a check on TFMAIN
  naxes = TFMAIN["naxes"]
  for(i=0;i<naxes;i++) { dacs[i]=0 }
  _tfsetdacs(dacs)
}'



#%IU%(bit_pattern)
#%MDESC%
# Move all the actuators according to the bit pattern passed. 
# If the bit is set then the corresponding actuator is moved in the beam.
# If the bit is clear then the actuator is moved out.
# %BR%Return -1 in case of error, 0 otherwise
#
def _tfset(bp_end) '{
  local bpm
  local dacs[]
  local bp_beg
  local bp_sta
  local bps[]
  local tomove
  local naxes
  local vin
  local vout
  local vstop
  local i




  # TODO: add a check on TFMAIN
  naxes = TFMAIN["naxes"]
  vin   = TFMAIN["vin"]
  vout  = TFMAIN["vout"]
  vstop = TFMAIN["vstop"]


  # check the bit pattern value
  bpm = (1<<naxes)-1
  if((bp_end<0) || (bp_end>bpm)) {
    print "ERROR: invalid integer, must be within [0:"sprintf("0x%x",bpm)"]"
    return(-1)
  }

  # check if a motion is requested on unconfigured axis
  for(i=0;i<naxes;i++) {
    if((1<<i) & bp_end) {
      if(!((i+1) in TFDESC)) {
        print "ERROR: try to move unconfigured axis:",i+1
        exit
      }
    }
  }

  # get the current state of all actuators
  _tfgetls_pattern(bps)
  bp_beg = bps[0]
  bp_sta = bps[1]


  # get the current DAC values
  if(_tfgetdacs(dacs) == -1) { return(-1) }

  # calculate motions needed (include actuators inbetween switches)
  for(i=0, tomove=0;i<naxes;i++) {
    if((((bp_beg>>i) & 0x01) != ((bp_end>>i) & 0x01)) || \
       (((i+1) in TFDESC) && (bp_sta&(1<<i)))) {
      dacs[i] = (bp_end & (1<<i)) ? vin : vout
      tomove |= (1<<i)
      printf("Moving actuator %2d 0x%05x : %-3s\n", \
        i+1, \
        (1<<i), \
        (dacs[i]==vin)?"IN":"OUT")
    }
  }


  # enable PIEZO controller
  tfenable

  # start all motions simultaneously
  _tfsetdacs(dacs)
  t_beg = time()

  # wait for the end of all motions and stop them one by one
  while(tomove) {
    # get the current state of all actuators
    _tfgetls_pattern(bps)
    bp_beg = bps[0]
    bp_sta = bps[1]

    # an actuator may not reach its limitswitch
    if((time()-t_beg)>TFTIMEOUT) {
      print "ERROR: "TFTIMEOUT"sec timeout waiting for end of motion, aborting"
      tfabort
      tfdisable
      return(-1)
    }

    # detect end of motion
    for(i=0;i<naxes;i++) {
      if(((bp_beg>>i) & 0x01) == ((bp_end>>i) & 0x01)) {
        
        # to save time, do not stop a non moving actuator
        if(!(tomove & (1<<i))) { continue }

        # do not stop until the limitswitch is reached
        if(bp_sta & (1<<i)) { continue }

        # stop motion only on concerned actuator
        dacs[i] = vstop
        tomove &= ~(1<<i)
        _tfsetdacs(dacs)
        printf("Stop   actuator %2d 0x%05x\n", \
          i+1, \
          (1<<i))
      }
    }

    # do not overload the WAGO box
    sleep(.1)
  }

  # disable PIEZO controller
  tfdisable

  # normal end
  return(0) 
}'




#%UU%
#%MDESC%
# Enable the piezo controller
# 
def tfenable '{ _tfcontrol(1) }'

#%UU%
#%MDESC%
# Disable the piezo controller
# 
def tfdisable '{ _tfcontrol(0) }'

#%UU%
#%MDESC%
# Reset the piezo controller
# 
def tfreset '{ _tfcontrol(-1) }'




#%IU%(action)
#%MDESC%
#action = 1 -> enable axis%BR%
#action = 0 -> disable axis%BR%
#action = -1 -> reset axis%BR%
#
def _tfcontrol(action) '{
   local enable_axis
   local array enable[2]

   # TODO: Add check on TFMAIN
   enable_axis = TFMAIN["ena_ch"]

   if (action == -1) {
      enable[0] = 1
      wago_writech(enable_axis, enable)
      sleep(.2)
      enable[0] = 0
      wago_writech(enable_axis, enable)
      sleep(.5)
      enable[0] = 1
      wago_writech(enable_axis, enable)
   } else if (action == 1) {
      enable[0] = 1
      wago_writech(enable_axis, enable)
      sleep(.5)
   } else if (action == 0){
      enable[0] = 0
      wago_writech(enable_axis, enable)
   }
   return(0)
}'





#%MACROS%
#%IMACROS%
#%SETUP%
# The Wago controllers must be declared in the setup file by means of the
# %B%wagosetup%B% macro (see help in %B%wagocore.mac%B%).
# This requires that the the device server Wagods must be properly configured
# to access to the Wago controllers.%BR%
#
#%AUTHOR% R.Hino, M.Perez, (Original 5/2010).
#  $Revision: 1.4 $ / $Date: 2010/06/22 06:25:19 $
#%TOC%