esrf

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

#%TITLE% OXPS.MAC
#%NAME% Oxford PS120/PS180 Power Supply controllers macros.
#
#%CATEGORY% Other hardware
#%DESCRIPTION% Those macros provide users with remote setup and control of Oxford PS power supplies; control and monitoring can be done through SPEC pseudo motor/counter feature or not. 
#%OVERVIEW%
#%DL%
#%DT% oxpssetup %DD% Setup menu. always run this first. One controller is set here as the current one, and the others macros generally apply to it.
#%DT% oxpsdev %DD% Changes current controller. 
#%DT% oxpsinit  %DD% Same as setup without parameter input. 
#%DT% oxpsoff, oxpson %DD% Enabling-Disabling regarding pseudo motor/counter feature.
#%DT% oxpsflush %DD% Cleans up the communication line, (or bus).
#%DT% oxpspar  %DD% Prints out parameters of current device. (R firmware command)
#%DT% oxpsstat %DD% Prints out status of current device. (X firmware command)
#%DT% oxpsmaxf %DD% Sets maximum field software limit.
#%DT% oxpsmaxr %DD% Sets maximum sweep rate software limit.
#%DT% oxpssetfield %DD% Changes field. 
#%DT% oxpsfield %DD% Changes field setpoint.
#%DT% oxpspol %DD% Changes field polarity.
#%DT% oxpsheater  %DD% Switches heater on and off.
#%DT% oxpsdo %DD% Sets controller activity.
#%DT% oxpsrate %DD% Changes field sweep rate.
#%XDL%
#%SETUP%
#%UL%%B%Remote communication interfaces supported:%B%%BR%
#%LI%%B%GPIB%B% (IOtech, HPnet, NI, whatever supported by and configured into SPEC (config)). 
#%LI%%B%Serial%B% (Workstation, VME IBAM card, whatever supported by and configured into SPEC (config)). 
#%UL%
#%LI% 9600 Baud
#%LI% 8 Data Bits
#%LI% 2 Stop Bits
#%LI% 1 Start Bit
#%LI% No parity
#%XUL%
#%LI% Oxford %B%ISOBUS%B% 
#%XUL%
#%BR%
#%UL%%B%Macros Setup:%B%%BR%
#%LI% %B%oxpssetup%B%: Either run the menu if you give no argument, or check in MACROS description how to do command line setup.
#%XUL%
#%END%

#%UU% [number_of_devices]  ,   OR  [device_index] ["Serial"|"GPIB"|"ISOBUS"] [address1] [address2] [sleep_time] [motor_mnemonic] [mode]
#%MDESC% 
# Sets both the macros and the controller up.%BR%
# When command line setup, that macro must be called first with the total number of devices as only argument, then it must be called once for each device with the following arguments: 
#%UL%
#%LI% [device_index]: starts from 0.
#%LI% ["Serial"|"GPIB"|"ISOBUS"]: texto please.
#%LI% [address1]: Serial Line number regarding config, or GPIB address.
#%LI% [address2]: Isobus address.
#%LI% [sleep_time]: between command and response.
#%LI% [motor_mnemonic]: for field setpoint control as pseudo motor.
#%LI% [mode]: switch heater fitted (=1), persistant mode (=2).
#%XUL%
# That macro also sets the controller in remote and unlocked control mode, and prints out a message indicating the instrument type and firmware number when all is ok.%BR%
# A list of mnemonics is proposed as "parameters readout". if those mnemonics are simply put as counters in %B%config%B%, the corresponding parameter is automatically read at counting. 
#The list of mnemonics is configurable through edition of the macro %B%_oxps_Read_parameter_list%B%. It is safe to copy that macro in a private file that you load after oxPS.mac. 
#Note that you can eventually configure more mnemonics that the ones listed in the menu, as far as it is formatted that way "psn_d", where n corresponds to Rn command (see manual or %B%_oxps_Read_parameter_text%B%), and d is the device number started from 1 and assigned by %B%oxpssetup%B%. %BR% 
# Pseudo motor/Field Setpoint control is also automatically installed as soon as a motor mnemonic (whatever it is) is specified here and put in %B%config%B%.

def oxpssetup '{
  global PS PSX PS_NUM PS_COM PS_DEV PS_ISO PS_SLEEPT PS_MOT PS_MODE 
  global PS_LIM_F PS_LIM_R PS_ON PS_MOVE

  if (0>=PS_LIM_F[PSX]) PS_LIM_F[PSX]=7
  if (0>=PS_LIM_R[PSX]) PS_LIM_R[PSX]=7
  if (0>=PS_NUM) PS_NUM=1

  PS["SERIAL"]= 0	
  PS["GPIB"]  = 1	
  PS["ISOBUS"]= 2

  setup_tail ("oxps")		

  if ($#) {

    if ($#==1) PS_NUM=$1
    else {
      PSX = $1
      PS_COM [PSX] = "$2"
      PS_DEV [PSX] = $3
      PS_ISO [PSX] = $4
      PS_SLEEPT [PSX] = $5
      PS_MOT [PSX] = "$6"
      PS_MODE [PSX] = $7
# MCD added: to notify that we must read
      PS_MOVE[PSX]=1      
      oxps_init
      oxps_pseudomotordef
      oxps_pseudocounterdef 
    }
  } else {
    menu("OXFORD POWER SUPPLY SETUP","oxpssetup_menu","","oxpsinit")
  }
}'

