esrf

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

#%TITLE% VPDU.MAC
#%NAME%
# ESRF Vme programmable delay unit.
#%OVERVIEW%
#%DL%
#%DT%vpdusetup%DD%command line setup
#%DT%vpdu%DD%menu setup and control 
#%DT%vpdu_get*%DD% read in board settings
#%DT%vpdu_set*%DD% change board settings 
#%DT%vpdutrig%DD% board trigger
#%DT%vpdureset%DD% board reset
#%XDL%
#%END%

global VPDU_NUM VPDU_DEV VPDU_NAME VPDU_PWDT VPDU_MHZ
global VPDU_OR VPDU_TRIG VPDU_EDGE VPDU_CLK VPDU_ECL
global VPDU_CHAN VPDU_DELAY VPDU_CNUM VPDU_TNUM
global VPDU_SOFF VPDU_COFF
global VPDU_COR VPDU_NCOR
global VPDU_MNE VPDU_TMNE VPDU_PMNE

VPDU_COR["automatic"]=2	; VPDU_NCOR["automatic"]=3	
VPDU_COR["external"]=0 	; VPDU_NCOR["external"]=3 
VPDU_COR["internal"]=1	; VPDU_NCOR["internal"]=3
VPDU_COR["none"]=8	; VPDU_NCOR["none"]=0x4c 	# multiply COR by 8
VPDU_COR["ttl"]=0.5	; VPDU_NCOR["ttl"]=0x4c		# multiply COR by 8
VPDU_COR["ecl"]=0	; VPDU_NCOR["ecl"]=0x4c		# multiply COR by 8
VPDU_COR["rising"]=0x10	; VPDU_NCOR["rising"]=0
VPDU_COR["falling"]=0	; VPDU_NCOR["falling"]=0x10

VPDU_SOFF = 0x41 << 8
VPDU_COFF = 0x4b << 8
VPDU_CNUM = 6
VPDU_TNUM = 3

#%UU%
#%MDESC% Put this into your "setup" file.
# After a start fresh of SPEC is will redefine the hook macros for those
# VPDU channels which are defined as software motors. %BR%

def vpdu_setup '
{
  global VPDU_NUM VPDU_NAME VPDU_DEV VPDU_MNE[] VPDU_TMNE[] VPDU_PMNE[]
  local ii jj t

  VPDU_NUM=value("vpdu/number")
  for (ii=0;ii<VPDU_NUM;ii++) 
  {
    VPDU_NAME[ii] = value("vpdu"ii+1"/name")
    VPDU_DEV=ii ; # vpdu_read
    for (jj=0;jj<VPDU_CNUM;jj++)
    {
      VPDU_MNE[ii*VPDU_CNUM+jj] = value(t="vpdu"ii+1"/mnemonic/"jj+1)
    }
    for (jj=0;jj<VPDU_TNUM;jj++)
    {
      VPDU_TMNE[ii*VPDU_TNUM+jj] = value(t="vpdu"ii+1"/mnemonic/t"jj+1)
      VPDU_PMNE[ii*VPDU_TNUM+jj] = value(t="vpdu"ii+1"/mnemonic/p"jj+1)
    }
    VPDU_MHZ[ii]=value("vpdu"ii+1"/MHz")
  }
  vpdu_define
}'

#%UU%
#%MDESC% 
# Menu to configure the VPDU board(s). %BR%
#%PRE%
#VME PROGRAMMABLE DELAY UNIT SETUP
#
#
#- Device name <ID/SHUTTER/01> num 1.
#- Pulsed Width 300 mu-sec.
#- Channel 1 ON    delay 1 mu-sec.
#- Channel 2 ON    delay 2 mu-sec.
#- Channel 3 OFF 
#- Channel 4 ON    delay 4 mu-sec.
#- Channel 5 OFF 
#- Channel 6 OFF 
#- Trigger <external> on <rising> edge.
#- OR output OFF
#- Clock <internal> - Frequency 32 MHz
#
#         RESET         TRIGGER 
#_________________________________
#Enter highlighted key or 0 to exit (0)? 
#%PRE%
# - The board which device name appears here together with the generic
#   number the software assigned to it is set as the %B%active%B% one.
#   You can select other board by typing %B%D%B%. %BR%
# - Type any other highligthed letters to change any other parameters.
# - To change channel delay values, put the channel number in front of 
#   the "d" letter. (e.g. %B%3d%B%). %BR%
# - RESET (%B%RE%B%) resets the board.%BR%
# - TRIGGER (%B%TR%B%) triggers the board once.%BR%
# - Internal clock cannot be set another rate that 32 MHz.%BR%%BR%

def vpdu '{
  menu ("  VME PROGRAMMABLE DELAY UNIT SETUP","vpdu_menu_disp","vpdu_read")
}'

#%IU% 
#%MDESC% Vpdu menu display.
def vpdu_menu_disp '{
local n i t resource
 
if (!VPDU_NUM) vpdu_setup

n = "\n"

menuoptval (0,0,"Device name",VPDU_NAME[VPDU_DEV],"D") 
menuvardynl("VPDU_DEV","VPDU_NAME","VPDU_NUM","_vpdu_menu_add_dev",                                                          "_vpdu_menu_rem_dev")
menuprint  (0,n,"\#",VPDU_DEV+1)

