#%TITLE% XRP_XGENE.MAC
#
#%NAME%
# Implements macro motors and control macros for COMET XRP X generator
#
#%DESCRIPTION%
#%DL%
# %DT% - Configure the serial line connected to the controler.
#
# %DT% - Configure a macro motor controller:
# %B%"device"%B%field set to string "xrp"
# %B%"addr"%B%field set to the serial line entry
# %B%"num"%B%field set to 4
# %B%"type"%B%field set to "Macro Motors"
#
# %DT% - Configure a macro counter controlle:
# %B%"device"%B%field set to string "xrp"
# %B%"addr"%B%field set to the serial line entry
# %B%"num"%B%field set to 4
# %B%"type"%B%field set to "Macro Counter"
#
# %DT% - Configure motors:
# %B%"Controller"%B%field set to "MAC_MOT"
# %B%"Unit"%B%field set to macro motor controller entry
# %B%"Channel"%B%used to select the XRP parameter to control
# %DL%
# %DT%
# %B%0:%B% Voltage
# %B%1:%B% Current
# %XDL%
#
# %DT% - Configure counters:
# %B%"Controller"%B%field set to "MAC_MOT"
# %B%"Unit"%B%field set to macro motor controller entry
# %B%"Channel"%B%used to select the XRP parameter to read
# %DL%
# %DT%
# %B%0:%B% Voltage (set point)
# %B%1:%B% Current (set point)
# %B%2:%B% Voltage (actual)
# %B%3:%B% Current (actual)
# %XDL%
#
#%XDL%
#%IU%()
#%MDESC%
# Macro motor/counter implementation
#
def xrp_config(num,type,p1,p2,p3) '{
global XRP_RD_CMDS[]
global XRP_WR_CMDS[]
global XRP_DEV
local dev
local silent
#
# p1==controller index p2==number of motors supported
#
if(type == "ctrl") {
# only one controller is the universe, so use a global
XRP_DEV = -1
# check serial line
dev = xrp_ADDR
if(ser_par(dev, "responsive") != 1) {
xrp_err("Wrong serial line configured")
return ".error."
}
XRP_DEV = dev
# check communication with instrument
if(xrp_check(dev, silent=1)) {
# try once to switch controller to remote mode
xrp_remote_on
if(xrp_check(dev)) {
xrp_err("No communication with instrument")
_xrp_motors_disable(1)
XRP_DEV = -1
return ".error."
}
}
XRP_RD_CMDS[0] = "US"
XRP_RD_CMDS[1] = "IS"
XRP_RD_CMDS[2] = "U"
XRP_RD_CMDS[3] = "I"
XRP_WR_CMDS[0] = "U"
XRP_WR_CMDS[1] = "I"
# the macro motor controller has only 2 possible channels
# TODO: found a safer workaround to the double call from
# reconfig to xrp_config("..", "ctrl")
if(!(p2 > 2)) {
xrp_log("motors and counters ready to be used")
}
}
#
# p1==unit p2==0 p3==channel
#
if(type == "cnt") {
}
#
# p1==unit p2==0 p3==channel
#
if(type == "mot") {
}
}'
#%IU%()
#%MDESC%
# Macro motor/counter implementation
#
def xrp_cmd(num,key,p1,p2) '{
global XRP_RD_CMDS[]
global XRP_WR_CMDS[]
local dev
local cha
local mne
local cmd
#
# Handle actions at controller level
#
if(num == "..") {
return
}
#
# Handle actions at motor/counter level
#
dev = xrp_ADDR
if(motor_mne(num) == "?") {
cha = counter_par(num, "channel")
mne = cnt_mne(num)
} else {
cha = motor_par(num, "channel")
mne = motor_mne(num)
}
#
# Returns the current counter value
#
if ((key == "counts") || (key == "position")) {
local ans
local val
cmd = XRP_RD_CMDS[cha]
if(cmd == 0) {
xrp_err(sprintf("invalid channel for counter \"%s\"", mne))
return(0)
}
ans = xrp_query(dev, cmd)
val = xrp_parse(ans)
return(val)
}
#
# Starts a motion (p1==abs pos, p2==rel pos, with pos in mm or deg)
#
# Note: the "start_one" is called for the macro counters also
# but with a null p2 value which is the relative motion
if((key == "start_one") && (p2!=0)) {
local cmd
# the unit of voltage is 0.1Kv
if(cha == 0) { p1 *= 10 }
# the unit of current is 0.01A
if(cha == 1) { p1 *= 100 }
cmd = sprintf("%s%04d",XRP_WR_CMDS[cha], p1)
ans = xrp_query(dev, cmd)
}
#
# Returns the current motor status
#
if (key == "get_status") {
}
}'
#%IU%(sl)
#%MDESC%
# Returns null if ok
#
def xrp_check(dev, silent) '{
# any command fails if the controller is off
# or if controller is on but not on remote mode
return((xrp_query(dev, "U", silent) == ""))
}'
#%UU%
#%MDESC%
# Print out the current status of the controller
#
def xrp_status '{
global XRP_DEV
local dev
local ans
xrp_log("Current controller status")
dev = XRP_DEV
# Example of answer (n=000 means the actual system parameters):
# SERIAL got cnt=117 s=<L\r\n
# n=000 U=095,0kV I=00,60mA T=00031H E= 0000 E00 \r\n
# n=001 U=020,0kV I=10,00mA T=00031H E= 2000 off button \r\n\n>
xrp_wr(dev, "L00")
if((tmp = xrp_rd(dev)) == "") { exit }
if((ans = xrp_rd(dev) ) == "") { exit }
# TODO: parse the answer
xrp_log(ans)
ans = _xrp_status()
xrp_log(ans)
}'
#%IU%
#%MDESC%
# Returns the current status of the controller
#
def _xrp_status() '{
global XRP_DEV
local dev
local ans
local ret
local i
dev = XRP_DEV
# Example of answer
# SERIAL got cnt=57 s=<Z STAND BY\n\r
# Mode400 Tube MXR160/22 System XP160\n\n\r>
xrp_wr(dev, "Z")
# TODO: parse the answer
#ans = xrp_rd(dev)
#ans = ser_get(dev, "\n\n\r")
# TODO: add the answer to the debug
if((ans = ser_get(dev, "\n\r")) == "") { exit }
ln = length(ans)
string array str_a[ln]
str_a = ans
for(i=1; i<ln;i++) {
if(str_a[i] != 0x20) {
break
}
}
ans = (i>=ln)?"?????":substr(ans, i)
return(ans)
}'
#%UU%
#%MDESC%
# Switch the controller to remote mode.
# NOTE: the controller must be set in "mode 400" using front panel keys
# NOTE: the controller front panel will be disabled
#
def xrp_remote_on '{
global XRP_DEV
xrp_log("switching generator to remote mode")
# NOTE: seems that it can be done several time
xrp_wr(XRP_DEV, "\002")
sleep(.1)
xrp_flush(XRP_DEV)
# NOTE: the unique way to check that the controller has
# switched to remote mode is to send a command
# just in case
_xrp_motors_disable(0)
}'
#%UU%
#%MDESC%
# Switch the controller to local mode.
# NOTE: the controller front panel will be re-activated
#
def xrp_remote_off '{
global XRP_DEV
xrp_log("switching generator to local mode")
xrp_wr(XRP_DEV, "\003")
sleep(.1)
xrp_flush(XRP_DEV)
# to avoid motor discrepancy messages on next reconfig
_xrp_motors_disable(1)
}'
#%IU%(disable)
#%MDESC%
# Enable or disable all macro motors
#
def _xrp_motors_disable(todo) '{
local i
for(i=0;i<MOTORS;i++) {
if(motor_par(i, "device_id") != "xrp") {
continue
}
xrp_log(sprintf("%sbling motor \"%s\"", \
(todo?"disa":"ena"),motor_mne(i)))
motor_par(i, "disable", todo)
}
}'
#%UU% seconds
#%MDESC%
# Set the automatic HV switching off time.
# The minium is 2 seconds.
#
def xrp_set_hvoff_time '{
global XRP_DEV
local dodo
local dev
local cmd
local ans
if($# != 1) {
xrp_err("missing time argument, given in seconds")
exit
}
dodo = $1
if((dodo<2) || (dodo>(99*60))) {
xrp_err("invalid time given, must be in seconds")
exit
}
cmd = sprintf("%02d:%02d", (dodo/60), (dodo%60))
xrp_log(sprintf("setting automatic HV off time to \"%s\"(MM:SS)", cmd))
dev = XRP_DEV
cmd = sprintf("T%02d%02d", (dodo/60), (dodo%60))
ans = xrp_query(dev, cmd)
}'
#%UU%
#%MDESC%
# Disable the automatic HV switching off.
#
def xrp_no_hvoff_time '{
global XRP_DEV
xrp_log("disable automatic HV switching off")
dev = XRP_DEV
cmd = "T9999"
ans = xrp_query(dev, cmd)
}'
#%UU%
#%MDESC%
# Select the standard focal spot
#
def xrp_focal_normal '{
global XRP_DEV
xrp_log("selecting the standard focal spot")
xrp_wr(XRP_DEV, "F0")
}'
#%UU%
#%MDESC%
# Select the small focal spot
#
def xrp_focal_small '{
global XRP_DEV
xrp_log("selecting the small focal spot")
xrp_wr(XRP_DEV, "F1")
}'
#%UU% [on | onwait | off]
#%MDESC%
# Control the output of the controller.
#
def xrp_hv '{
global XRP_DEV
local todo
local state[] cmds[]
state[0] = "OFF"
state[1] = "ON"
state[2] = "ON&WAIT"
cmds[0] = "OF"
cmds[1] = "ON"
cmds[2] = "ON"
if($# != 1) {
xrp_err("missing argument, must be ON | ONWAIT | OFF")
exit
}
# a command has been requested
todo = -1
if(("$1" == "ON") || ("$1" == "on")) {
todo = 1
} else if(("$1" == "OFF") || ("$1" == "off")) {
todo = 0
} else if(("$1" == "ONWAIT") || ("$1" == "onwait")) {
todo = 2
}
if(todo == -1) {
xrp_err("invalid argument, must be ON | OFF | AUTO")
exit
}
xrp_log(sprintf("switching controller HV to \"%s\"", \
state[todo]))
xrp_wr(XRP_DEV, cmds[todo])
# check if front panel authorization is missing
if((todo == 1) || (todo == 2)) {
sleep(0.5)
ans = _xrp_status()
if(index(ans, "STAND BY")) {
xrp_err("cannot switch \"ON\" the HV")
xrp_err("Hint: turn the key and press the big black \"1\" button")
exit
}
}
# wait for the HV
if(todo == 2) {
xrp_log("waiting for the HV...")
for(i=0;i<20;i++) {
sleep(0.5)
ans = _xrp_status()
if(index(ans, "HIGH TENSION ON")) {
xrp_log("got it!")
break
}
xrp_log("...")
}
if(i>=20) {
xrp_err("missing HV")
exit
}
}
}'
#%IU%(string)
#%MDESC%
# Parse the given string answer from controller and returns a float value
# or -1 if an error occured
#
def xrp_parse(ans) '{
local ln
local val
local ch
# TODO: ensure that the controller can not send negative values
val = -1
# examples of expected answers "U 015,2" or "US 199,0"
ln = length(ans)
if(ln == 0) { return(-1) }
# replace comma by dot char
string array str_a[ln]
str_a = ans
for(ln--;ln>=0;ln--) {
lc = str_a[ln]
if(lc == 0x2c) {
str_a[ln] = 0x2e
break
}
}
ch = ""
val = -1.0
if(sscanf(str_a,"%s %f", ch, val) != 2) {
xrp_err(sprintf("unable to parse answer \"%s\"", ans))
return(-1)
}
return(val)
}'
#%IU%(sl, cmd)
#%MDESC%
# Execute the given query on controller and returns the answer
# (remove terminators)
#
def xrp_query(dev, cmd, silent) '{
local ans
# called using global variable
if(dev == -1) {
return("")
}
xrp_wr(dev, cmd, silent)
ans = xrp_rd(dev, silent)
return(ans)
}'
#%IU%(sl)
#%MDESC%
# Paranoic
#
def xrp_flush(dev) '{
ser_par(dev, "flush")
}'
#%IU%(sl, cmd)
#%MDESC%
# Send command to controller (add terminator)
#
def xrp_wr(dev, cmd, silent) '{
# called using global variable
if(dev == -1) {
return
}
_xrp_debug(sprintf("-> \"%s\"", cmd))
cmd = cmd "\r"
xrp_flush(dev)
ser_put(dev, cmd)
}'
#%IU%(sl)
#%MDESC%
# Read answer from controller
# (remove terminators)
# TODO: handle multi lines answers (protocol??)
# TODO: handle multi SPEC sessions talking to same controller
#
def xrp_rd(dev, silent) '{
local ans
local ln
# blocking call
ans = ser_get(dev, "\r\n")
# TODO: handle error messages, currently interpreted as
# empty answer because different terminators, example
#SERIAL send a=0 b=3 s=<F2\r>
#SERIAL get a=0 b=0 \r\n
#SERIAL got cnt=8 s=<F2 Input>
#SERIAL got cnt=8 s=< error\n\r>
#SERIAL select timed out
#[XRP]: ERROR: missing answer
if(ans == "") {
if(!silent) { xrp_err("missing answer") }
return("")
}
# remove terminator character and useless blanks
ln = length(ans)
if(ln) {
string array str_a[ln]
str_a = ans
for(ln--;ln>=0;ln--) {
lc = str_a[ln]
if((lc!=0x0a) && (lc!=0x0d) && (lc!=0x20))
break
}
ans = substr(ans, 0, ln+1)
}
_xrp_debug(sprintf("<- \"%s\"", ans))
return(ans)
}'
#%IU% [on|off]
#%MDESC%
# Cosmetics
#
def xrp_debug '{
global XRP_DEBUG
if(("$1" == "on") || ("$1" == "ON")) {
XRP_DEBUG=1
} else if(("$1" == "off") || ("$1" == "OFF")) {
XRP_DEBUG=0
} else {
if(XRP_DEBUG) {
XRP_DEBUG=0
} else {
XRP_DEBUG=1
}
}
printf("XRP debug is %s\n", (XRP_DEBUG?"ON":"OFF"))
}'
#%IU%(msg)
#%MDESC%
# Cosmetics
#
def xrp_log(msg) '{
printf("[XRP]: %s\n", msg)
}'
#%IU%(msg)
#%MDESC%
# Cosmetics
#
def _xrp_debug(msg) '{
global XRP_DEBUG
if(XRP_DEBUG == 0) { return }
printf("\t[XRP]: %s\n", msg)
}'
#%IU%(msg)
#%MDESC%
# Cosmetics
#
def xrp_err(msg) '{
tty_cntl("md")
printf("[XRP]: ERROR: ")
tty_cntl("me")
printf("%s\n", msg)
}'
#%MACROS%
#%IMACROS%
#%AUTHOR% MP BCU (Original 06/2018).
# %BR%$Revision: 1.0 $ / $Date: 2018/06/19 07:02:24 $
#%TOC%
|