#%UU%
#%MDESC% This macros helps customising your setup. Edit it and comment out the lines you don\'t want to appear in %B%oxpssetup%B% as parameter readout proposals.
def _oxps_Read_parameter_list '

# list [0] = "output current    "
# list [1] = "ps voltage        " 
# list [2] = "magnet current    "
# list [5] = "current target    " 
# list [6] = "current sweep rate"
 list [7] = "output field      " 
 list [8] = "field target      " 
 list [9] = "field sweep rate  " 
# list [15] = "soft volt. limit  " 
# list [16] = "persistent current" 
# list [17] = "trip current      " 
 list [18] = "persistent field  " 
 list [19] = "trip field        " 
'


#%IU%
#%MDESC% Disables Oxford control. Used by "general setup" feature, what means it is called when you comment out %B%oxpssetup%B% lines in your %B%setup%B% file (%B%setup%B% command from SPEC). Goal is to get rid of ALL oxford power supply software. 

def oxpsunsetup '
  oxpsoff
'

#%UU% 
#%MDESC% Same as %B%oxpssetup%B% without parameters input.  

def oxpsinit '{
  local current
  current = PSX
  for (PSX=0;PSX<PS_NUM;PSX++) {
      oxps_init
      oxps_pseudomotordef
      oxps_pseudocounterdef
  }
  PSX = current
}'

#%IU%
#%MDESC% Sets controller in remote and unlocked mode, then get the firmware message.

def oxps_init '
  cdef ("user_cleanup2","_oxps_cleanup ;","oxps")
  oxps_iof (PSX,"\$C3") # Set Remote and Unlocked	 
#  oxpsflush	 
  oxps_iof (PSX,"V",1)  # Get Firmware version
  printf ("OXFORD PS %d: %s\n",PSX+1,PS_RESP)
'

#%IU% 
#%MDESC% Installs field setpoint control as pseudo-motor. this is automatically done if a motor mnemonic is specified in %B%oxpssetup%B%, and configured.

def oxps_pseudomotordef '{
  global OXPOLF
  OXPOLF=1
  if (!PS_OFF[PSX] && PS_MOT[PSX]) {
    if (motor_num(PS_MOT[PSX])<0) {
      printf("OXFORD PS %d: put motor %s in config\n",PSX+1,PS_MOT[PSX])
    }
    cdef ("user_getpangles",                                                             sprintf("oxps_getfield %s %d OXPOLF;",PS_MOT[PSX],PSX),PS_MOT[PSX],1)

    cdef ("user_checkall",                                                                sprintf("oxps_mvsetpt %s %d OXPOLF;",PS_MOT[PSX],PSX),PS_MOT[PSX],1)
    cdef ("user_finished1",                                                                         sprintf("oxps_wait %s %d;",PS_MOT[PSX],PSX),PS_MOT[PSX],1)

  }
  
  if (PS_MODE[PSX]==2) {
    rdef _oxps_checkmode \'
      heateron = oxps_stat(\$1,"H",1) 
      if (8&heateron) {
        print "Sorry, No heater fitted; check out; nothing done."
        exit
      }
      if (5==(heateron&5)) {
        print "Heater FAULT; nothing done"
        exit 
      }
      if (!(heateron&1)) heateron = 0  
      _oxpspersistoff \$1 silent
    \'
    rdef _oxps_ifpersistant \'_oxpspersiston \$1 silent\'
  } else if (PS_MODE[PSX]==1) {
    rdef _oxps_checkmode \'
      heateron = oxps_stat(\$1,"H",1) 
      if (8&heateron) {
        print "Sorry, No heater fitted; check out; nothing done."
        exit
      }
      if (5==(heateron&5)) {
        print "Heater FAULT; nothing done"
        exit 
      }
      if (!(heateron&1)) heateron = 0  
      if (!heateron)  {
          oxps_heater 1
      }  
    \'
    rdef _oxps_ifpersistant \'#\$*\'
  } else {
    rdef _oxps_checkmode \'#\$*\'
    rdef _oxps_ifpersistant \'#\$*\'
  }
}'

if (!(2&whatis("_oxps_ifpersistant"))) rdef _oxps_ifpersistant '#$*'
if (!(2&whatis("_oxps_checkmode"))) rdef _oxps_checkmode '#$*'

#%IU%
#%MDESC% Installs various parameters readout as pseudo-counters. this is automatically done if the relevant mnemonics are configured as counters in config. see %B%oxpssetup%B%. %BR% Note that you can eventually configure more mnemonics that the ones listed in %B%oxpssetup%B%, as far as it is formatted that way "psn_d", where n corresponds to Rn command (see manual or %B%_oxps_Read_parameter_text%B% macro), and d is the device number, as assigned by %B%oxpssetup%B%, started from 1. 
 
def oxps_pseudocounterdef '{
  local pseudoc command
  if (!PS_OFF[PSX]) {
    for (i=0;i<=19;i++) {
      if (i==3) i=5
      if (i==10) i=15
      pseudoc = sprintf("ps%d_%d",i,PSX+1)
      command = sprintf("oxps_getpar %s;",pseudoc)
      cdef ("user_getcounts",command,pseudoc,2)
    }
  }
}'

#%IU%
#%MDESC% Setup menu display.