for (i=0;i<VPDU_CNUM;i++) {

menuoptval (0,0,sprintf(" Channel %d",i+1),\
  VPDU_CHAN[VPDU_DEV*VPDU_CNUM+i]?"ON":"OFF",i+1)
menuvartogg(sprintf("VPDU_CHAN[VPDU_DEV*VPDU_CNUM+%d]",i))
menuaction (\
  t="vpdu_menu_setcor VPDU_COFF VPDU_CHAN[VPDU_DEV*VPDU_CNUM+"i"] (1<<"i")")

if (VPDU_CHAN[VPDU_DEV*VPDU_CNUM+i]) {
  menuoptval("%9.2f","mu-sec.","delay",\
    VPDU_DELAY[VPDU_DEV*VPDU_CNUM+i],sprintf("%dd",i+1))
  menuvargetv(sprintf("VPDU_DELAY[VPDU_DEV*VPDU_CNUM+%d]",i),\
    sprintf("Channel %d delay",i+1))
  menuaction(\
    t="vpdu_setdelay VPDU_DEV+1 "i"+1 VPDU_DELAY[VPDU_DEV*VPDU_CNUM+"i"]")
} 

  if (!VPDU_MNE[VPDU_DEV*VPDU_CNUM+i]) VPDU_MNE[VPDU_DEV*VPDU_CNUM+i]=""
  menuoptval(0,0," mnemonic",VPDU_MNE[VPDU_DEV*VPDU_CNUM+i],sprintf("%dm",i+1))
  menuvargetv (sprintf("VPDU_MNE[VPDU_DEV*VPDU_CNUM+%d]",i))
  resource = "vpdu"VPDU_DEV+1"/mnemonic/"i+1
  menuaction(t="set_value(\""resource"\",VPDU_MNE["VPDU_DEV*VPDU_CNUM+i"])")  

print n
}

menuoptval (0,"mu-sec"," Pulsed Width",VPDU_PWDT[VPDU_DEV],"P")
menuvargetv ("VPDU_PWDT[VPDU_DEV]")
menuaction ("vpdu_setpulse VPDU_DEV+1 VPDU_PWDT[VPDU_DEV]")

menuoptval(0,n," OR output",VPDU_OR[VPDU_DEV]?"ON":"OFF","O")
menuvartogg("VPDU_OR[VPDU_DEV]")
menuaction ("vpdu_menu_setcor VPDU_SOFF VPDU_OR[VPDU_DEV] 0x80")
print n

menuoptval(0,0," Trigger",VPDU_TRIG[VPDU_DEV],"T")
menuvarlist("VPDU_TRIG[VPDU_DEV]","external  internal  automatic")
menuaction("vpdu_menu_setcor VPDU_SOFF VPDU_COR[VPDU_TRIG[VPDU_DEV]] 3")
if ("external" == VPDU_TRIG[VPDU_DEV]) {
  menuoptval(0,0,"Edge",VPDU_EDGE[VPDU_DEV],"E")
  menuvartogg("VPDU_EDGE[VPDU_DEV]","falling","rising")
  menuaction(t="vpdu_menu_setcor VPDU_SOFF VPDU_COR[VPDU_EDGE[VPDU_DEV]] "\
    "VPDU_NCOR[VPDU_EDGE[VPDU_DEV]]")
}
print n

menuoptval(0,0," Clock",VPDU_CLK[VPDU_DEV],"C")
menuvarlist("VPDU_CLK[VPDU_DEV]","none  internal  ttl  ecl")
resource = "vpdu"VPDU_DEV+1"/MHz"
menuaction(t="vpdu_menu_setcor VPDU_SOFF 8*VPDU_COR[VPDU_CLK[VPDU_DEV]] 0x4c; "\
  "if (\"internal\"==VPDU_CLK[VPDU_DEV]) VPDU_MHZ[VPDU_DEV] = 32;"\
  "set_value(\""resource"\",VPDU_MHZ[VPDU_DEV])")

if ("none" != VPDU_CLK[VPDU_DEV]) {
  if ("internal"==VPDU_CLK[VPDU_DEV]) {
    VPDU_MHZ[VPDU_DEV] = 32
    set_value(t="vpdu"VPDU_DEV+1"/MHz",32)
    menuprint(0,"MHz","Frequency",  VPDU_MHZ[VPDU_DEV])
  } else {
    menuoptval(0,"MHz","Frequency",  VPDU_MHZ[VPDU_DEV],"F")
    menuvargetv("VPDU_MHZ[VPDU_DEV]","External clock frequency")
    resource = "vpdu"VPDU_DEV+1"/MHz"
    menuaction(t="VPDU_MHZ[VPDU_DEV] = fabs(VPDU_MHZ[VPDU_DEV]); "\
      "set_value(\""resource"\",VPDU_MHZ[VPDU_DEV])")
  }
}

if ("ecl" != VPDU_CLK[VPDU_DEV]) {
  menuoptval(0,n," Switch to internal clock if no ECL",\
    VPDU_ECL[VPDU_DEV]?"YES":"NO","S")
  menuvartogg("VPDU_ECL[VPDU_DEV]")
  menuaction("vpdu_menu_setcor VPDU_SOFF VPDU_ECL[VPDU_DEV] 0x20")
}

