esrf

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

#%NAME%  st_falcon.mac
#%TITLE% st_falcon.mac
#$Revision: 1.3 $
#$Log: st_falcon.mac,v $
#Revision 1.3  2007/11/06 15:10:19  witsch
#tested version with appropriate changes for real operation.
#
#Revision 1.1  2007/03/14 13:15:06  witsch
#Initial revision
#
#%DESCRIPTION%
# Macro motors for the ST France pump (>MCAPC)
#%END%

#%SETUP%
# There must be a serial line device server running on the PC with the ST France
#software called Falcon. The Falcon software and the device server must not run
#at the same time! The serial line device server must be set to use 19200 baud, 
#7 data bits, 1 stop bit, even parity.
#%SETUP%%B%falcon_scan_user%B% is the local hook macro, that  ID27 can modify,
#which will be executed before the counting at each scan point. I will be empty
#at each fresh start, so please save with savmac and use jtdo() to read it from
#the setup. 
#%END%

# Modifications history:


#-----------------------------------------------------------
#%IU%(mne)
#%MDESC% Setting a couple of preset values. Device and controller ID.
def mm_falcon_set_serial_parameters(mne) '{
  global ESRF_ERR
  local params[]
  local device
  if((device = list_getpar(ST_FALCON, mne, "device")) == -1) {
    eprint "ST_FALCON: device undefined."
    return(0)
  }
  # setting the parameters as wished by the Tango DS
  params[ 0] =     3 # SL_TIMEOUT    /* timeout parameter */
  params[ 1] =   500 # timeout value
  params[ 2] =     4 # SL_PARITY     /* number of parity bits parameter */
  params[ 3] =     3 # even parity
  params[ 4] =     5 # SL_CHARLENGTH /* number of data bits parameter */
  params[ 5] =     1 # 7 data bits
  params[ 6] =     6 # SL_STOPBITS   /* number of stop bits parameter */
  params[ 7] =     0 # 1 stop bit
  params[ 8] =     7 # SL_BAUDRATE   /* baud rate parameter */
  params[ 9] = 19200 # baud
  params[10] =     8 # SL_NEWLINE    /* new line character parameter */
  params[11] =     3 # linefeed
  params[12] =     9 # set DTR signal on
  params[13] =     1 # yes

  ESRF_ERR = -1
  esrf_io(device, "DevSerSetParameter", params)
  if (ESRF_ERR){
    if (FALCON & 128) eprint "ST_FALCON: esrf_io(\"" device "\", \"DevSerSetParameter\", params) failed!"
    return(0)
  } else {
    return(1)
  }
}'


#-----------------------------------------------------------
#%IU%(str)
#%MDESC% Don't use this macro directly. Calculates bcc for command to send to
# the controller. simple exclusive or going from the <EOT> to the <ETC>
# character. 
def mm_falcon_bcc(str) '{
	local bcc, i, char, len
  if (FALCON & 128) eprint "mm_falcon_bcc(): string <" str ">"
  len = length(str)
  if (FALCON & 128) eprint "mm_falcon_bcc(): length:", len
	for (i=1; i <= len; i++) {
  	char = substr(str, i, 1)
    bcc = bcc ^ asc(char)
#    if (FALCON & 128) {eprint "mm_falcon_bcc(): char", char, asc(char), "bcc:", bcc, sprintf("%c", bcc)}
  }
  return(bcc)
}' # Ends macro mm_falcon_bcc

#-----------------------------------------------------------
#%IU%(mne, send)
#%MDESC% Don't use this macro directly. Writes a string to the pump.
def mm_falcon_write_aux(mne, send)'{
  local len device
  if((device = list_getpar(ST_FALCON, mne, "device")) == -1) {
    eprint "ST_FALCON: device undefined."
    return(0)
  }
  len = length(send)
  if (len > 31) {
    eprint "request is too long!"
    return(0)
  }
  
  ESRF_ERR = -1
  esrf_io(device, "DevSerWriteString", send)
  if (ESRF_ERR){
    if (FALCON & 128) eprint "ST_FALCON: esrf_io(\"" device "\", \"DevSerWriteString\",", send, ") failed!"
    return(0)
  } else {
    return(1)
  }
}'