def oxpssetup_menu '{
 local n numarr[]

 n= "\n"

 if (0==PS_COM[PSX]) PS_COM[PSX]="SERIAL"
 for (i=0;i<PS_NUM;i++) numarr[i]=i+1

 menuoptgroupselect1(0,", ","device: ",numarr,PSX,PS_NUM,"PSX",2);p

 menuoptbutton (0,", or ","+ add one","+")
 menuaction("PSX=PS_NUM++")

 menuoptbutton (0,n,"remove one.","r")
 menuaction ("_oxpsdeldev")

 menusep
 
 print "COMMUNICATION:"
 menuoptval(-35,0," interface",PS_COM[PSX],"i")
 menuvarlist("PS_COM[PSX]","SERIAL  GPIB  ISOBUS")

 if (PS[PS_COM[PSX]]==1) menuoptval(-35,n," address",PS_DEV[PSX],"a")
 else if (PS[PS_COM[PSX]]==2) {
   menuoptval(-35,n," address",PS_ISO[PSX],"a")
   menuvargetv("PS_ISO[PSX]")
   menuoptval(-35,0," line number (config)",PS_DEV[PSX],"l")
 }
 else {
   print n
   menuoptval(-35,0," line number (config)",PS_DEV[PSX],"l")
 }
 menuvargetv("PS_DEV[PSX]")

 menuoptval(-35,n," sleep time",PS_SLEEPT[PSX],"s")
 menuvargetv("PS_SLEEPT[PSX]")

 print n "SETPOINT CONTROL:" "                        SOFT. LIMITS:  "

 menuoptval(-39,0," pseudo-motor mnemonic",PS_MOT[PSX],"p")
 menuvargetv("PS_MOT[PSX]") 

 menuprint (0,"\n","   Field:",PS_LIM_F[PSX])

 menuoptval(-39,0," switch heater fitted",PS_MODE[PSX]?"YES":"NO","w")
 menuvartogg("PS_MODE[PSX]")

 menuprint (0,"\n", "   Sweep rate:",PS_LIM_R[PSX])

 if (PS_MODE[PSX]) { 
   menuoptval(-40,n," persistant mode",PS_MODE[PSX]==2?"YES":"NO","t")
   menuvartogg("PS_MODE[PSX]",2,1)
 }

 print n "PARAMETERS READOUT:  (pseudo-counters mnemonics, <X> if in config)"

 _oxps_Read_parameter_list

 local nyes

 for (i=0;i<=19;i++) { 
   if (list[i]) {
     pseudoc = sprintf("ps%d_%d",i,PSX+1)
     menuprint(30,list[i],pseudoc,cnt_num(pseudoc)>0?"X":" ")
     if (nyes) print 
     nyes=!nyes
   }
 }
 if (nyes) print 
}'
 
#%UU% [field_value]
#%MDESC% Sets absolute field upper limit. (software limit).

def oxpsmaxf '
  if (PS_NUM>1) printf ("OXFORD PS %d: ",PSX+1)

  if ($#) PS_LIM_F[PSX] = $1
  else PS_LIM_F[PSX] = getval("Absolute Field Limit",PS_LIM_F[PSX])
'

#%UU% [rate]
#%MDESC% Sets absolute sweep rate upper limit. (software limit).

def oxpsmaxr '
  if (PS_NUM>1) printf ("OXFORD PS %d: ",PSX+1)

  if ($#) PS_LIM_R[PSX] = $1
  else PS_LIM_R[PSX] =getval("Absolute Sweep Rate Limit (T/sec)",PS_LIM_R[PSX])
'

#%IU%
#%MDESC% Deletes one controller from menu.

def _oxpsdeldev '{
  local dev undelarr
  if (dev = getval("Device to be deleted (0:cancel)",PSX+1)) {
    PS_NUM--
    PSX=PS_NUM
    if (PS_NUM>=dev) {
        undelarr[0] = PS_COM   [dev-1]; undelarr[1] = PS_DEV   [dev-1]
        undelarr[2] = PS_ISO   [dev-1];	undelarr[3] = PS_SLEEPT[dev-1]
	undelarr[4] = PS_MOT   [dev-1];	undelarr[5] = PS_OFF   [dev-1];
	undelarr[6] = PS_MODE   [dev-1];
        PS_COM [dev-1] = PS_COM [PS_NUM]; PS_DEV   [dev-1] = PS_DEV   [PS_NUM]
        PS_ISO [dev-1] = PS_ISO [PS_NUM]; PS_SLEEPT[dev-1] = PS_SLEEPT[PS_NUM]
        PS_OFF [dev-1] = PS_OFF [PS_NUM]; PS_MOT   [dev-1] = PS_MOT   [PS_NUM]
        PS_MODE [dev-1] = PS_MODE [PS_NUM]; 
        PS_COM [PS_NUM] = undelarr[0]; PS_DEV   [PS_NUM] = undelarr[1]
        PS_ISO [PS_NUM] = undelarr[2]; PS_SLEEPT[PS_NUM] = undelarr[3]
	PS_OFF [PS_NUM] = undelarr[4]; PS_MOT   [PS_NUM] = undelarr[5]
	PS_MODE [PS_NUM] = undelarr[6]; 
    }
  }
}'

#%IU% (device_index , command , flag)
#%MDESC% Input/Output function. %BR%If %B%flag%B% is 1, the response is read back, but not checked. Responses are read up to a "\r". device_index from 0.

def oxps_iof (devindex,command,flag) '{

  global PS_RESP
  local checkyes checkres checkcom response
  local address isoadd

  address = PS_DEV[devindex]
  isoadd = PS_ISO[devindex]

  checkyes = !flag 
  response = !index(command,"$")

  oxps_put (address, isoadd, command)
  sleep (PS_SLEEPT[devindex])

  if (response) {

    PS_RESP = oxps_get (address,"\r")

    if (checkyes) {
      checkres = substr(PS_RESP,1,1)
      checkcom = substr(command,1,1)
      if (checkres=="?") { 
        oxps_put (address, isoadd, command)
        sleep (PS_SLEEPT[devindex])
        PS_RESP = oxps_get (address,"\r")
        checkres = substr(PS_RESP,1,1)
        if (checkres=="?") { 
          printf("OXFORD PS %d: Command not acknowledged : \n", devindex+1)
          printf("        Sent : <%s>\n",command)
          printf("        Got  : <%s>\n",PS_RESP) 
        }
      } 
      if (checkres != "?" && checkres != checkcom) {
        oxpsflush devindex+1
        oxps_put (address, isoadd, command);
        PS_RESP = oxps_get (address)      
        if (substr(PS_RESP,1,1) != checkcom) \
          printf("OXFORD PS %d: Error while reading response to %s\n",\
                                                            devindex+1,command)
      }
    }
  }  
  return(PS_RESP)
}'