print n
# menu options for displaying T channel delays and widths i.e. 1-4, 2-5, 3-6
for (i=0; i<VPDU_TNUM; i++) {
  if (!VPDU_TMNE[VPDU_DEV*VPDU_TNUM+i]) VPDU_TMNE[VPDU_DEV*VPDU_TNUM+i]=""
  if (!VPDU_PMNE[VPDU_DEV*VPDU_TNUM+i]) VPDU_PMNE[VPDU_DEV*VPDU_TNUM+i]=""
  printf(" T%d-%d (delay %8.1f width %8.1f) ",i+1,i+4,\
    VPDU_DELAY[VPDU_DEV*VPDU_CNUM+i],\
    fabs(VPDU_DELAY[VPDU_DEV*VPDU_CNUM+i]-VPDU_DELAY[VPDU_DEV*VPDU_CNUM+i+3]))
  menuoptval (0,0," mnemonic ",VPDU_TMNE[VPDU_DEV*VPDU_TNUM+i],t="t"i+1"m")
  menuvargetv (t="VPDU_TMNE[VPDU_DEV*VPDU_TNUM+"i"]")
  resource = "vpdu"VPDU_DEV+1"/mnemonic/t"i+1
  menuaction(t="set_value(\""resource"\",VPDU_TMNE["VPDU_DEV*VPDU_TNUM+i"])")  
  menuoptval (0,0,"",VPDU_PMNE[VPDU_DEV*VPDU_TNUM+i],t="p"i+1"m")
  menuvargetv (t="VPDU_PMNE["VPDU_DEV*VPDU_TNUM+i"]")
  resource = "vpdu"VPDU_DEV+1"/mnemonic/p"i+1
  menuaction(t="set_value(\""resource"\",VPDU_PMNE["VPDU_DEV*VPDU_TNUM+i"])")  
  print n
}


print
menuoptbutton (20,0,"RESET","RE"); 	menuaction("vpdureset")
menuoptbutton (20,n,"TRIGGER","TR"); 	menuaction("vpdutrig")
}'

#%IU% %MDESC% Define hook macros for software motors

def vpdu_define '{
  global VPDU[];
  global VPDU_DEV
  
  cdef("","","vpdu","delete") # remove old hook macros
  cdef("user_getpangles","vpdu_get\n","vpdu")
  cdef("user_moveall","vpdu_change\n","vpdu")
  
}'

#%IU% <device_index>
#%MDESC% menu init macro when adding a new device.
def _vpdu_menu_add_dev '{
local ii t
VPDU_MHZ[$1] = 32
set_value(t="vpdu"$1+1"/MHz",32)
# update "$BLISSADM/local/spec/userconf/vpdu" settings
VPDU_NUM=VPDU_NUM+1
set_value("vpdu/number",VPDU_NUM)
for (ii=0;ii<VPDU_NUM;ii++) set_value(t="vpdu"ii+1"/name",VPDU_NAME[ii])
}'

#%IU% <device_index> <number_of_devices_prior_to_deletion>
#%MDESC% menu reset macro when deleting a device.
def _vpdu_menu_rem_dev '{
local ii t

if ($1!=$2-1) {
  for (ii=0;ii<VPDU_CNUM;ii++) {
    VPDU_CHAN[$1*VPDU_CNUM+ii] = VPDU_CHAN[($2-1)*VPDU_CNUM+ii] 
    VPDU_DELAY[$1*VPDU_CNUM+ii]= VPDU_DELAY[($2-1)*VPDU_CNUM+ii] 
  }
  VPDU_PWDT[$1] = VPDU_PWDT[$2-1]
  VPDU_EDGE[$1] = VPDU_EDGE[$2-1]
  VPDU_CLK[$1] = VPDU_CLK[$2-1]
  VPDU_MHZ[$1] = VPDU_MHZ[$2-1]
  VPDU_TRIG[$1] = VPDU_TRIG[$2-1]
  VPDU_ECL[$1] = VPDU_ECL[$2-1]
  VPDU_OR[$1] = VPDU_OR[$2-1]
  VPDU_NAME[$1] = VPDU_NAME[$2-1]
}
# update "$BLISSADM/local/spec/userconf/vpdu" settings
VPDU_NUM=VPDU_NUM-1
set_value("vpdu/number",VPDU_NUM)
for (ii=0;ii<VPDU_NUM;ii++)
{
  set_value(t="vpdu"ii+1"/name",VPDU_NAME[ii])
}
}'

#%IU% <offset> <condit> <bitmsk> <resfix>
#%MDESC% menu macro to change CoR register automatically.
def vpdu_menu_setcor '{

  local set res


  if ($1==VPDU_SOFF) {
    set = $2
    res = $3
  } else {
    set = $2?$3:0
    res = $2?0:$3
  }
  vpdu_setCoR VPDU_NAME[VPDU_DEV] $1 set res
}'

#%IU% <device_name> <cmd_offset_word> <set_word> <reset_word>
#%MDESC% Writes one board command register.
def vpdu_setCoR '{