#-----------------------------------------------------------
#%IU%(mne)
#%MDESC% Don't use this macro directly. Reads a string from the pump.
def mm_falcon_read_aux(mne) '{
  local device
  if((device = list_getpar(ST_FALCON, mne, "device")) == -1) {
    if (FALCON & 128) eprint "ST_FALCON: device undefined."
    return(0)
  }
  local x
  local ans
  # read up to before the BCC
  global ESRF_ERR
  ESRF_ERR = -1
  ans = esrf_io(device, "DevSerReadLine")
  if (ESRF_ERR) {
    if (FALCON & 128) eprint "ST_FALCON: esrf_io(\"" device "\", \"DevSerReadLine\") failed!"
    return(0)
  } else {
    bcc = esrf_io(device, "DevSerReadNChar", 1)
    ans = ans bcc
    if (FALCON & 128) {eprint "mm_falcon_read_aux() answer is:", ans}
    if (length(ans) < 3) {
      return(0)
    }
    return(ans)
  }
}'

#-----------------------------------------------------------
#%IU%(mne)
#%MDESC% Don't use this macro directly. Reads a string from the pump.
def mm_falcon_read_char(mne) '{
  local device
  if((device = list_getpar(ST_FALCON, mne, "device")) == -1) {
    eprint "ST_FALCON: device undefined."
    return(0)
  }
  local ans
  global ESRF_ERR
  ESRF_ERR = -1
  ans = esrf_io(device, "DevSerReadNChar", 1)
  ans = asc(ans)
  if (ESRF_ERR) {
    if (FALCON & 128) eprint "ST_FALCON: esrf_io(\"" device "\", \"DevSerReadNChar\", 1) failed!"
    return(0)
  } else {
    if (FALCON & 128) {eprint "mm_falcon_read_char() answer is:", ans}
    return(ans)
  }
}'

#-----------------------------------------------------------
#%IU%(mne, cmd)
#%MDESC% Don't use this macro directly. Read a value from the ST France pump.
# It returns the answer of the controller. No BCC is added ! %BR%
# Examples: %BR%
#     SW (status word), II (indentification), VO (software version)
def mm_falcon_read_value(mne, cmd) '{
  local command answer addr looper
  addr = list_getpar(ST_FALCON, mne, "addr")
  # I\'m not quite sure how the addressing works, this should however be fine,
  # for a low address. One is good!
  if (FALCON & 128) {
    eprint "mm_falcon_read_value() - writing:", cmd
  }
  command = sprintf("%c%02x%02x%s%c", 4, 0,((addr<<4) + addr), cmd, 5)
  # no bcc created for inquiries!
  if (!mm_falcon_write_aux(mne, command)) {
    eprint "mm_falcon_read_value() - mm_falcon_write_aux failed!"
    return(0)
  }
  if (!(answer = mm_falcon_read_aux(mne))) {
    eprint "mm_falcon_read_value() - mm_falcon_read_aux failed!"
    return(0)
  }
  if (FALCON & 128) {
    eprint "mm_falcon_read_value() - reading: \"" answer "\""
  }
	local char i len bcc bccstr x
  # check the answer for correct BCC
  # strip received bcc
  len = length(answer)
  char = substr(answer, len, 1)
  bcc = mm_falcon_bcc(substr(answer, 2, len - 2))
  if (bcc != asc(char)) {
    eprint "mm_falcon_read_value: Bad BCC in answer: \"" answer "\"!"
    return(0)
  }

# now strip the answer off everything we dont want. 
# ie. STX, ETX, BCC and mnemonic
# first char should be STX
  if (asc(answer) != 2) {
    eprint "mm_falcon_read_value: bad first char."
    return(-1)
  }
  local stripped, x
  stripped = substr(answer, 2)
  x = index(stripped, "\003") -1
  stripped = substr(stripped, 1, x)
  if ((x = index(stripped, cmd)) != 1) {
    eprint "mm_falcon_read_value: command in answer not in right spot.", x
    return(0)
  }
  stripped = substr(stripped, length(cmd)+1)
  return(stripped)
}' # Ends mm_falcon_read_value

