esrf

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

#%TITLE% kepco_bop_mm.mac
#$Revision: 1.8 $
#%NAME% Macros for a the use of a KEPCO,BOP 1000W 100-10 powersuppy
#%DESCRIPTION%
# The intrument pool power supply is a KEPCO,BOP 1000W 100-10 08/04/08 ,A98599,1.61b
#%BR%
# the string it answers is : KEPCO,BOP1KW 100-10 ,A98599, 2.04
#%BR%
# ID18`s power supply answers: KEPCO,BOP1KW 20-50 10-15-2009 ,E157846, 3.04
#%BR%
#Kepco's auto-crossover digital supplies can operate in either voltage mode with current limit, or
#current mode with voltage limit. The operating mode is determined by the voltage and current
#commands received, as well as the load. Each time voltage and current commands are
#received, the unit must evaluate the commands aand the load conditions to determine the necessary
#operating mode. Reducing the number of times this evaluation must be made is desirable
#because Kepco's digital auto-crossover supplies employ two separate feedback loops. Each
#time there is a potential mode change, there is always an uncontrolled period of a few milliseconds
#while the two feedback loops compete for control of the output. By changing only the
#active parameter (e.g., voltage for voltage mode), there is no doubt as to what the operating
#mode will be, so the unit is never uncontrolled, response is quick and no transients are possible
#%BR% %BR%
# To define a macro motor controller you must define 
# a kepco motor controller in config, ADDR is the GPIB address.
# %BR%
#%PRE%
#\0\0\0MOTORS\0\0\0\0\0\0\0DEVICE\0\0\0\0ADDR\0\0\0<>MODE\0\0\0\0\0\0NUM\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0<>TYPE
#\0\0\0\0\0\0YES\0\0\0\0\0\0\0kepco\0\0\0\0\0\00:6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01\0\0\0\0\0\0\0\0\0Macro\0Motors
#%PRE%
# %BR% %BR%
# Then declare a motor with that controller.
# %BR% %BR%
#%PRE%
#Number:\0<>Controller\0\0\0\0\0\0\0\00:\0MAC_MOT
#Unit/[Module/]Channel\0\0\0\0\0\0\0\0\0\0\0\0\0\00/1
#Name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Kepco\0Curr
#Mnemonic\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0curr
#Steps\0per\0degree/mm\0\0\0\0\0\0\0\0\0\0\0\0\0\010000
#Sign\0of\0user\0*\0dial\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Backlash\0[steps]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00
#Steady-state\0rate\0[Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Base\0rate\0[Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Acceleration\0time\0[msec]\0\0\0\0\0\0\0\0\0\0\0\0\01
#Motor\0accumulator\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00
#Restrictions\0<>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NONE
#
#Dial\0=\0accumulator\0/\0steps
#\0\0High\0limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01.6000
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0Low\0limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0-1.6000
#User\0=\0sign\0*\0dial\0+\0offset
#\0\0Offset\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0`High'\0limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01.6000
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0`Low'\0limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0-1.6000
#%PRE%
## hit `m` twice to move to the third motor config screen
#%PRE%
#Number:\0<>Controller\0\0\0\0\0\0\0\00:\0MAC_MOT
#Unit/[Module/]Channel\0\0\0\0\0\0\0\0\0\0\0\0\0\00/1
#Name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Kepco\0Curr
#Mnemonic\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0curr
#
#Encoder\0steps\0per\0deg/mm
#Step\0mode\0(0=full,1=half)
#Linear/Rotary\0(1=rotary)
#Disable\0limit\0checks
#Readback\0slop\0[steps]\0\0\0\0\0\0\0\0\0\0\0\0\0\0100
#Hardware\0read\0mode\0<>\0\0\0\0\0PR\0+\0AL\0+\0NQ
#
#%PRE%
#%BR%
#%END%
#NOTES:
#%PRE%
#*IDN? works fine.
#*RST resets power to 0, rather not!
#OUTP ON will switch the PS to a usable state.
#*OPC? operation completed ? should be used for error catching. then ask the status registers.
#we want to work in current mode. The voltage is kept constant by the power supply and limited to a maximum/minimum.
#Marcin says: he wants to work at 10 V and vary the current.
#Switch to current mode with FUNC:MODE CURR/VOLT for voltage mode.
#Then protect the ps and the magnet with a VOLT:PROT 10 (10 volt is max/min). One can set the minimum separately, but we should use both + and -.

#$Log: kepco_bop_mm.mac,v $
#Revision 1.8  2016/01/05 16:14:44  guilloud
#fix wrong chars --> "1"
#
#Revision 1.7  2011/02/03 10:48:06  witsch
#only cosmetical changes --> help local kepco_bop_mm.mac
#
#Revision 1.6  2010/09/10 15:16:14  witsch
#some corrections for the variable KEPCO_ADDR, which seemed to be invalid
#for the KEPCO_cmd and KEPCO_par functions. Read the address with
#motor_par(mnum, "address")
#
#Revision 1.5  2010/05/03 12:34:42  witsch
#lock device from being used, when power was cycled. This might happen, when
#sensor connection was opened. Cycling power is necessary to achieve this.
#
#The check is done by asking the device for the source mode (current or
#voltage). As ID18 will use it in current mode only and voltage is the
#default, we check if device is in voltage mode and if so, refuse all
#commands.
#
#Revision 1.4  2010/04/21 14:10:28  witsch
#some better error handling. Basically Spec will stop working with the device
#if an error occurs. Some more cosmetics for the startup in fresh mode.
#
#Revision 1.2  2010/01/13 14:25:25  witsch
#new version, now no longer based on tango server, but direct access
#via gpib builtin spec support.
#Slightly modified in _io_ function for error reporting and
#lower protection voltage for new power supply.
#
#added function for voltage change.
#

if (!(whatis("__kepco_debug")  & 2)) rdef __kepco_debug \'#$*\'  

#%UU%
#%MDESC% toggle debug mode for the present macros.
def kepco_debug '{
  if ((whatis("__kepco_debug")>>16) <= 4) { # just a # sign -> off
    rdef __kepco_debug "eprint"
    print "kepco debug is ON"
  } else {
    rdef __kepco_debug \'#$*\'
    print "kepco debug is OFF"
  }
}
'

#%IU%(mnum, type, unit, mod, chan)
#%MDESC%
# Called by spec
def KEPCO_config(mnum, type, unit, mod, chan) '{
  __kepco_debug "KEPCO config", mnum, type, unit, mod, chan
  global __KEPCO[]
  __KEPCO["active"] = "CURR"
  __KEPCO["complm"] = "VOLT"
  __KEPCO["PROT"]   = 20 # whatever the unit :-)
  delete __KEPCO["error"]
  local  answer, ident
  if (mnum == "..") {
    if ((answer = __kepco_io(KEPCO_ADDR, "*IDN?")) == "") {
      eprint "No connection with the Kepco power supply!"
      return(".error.")
    }
    ident  = "KEPCO,BOP1KW 100-10 "
    if (substr(ident, answer) != ident) {
      print "The interface does not answer the correct identification string!"
      print "Are the interface informations correct?"
      return(".error.")
    }
    # OUTP ON Turns the output on.
    __kepco_put(KEPCO_ADDR, "OUTP ON")
    # Activate 
    __kepco_put(KEPCO_ADDR, "*OPC")
    __kepco_put(KEPCO_ADDR, "OUTPUT:MODE ACTIVE")
  }
  else {
    motor_par(mnum, "mode", chan)
    motor_par(mnum, "prot", __KEPCO["PROT"])
    __KEPCO["SLOP"]   = motor_par(mnum, "slop") / motor_par(mnum, "step_size")
    local aux[], pos, str, addr
    addr = motor_par(mnum, "address")
    str = "*wai;meas:" __KEPCO["active"] "?;*wai;*OPC?"
    answer = __kepco_io(addr, str)
    split(answer, aux, ";")
    answer = aux[0]
    __KEPCO["pos"] = answer
  }
}
'


#%IU%(mnum, key, p1, p2, p3)
#%MDESC%
# Called by spec
def KEPCO_cmd(mnum, key, p1, p2, p3) '{
  __kepco_debug "KEPCO command", mnum, key, p1, p2, p3
  if (mnum == "..") {
    return
  }
  if (!__KEPCO["active"]) {
    return
  }
  if ( ! motor_par(mnum, "mode") ) {
    eprint "Kepco Power supply: Did you cycle power ? Please reconfig!"
    return(".error.")
  }
  local str, addr
  addr = motor_par(mnum, "address")
  if (key == "position") {
    local str, answer
    #[VOLT|CURR]? Returns programmed output
    str = "*wai;meas:" __KEPCO["active"] "?;*wai;*OPC?"
    answer = __kepco_io(addr, str)
    # with "*OPC?" outputs curr;1, but spec seems to cope, 
    # so no treatment of output
#    print answer
    return(answer)
  } else
#  if (key == "magnitude") {
#    print "magnitude", p1, p1 * .5, __KEPCO["SLOP"]
#    if (__KEPCO["SLOP"] > p1 *.5)
#    __KEPCO["SLOP"] = p1  * .5
#  } else
  if (key == "get_status") {
    local answer, aux[], pos
    str = "*wai;meas:" __KEPCO["active"] "?;*wai;*OPC?"
    answer = __kepco_io(addr, str)
    split(answer, aux, ";")
    answer = aux[0]
    pos = __KEPCO["pos"]
    __kepco_debug "pos", pos, "answer", answer, "diff", pos - answer, "fabs", fabs(pos - answer), "slop", __KEPCO["SLOP"]
    if (fabs(pos - answer) > __KEPCO["SLOP"])  {
      return(2)
    }
    return(0)
  } else
  if (key == "start_one") {
    __KEPCO["pos"] = p1
    str = __KEPCO["active"] " " p1 ";*wai"
    # output is unused
    return(__kepco_io(addr, str))
  }
}
'


#%IU%(mnum, key, action, p1, p2)
#%MDESC%
# Called by spec
def KEPCO_par(mnum, key, action, p1, p2) '{
  __kepco_debug "* kepco parameters", mnum, key, action, p1, p2, KEPCO_ADDR
  local str, answer, addr
  addr = motor_par(mnum, "address")
  __kepco_debug "addr", addr
  if (key == "prot") { # set protection limits
    __kepco_debug "* kepco prot"
    if (action == "set") {
      __KEPCO["PROT"] = p1
      str = __KEPCO["complm"] ":PROT " __KEPCO["PROT"]
      answer = __kepco_put(addr, str)
#      if (__kepco_opc(addr) == 0) {
#        eprint "Kepco: ERROR ! Operation", str, "NOT complete!"
#        return(".error.")
#      }
      return(answer)
    }
    else if (action == "get") {
      str = __KEPCO["complm"] ":PROT?"
      answer = __kepco_io(addr, str)
      __kepco_debug "get prot", str, answer
      return answer
    }
  }
  else if (key == "mode") { # set current or voltage mode as active mode
    __kepco_debug "* kepco mode"
	# motor_par only allow numerical values for p1 --->
	# use 0 for voltage mode and 1 for current mode, which are the values the
	# system wil answer, if asked "func:mode?"
    if (action == "set") {
      if (p1 == 0) {
        __KEPCO["active"] = "VOLT"
        __KEPCO["complm"] = "CURR"
      } else {
        __KEPCO["active"] = "CURR"
        __KEPCO["complm"] = "VOLT"
      }
      str = "FUNC:MODE " __KEPCO["active"]
      answer = __kepco_put(addr, str)
      if (answer == ".error.") {
        return(answer)
      }
      if (__kepco_opc(addr) == 0) {
        eprint "Kepco: ERROR ! Operation", str, "NOT complete!"
        return(".error.")
      }
      return 0
      # can we set the protection from here ?
      motor_par(mnum, "prot", __KEPCO["PROT"])
    }
    else if (action == "get") {
      str = "func:mode?"
      answer = __kepco_io(addr, str)
      __kepco_debug "get mode", str, answer, answer ? "curr" : "volt"
      return answer
    }
  }
}
'


#%PRE%
# Copy of the oepration manual
# OPERATION COMPLETE QUERY  *OPC?
#Syntax: *OPC?
#Return value: <1 or 0> (ASCII) 
#0 placed in output queue if power supply has not completed operation after prior *OPC command.
#1 placed in output queue when power supply has completed operation.
#Description: Indicates when pending operations have been completed. *OPC command must be sent to first to
#clear status bit 0 (Operation Complete). *OPC? will return "0" until all pending operations are complete
#(all previous commands have been executed and changes in output level have been completed) At
#that time *OPC? will return "1". Unlike the *WAI command (see PAR. A.18), subsequent commands
#are not inhibited while status bit 0 is "0". *OPC? is intended to be used at the end of a command line
#so that the application program can monitor the bus  for data until it receives "1" from the power
#supply Output Queue
#%PRE%


#%IU%(addr, cmd)
#%MDESC% Called by spec, write command to controller and read answer.
def __kepco_io(addr, cmd) '{
  __kepco_debug "__kepco_io(\"" addr "\", \"" cmd "\")"
  if (__kepco_put(addr, cmd) == ".error.") {
    return(".error.")
  } 
  local i, x, str, answer, error, aux[]
  answer = gpib_get(addr, "\n")
  # the answer can sometimes be
  # \0000,"no error". Of course with the \0 at the beginning, the string 
  # becomes empty in the sense of spec. 
  if (__kepco_put(addr, "SYSTem:ERRor?") == ".error.") {
    return(".error.")
  }
  errors = gpib_get(addr, "\n")
  if (split(errors, aux, ",") == 2) {
    if (aux[0] != 0) {
      eprint "Kepco PS ERROR:", aux[1]
      __KEPCO["error"] = aux[0]
      return(".error.")
    }
  }
  __kepco_debug answer
  return(answer)
}
'


#%IU%(addr, cmd)
#%MDESC% Called by spec, write command to controller, no answer expected.
def __kepco_put(addr, cmd) '{
  local str
  str = cmd "\r\n"
  __kepco_debug "__kepco_put(\"" addr "\", \"" cmd "\")"
  if ((answ = gpib_put(addr, str)) == -1) {
    return(".error.")
  }
  return 0
}
'


#%IU%(addr, cmd)
#%MDESC% Called by spec, here check if command complete
def __kepco_opc(addr) '{
  __kepco_debug "__kepco_opc(" addr  ")"
  return __kepco_io(addr, "*OPC?")
}
'

#%UU% motor protection_voltage
#%MDESC% read or set the protection voltage
def kepco_prot '{
  if ($# == 0) {
    eprint "Please give motor mnummonic as first argument!"
    exit
  }
  local aux[], answer
  answer = motor_par($1, "prot")
  split(answer, aux, ",")
  print "Kepco power supply protection voltage is", aux[1]
  if ($# > 1) {
    print "Set protection to:", $2
    motor_par($1, "prot", $2)
    local aux[], answer
    answer = motor_par($1, "prot")
    if (answer == "0") {
      exit
    }
    split(answer, aux, ",")
    print "Kepco power supply protection voltage is", aux[1]
  }
}
'

# this is an attempt to come around the problem with the macro motor macros
# being loaded at the end of the config/setup, thus not executing the
# KEPCO_config function. Variable remain uninitialised. We need a reconfig
if (!whatis("__KEPCO")) {
  eprint "\n\n\n"
  eprint "***************************************************************"
  eprint "Make sure to do a reconfig for the KEPCO power supply macros!!!"
  eprint "***************************************************************"
  eprint "\n\n\n"
}
  

#%MACROS%
#%IMACROS%
#%END%
#%BUGS% Bugs ? What bugs ??
#%AUTHOR%
#Holger (BLISS) for ID18.
#%TOC%