if ($# == 4) {
  local _off _set _res

  _off = $2; _set = $3; _res = $4
  _set += _off
  _res += _off

  if (_res) { vpdu_io $1 DevResCoR _res }
  if (_set) { vpdu_io $1 DevSetCoR _set }
}
}'

#%UU% <device_num> <micro_sec_pulse_width>
#%MDESC% Sets one board pulse width; the board is referenced to by the generic 
# number the software has assigned it at setup (see %B%vpdu%B% or 
# %B%vpdusetup%B%).

#%{%<A NAME="newsetchan"></A>%}%

def vpdu_setpulse '{
if ($# == 2) {   
  local _word

  _word = ($2<0) ? 255 : (($2>31.9)?0:255-($2/0.125))
  vpdu_io VPDU_NAME[$1-1] DevSetWd _word
}
}'


#%UU% <device_num> <channel_num> <0|1>
#%MDESC% Enables (1) or disables (0) the specified channel
# on the specified board, referenced by the generic number
# the software has assigned it at setup (see %B%vpdu%B% or
# %B%vpdusetup%B%).

#%{%<A NAME="newsetdelay"></A>%}%

def vpdu_setchannel '{

if ($# == 3) {

  local setb resb
  
  setb = resb = 0
  if ($3) {
    setb = 1 << $2-1
    VPDU_CHAN[($1-1)*VPDU_CNUM+$2-1] = 1
  } else {
    resb = 1 << $2-1
    VPDU_CHAN[($1-1)*VPDU_CNUM+$2-1] = 0
  }
  vpdu_setCoR VPDU_NAME[($1-1)] VPDU_COFF setb resb
}
}'

#%UU% <device_num(1,2,...)> <channel(1-6)> <micro_sec_delay>
#%MDESC% Writes to one board one channel delay; the board 
# is referenced by the generic number the software has 
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).
def vpdu_setdelay '{
if ($# == 3) {
  local _word

  _word = (($2-1)*8+1) << 24 
  _word |= $3 < 0 ? 0 : \
  ( $3*VPDU_MHZ[$1-1] > 0xffffff ? 0xffffff : ($3*VPDU_MHZ[$1-1]))

  vpdu_io VPDU_NAME[$1-1] DevWrDlR _word

}
}'

#%UU% <device_num> <"internal"|"external"|"automatic">
#%MDESC% Writes to one board the type of triggering to be used; the board 
# is referenced by the generic number the software has 
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).

def vpdu_settrigger '{
  
  local mod dev set res 

  dev = $1-1
  mod = "$2"
  set = res = 0

  if ($#==2) {
    if (mod == "external" || mod == "internal" || mod == "automatic") {
      VPDU_TRIG[dev] = mod
      res = 3
      set = "automatic" == mod ? 2 : ("external" == mod ? 0 : 1)

      vpdu_setCoR VPDU_NAME[dev] VPDU_SOFF set res
  }
}
}'

#%UU% <device_num> <"falling"|"rising">
#%MDESC% Writes to one board which edge the external trigger would react on; the board 
# is referenced by the generic number the software has 
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).

def vpdu_setedge '{

  local dev set res 

  dev = $1-1
  mod = "$2"
  set = res = 0

  if ("external" == VPDU_TRIG[dev]) {
    if (2==$#) {
      if (mod == "rising" || mod == "falling") {
        set = res = 0
        VPDU_EDGE[dev] = mod
        if (mod == "rising") set = 0x10 
        else res = 0x10 
        vpdu_setCoR VPDU_NAME[dev] VPDU_SOFF set res
      }
    }
  }
}'

#%UU% <device_num> <1|0>
#%MDESC% Writes to one board whether the OR output is activated or not; 
#the board  is referenced by the generic number the software has 
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).

def vpdu_setor '{
  local dev set res 

  dev = $1-1
  mod = $2
  set = res = 0

  if (2==$#) {
    VPDU_OR[dev] = mod
    if (VPDU_OR[dev]) set = 0x80
    else res = 0x80
    vpdu_setCoR VPDU_NAME[dev] VPDU_SOFF set res
  }
}'

#%UU% <device_num> <mhz>
#%MDESC% Writes to one board the external clock frequency; if internal clock is used, the frequency is 32. 
#the board  is referenced by the generic number the software has 
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).

def vpdu_setmhz '{

  local dev mod set res t

  dev = $1-1
  mod = $2
  set = res = 0

  if (2==$#) {
      if ("internal"==VPDU_CLK[dev]) VPDU_MHZ[dev] = 32
      else VPDU_MHZ[dev] = fabs($2)
  }
  set_value(t="vpdu"dev+1"/MHz",VPDU_MHZ[dev])
}'

#%UU% <device_num> <1|0>
#%MDESC% Writes to one board whether it should switch to internal clock if no 
#ecl or not; 
#the board  is referenced by the generic number the software has 
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).

def vpdu_setecl '{
  local dev mod set res 

  dev = $1-1
  mod = $2
  set = res = 0

  if (2==$#) {
    VPDU_ECL[dev] = val
    if (mod) res = 0x20
    else set = 0x20
    vpdu_setCoR VPDU_NAME[dev] VPDU_SOFF set res
  }
}'

