esrf

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

#%NAME%
#  Macromotor to control the PACE5000 K0443 Pressure Automated Calibration Equipment
#  
#%DESCRIPTION%
#Control of the PACE5000 K0443 Pressure Automated Calibration Equipment.
#%BR% Control with serial line with following resources:
#%BR%- baudrate: 9600
#%BR%- parity: none
#%BR%- charlength: 8
#%BR%- stopbits: 1
#%BR%- timeout: 0xa
#%BR% in spec: serial line in cooked mode
#%BR% Control via etrernet sockets
#%BR% The macromotor controller to be used is 'pace5000'. Each macromotor of
#that type must define a parameter: "serial" with the serial device index or
#"etrernet" with the "address:socket_number" in the config.
#There are two protocols. If nothing specified, ptotocol emulationg older
#Drug instruments will be used. Otherwise in the "ptotocol" parameter should be
#set to "SCPI".
#%END%

global PACE5000_DEBUG  PACE5000_SIMUL 
global PACE5000_ERR PACE5000

  PACE5000_ERR[0]= "ERROR on PACE5000: Command not accepted"
  PACE5000_ERR[1]= "ERROR on PACE5000: Secondary address not available"
  PACE5000_ERR[2]= "ERROR on PACE5000: Data string not valid"
  PACE5000_ERR[3]= "ERROR on PACE5000: Reading in limits"
  PACE5000_ERR[4]= "ERROR on PACE5000: Over range (reading exceeds transducer range or 99999"
  PACE5000_ERR[5]= "ERROR on PACE5000: end of conversion"
  PACE5000_ERR[6]= "ERROR on PACE5000: Valve over temperature"
  PACE5000_ERR[7]= "ERROR on PACE5000: checksum error"

#%UU%
#%MDESC% toggle the debug mode
if (!(whatis("__pace5000_debug")  & 2)) rdef __pace5000_debug \'#$*\'
def pace5000_debug '{
  if ((whatis("__pace5000_debug")>>16) <= 3) { # macro length is 3 bytes: comment
     rdef __pace5000_debug "eprint"
     print "pace5000 macro debug mode is ON"
     PACE5000_DEBUG = 1
  }
  else {
     rdef __pace5000_debug \'#\$*\'
     print "pace5000 macro debug mode is OFF"
     PACE5000_DEBUG = 0
  }
}'

# some programmer help
#%UU%
#%MDESC% toggle debug mode for the present macros.
def pace5000_simul '{
  if (PACE5000_SIMUL) { # if present should be true
      PACE5000_SIMUL = 0
    print "pace5000 simulation is OFF"
  } else {
    PACE5000_SIMUL      = 1
    print "pace5000 simulation is ON"
  }
}
'


#%IU%
#%MDESC%
# MACRO MOTOR: 
# Called by spec after reading the config file
#
def pace5000_config(num,cmd,p1,p2,p3) '{  
   local  dev

   __pace5000_debug ">>> pace5000_config: ",num,cmd,p1,p2,p3

   # called for each macro-hardware controller 
   # p1:unit p2: nbchan
   if(cmd=="ctrl") {
      __pace5000_debug ">>>                   new controller " 
   }

   # called for each macro motor channel
   # p1:unit p2:module p3:channel
   if(cmd=="mot") {   
     PACE5000[num]["protocol"] = 0
      #nothing
   }
   

   #returns nothing or .error.
}'