#%IU% (interface_address , isobus_address , command) 
#%MDESC% Sends command to the controller.

def oxps_put (add,iso,cmd) '{
  if (PS[PS_COM[PSX]]==1)      gpib_put(add,sprintf("%s\r",cmd))
  else if (PS[PS_COM[PSX]]==2) ser_put (add,sprintf("@%d%s\r",iso,cmd))
  else {
#    ser_put (add,sprintf("%s\r",cmd)) 
    for (i=1 ; i<=length(cmd) ; i++) {
      chr = substr(cmd,i,1)
      ser_put(add,chr); #sleep(0)
    } 
    ser_put(add,"\r"); #sleep(0)
  }
}'

#%IU% (interface_address , ending_character)
#%MDESC% Reads back response from the controller, up to the specified character (default is "\r").

def oxps_get(add,chr) '{
  if (!chr) chr="\r";
  if (PS[PS_COM[PSX]]==1) return gpib_get(add,chr) 
  else                    return ser_get(add,chr)
}'

#%UU% 
#%MDESC% Get rid of all unwanted data remaining on the line or on the bus.

def oxpsflush '{ 
  local dev
  dev = $#?$1-1:PSX
  if (0==PS[PS_COM[dev]]) PS_RESP=ser_get(PS_DEV[dev],"") 
  else {
    while (gpib_poll(PS_DEV[dev]) & 18) PS_RESP = oxps_get(PS_DEV[dev]) ;
  }
}'

#%UU% 
#%MDESC%
#Prints out various controller parameters. 
#Parameters selection and scaling is configurable through edition 
#of %B%_oxps_Read_parameter_scale%B% and %B%_oxps_Read_parameter_text%B% macros. 
#It is safe to copy those macros in a private file that you load after oxPS.mac.

def oxpspar '{
  local text scale i dev

  dev = $#?$1-1:PSX
  PSX = dev

  _oxps_Read_parameter_scale
  _oxps_Read_parameter_text

  print;printf("OXFORD PS %d:\n\n",dev+1)

  for (i=0;i<=19;i++) {
    if (!text[i]) continue
    if (!scale[i]) scale[i]=1
    oxps_iof (dev, sprintf("R%d",i))
    sscanf(substr(PS_RESP,2),"%g",val)
    printf("%s : %g\n",text[i],scale[i]*val)
  }
}'


#%UU%
#%MDESC% Edit that macro to set your own scaling on parameters readout. (oxpspar output, pseudo counters read out, pseudo motor, as well as any reference to those parameters (e.g. oxpsfield or oxpsrate...) is affected by that scaling). scale not specified is converted into 1. The number into brackets (array index) corresponds to the one of the R command. see the manual, or %B%_oxps_Read_parameter_text%B% macro.

def _oxps_Read_parameter_scale '
# for PS180 on id12b
# scale[0]=1/100	
# scale[2]=1/100 	
# scale[5]=1/100 	
# scale[6]=1/100 	
# scale[7]=1/1000 	
# scale[8]=1/1000 	
# scale[9]=1/1000 	
'

#%UU%
#%MDESC% This macros helps customising your %B%oxpspar%B% output. Edit it and comment out the lines of parameters you don\'t want to read. The number enclosed into brackets corresponds to the one of the R command.  

def  _oxps_Read_parameter_text '
# text[0]="Demand current to PSU (A)"	#R0
# text[1]="Measured PS voltage (V)  "	#R1
# text[2]="Measured current (A)     "	#R2
# text[5]="Set point current (A)    "	#R5
# text[6]="Sweep rate (A/min)       "	#R6
 text[7]="Demand field (T)         " 	#R7
 text[8]="Set point field (T)      "	#R8
 text[9]="Sweep rate (T/min)       "	#R9
 text[15]="Software voltage limit   "	#R15