#%UU% <device_num> <"none"|"internal"|"ttl"|"ecl">
#%MDESC% Writes to one board the of clock to be used; internal clock frequency
#is always 32 Mhz.
#the board  is referenced by the generic number the software has 
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).

def vpdu_setclock '{

  local dev mod set res 

  dev = $1-1
  mod = "$2"
  set = res = 0

  if (2==$#) {
    if (mod == "none" || mod == "internal" || mod == "ttl" || mod == "ecl") {
      VPDU_CLK[dev] = mod
      res = 0x4c
      set = "none"==mod ? 0x40 : ("internal"==mod ? 8 : ("ttl"==mod? 4 : 0))
      if ("internal" == mod) 
      { 
        VPDU_MHZ[dev] = 32; set_value(t="vpdu"dev+1"/MHz",32)
      }

      vpdu_setCoR VPDU_NAME[dev] VPDU_SOFF set res
    }
  }
}'


#%UU% [device_num]
#%MDESC% Triggers once the active board, or the specified 
# one, referenced by the generic number the software has
# assigned it at setup (see %B%vpdu%B% or %B%vpdusetup%B%).


def vpdutrig ' {
if ($1) {
  vpdu_io VPDU_NAME[$1-1] DevSofTr       
} else {
  vpdu_io VPDU_NAME[VPDU_DEV] DevSofTr       
}
}'


#%UU% [device_num]
#%MDESC% Resets the active board, or the one specified (referenced
# by the generic number the software has assigned it at setup);
# 0 means all boards.

def vpdureset '{ 
if ($1) {
  vpdu_io VPDU_NAME[$1-1] DevReset
} else if ($#) {
  for (ii=0;ii<VPDU_NUM;ii++) {
    vpdu_io VPDU_NAME[ii] DevReset
  }
} else {
  vpdu_io VPDU_NAME[VPDU_DEV] DevReset
}
sleep(1)
}'

#%IU% <device_name> <command_string> [input] [output] 
#%MDESC% Manages the I/O to the board.%BR%
# The value returned by esrf_io() is put into ESRF_RET global variable.

def vpdu_io '
{
  global ESRF_RET ESRF_ERR

if ("0"!="$1") {
  if ($# == 2) ESRF_RET = esrf_io($1,"$2")
  else if ($# == 3) ESRF_RET = esrf_io($1,"$2",$3)
  else if ($# == 4) ESRF_RET = esrf_io($1,"$2",$3,$4)

  if (ESRF_ERR) { print "VPDU SERVER ERROR occured"; sleep(1) }
}
}'

#%IU%
#%MDESC% Reads in active board settings.
def vpdu_read '
{
 if (VPDU_NAME[VPDU_DEV])
 {
  local offs 
  
  #if (VPDU_MHZ[VPDU_DEV]==0) VPDU_MHZ[VPDU_DEV]=32

  offs = 0x5b << 8
  vpdu_io VPDU_NAME[VPDU_DEV] DevRdCoR offs 
  VPDU_PWDT[VPDU_DEV] = (ESRF_RET == 0xff ? 0.04 : ((255 - ESRF_RET) * 0.125))
  
  for (i=1;i<=VPDU_CNUM;i++) {
    offs = ((i-1)*8+1) << 24
    vpdu_io VPDU_NAME[VPDU_DEV] DevRdDlR offs 
    VPDU_DELAY[VPDU_DEV*VPDU_CNUM+i-1] = VPDU_MHZ[VPDU_DEV] <= 0 ? -1 :\
    ((ESRF_RET&0x00ffffff)/VPDU_MHZ[VPDU_DEV])
  }

  offs = 0x4b << 8
  vpdu_io VPDU_NAME[VPDU_DEV] DevRdCoR offs 
  for (i=0;i<VPDU_CNUM;i++) {
    VPDU_CHAN[VPDU_DEV*VPDU_CNUM+i]=((ESRF_RET & (1 << i)) != 0);
  }

  offs = 0x41 << 8
  vpdu_io VPDU_NAME[VPDU_DEV] DevRdCoR offs 
  if (0 == (ESRF_RET & 3)) VPDU_TRIG[VPDU_DEV]="external"
  else if (1 == (ESRF_RET & 3)) VPDU_TRIG[VPDU_DEV]="internal"
  else if (2 == (ESRF_RET & 3)) VPDU_TRIG[VPDU_DEV]="automatic"
  else VPDU_TRIG[VPDU_DEV]=""
  if (ESRF_RET & 0x10) VPDU_EDGE[VPDU_DEV]="rising"
  else VPDU_EDGE[VPDU_DEV]="falling"

  VPDU_CLK[VPDU_DEV] = "unknown"
  if (ESRF_RET & 0x40) VPDU_CLK[VPDU_DEV] = "none"
  else if (ESRF_RET & 8) VPDU_CLK[VPDU_DEV] = "internal"
  else {
    if (ESRF_RET & 4) VPDU_CLK[VPDU_DEV] = "ttl"
    else VPDU_CLK[VPDU_DEV] = "ecl"
  } 

  if (ESRF_RET & 0x80) VPDU_OR[VPDU_DEV]=1
  else VPDU_OR[VPDU_DEV]=0
 }
}'


#%UU% <device_index>
#%MDESC% Reads in pulse width.
def vpdu_getpulse '{
  local offs dev 

  dev = $1-1 
  offs = 0x5b << 8

  vpdu_io VPDU_NAME[dev] DevRdCoR offs 
  VPDU_PWDT[dev] = (ESRF_RET == 0xff ? 0.04 : ((255 - ESRF_RET) * 0.125))
}'

#%UU% <device_index> [channel_num]
#%MDESC% Reads in delay settings.
def vpdu_getdelay '{
  local offs dev cha

  dev = $1-1
  cha = $2

  #if (VPDU_MHZ[dev]==0) VPDU_MHZ[dev]=32

  if (cha) {
    offs = ((cha-1)*8+1) << 24
    vpdu_io VPDU_NAME[dev] DevRdDlR offs 
    VPDU_DELAY[dev*VPDU_CNUM+cha-1] = VPDU_MHZ[dev] <= 0 ? -1 :\
                        ((ESRF_RET&0x00ffffff)/VPDU_MHZ[dev])

  } else {
    for (i=1;i<=VPDU_CNUM;i++) {
      offs = ((i-1)*8+1) << 24
      vpdu_io VPDU_NAME[dev] DevRdDlR offs 
      VPDU_DELAY[dev*VPDU_CNUM+i-1] = VPDU_MHZ[dev] <= 0 ? -1 :\
                          ((ESRF_RET&0x00ffffff)/VPDU_MHZ[dev])
    }
  }
}'

#%UU% <device_index> [channel_num]
#%MDESC% Reads in channel state.

def vpdu_getchannel '{
  local offs dev 

  dev = $1-1
  offs = 0x4b << 8

  vpdu_io VPDU_NAME[dev] DevRdCoR offs 
  for (i=0;i<VPDU_CNUM;i++) {
    VPDU_CHAN[dev*VPDU_CNUM+i]=((ESRF_RET & (1 << i)) != 0);
  }
}'