#-----------------------------------------------------------
#%IU%(mne, cmd)
#%MDESC% Don't use this macro directly. Set a value on the ST France pump.
# It returns the answer of the controller, if it is anything else than <ACK>.
# The request is passed through the function creating the BCC, here. Sent
# strings mustn\'t be longer than 32 bytes. %BR%
# Examples: %BR%
#     1SP (Consigne de pression), 1RP (Rampe de pression)
def mm_falcon_write_value(mne, cmd) '{
  local answer bcc x addr
  addr = list_getpar(ST_FALCON, mne, "addr")

  if (FALCON & 128) { eprint "mm_falcon_write_value() - writing: <" cmd ">"}
  # add one char in front to make bcc start at the right spot
  if ((bcc = mm_falcon_bcc(sprintf("%s%c", cmd, 3))) == -1) {
    return(0)
  }
  command = sprintf("%c%02x%02x%c%s%c%c", \
    4, 0,((addr<<4) + addr), 2, cmd, 3, bcc)

#  if (FALCON & 128) { eprint "mm_falcon_write_value() - string: <" command ">"}

  if (!mm_falcon_write_aux(mne, command)) {
    eprint "mm_falcon_write_value() - mm_falcon_write_aux failed!"
    return(0)
  }
  answer = mm_falcon_read_char(mne)
  if (answer != 6 )  {
    eprint "mm_falcon_write_value", command, "NOT acknowledged (" answer ")!"
    return(0)
  }
  if (FALCON & 128) {eprint "mm_falcon_write_value() - reading:", answer == 6 ? "ACK" : "NACK"}
  return(1)
}' # Ends macro mm_falcon_write_value

#-----------------------------------------------------------
#%IU%(mne)
#%MDESC% Don't use this macro directly. Use function mm_falcon_read_value() to
#send command "SW" and analyse the answer.
def mm_falcon_get_state(mne) '{
  local answer char str
  if (!(answer = mm_falcon_read_value(mne, "SW"))) {
    eprint "mm_falcon_get_state failed!"
    return(0)
  }
  if (FALCON & 128) {eprint "mm_falcon_get_state(): answer:", answer}
  # yields six bytes d1 to d6
  # d1 Affichage face avant, 1 - pression, 2 - volume, 3 - temperature, 4 - ana out
  # d2 bits d\'alarme: bit0 - alarme basse pression ou 2e alarme haute pression, 
  #                    bit 1 - alarme haute pression, bit 2 - alarme haute volume
  # d3 face avant: A - avec 1 afficheur, S - sans afficheur, Q - avec 4 afficheurs
  # d4 - d6 unused
  print "Status word of the ST France Falcon pump"
  char = substr(answer, 1, 1)
  if (FALCON & 128) {eprint "mm_falcon_get_state() - D1:", char}

  str = "Affichage face avant:\n\ten "
  if (char == "1") {
    str = str "pression"
  } else if (char == "2") {
    str = str "volume"
  } else if (char == "3") {
    str = str "temperature"
  } else if (char == "4") {
    str = str "ana out"
  } else {
    str = "Strange output from SW command"
  }
  print str
    
  char = substr(answer, 2, 1)
  char = asc(char)
  str = "Bits d\'alarme:\n"
  if (char & 0x01) {
    str = str "alarme basse pression ou 2e alarme haute pression\n"
  } 
  if (char & 0x02) {
    str = str "alarme haute pression\n"
  } 
  if (char & 0x04) {
    str = str "alarme haute volume\n"
  }
  print str

  char = substr(answer, 3, 1)
  str = "Face avant:\n\t"
  if (char == "A") {
    str = str "avec 1 afficheur"
  } else if (char == "S") {
    str = str " sans afficheur"
  } else if (char == "Q") {
    str = str " avec 4 afficheurs"
  } else {
    str = "Strange output from SW command"
  }
  print str
}'

#-----------------------------------------------------------
#%IU%(mne)
#%MDESC% Don't use this macro directly. Use function mm_falcon_read_value() to
#send command "II" and analyse the answer.
def mm_falcon_ident(mne) '{
  local answer char str Ident
  Ident = ">MCAPC"
  if (!(answer = mm_falcon_read_value(mne, "II"))) {
    eprint "mm_falcon_ident() failed!"
    return(0)
  }
  if (answer != Ident) {
    print "mm_falcon_ident(): bad answer from controller"
    return(0)
  }
  return(1)
}'

