esrf

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

#%TITLE% VAT_PM5.MAC
#%NAME%
# Macros for controlling VAT Adaptive Pressure Controller
# through a serial line. The current macros have been tested with 
# model PM5 but should work with model PM3. 
#

#%DESCRIPTION%
#%DL%
# %DT% - Configure the serial line connected to the controler.
# The default serial line configuration is 4800bauds/7bits/even 
#
# %DT% - Configure a macro motor:
# The motor will control the valve in position or in pressure.
# The channel field is used to select the mode (1 for position, 
# 2 for pressure)
#   %B%"device"%B%field set to string "vat_pm5"
#   %B%"addr"%B%field set to the serial line entry
#   %B%"num"%B%field set to 4
#   %B%"type"%B%field set to "Macro Motors"
#
# %DT% - Configure a macro counter (optional):
#   %B%"device"%B%field set to string "vat_pm5"
#   %B%"addr"%B%field set to the serial line entry
#   %B%"num"%B%field set to 4
#   %B%"type"%B%field set to "Macro Counter"
#
# %DT% - Some macros are also available
#%XDL%
#


global VAT_PM5_ERR[]
VAT_PM5_ERR["000001"] = "Parity Error"
VAT_PM5_ERR["000002"] = "CR or LF is missing"
VAT_PM5_ERR["000003"] = "Semicolon is missing"
VAT_PM5_ERR["000004"] = "Wrong letter code"
VAT_PM5_ERR["000005"] = "Numerical value not given is 6 digits"
VAT_PM5_ERR["000006"] = "Numerical value larger than 1000"
VAT_PM5_ERR["000007"] = "Missing sensor, check connection"
VAT_PM5_ERR["000008"] = "Instruction given in operating mode LOCAL"
VAT_PM5_ERR["000101"] = "Error with LEARN command"
VAT_PM5_ERR["000200"] = "Error with ZERO command"


global VAT_PM5_DEV[]




#%IU%(action, [controller])
#%MDESC%
# Open (action == 1) or close (action == 2) the valve
#
def _vataction(act, args) '{
  local dev
  local ctl
  local val
  local tgt
  local silent
  

  # Minimum check
  if(whatis("VAT_PM5_DEV") & 0x08000000) {
    _vat_pm5_err
    print "missing at least one macro motor configuration for the VAT"
    return
  }

  # Retrieve serial line to use from macro motor configuration
  if(args != "") { ctl = sscanf(args, "%d") }
  dev = VAT_PM5_DEV[ctl]
  silent = 1

  # Action
  printf("%s valve... ", (act==1)?"Opening":"Closing")
  if( _vat_pm5_query(dev, (act==1)?"O:":"C:", silent) == "") {
    _vat_pm5_err
    printf("unable to %s valve\n", (act==1)?"open":"close")
    return
  }

  # Waits for the end of the valve motion
  for(tgt = ((act==1)?1000:0);;sleep(0.1)) {

    # Get current and target valve positions
    val = _vat_pm5_getpos(dev, "position")

    # consider that the valve is still moving
    if(fabs(tgt-val) <= 1) { break }
  }
  print "done"

  # Update current motor positions
  read_motors(0x06)
}'



#%UU% [controller]
#%MDESC%
# Open the valve
#
def vatopen '{
  _vataction(1, "$*")
}'


#%UU% [controller]
#%MDESC%
# Close the valve
#
def vatclose '{
  _vataction(2, "$*")
}'



#%IU%
#%MDESC%
# Called by spec after reading the config file 
# 
def vat_pm5_config(num,type,p1,p2,p3) '{
  local silent
  local mne

  silent = 1

  # p1 is the controller number
  # p2 is the number of channels
  if(type=="ctrl") {

    # Get the serial line
    dev = vat_pm5_ADDR  
    if(ser_par(dev, "responsive") == -1) {
      _vat_pm5_err
      print "wrong serial line, update ADDR field"
      return ".error."
    }

    # Check that the controller is usable
    if( _vat_pm5_query(dev, "v:", silent) == "") {
      _vat_pm5_err
      print "unusable controller"
      return ".error."
    }

    # Switch on remote mode
    if( _vat_pm5_query(dev, "U:01", silent) == "") {
      _vat_pm5_err
      print "unable to switch controller to remote mode"
      return ".error."
    }

    # Remove any previous global resource
    if(p1 == 0) { 
      local i

      for(i in VAT_PM5_DEV) { 
        delete VAT_PM5_DEV[i] 
      }
    }

    # Keep a record of the serial line for direct access commands
    VAT_PM5_DEV[p1] = dev
  }


  # p1 == unit 
  # p2 == module 
  # p3 == channel
  if(type == "mot") {

    # get the motor mnemonic string
    mne=motor_mne(num)
  
    # check limits
    if(motor_par(num, "high_limit") != 1000) {
      _vat_pm5_warn
      print "motor \""mne"\": changing high limit to 1000"
      motor_par(num, "high_limit", 1000)
    }

    # check limits
    if(motor_par(num, "low_limit") != 0) {
      _vat_pm5_warn
      print "motor \""mne"\": changing low limit to 0"
      motor_par(num, "low_limit", 0)
    }

    # remove any configured backlash
    if(motor_par(num, "backlash") != 0) {
      _vat_pm5_warn
      print "motor \""mne"\": removing backlash"
      motor_par(num, "backlash", 0)
    }

    # check motor type
    if(p2 != 0) {
      _vat_pm5_err
      print "motor \""mne"\": invalid module value, must be 0"
      return ".error."
    }
    if((p3 != 1) && (p3 != 2)) {
      _vat_pm5_err
      print "motor \""mne"\": invalid channel, must be 1(pos) or 2(press)"
      return ".error."
    }

    if(p3 == 1) {
      motor_par(num, "type", "position", "add")
    } else {
      motor_par(num, "type", "pressure", "add")
    }
    typ = motor_par(num, "type")
    print "motor   \""mne"\": moving VAT valve in \""typ"\""

    # needed to emulate moving status
    val = _vat_pm5_getpos(dev, typ)
    motor_par(num, "target", val, "add")
  }



  # p1 == unit 
  # p2 == always 0 
  # p3 == channel
  if(type == "cnt") {
    # get the counter mnemonic string
    mne=cnt_mne(num)

    if(p3 == 1) {
      counter_par(num, "type", "position", "add")
    } else {
      counter_par(num, "type", "curpress", "add")
    }
    typ = counter_par(num, "type")
    print "counter \""mne"\": reading VAT valve \""typ"\""
  }

}'