# text[16]="Persistant magnet current"	#R16
# text[17]="Trip current             "	#R17
 text[18]="Persistant magnet field  "	#R18
 text[19]="Trip field               "	#R19
'	

#%UU% 
#%MDESC%Prints out controller status.%BR%

def oxpsstat '{
  local dev 
  local zz st lm act remlock mod1 mod2 pol1 pol2 chk hea

  dev = $#?$1-1:PSX
  PSX = dev

  print;printf("OXFORD PS %d:\n\n",dev+1)

  oxps_iof (dev,"X")

  if (15==(chk=sscanf(PS_RESP,"%1s%1d%1d%1s%1d%1s%1d%1s%1d%1s%1d%1d%1s%1d%1d",                zz,st,lm,zz,act,zz,remlock,zz,hea,zz,mod1,mod2,zz,pol1,pol2))) {
 
    printf("System status      : ")
    if ((!lm) && (!st)) printf ("Normal\n")
    else { 
      if (st & 1) printf("Quenched. ")
      if (st & 2) printf("Over Heated. ")
      if (st & 4) printf("Warming up. ")
      if (st & 8) printf("FAULT. ")

      if (lm & 1) printf("On positive voltage limit. ")
      if (lm & 2) printf("On negative voltage limit. ")
      if (lm & 4) printf("Outside negative current limit. ") 
      if (lm & 8) printf("Outside positive current limit. ")
      printf("\n")
    }

    printf("Activity status    : ")
    if (!act) printf("Hold\n")
    else {
      if (act & 1) printf("To set point\n")
      if (act & 2) printf("To zero\n")
      if (act & 4) printf("Clamped\n")
    }

    printf("Local/Remote status: ")
    if (remlock & 1) printf("Remote ") 
    else printf("Local &")
    if (remlock & 2) printf(" Unlocked")
    else printf(" Locked")
    if (remlock & 4) printf(", Auto run down.")
    printf("\n")

    printf("Switch Heater      : ")
    if (hea & 8) print ("not fitted")        
    else if (hea & 4) printf("FAULT ") 
    else if (hea & 1) printf("on")
    else {
      printf("off")
      if (hea & 2) printf(" at Field")       
      else printf(" at Zero")
    } 
    printf("\n")

    printf("Mode               : ")
          
#    if (mod1 & 1) printf(" Tesla ") 
#    else printf(" Amps ")
#    if (mod1 & 2) printf("Sweep ")
#    else printf("Immediate ")
#    if (mod1 & 4) printf(", Slow. ")
#    else printf(", Fast. ")

    if (!mod2) printf("At rest\n")
    else {
      if (mod2 & 1) printf("Sweeping")
      if (3==(mod2 & 3)) printf(", ")
      if (mod2 & 2) printf("Rate limiting") 
      printf("\n")
    }

    printf("Polarity           : \n")
    printf("         Desired   : "); print (pol1&4)?"Negative":"Positive"
    printf("         Magnet    : "); print (pol1&2)?"Negative":"Positive"
    printf("         Commanded : "); print (pol1&1)?"Negative":"Positive"

# PS180 only
#    if (!pol2) printf(", Output clamped\n")
#    else {
#      if (pol2 & 1) printf(", Present polarity: Forward\n")
#      if (pol2 & 2) printf(", Present polarity: Reverse\n")
#      if (pol2 & 4) printf(", Output clamped requested\n")
#    }
  } else print "              Error while reading controller status"
}'

#%UU% [device_num]
#%MDESC% Changes current device index. device_num from 1.

def oxpsdev '{
  if (($1>PS_NUM) || ($1<=0)) {
    printf("usage: $0 device_number (up to %d)\n",PS_NUM)
    printf("-> Current is ");menuprb(PSX+1);p
  } else {
    PSX = $1-1
    printf("-> Current is ");menuprb(PSX+1);p
  }
}'

#%UU% [field_value] 
#%MDESC% Sets field target absolute value. 

def oxpsfield '{
  local field actual curpol

  if (PS_NUM>1) {
    p;printf("OXFORD PS %d:     (change with \"oxpsdev\")\n\n",PSX+1)
  }

# add that because may not exist
  scale[8]=1
  _oxps_Read_parameter_scale; #if (!scale[8]) scale[8]=1

  curpol = (4&oxps_stat(PSX,"P",1))?-1:1	# Desired

  if (!$#) {
    sscanf(substr(oxps_iof(PSX,"R8"),2),"%g",actual)
    field=getval("Field setpoint",curpol*actual*scale[8])
  }
  else field = $1  

  oxps_field field 
}'

#%UU% <field_value> 
#%MDESC% Sets field target polarised value. 

def oxps_field '{
  local pol

# add that because may not exist
  scale[8]=1
  _oxps_Read_parameter_scale; #if (!scale[8]) scale[8]=1

  if (fabs($1)>PS_LIM_F[PSX]) {
    print "Sorry, out of limits (-"PS_LIM_F[PSX]":+"PS_LIM_F[PSX]")"
    exit
  }

  pol = $1<0?2:1
  cmd = sprintf("J%g",fabs($1/scale[8]))
# MCD added: to notify that we must read
  PS_MOVE[PSX]=1
  
  oxps_iof(PSX,cmd)
  oxps_iof(PSX,sprintf("P%d",pol))
}'

#%UU% [1|2|3] 
#%MDESC% Sets field polarity. (Forward (1), or reverse (2), or toggles (3))

