esrf

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

#%TITLE% PI_861.MAC
#%NAME% PI_861.MAC - Macros to control the Physik Instrumente NEXACT Motor Controller E-861 alternatively in
# open- and closed-loop mode. %BR%
#$Revision:  $
#%DESCRIPTION%
# To define a macro motor controller you must define 
# a pi861 controller in config, ADDR is the serial line index number defined
# in spec:
#
#\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\0pi861\0\0\0\0\04\0\0\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
#
# Then declare a motor with that controller.
#%SETUP%
# The default serial line speed is 38.4 kB.
#%ATTENTION%
# When being used in open-loop operation, in the so-called "servo-off state". All
# movements are in steps, which, documentation says, are about 10
# micro-meters, depending on the load!  
#%END%

# leave those macros alone, if they`re already defined.
if (!(whatis("__pi861debug") & 2)) rdef __pi861debug \'#$*\'

def __pi861_io(sline, send) '{
  if (!__PI861["simu"]) { # if NOT simulating
    local str
    __pi861debug "* Command pi861:", send
    str = send "\n"
    if (ser_put(sline, str) == -1) {
      eprint "PI861 communication error!"
      return(".error.")
    }
    if (index( send, "?") )  { # is there an answer requested ?
      return(ser_get(sline, "\n"))
    }
    else {
      return(0)
    }
  }
  else {
    local aux[], x
    if (index( send, "?") )  { # is there an answer requested ?
      x = split(send, aux)
      __pi861debug "SIMU:", send
      if (aux[0] == "ERR?"       ) { answer = 0 }
      else if (aux[0] == "ONT?"  ) { answer = "1=1" }
      else if (aux[0] == "*IDN?" ) { answer = "(c)2008 Physik Instrumente(PI) Karlsruhe,E-861 SIMULATION!!!!!" }
      else { 
        x = split(send, aux, "?")
        answer = "1=" __PI861simu[aux[0]]
      }
      __pi861debug "SIMU:", send, "answer", answer
      return(answer)
    }
    else {
      x = split(send, aux)
      if (x > 2) {
      __pi861debug "SIMU:", send
        __PI861simu[aux[0]] = aux[2]
      }
    }
  }
}
'
def pi861_sim '{
  __PI861["simu"] = (__PI861["simu"] == 0)
  if (!__PI861["simu"]) {
    unglobal __PI861simu
  } 
  else {
    global  __PI861simu[]
    __PI861simu["MOV"] = A[pi1]
  }
}
'

global __PI861[]

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

def pi861_config(mne, type, unit, module, channel) '{
  __pi861debug "* Configuring PI861", mne, type, unit, module, channel

  local addr
  addr = pi861_ADDR

  if (type == "ctrl") {
    local answer
    local IDstr
    IDstr = "(c)2008 Physik Instrumente(PI) Karlsruhe,E-861"
    answer = __pi861_io(sline, "*IDN?")
    if (index(answer, IDstr)) {
      eprint "PI E861 used as macro motor controller"
      eprint "Be aware that the position of this kind of motor is relative only!"
      eprint "The position in place might not be what you expect!"
    } else {
      return ".error."
    }
  }

  if (type == "mot") {
    local bla[], str, x
    x = motor_par(mne, "velocity")
    pi861_par(mne, "velocity", "set", x)
    x = motor_par(mne, "step_size")
    pi861_par(mne, "step_size", "set", x)
    __PI861[mne]["axis"]      = motor_par(mne,"channel")+1
    motor_par(mne, "mode") # sets our variable __PI861[mne]["mode"]
  }
}
'