#%IU%
#%MDESC%
# MACRO MOTOR: 
# Called by spec for setting after reading the config file, after calling _config()
# and only if parameters are set in the config file for a motor. 
# Called with motor_par for setting a parameter (if parameter in config file
# or not), and for getting a parameter (if motor NOT in the config file, otherwise
# spec manages it). It manages:
#%BR%- 'serial' or 'ethernet'
#
def pace5000_par(num,key,todo,p1) '{
   local mne dev ch

   __pace5000_debug ">>> pace5000_par:    ",num, key, todo, p1

  if (key == "protocol") {
    if (todo == "set") {
      PACE5000[num]["protocol"] = p1
      return
    } else {
      return PACE5000[num]["protocol"]
    }
  }
  if (key == "serial") {
    if (todo == "set") {
      PACE5000[num]["dev"] = p1 
      return
    } else {
      #note: in principle, todo == get not called here as this parameter
      #is in the config file. 
      __pace5000_debug ">>> pace5000_par:    returns ", PACE5000[num]["serial"]
     return PACE5000[num]["dev"]      
    }      
  }

  if (key == "dev") {
   if (todo == "set") {
      PACE5000[num]["dev"] = p1
      return
    } else { 
      return PACE5000[num]["dev"]
    }
  }

  if (key == "type") {
    if (todo == "set") {
      PACE5000[num]["type"] = p1
      return
    } else {
      return PACE5000[num]["type"]
    }
  }
}'

#%IU%
#%MDESC% sends to serial line 'serial_number' the string 'cmd'.
#%BR%It automatically ends the command with \\r and \\n
def _pace5000_comm(num, cmd, norep) '{
local splchar aaa
  cdef("cleanup_once", sprintf("pace5000_flush %d;",num),"_pace5000")

  __pace5000_debug ">>> _pace5000_comm                 " cmd 

  if (PACE5000_SIMUL != 1) {
    if (PACE5000[num]["type"] == "serial") {
      ser_put(PACE5000[num]["dev"], sprintf("%s\r\n", cmd))
      if (!norep)
        ret = ser_get(PACE5000[num]["dev"], "\r")
    } else if (PACE5000[num]["type"] == "ethernet") {
      sock_put(PACE5000[num]["dev"], sprintf("%s\r", cmd))
      if (!norep)
        ret = sock_get(PACE5000[num]["dev"], "\r")    
    }
  }
  else {
     ret = "0.1"
  }
  __pace5000_debug ">>> _pace5000_comm returns         " ret
  nb = split(ret, aaa,PACE5000[num]["split"])
  if (nb > 1)
    ret = aaa[1]*1
  return ret
}'



#%IU%
#%MDESC%
# MACRO MOTOR: 
# Called by spec on motor operation. It manages: 
#%BR%- position
#%BR%- start_one 
#%BR%- get_status 
def pace5000_cmd(num,key,p1,p2) '{
local type cmd

  __pace5000_debug ">>> pace5000_cmd:    ",num, key, p1, p2

  if(num == "..") { return }

  # get the motor communication tyoe
  type = PACE5000[num]["type"]

  # return the current pressure
  if (key == "position") {
    local ret

    __pace5000_debug ">>> pace5000_cmd:     acting"

    if (type) {
      cmd = ":SENS:PRES?"
      ctrlchar = num
      PACE5000[num]["split"] = "PRES"
    } else {
      # command format: <error reporting>,<notation code>,<current pressure> 
      # it will respond as: <value(7)><error/status code(3)><terminator>
      # if no error, <error/status code(3)> is omitted
      cmd = "@1,N1,D0"
      ctrlchar = "@"
    }
    ret = _pace5000_comm(num,cmd)

    if (_pace5000_report_error(ret,ctrlchar) == 1) {
      __pace5000_debug ">>> pace5000_cmd:     ---------------  reporting an .ERROR."
      return ".error."  
    } else {
      __pace5000_debug ">>> PACE5000(bar): " ret
      return ret
    }
  }

  # start a motion. p1: dial abs. pos., p2: dial rel. pos.
  if (key == "start_one") {
    _pace_setpress(num, p1)

    #returns nothing
  }

#
# return motor status
#
#0x02: moving
#0: otherwise
#

   if (key == "get_status") {
      local ret retarr reply
    
      __pace5000_debug ">>> pace5000_cmd:     acting"
    if (type) {
      cmd = ":SENS:PRES?"
      ctrlchar = num
      PACE5000[num]["split"] = "PRES"
    } else {
      cmd = "N3"
      ctrlchar = "@"
    }

      reply = _pace5000_comm(dev,cmd)

#     it will respond as: <in limit status()><error/status code(3)><terminator>
#     if no error, <error/status code(3)> is omitted  

      if (_pace5000_report_error(reply, ctrlchar) == 1) {
         __pace5000_debug ">>> pace5000_cmd:     ---------------  reporting an .ERROR."
         return ".error."  
      }
      
      if (substr(reply,1,1)=="1") {
         __pace5000_debug ">>> pace5000_cmd:     status: ", reply , " (not moving)"
         return 0
      } else if (substr(reply,1,1)=="0"){
         __pace5000_debug ">>> pace5000_cmd:     status: ", reply , " (moving)"
         return 0x02
      } else {
         __pace5000_debug ">>> pace5000_cmd:     unknown status:  ", reply
         return ".error."
      }
      
   }
}'      