def oxpspol '{
  local cmd cur txt mask

  if (PS_NUM>1) {
    p;printf("OXFORD PS %d:     (change with \"oxpsdev\")\n\n",PSX+1)
  }

  txt[1]="forward" ; mask[txt[1]]=1; mask[1]=1
  txt[2]="reverse" ; mask[txt[2]]=2; mask[2]=2
  txt[3]="toggles"  ; mask[txt[3]]=4; mask[3]=4
  txt[4]="toggles"

  cur =  (4&oxps_stat(PSX,"P",1))?2:1	# Desired

  if (!$#) cmd = mask[menu_list("forward  reverse  toggle",txt[cur])]
  else if (($1>=1)&&($1<=3)) cmd = mask[$1]  
  else { 
    print "Wrong parameter: $1; nothing done"; 
    exit 
  }
  print "polarity " txt[cmd]
  cmd = "P" cmd
  oxps_iof(PSX,cmd)
}'

#%UU% [|1|2|3|4] 
#%MDESC% Performs an action with the magnet (HOLD (1), go to SET POINT (2), go to ZERO (3), CLAMP (4))

def oxpsdo '{
  local mask cmd act txt

  if (PS_NUM>1) {
    p;printf("OXFORD PS %d:     (change with \"oxpsdev\")\n\n",PSX+1)
  }

  txt[0]="HOLD"		   ; mask[txt[0]]=mask[1]=0
  txt[1]="GO TO SET POINT" ; mask[txt[1]]=mask[2]=1
  txt[2]="GO TO ZERO"	   ; mask[txt[2]]=mask[3]=2
  txt[4]="CLAMP"	   ; mask[txt[4]]=mask[4]=4

  act = oxps_stat(PSX,"A")

  if (!$#) {
    if (4==act)  {
      print "Magnet is CLAMPED, cannot go neither to setpoint nor to zero\n"
      cmd= mask[menu_list("HOLD  CLAMP",txt[act])]
    } else {
      cmd= mask[menu_list("HOLD  GO TO SET POINT  GO TO ZERO  CLAMP",txt[act])]
    }
  } else if (($1>0) && ($1<=4)) {
    if ((4==act) && ($1==2 || $1==3)) { 
      print "Magnet is CLAMPED: $1 ("text[$1]") not allowed; nothing done"; 
      exit 
    }
    else cmd = mask[$1]
  } else { 
    print "Wrong parameter: $1; nothing done"
    exit 
  }
  print "NOW "txt[cmd]
  cmd = "A" cmd
  oxps_iof(PSX,cmd)
}'

#%UU% [1|0]
#%MDESC% Switches heater on (1) and off (0). Waits 10 seconds.

def oxpsheater '{

  local cmd stat inp

  if ("$1"=="on") inp = 1
  else if ("$1"=="off") inp = 0 
  else inp = "$1"

  if (PS_NUM>1) {
    p;printf("OXFORD PS %d:     (change with \"oxpsdev\")\n\n",PSX+1)
  }

  if (8&(stat=oxps_stat (PSX,"H",1))) {
    print "No Heater fitted"
  } else if (stat&4) {
    print "Heater switch is FAULT"
  } else if (inp>2) {
    print "Wrong parameter $1"
  } else {
    if (!$#) {
      if (stat&1) cmd=!yesno("Heater is On; switch it off",1)
      else if (cmd=yesno("Heater is Off; switch it on",1)) {
        cmd+=!yesno("Magnet must be at PSU",0)
      }
    } else {
     cmd = inp
    }
    print "Switch heater ",cmd?"on":"off" 
    oxps_heater cmd
  }
}'

#%IU% <1|0>
#%MDESC% Switches heater on (1) and off (0). Waits 10 seconds.

def oxps_heater '
    cmd = "H" $1
    oxps_iof(PSX,cmd)
    sleep (10)
'

#%UU% [sweep_rate] 
#%MDESC% Sets field sweep rate. 

def oxpsrate '{
  local cmd actual
  
  _oxps_Read_parameter_scale ; if (!scale[9]) scale[9]=1

  if (PS_NUM>1) {
    p;printf("OXFORD PS %d:     (change with \"oxpsdev\")\n\n",PSX+1)
  }
  
  if (!$#) {
    sscanf(substr(oxps_iof(PSX,"R9"),2),"%g",actual)
    cmd = getval("Field sweep rate",actual*scale[9])
    if (cmd>PS_LIM_R[PSX]) {
      print "Rate too high - ignore"
      exit
    }

  } else {
    if ($1>PS_LIM_R[PSX]) {
      print "Rate too high - ignore"
      exit
    }
    cmd = $1
  }

  cmd = sprintf("T%d",cmd/scale[9])
  oxps_iof(PSX,cmd)
}'

#%UU% [Field_value]
#%MDESC% Changes field value. i.e., changes setpoint, switches, if fitted, heater, then go to setpoint and wait till it is reached. 