#%UU% <device_index>
#%MDESC% Reads in trigger and edge settings.
def vpdu_gettrigger '{
  local offs dev

  offs = 0x41 << 8
  dev = $1-1

  vpdu_io VPDU_NAME[dev] DevRdCoR offs 
  if (0 == (ESRF_RET & 3)) VPDU_TRIG[dev]="external"
  else if (1 == (ESRF_RET & 3)) VPDU_TRIG[dev]="internal"
  else if (2 == (ESRF_RET & 3)) VPDU_TRIG[dev]="automatic"
  else VPDU_TRIG[dev]=""
  if (ESRF_RET & 0x10) VPDU_EDGE[dev]="rising"
  else VPDU_EDGE[dev]="falling"
}'

#%UU% <device_index>
#%MDESC% Reads in OR output and clock settings settings.
def vpdu_getclock '{
  local offs dev 

  #if (VPDU_MHZ[dev]==0) VPDU_MHZ[dev]=32

  dev = $1-1
  offs = 0x5b << 8
  vpdu_io VPDU_NAME[dev] DevRdCoR offs 

  VPDU_CLK[dev] = "unknown"
  if (ESRF_RET & 0x40) VPDU_CLK[dev] = "none"
  else if (ESRF_RET & 8) VPDU_CLK[dev] = "internal"
  else {
    if (ESRF_RET & 4) VPDU_CLK[dev] = "ttl"
    else VPDU_CLK[dev] = "ecl"
  } 

  if (ESRF_RET & 0x80) VPDU_OR[dev]=1
  else VPDU_OR[dev]=0
}'

#%UU% <device_index>
#%MDESC% Reads in OR output and clock settings settings.
def vpdu_getor 'vpdu_getclock $*'



#%IU% 
#%MDESC% obsolete: use %B%%{%<A HREF="#newsetchan">vpdu_setchannel</A>%}%%B%.
def vpduchannel '{

if ($# == 3) {

  local setb resb
  
  setb = resb = 0
  if ($3) {
    setb = 1 << $2-1
    VPDU_CHAN[$1*VPDU_CNUM+$2-1] = 1
  } else {
    resb = 1 << $2-1
    VPDU_CHAN[$1*VPDU_CNUM+$2-1] = 0
  }
  vpdu_setCoR VPDU_NAME[$1] VPDU_COFF setb resb
}
}'

#%IU% 
#%MDESC% obsolete: use %B%%{%<A HREF="#newsetdelay">vpdu_setdelay</A>%}%%B%.
def vpdusetdelay '{
if ($# == 3) {
  local _word

  _word = (($2-1)*8+1) << 24 
  _word |= $3 < 0 ? 0 : \
  ( $3*VPDU_MHZ[$1-1] > 0xffffff ? 0xffffff : ($3*VPDU_MHZ[$1-1]))

  vpdu_io VPDU_NAME[$1-1] DevWrDlR _word

  }
}'

#%IU%
#%MDESC% called implicitly when you use do a "mv" or "scan" of a delay.
# Since the A[] array gives no information which motor is currently going to 
# be moved, we have to guess it from the last recorded position values.