#%UU% <mnemonic>
#%MDESC% setting as remote the required bender
def pace5000_remote'{
   local mirind mynum 

   mynum = motor_num("$1")
   mirind = motor_par(mynum,"serial")
   __pace5000_debug ">>>>>> pace5000_remote : motor: " mynum " serial:" mirind
   _check_pace5000 "$1"
   _pace5000_comm(mirind,"R1")
}'

#%UU% <mnemonic>
#%MDESC% flush the serial line associated to that bender
def pace5000_flush'{
    local mirind mynum
    
   mynum = motor_num("$1")
   mirind = motor_par(mynum,"serial")
   __pace5000_debug ">>>>>> pace5000_flush : motor: " "$1" " serial:" mirind
   _check_pace5000 "$1"
   
    ser_par(mirind, "flush", 2)
}'


#%UU% <mnemonic>
#%MDESC% setting as local the required bender
def pace5000_local'{
   local mirind mynum 

   mynum = motor_num("$1")
   mirind = motor_par(mynum,"serial")
   __pace5000_debug ">>>>>> pace5000_local : motor: " mynum " serial:" mirind
   _check_pace5000 "$1"
   _pace5000_comm(mirind,"M")
}'

#%IU% <mnemonic>
#%MDESC% check that it is a motor
def _check_pace5000'{
   if (motor_num("$1") != -1) {
      p "_check_pace5000: to be DEVELOPPED"
   }
   else {
      p "$1" " is not a motor"
      exit
   }
}'

#%IU% (code, ctrlchar)
#%MDESC% In the case of SCPI protocol: the %B%ctrlchar%B% is the device name.
#A command is sent to the controller's error register to get the error code.
#In the case of the old protocol, the %B%code%B% has the format
#<value>\@<8biterrorcode> and the %B%ctrlchar%B% is "@".
#In the both cases the function returns 0 if no error or 1 if there is an
#error, which is also printed.
def _pace5000_report_error(code, ctrlchar)'{
local myind mycode ii

  if (ctrlchar == "@") {
    myind =  index(code,ctrlchar)

    if (myind == 0) {
      return(0)
    }

    mycode = substr(code,myind+1)
    for (ii in PACE5000_ERR) {
       if ((mycode & 1 << ii) != 0){
         p PACE5000_ERR[ii]
      }
    }
    return(1)
  } else {
    cmd = ":SYST:ERR?"
    PACE5000[ctrlchar]["split"] = "ERR"
    mycode = _pace5000_comm(ctrlchar,cmd)
    if (mycode == 0)
      return(0)
    return(1)
  }
}'

def _pace_setpress(num, val) '{
local cmd dev

  dev = PACE5000[num]["dev"]

  if (PACE5000[num]["type"]) {
   _pace5000_comm(dev,":OUTP 1", 1)
   _pace5000_comm(dev, sprintf(":SOUR %f",val))
  } else {
    # sets <remote mode>,<scale bar>,<P=pressure value>,
    # <wait in limit time(sec)>,<controller on>  
    cmd = sprintf("R1,S0,P=%f,W1,C1",p1)
    _pace5000_comm(dev,cmd)
  }
}'