def oxpssetfield '{
  local field heateron

  if (PS_NUM>1) {
    p;printf("OXFORD PS %d:     (change with \"oxpsdev\")\n\n",PSX+1)
  }

  field = $#?$1:getval("Enter field",0)
  if (fabs(field)>PS_LIM_F[PSX]) {
    print "Sorry, out of limits (-"PS_LIM_F[PSX]":+"PS_LIM_F[PSX]")"
    exit
  }

  heateron = oxps_stat(PSX,"H",1) 

  if (PS_MODE[PSX]) {
    if (8&heateron) {
      print "MODE: No heater fitted"
      PS_MODE[PSX]=0
    } 
    else if (heateron&4) {
      print "Heater FAULT; nothing done"
      exit 
    }
  } else {
    if (!8&heateron && !1&heateron) {
      print "WARNING: Heater fitted and off; check your setup (\"oxpssetup\")"
      PS_MODE[PSX]=1
#      exit
    }
  }

  if (PS_MODE[PSX]) {
    if (!(heateron&1)) heateron = 0  
    p "Heater is "heateron?"ON":"OFF"
 
    if (PS_MODE[PSX]==2) {
      _oxpspersistoff PSX
    } else if (!heateron)  {
      print "Switch it on"
      oxps_heater 1
    }  
  }

  p "Field setpoint at",field
  oxps_field field
  oxpsdo 2;  _oxps_wait_mag_pol PSX field

  if (2==PS_MODE[PSX]) {
    _oxpspersiston PSX
  }
}'

#%IU% <device_index>
#%MDESC% Turns the magnet out of persistant mode. device_index from 0.