def pi861_par(mne, cmd, p1, p2) '{
  if ( mne == ".." ) {
    return 
  }
  local sline
  sline = motor_par(mne, "address")
  __pi861debug "Parameters PI861",mne, cmd, p1, p2
  if (cmd == "velocity" && p1 == "set") {
    local str
    str = "OVL " channel + 1 " " p2
    return(__pi861_io(sline, str))
  }
  else if (cmd == "torque" && p1 == "set") {
    local str
    str = "SSA " channel + 1 " " p2
    return(__pi861_io(sline, str))
  }
  else if (cmd == "mode") {
    local str, answer, loopmode
    if (p1 == "get") {
      str = "SVO? " __PI861[mne]["axis"] # answers axis=1|0\n - 1 for servo on!
      if ((answer = __pi861_io(sline, str)) == ".error.") {
        return(".error.")
      }
      loopmode = int(substr(answer, 3, 1))
      __PI861[mne]["mode"] = loopmode
      __PI861[mne]["mtimestamp"] = time()
      return (loopmode)
    }
    else if (p1 == "set") {
      str = "SVO " __PI861[mne]["axis"] " " (p2!=0)
      # set position
      if (p2) { # switch servo on
        __PI861[mne]["position"] = A[mne] = pi861_cmd(mne, "position")
      }
      else {
        __PI861[mne]["position"] = A[mne] = 0
      }
      __PI861[mne]["mode"] = (p2!=0)
      return(__pi861_io(sline, str))
    }
  }
}
'


def pi861_cmd(mne, cmd, p1, p2) '{
  if ( mne == ".." ) { 
    return 
  }
  __pi861debug "Command PI861",mne, cmd, p1, p2

  # some house keeping for the open/closed-loop mode. If the last check for the mode was more
  # than 24 H ago, do it again.
  __pi861debug "Command PI861", time() - __PI861[mne]["mtimestamp"]
  if ((time() - __PI861[mne]["mtimestamp"]) > 86400) { #seconds are 24H
    motor_par(mne, "mode") # sets our variable __PI861[mne]["mode"]
  }
  # from hereon, the variable __PI861[mne]["mode"] will be used to distinguish between open-
  # and closed-loop mode. 1 is closed-loop / servo on!
  local sline
  sline = motor_par(mne, "address")

  if ( cmd == "get_status" ) {
#    __pi861_geterr(sline)
    local str, answer
    str = "ONT? " __PI861[mne]["axis"] # answers axis=1|0\n - 1 for on target!
    if ((answer = __pi861_io(sline, str)) == ".error.") {
      return(".error.")
    }
    # this will be ok for both open- and closed-loop
    if (substr(x, 3, 1)) {
      return 0x02
    } 
    return(0)
  } 
  else if ( cmd == "start_one") {
    local str
    if (__PI861[mne]["mode"]) { # servo on 
      str = "MOV " __PI861[mne]["axis"] " " p1
    }
    else {
      # we need to do relative moves only, use p2 instead of p1!
      str = "OSM " __PI861[mne]["axis"] " " p2
    }
    __PI861[mne]["position"] = p1
    return(__pi861_io(sline, str))
  } 
  else if ( cmd == "position") {
    local str
    if (__PI861[mne]["mode"]) { # servo on 
      str = "MOV? " __PI861[mne]["axis"]
      x = __pi861_io(sline, str)
      __PI861[mne]["position"] =  substr(x, 3)
      __pi861debug "position", __PI861[mne]["position"]
      return(__PI861[mne]["position"])
    }
    else {
      # in open-loop mode we can`t get a position. We have to rely
      # on spec to do the house keeping.
      return __PI861[mne]["position"]
    }
  } 
  else if ( cmd == "set_position") {
    __PI861[mne]["position"] = p1
  } 
  else if ( cmd == "abort_one") { 
#    __pi861_io("STP")
    return(__pi861_io(sline, "\030")) # ascii 24  to prefer over STP, says manual
  }
}
'


#%IU%
#%MDESC% Returns identification message from the controller.
def __pi861_version() '{
  return __pi861_io(sline, "*IDN?")
}'


#%IU%
#%MDESC% Returns identification message from the controller.
def __pi861_geterr(sline) '{
  local err
  err = __pi861_io(sline, "ERR?")
  if (err == ".error.")  return(err)
  if (err != 0)  eprint __PI861["error"][err]
  return err
}'