def _pace_scpi_error(err) '{
local pace5000_err lerr

  if (err == 0)
    return(0)

  if (err < 0)
   err *= -1
  else if (err > 0)
    err += 200

  pace5000_err[102] = "ERROR on PACE5000: Syntax error"
  pace5000_err[104] = "ERROR on PACE5000: Data type error"
  pace5000_err[108] = "ERROR on PACE5000: Parameter not allowed"
  pace5000_err[109] = "ERROR on PACE5000: Missing parameter"
  pace5000_err[110] = "ERROR on PACE5000: Command Header Error"
  pace5000_err[111] = "ERROR on PACE5000: Header Separator Error"
  pace5000_err[112] = "ERROR on PACE5000: Program mnemonic too long"
  pace5000_err[113] = "ERROR on PACE5000: Undefined header"
  pace5000_err[114] = "ERROR on PACE5000: Header suffix out of range"
  pace5000_err[120] = "ERROR on PACE5000: Numeric data error"
  pace5000_err[121] = "ERROR on PACE5000: Invalid character in number"
  pace5000_err[123] = "ERROR on PACE5000: Exponent too large"
  pace5000_err[124] = "ERROR on PACE5000: Too many digits"
  pace5000_err[128] = "ERROR on PACE5000: Numeric data not allowed"
  pace5000_err[130] = "ERROR on PACE5000: Suffix error"
  pace5000_err[131] = "ERROR on PACE5000: Invalid suffix"
  pace5000_err[134] = "ERROR on PACE5000: Suffix too long"
  pace5000_err[138] = "ERROR on PACE5000: Suffix not allowed"
  pace5000_err[140] = "ERROR on PACE5000: Character data error"
  pace5000_err[141] = "ERROR on PACE5000: Invalid character data"
  pace5000_err[144] = "ERROR on PACE5000: Character data too long"
  pace5000_err[148] = "ERROR on PACE5000: Character data not allowed"
  pace5000_err[150] = "ERROR on PACE5000: String data error"
  pace5000_err[151] = "ERROR on PACE5000: Invalid string data"
  pace5000_err[158] = "ERROR on PACE5000: String data not allowed"
  pace5000_err[200] = "ERROR on PACE5000: Execution error"
  pace5000_err[201] = "ERROR on PACE5000: Invalid while in local"
  pace5000_err[202] = "ERROR on PACE5000: Settings lost due to rtl"
  pace5000_err[203] = "ERROR on PACE5000: Command protected"
  pace5000_err[220] = "ERROR on PACE5000: Parameter error"
  pace5000_err[221] = "ERROR on PACE5000: Settings conflict"
  pace5000_err[222] = "ERROR on PACE5000: Data out of range"
  pace5000_err[223] = "ERROR on PACE5000: Too much data"
  pace5000_err[224] = "ERROR on PACE5000: Illegal parameter value"
  pace5000_err[240] = "ERROR on PACE5000: Hardware error"
  pace5000_err[241] = "ERROR on PACE5000: Hardware missing"
  pace5000_err[310] = "ERROR on PACE5000: System error"
  pace5000_err[350] = "ERROR on PACE5000: Queue overflow"
  pace5000_err[400] = "ERROR on PACE5000: Query error"
  pace5000_err[401] = "ERROR on PACE5000: Query only"
  pace5000_err[402] = "ERROR on PACE5000: No query allowed"
  pace5000_err[403] = "ERROR on PACE5000: Paramerter(s) not expected"
  pace5000_err[407] = "ERROR on PACE5000: Emumerated value not in union"
  pace5000_err[408] = "ERROR on PACE5000: Illegal number of parameters"
  pace5000_err[410] = "ERROR on PACE5000: Run out of memory handle"
  pace5000_err[411] = "ERROR on PACE5000: Unit not matched"
  pace5000_err[412] = "ERROR on PACE5000: Unit not required"

  for (lerr in pace5000_err) {
    if (lerr == err)
      return(pace5000_err[err])
  }

}'


#%MACROS%
#%IMACROS%
#%AUTHOR% mcd january 2011
#%TOC%
#$Revision: 1.3 $
#%END%
#%LOG%
#$Log: pace5000.mac,v $
#Revision 1.3  2013/08/19 12:20:27  beteva
#added socket communication. Some cosmetic changes.
#
#Revision 1.2  2011/07/18 13:08:41  domingue
#inverse polarity for in-limit flag when reading status (with N3)