def _oxpspersistoff '{
  local persfield
  oxps_iof($1,"R18")
  sscanf(substr(PS_RESP,2),"%g",persfield)
  
  if (!scale[18]) scale[18]=1

  if (!($#>1)) { 
    p "Field setpoint at last put persistant field",persfield*scale[18] 
  }
  if (scale[8] && (scale[18]!=scale[8])) print "Oops!!!!"

  oxps_field persfield
  if (!($#>1)) { print "NOW GO TO SETPOINT"}
  oxps_do 1 $1;  _oxps_wait_mag_pol $1
  oxps_iof ($1,"A0")
  if (!($#>1)) { print "Then HOLD and switch heater on" }
  oxps_heater 1
  if (!($#>1)) { print "Magnet no longer in persistant mode" }
}'

#%IU% <device_index>
#%MDESC% Turns the magnet into persistant mode. device_index from 0.

def _oxpspersiston '{
  if (!($#>1)) { print "NOW HOLD"}
  oxps_iof ($1,"A0");  _oxps_wait_mag_pol $1
  if (!($#>1)) { print "Switch heater off" }
  oxps_heater 0
  if (!($#>1)) { print "Then GO TO ZERO"}
  oxps_do 2 $1;  _oxps_wait_mag_pol $1
  if (!($#>1)) { print "Magnet in persistant mode" }
}'

#%IU% (device_index , key_letter)
#%MDESC% Returns the value marked by the key letter in the response to the Oxford "X" command. device_index from 0.

def oxps_stat (dev,which,pos) '{
  local state fmt

  oxps_iof(dev,"X")

  fmt = sprintf("%%[^%s]%s%%d",which,which)
  if (2!=sscanf(PS_RESP,fmt,zz,state)) return -1

  if (pos) {
    if (pos<=length(state)) state = substr (state,pos,1)
    else return -1
  }
  
  return state
}'

#%IU% <motor_mnemonic> <device_idx> <polarity_care (if 1)>
#%MDESC% Reads in as pseudo motor position the desired field value. (- Hooked to %B%user_getpangles%B% -). device_idx from 0.

def oxps_getfield '{
  global PS_OLD A2
  local val pol 

# to avoid reading when not necessary 
  if ( ( A2[$1] != PS_OLD[$1] ) || (PS_MOVE[PSX] != 0) ) {

    PS_MOVE[PSX] = 0
    if ($3) pol= (4&oxps_stat($2,"P",1))?-1:1	# Desired
    else pol = 1

#  pol = 1
#--

# R7	: Demanded field		P&1
# R8	: Setpoint			P&4
# R18	: Last put persistent field	P&2

# add that because may not exist
  scale[8]=1
    _oxps_Read_parameter_scale; #if (!scale[8]) scale[8]=1

    oxps_iof($2,"R8") 	
    sscanf(substr(PS_RESP,2),"%g",val)

    A[$1] = val*pol*scale[8]
    PS_OLD[$1] = A[$1]
    A2[$1] = A[$1]
  }
}'

#%UU% 
#%MDESC% Will force next "wa" to read position from devices, even if the field value was not "mved" beforehand. All devices set up are concerned. 

def oxpssync '{
  local mnenum idx
  for (idx=0;idx<PS_NUM;idx++) {
    mnenum = motor_num (PS_MOT[idx])
    A2[mnenum]++
  }
}'

#%IU% <motor_mnemonic> <device_idx> <polarity_care (if 1)>
#%MDESC% Sets field setpoint from standard SPEC motors commands. (- Hooked to %B%user_checkall%B% -). device_idx from 0.

def oxps_mvsetpt '{
local field heateron
if (A[$1] != PS_OLD[$1]) {

  field = A[$1]

  if (fabs(field)>PS_LIM_F[PSX]) {
    print "Sorry, out of limits (-"PS_LIM_F[PSX]":+"PS_LIM_F[PSX]")"
    exit
  }

  _oxps_checkmode $2

# add that because may not exist
  scale[8]=1
  _oxps_Read_parameter_scale; #if (!scale[8]) scale[8]=1
  if ($3) oxps_iof($2,sprintf("P%d",field<0?2:1))
  oxps_iof($2,sprintf("J%g",fabs(field/scale[8])))
# MCD added: to notify that we have moved
  PS_MOVE[PSX]=1
    
  oxps_do 1 $2

  A2[$1] = A[$1]
}
}'

#%IU% <0|1|2|4> <device_idx>
#%MDESC% Performs an action with the magnet (HOLD (0), go to SET POINT (1), go to ZERO (2), CLAMP (4)). (command %B%A%B%). device_idx from 0.

def oxps_do '{
  local act
  act = oxps_stat($2,"A")
  if (4==act && ($1==2||$1==1)) { 
      print "Magnet is CLAMPED: Cannot go to "$1==1?"setpoint":"zero" 
      exit 
  }
  oxps_iof($2,"A$1")
}'

#%IU% <mnemonic> <device_idx> 
#%MDESC% Waits till setpoint is reached. (- Hooked to %B%user_finished1%B% -). The setpoint is assumed to be reached when the PowerSupply output is constant and the desired polarity in accordance with the one commanded. device_idx from 0.

def oxps_wait '{
  if (A[$1]!=PS_OLD[$1]) {
    _oxps_wait_mag_pol $2 A[$1]
    _oxps_ifpersistant $2
  }
}'

#%IU% <device_idx> <polarity_sign>
#%MDESC% Waits till setpoint and desired polarity are reached. called by %B%oxps_wait%B%. device_idx from 0. polarity sign is the sign of the second argument. 
def _oxps_wait_mag_pol '{
  local wt zz mod pol rpol

  wt=1
  rpol = $2<0?5:0
  while (wt) {
#    sleep (1) 
    oxps_iof($1,"X")
    sscanf(PS_RESP,"%[^M]M%1d%1d%1s%1d",zz,zz,mod,zz,pol)
    pol &=5						#Desired && Commanded
    wt = !((0==mod)&&(rpol==pol))
  }
}'

#%IU% <counter_mnemonic> 
#%MDESC% Reads in the value returned by PS Rn command as a pseudo counter value. (- Hooked to %B%user_getcounts%B% -).

def oxps_getpar '{
  local val dev cmd scale

  sscanf ("$1","ps%d_%d",cmd,dev)
  dev-=1
  _oxps_Read_parameter_scale
  if (!scale[cmd]) scale[cmd]=1

  cmd = "R" cmd
  oxps_iof(dev,cmd)
  sscanf(substr(PS_RESP,2),"%g",val)

  S[$1] = val*scale[cmd]
}'

#%UU% [device_num]
#%MDESC% Disables Oxford PS control. All devices set up are concerned, unless specified. device_num from 1.

def oxpsoff '
  global PS_OFF
  if ($#) {
    PSX=$1-1
    cdef ("","",PS_MOT[PSX],"delete") 
    for (j=0;j<=19;j++) cdef ("","",sprintf("ps%d_%d",j,$1),"delete")
    p;printf("OXFORD PS %d disabled\n\n",PSX+1)
    PS_OFF[PSX]=1
    
  } else {
    for (i=0;i<PS_NUM;i++) {
      printf("OXFORD PS %d disabled\n",i+1)
      cdef ("","",PS_MOT[i],"delete") 
      for (j=0;j<=19;j++) cdef ("","",sprintf("ps%d_%d",j,i+1),"delete")
      PS_OFF[PSX]=1
    }
  }
  cdef ("","","oxps","delete")

  PS_ON = 0
'

#%UU% [device_num]
#%MDESC% Enables back Oxford PS control. All devices set up are concerned, unless specified. device_num from 1.

def oxpson '{
  local current
  current = PSX
  if ($#) {
    PSX=$1-1
    PS_OFF[PSX]=0
    oxps_pseudomotordef
    oxps_pseudocounterdef
    p;printf("OXFORD PS %d enabled\n\n",PSX+1)
  
  } else {
    for (PSX=0;PSX<PS_NUM;PSX++) {
      PS_OFF[PSX]=0
      oxps_pseudomotordef
      oxps_pseudocounterdef
      printf("OXFORD PS %d enabled\n",PSX+1)
    }
    PSX=current
  }

  PS_ON = 1
}'

#%IU% 
#%MDESC% Cleanup macros. (- Hooked to %B%user_cleanup2%B% -). Sets the magnet activity to HOLD.

def _oxps_cleanup '{
  for (i=0;i<PS_NUM;i++) {
    oxps_do 0 i
    oxpsflush i+1
  }
}'

#%IU% 
#%MDESC% For blmenu (use blmenu.mac).
def oxpsbody(mode) '{

   if (mode == 1) {
      if (PS_ON) {
         oxpsoff
      }
      else {
         oxpson
      }
      
   }
   return(sprintf("%s",PS_ON?"ON":"OFF"))
}'


#%MACROS%
#%IMACROS%
#%DEPENDENCIES%
#%PRE%
#  - The file %B%oxPS.mac has%B% has to be read in; it needs: %B%pseudo.mac%B% and %B%menu.mac%B%.
#%PRE%
#%AUTHOR%
# OXPS.MAC - JBG - July 11 1995
# - changed by MCL - January 1996, and May 96.%BR%
## DATE Thu Feb 19 09:15:59 MET 1998 REV 1.14;
#%TOC%