# there are about 600 error messages, which I didn`t think necessary to list
# here.   
__PI861["error"][ 0] = "No error"
__PI861["error"][ 1] = "Parameter syntax error"
__PI861["error"][ 2] = "Unknown command"
__PI861["error"][ 3] = "Command length out of limits or command buffer overrun"
__PI861["error"][ 4] = "Error while scanning"
__PI861["error"][ 5] = "Unallowable move attempted on unreferenced axis, or move attempted with serv off"
__PI861["error"][ 6] = "Parameter for SGA not valid"
__PI861["error"][ 7] = "Position out of limits"
__PI861["error"][ 8] = "Velocity out of limits"
__PI861["error"][ 9] = "Attempt to set pivot point while U,V and W not all 0"
__PI861["error"][10] = "Controller was stopped by command"
__PI861["error"][11] = "Parameter for SST or for one of the embedded scan algorithms out of range"
__PI861["error"][12] = "Invalid axis combination for fast scan"
__PI861["error"][13] = "Parameter for NAV out of range"
__PI861["error"][14] = "Invalid analog channel"
__PI861["error"][15] = "Invalid axis identifier"
__PI861["error"][16] = "Unknown stage name"
__PI861["error"][17] = "Parameter out of range"
__PI861["error"][18] = "Invalid macro name"
__PI861["error"][29] = "Error while recording macro"
__PI861["error"][20] = "Macro not found"
__PI861["error"][21] = "Axis has no brake"
__PI861["error"][22] = "Axis identifier specified more than once"
__PI861["error"][23] = "Illegal axis"
__PI861["error"][24] = "Incorrect number of parameters"
__PI861["error"][25] = "Invalid floating point number"
__PI861["error"][26] = "Parameter missing"
__PI861["error"][27] = "Soft limit out of range"
__PI861["error"][28] = "No manual pad found"
__PI861["error"][29] = "No more step-response values"
__PI861["error"][30] = "No step-response values recorded"
__PI861["error"][31] = "Axis has no reference sensor"
__PI861["error"][32] = "Axis has no limit switch"
__PI861["error"][33] = "No relay card installed"
__PI861["error"][34] = "Command not allowed for selected stage(s)"
__PI861["error"][35] = "No digital input installed"
__PI861["error"][36] = "No digital output configured"
__PI861["error"][37] = "No more MCM responses"
__PI861["error"][38] = "No MCM values recorded"
__PI861["error"][39] = "Controller number invalid"
__PI861["error"][40] = "No joystick configured"
__PI861["error"][41] = "Invalid axis for electronic gearing, axis can not be slave"
__PI861["error"][42] = "Position of slave axis is out of range"
__PI861["error"][43] = "Slave axis cannot be commanded directly when electronic gearing is enabled"
__PI861["error"][44] = "Calibration of joystick failed"
__PI861["error"][45] = "Referencing failed"
__PI861["error"][46] = "OPM (Optical Power Meter) missing"
__PI861["error"][47] = "OPM (Optical Power Meter) not initialized or cannot be initialized"
__PI861["error"][48] = "OPM (Optical Power Meter) Communication Error"
__PI861["error"][49] = "Move to limit switch failed"

#-----------------------------------------------------------
#%UU% motor [open/close]
#%MDESC% changes the controller from open- to closed-loop  mode and vice versa
def pi861_mode '{
  local mode, newmode, Usage
  Usage  = "$0: bad argument: use motor_mne [open|closed]"
  if ($# == 0) {
    eprint Usage
    exit
  }
  if (motor_num("$1") == -1) {
    eprint "No such motor \"$1\""
    exit
  }
  mode = motor_par("$1", "mode")
  print "Controller is in", mode ? "closed" : "open", "loop mode"
  if ($# == 2) {
    if ("$2" == "open") {
      newmode = 0
    }
    else if ("$2" == "closed" || "$2" == "close") {
      newmode = 1
    }
    else {
      eprint Usage
      exit
    }
    if (newmode != mode) {
      motor_par("$1", "mode", newmode)
      print "change to", newmode ? "closed" : "open", "loop mode"
    }
    else {
      print "No change"
    }
  }

}' # Ends macro pi861_mode




#%MACROS%
#%IMACROS%
#%DEPENDENCIES%
# stlocal.mac, pseudo.mac
#%END%
#%BUGS% Bugs ? What bugs ??
#%AUTHOR%
#Holger (BLISS) for ID18.
#%TOC%