#%IU%
#%MDESC%
# Called by spec on motor operations
# 
def vat_pm5_cmd(num,key,p1,p2) '{
  local mne
  local dev
  local typ
  local cmd


  # Handle actions at controller level
  if(num == "..") {
    return
  }

  # try to guess if the action if for the motor or the counter
  if((mne = motor_mne(num)) != "?") {
    dev = motor_par(num, "address")
    typ = motor_par(num, "type")
  } else {
    mne = cnt_mne(num)
    dev = counter_par(num, "address")
    typ = counter_par(num, "type")
  }



  # return the current pressure
  if (key == "counts") {
    return(_vat_pm5_getpos(dev, typ))
  }




  # return the current motor position in mm or deg
  if (key == "position") {
    local val
    if((val = _vat_pm5_getpos(dev, typ)) == -1) { return ".error." }
    return(val)
  }



  # start a motion (p1==abs pos, p2==rel pos, with pos in mm or deg)
  # Note: the "start_one" is called for the macro counters also 
  # but with a null p2 value which is the relative motion
  if((key == "start_one") && (p2!=0)) {
    if(typ == "position") { cmd = "R:" } else { cmd = "S:" }
    if((ans = _vat_pm5_query(dev, sprintf("%s%06d", cmd, p1), silent)) == "") {
      _vat_pm5_err
      print "unable to set valve position"
      return ""
    }
    motor_par(num, "target", p1, "add")
  }



  # return the current motor status
  if (key == "get_status") {
    local val
    local tgt

    # get current and target valve positions
    if((val = _vat_pm5_getpos(dev, typ)) == -1) { return(0) }
    tgt = motor_par(num, "target")

    # considere that the valve is still moving
    if(fabs(tgt-val) > 1) { return(2) }
   
    # not moving
    return(0)
  }



}'




#%IU%(dev, cmd, silent)
#%MDESC%
# Returns current valve position/pressure setppint/current pressure
# or -1 if error.
def _vat_pm5_getpos(dev, typ) '{
  local cmd
  local val

  if(typ == "position") { cmd = "A:" } 
  if(typ == "pressure") { cmd = "W:" } 
  if(typ == "curpress") { cmd = "P:" } 

  if((ans = _vat_pm5_query(dev, cmd)) == "") {
    _vat_pm5_err
    print "unable to read valve position"
    return -1
  }
  if(sscanf(substr(ans, 3), "%d", val) != 1) {
    _vat_pm5_err
    print "unable to understand valve position"
    return -1
  }
  return(val)
}'




#%IU%(dev, cmd, silent)
#%MDESC%
# Send command to controler and returns its answer.
#
def _vat_pm5_query(dev, cmd, silent) '{
  local use_spec

  # Append the protocol terminators
  if(substr(cmd,length(cmd)-1) != "\r\n") { cmd = cmd"\r\n" }

  # Issue if several SPEC sessions are talking to the VAT controller
  if(use_spec) {

    # Send the command
    ser_par(dev, "flush")
    ser_put(dev, cmd)

    # Wait for the answer
    ans = ser_get(dev, "\n")

  } else {

    # Atomic send and read to support multi clients
    ans = esrf_io(ser_par(dev, "device_id"), "DevSerWriteReadString", cmd)
  }

  # Handle timeout
  if((ans == "") && !silent) {
    _vat_pm5_err
    print "timeout waiting for answer"
    return ""
  }

  # Remove protocol terminators
  if(substr(ans,length(ans)-1) == "\r\n") { ans=substr(ans,1,length(ans)-2) }

  # Check for errors
  if(substr(ans, 0, 2) == "E:") {
    local err

    err = substr(ans, 3)
    _vat_pm5_err
    if(err in VAT_PM5_ERR) {
      print VAT_PM5_ERR[err]
    } else {
      print "unknown controller error"
    }
    return ""
  }

  # Returns the comamnd answer or the command echo
  return ans
}'

#%IU%
#%MDESC%
# Cosmetics message
#
def _vat_pm5_err '{
 tty_cntl("md")
 printf("VAT PM5 ERROR: ")
 tty_cntl("me")
}'


#%IU%
#%MDESC%
# Cosmetics message
#
def _vat_pm5_warn '{
 tty_cntl("md")
 printf("VAT PM5 WARNING: ")
 tty_cntl("me")
}'

#%MACROS%
#%IMACROS%
#%AUTHOR% MP BLISS (Original Jun/2015).
# %BR%$Revision: 1.2 $ / $Date: 2016/07/21 05:51:52 $
#%TOC%