def vpdu_change '
{
  local i j n t dt t1 t2 mnemonic
    
  for (i=0; i<VPDU_NUM; i++)
  {
    for (j=0; j<VPDU_CNUM; j++) # single pulse delay
    {
      mnemonic = VPDU_MNE[i*VPDU_CNUM+j]
      n = motor_num(mnemonic)
      if (mnemonic && n>=0 && A[n] != OLD_A[n]) 
      {
        t = dial(n,A[n])
        if (t<0) t=0
        vpdu_setdelay i+1 j+1 t
      }
    }
    for (j=0; j<VPDU_TNUM; j++) # falling/rising edge pair
    {
      mnemonic = VPDU_TMNE[i*VPDU_TNUM+j]
      n = motor_num(mnemonic)
      if (mnemonic && n>=0 && A[n] != OLD_A[n])
      {
        vpdu_getdelay i+1 j+1
        t1=VPDU_DELAY[i*VPDU_CNUM+j]
        vpdu_getdelay i+1 j+1+3 
        t2=VPDU_DELAY[i*VPDU_CNUM+j+3]
        dt=t2-t1
        t = dial(n,A[n])
        t1=t; t2=t+dt
        vpdu_setdelay i+1 j+1 t1
        vpdu_setdelay i+1 j+1+3 t2
      }
    }
    for (j=0; j<VPDU_TNUM; j++) # pulse length
    {
      mnemonic = VPDU_PMNE[i*VPDU_TNUM+j]
      n = motor_num(mnemonic)
      if (mnemonic && n>=0 && A[n] != OLD_A[n])
      {
        vpdu_getdelay i+1 j+1
        t1=VPDU_DELAY[i*VPDU_CNUM+j]
        vpdu_getdelay i+1 j+1+3 
        t2=VPDU_DELAY[i*VPDU_CNUM+j+3]
        dt = dial(n,A[n])
        t2=t1+dt
        vpdu_setdelay i+1 j+1+3 t2
      }
    }
  }
}'

#%IU%
#%MDESC% called implicitly when you use do  a "wa".
# (hook macro for "user_getpangles").
# Reads back the current values from the device server for all those units
# and channels which have motor mnemonics and updates the
# corresponding entry in the motors array A[].

def vpdu_get '
{
  local i j n mnemonic
  global OLD_A[]
  
  for (i=0; i<VPDU_NUM; i++)
  {
    for (j=0; j<VPDU_CNUM; j++)
    {
      mnemonic = VPDU_MNE[i*VPDU_CNUM+j]
      n = motor_num (mnemonic)
      if (mnemonic && n >= 0)
      {
        local dial_value
        vpdu_getdelay i+1 j+1
        dial_value = VPDU_DELAY[i*VPDU_CNUM+j]
        A[n] = user(n,dial_value)
        OLD_A[n] = A[n]
        #print "vpdu_get: \""mnemonic"\": A["n"]="A[n]
      }
    }
    for (j=0; j<VPDU_TNUM; j++)
    {
      mnemonic = VPDU_TMNE[i*VPDU_TNUM+j]
      n = motor_num (mnemonic)
      if (mnemonic && n >= 0)
      {
        local dial_value
        vpdu_getdelay i+1 j+1
        dial_value = VPDU_DELAY[i*VPDU_CNUM+j]
        A[n] = user(n,dial_value)
        OLD_A[n] = A[n]
        #print "vpdu_get: \""mnemonic"\": A["n"]="A[n]
     }
    }
    for (j=0; j<VPDU_TNUM; j++)
    {
      mnemonic = VPDU_PMNE[i*VPDU_TNUM+j]
      n = motor_num (mnemonic)
      if (mnemonic && n >= 0)
      {
        local t1 t2 dial_value
        vpdu_getdelay i+1 j+1;   t1 = VPDU_DELAY[i*VPDU_CNUM+j]
        vpdu_getdelay i+1 j+1+3; t2 = VPDU_DELAY[i*VPDU_CNUM+j+3]
        dial_value = t2-t1
        OLD_A[n] = A[n]
        A[n] = user(n,dial_value)
      }
    }
  }
}'

#%UU% [[dev][pwdth][trig][edge][or][clk][frq][ecl]] [idem_2nd_board] ... 
# OR [dev_board1] [dev_board2] [dev_board3] ... 
#%MDESC% For "setup" file. Kept for compatibility with old version of the 
# VPDU macros %BR%
#%DL% detail of the inputs:
#%DT%dev%DD% is the device server name.
#%DT%pwdth%DD% is the pulse width in micro-seconds.
#%DT%trig%DD% is the trigger type, i.e. either the string "automatic", or 
# "internal", or "external". Any other string sets by default, the internal
# trigger.
#%DT%edge%DD% is "falling", or "rising", default being falling edge.
#%DT%or%DD% is the OR output line, set 1 or 0 depending wether you want it or
# not.
#%DT%clk%DD% is the clock signal, i.e. either the string "ecl", or "ttl", or 
# "internal", or "none". Default is ecl clock.
#%DT%frq%DD% is the clock frequency, Mhz, always set to 32 in case of an
# internal clock.
#%DT%ecl%DD% is a flag 1 or 0, which determines wether the board should
# automatically switch setup to internal clock if no ECL is available.
#%XDL%
#%BR%
# It is also possible to only give the list of device server names for all the
# board installed, and set the other parameters with the %B%vpdu%B% menu, or
# whatever mean.