def mm_falcon_just_spell_out_answer(str) '{
  local len, i, char
  len = length(str) +1
  for (i = 1; i < len; i ++) {
    char = substr(str, i, 1)
    printf("%3s ", char)
  }
  print
  for (i = 1; i < len; i ++) {
    char = substr(str, i, 1)
    printf(" %02x ", asc(char))
  }
  print
}'

#-----------------------------------------------------------
# Macro motor definitions
#-----------------------------------------------------------
#%IU%
#%MDESC%
# Called by spec after reading the config file
def stfalcon_config(motnum, type, unit, module, chan ) '{
  local mne
  global ST_FALCON
  if (motnum != "..") {
    mne = motor_mne(motnum)
    if (FALCON & 192) {eprint "stfalcon_config(" motnum, ":", type, ":", unit, ":", module, ":", chan }

  	# serial line device server here please
    # The serial line device server should be set in the ADDR field of the
    # configuration. It will be available as stfalcon_ADDR in the local scope of
    # this function.
    list_test ST_FALCON
    list_add (ST_FALCON, mne)
    list_setpar(ST_FALCON, mne, "device", stfalcon_ADDR)
    list_setpar(ST_FALCON, mne, "addr",    chan)
    
	 # now do some hardware checks
		if ( ! mm_falcon_set_serial_parameters(mne) ) {
      eprint "Falcon Pump Controller unresponsive, disabled!"
      return ".error."
    }
		if ( ! mm_falcon_ident(mne) ) {
      eprint "Falcon Pump Controller is not the right device, disabled!"
      return ".error."
    }
  }
}'


#%IU%
#%MDESC%
# Called by spec
def stfalcon_cmd(num, cmd, p1, p2) ' {
  local mne

  if (FALCON & 192) eprint "stfalcon_cmd(", num, ":", cmd, ":", p1, ":", p2
  if(num == "..") { return }

  mne=motor_mne(num)

  if ( cmd == "position" ) {
      return mm_falcon_read_value(mne, "1PV")
  }
  if ( cmd == "slew_rate" ) {
    local x
    x = "1RP" p1 * 16.666
    return mm_falcon_write_value(mne, x)
  }
  if ( cmd == "start_one" ) {
    local x
    x = "1SP" p1
    mm_falcon_write_value(mne, x)
    return mm_falcon_write_value(mne, "AXr")
  }
  if ( cmd == "abort_one" ) {
    local x
    mm_falcon_write_value(mne, "AXq")
    return 1
  }
  if ( cmd == "get_status" ) {
    local x
    # first check if its regulating!
    x = mm_falcon_read_value(mne, "AX")
    # the pump actually sends [rq] plus some spaces. 
    # reduce check on first char!
    if ( asc(x) == 113) return 0 # that means "q"
    x  = mm_falcon_read_value(mne, "1SP")
    x -= mm_falcon_read_value(mne, "1PV")
    if (fabs(x) >  motor_par(mne, "dc_dead_band")) return 0x02
    else return  0
  }
} '


#%UU% motor start finish intervals
#%MDESC% Do a scan with the Sanchez Technologies, Falcon Pump controller. Behaves like an ascan.
def falcon_scan '{
  local faloop faldel falpts newpts
  if ($# != 4) {
    eprint "Usage:  falcon_scan  motor start finish  intervals"
    exit
  }
  if((device = list_getpar(ST_FALCON, "$1", "device")) == -1) {
    eprint "ST_FALCON: device undefined."
    exit
  }
  _check0 "$1"

#print "$1", _s[0], _f[0], _n1
  falpts = int($4) + 1
  faldel = ($3 - $2 ) / falpts
  _mv  $1 $2; _update("$1")
  for ( faloop = 1 ; faloop < falpts; faloop ++) {
    falcon_scan_user
    newpts = $2 + faloop * faldel
    _mv $1 newpts; _update("$1")
  }
}'

#%UU% macro
#%MDESC% This is the local hook macro, that  ID27 can modify, which will be executed before the
# counting at each scan point. I will be empty at each fresh start, so please save with savmac and
# use jtdo() to read it from the setup.
# only defined if not a macro yet.
if (!(whatis("falcon_scan_user") & 2)) rdef falcon_scan_user 'print "falcon_scan_user"'



#%MACROS%
#%IMACROS%
#%AUTHOR% by H. Witsch, BLISS