# %MDESC%
def vpdusetup '{
  local settings num res set i
  
  num = split ("$*", settings)
  if (0==int(num/8)) {
    for (i=0;i<num;i++) VPDU_NAME[i] = settings[i]
  } else {
    for (i=0;i<int(num/8);i++) {
      VPDU_NAME [i] = settings [i*8]
      VPDU_PWDT [i] = settings [i*8+1]+0
      VPDU_TRIG [i] = settings [i*8+2]
      VPDU_EDGE [i] = settings [i*8+3]
      VPDU_OR   [i] = settings [i*8+4]+0
      VPDU_CLK  [i] = settings [i*8+5]
      VPDU_MHZ  [i] = settings [i*8+6]+0
      VPDU_ECL  [i] = settings [i*8+7]+0
 
      vpdu_setpulse i+1 VPDU_PWDT[i]
      
      res = 3
      set = "automatic"==VPDU_TRIG[i] ? 2 : ("external"==VPDU_TRIG[i] ? 0 : 1) 
      if (VPDU_EDGE[i] == "rising") set|=0x10
      else res |= 0x10
      if (VPDU_OR[i]) set |=0x80
      else res |= 0x80
      if (VPDU_ECL[i]) set |= 0x20
      else res |= 0x20 
      res |= 0x4c
      set |= "none"==VPDU_CLK[i] ? 0x40 : ("internal"==VPDU_CLK[i] ? 8 : \
             ("ttl"==VPDU_CLK[i] ? 4 : 0 ))
      if ("internal"==VPDU_CLK[i] || 0==VPDU_MHZ[i]) VPDU_MHZ[i]=32
      
      vpdu_setCoR VPDU_NAME[i] VPDU_SOFF set res
    }
  }
  VPDU_NUM=i
}'


#%IU% (name,value)
#%MDESC% Useful to save values other than in global variables
# which would be lost after a "spec -f".
# The value is stored in an ASCII file under in the
# directory "~blissadm/local/spec/userconf/values". One unique file is created
# for each name given.

def set_value (name,value)' {
  # to minimize hard disk I/O the value is kept is as a global varable
  # as well. 
  global VALUE[]
  if ((name in VALUE) && VALUE[name] == value) return;
  VALUE[name] = value
  local file dir command n i size
  file = BLISSADM"/local/spec/userconf/values/"SPEC"/"name
  # make sure that directory exists
  n = length (file); while (substr (file,n,1) != "/") n--
  dir = substr (file,1,n-1)
  # create directories as needed, make sure there is no file with the same name
  if (file_info(dir,"-d") == 0) 
    unix (command="rm -f \""dir"\"; mkdir -p \""dir"\"")
  # The only way I saw to make SPEC overwrite a file rather than
  # append is to use "fmt_write" rather than "fprintf"
  size = length(value); if (size == 0) size = 1
  local byte array a [size]
  for (i=0; i<length(value); i++) a[i] = asc(substr(value,i+1,1))
  close (file)
  fmt_write (file,"raw",a)
}'

#%IU% (name)
#%MDESC% 
# Returns a saved value from a previous "set_value" call.

def value (name) ' {
  # to minimize hard disk I/O a file as only read if the given name is not
  # contained in the VALUE array
  global VALUE[]
  if (name in VALUE) return VALUE[name]
  local file value n
  file = BLISSADM"/local/spec/userconf/values/"SPEC"/"name
  if (file_info(file) == 0) return ""
  getline (file,"open"); value = getline(file); getline (file,"close")
  n = length(value)
  if (substr(value,n,1) == "\n") value = substr(value,0,n-1)
  VALUE[name] = value
  return value
}'

#%INTERNALS%
# The following is a list of settings not from the VPDU device server,
# which are saved permanently:
#%DL%
#%DT% vpdu/number %DD% count of VPDU borads known to SPEC
#%DT% vpdu<n>/name %DD% device server resource name of board <n>
#%DT% vpdu<n>/MHz %DD% clock frequency if external clock of board <n>
#%DT% vpdu<n>/mnemonic/{1,2,3,4,5,6} %DD% (optional) SPEC software motor name
# for the delay of the short pulse output 1-6 
#%DT% vpdu<n>/mnemonic/{t1,t2,t3} %DD% (optional) SPEC software motor name
# for the delay of the long pulse outputs T1-4, T2-3, T3-6 
#%DT% vpdu<n>/mnemonic/{p1,p2,p3} %DD% (optional) SPEC software motor name
# for the pulse length of the long pulse outputs T1-4, T2-3, T3-6
#%XDL% 

#%MACROS%
#%IMACROS%
#%DEPENDENCIES%
#%PRE%
#  - The file %B%vpdu.mac%B% has to be read in
#  - it needs %B%menu.mac%B%.
#%PRE%
#%AUTHOR%
#  Marie-Claire LAGIER - 95/01/19 %BR%
#  modified by MCL - 97/04/04 %BR%
#  modified by ANDY GOTZ on request of FRIEDRICH SCHOTTE to use
#  value and set_value macros to store the VPDU device number
#  and names in $BLISSADM/local/spec/userconf. - 97/06/22 %BR%
#  modified by Friedrich SCHOTTE - 97/06/24
#%TOC%