#%TITLE% ID_tango.mac
#$Revision: 1.27 $
#%NAME% Macros for a the use of the (in 2012) new Insertion Device tango
# server.
#%DESCRIPTION%
# The macros provide users with an interface between SPEC and the (in 2011) new
# Insertion Device tango server.
#%INTERNALS%
# One server, which handles an entire beam line, is handled by one macro
# hardware controller. Underneath, one will
# find the "MovableNames" (ie.
# U32u_GAP, U32u_TAPER, HU52m_GAPBX, HU52m_GAPBZ, HU52m_PHASE, HU44m_GAPBX,
# HU44m_GAPBZ, HU44m_PHASE) as channel.
#%SETUP%
#%BR%\0%BR%%B%Please\0note,\0that\0there\0is\0a\0python\0utility\0to\0adapt%B%
#%B%the\0config\0file\0to\0the\0use\0of\0a\0beam\0line\0undulator\0tango%B%
#%B%device\0server!%B%
#%BR% %BR% Put the macro %B%ID_makewid%B% into the setup to create the macro
#%B%wid%B%, which will allow you to only see the undulator motors.
#%BR% %BR%When doing the config by hand, declare a %B%motor\0controller%B%:
#%BR% %BR%
#%PRE%
#MOTORS\0\0\0\0DEVICE\0\0\0\0\0\0ADDR\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0<>MODE\0\0NUM\0\0\0\0\0\0\0\0<>TYPE%BR%
#\0\0\0YES\0\0\0\0__ID\0\0\0\0\0\0\0//asc:10000/id/master/id00\0\0\0\0\0\0\0\0\0\010\0\0Macro Motors
#%PRE%
#%BR% %BR%
#Then create %B%macro\0motors%B% in a similar manner:
#%BR% %BR%Unit is index number of the controller, the channel num is the index
# of the movable as returned by the call tango_io("???", "MovableNames", x);%BR%
#%BR% %BR%
#%PRE%
#Number:\0<>Controller\0\0\0\0\0\0\0\0\0\0\0x:\0MAC_MOT
#Unit/[Module/]Channel\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00/1
#Name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U32u_GAP
#mnemonic\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U32u
#Steps per degree/mm\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01000
#Sign of user * dial\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Backlash [steps]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00
#Steady-state rate [Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\05000
#Base rate [Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Acceleration time [msec]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Motor accumulator\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00
#Restrictions <>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NONE
#%BR% %BR%
#Dial = accumulator / steps
#\0\0High limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0300.0000
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0Low limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\011.5000
#User = sign * dial + offset
#\0\0Offset\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0`High' limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0300.0000
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0`Low' limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\011.5000
#%PRE%
#%BR%
# Then hit 'm' twice. Set the following for your motor:
#%PRE%
#Hardware\0read\0mode\0<>\0\0\0\0\0\0\0\0%B%NO\0QUERY\0=NQ%B%
#%PRE%
# If you miss this, you will be asked to confirm motor positions after each
# change on a revolver.
#%END%
#%HISTORY%
#$Log: ID_tango.mac,v $
#Revision 1.27 2022/03/11 12:28:59 guilloud
#fix bug in config velocity: use steps per mm rather than fixed 1000.
#
#Revision 1.26 2021/03/15 16:45:49 guilloud
#change acs for acs.esrf.fr to avoid possible timeout
#
#Revision 1.25 2019/11/08 11:16:23 ohlsson
#another small bug
#
#Revision 1.24 2019/11/07 16:29:08 ohlsson
#*** empty log message ***
#
#Revision 1.23 2019/11/07 16:02:58 ohlsson
#fixed small bug
#
#Revision 1.22 2019/11/07 15:44:54 ohlsson
#*** empty log message ***
#
#Revision 1.21 2019/11/07 15:31:07 ohlsson
#made it ready for EBS
#orion -> asc and id/id/00 to id/master/id00
#
#Revision 1.20 2016/12/02 13:26:27 guilloud
#changed TANGO_ERR tests
#
#Revision 1.19 2013/05/29 07:38:27 witsch
#__ID_config although they had been defined in the new macro set.
#
#Added idbody and a variable for treatment with idbody.
#
#Revision 1.18 2013/03/12 12:11:26 lagier
#ID_switch_undulator command timeout changed from 60 to 120 sec.
#one revolver takes about 80 minutes to switch on ID20.
#
#Revision 1.17 2013/02/13 14:00:03 witsch
#On request of Peter Bösecke, write a new macro in file ID_tango.mac,
#which reads the undulator positions, without them being defined as motor.
#
#This is particularly useful, when using mistart (from machinfo) to save
#the current and other info and which historically also saved the ID positions.
#
#The new macro is called: idshow and it is linked to the machinfo.mac
#macro _miwrite (i.e.. def _miwrite 'mishow').
#
#Also I added idon and idoff, to allow users to just en/disable all ID motors.
#
#Revision 1.16 2013/02/05 13:02:12 witsch
#one bug, one improvement
#
#the bug: line 686, where the second arg of _ID_ustate was the undulator name rather than
#the index number. This would always yield the status of the first index and that, of
#course would be wrong in most cases :-(
#
#the improvement: line 644, where a check for the undulator state could be ALARM, which
#allows to continue, but the macros would block. Now the check is for NOT ON AND NOT
#ALARM.
#
#Revision 1.15 2012/10/31 16:45:23 witsch
#error messages have been completed with TANGO_ERR_STACK["0"]["desc"] on advice of J. Meyer.
#the check on completion of a gap movement is done via checking for state ON rather than
#moving on advice of C.Penel
#Created a macro ID_makewid, to allow the user to create the macro wid with
#the current motor names. To be added to setup.
#
#Revision 1.13 2012/10/19 08:45:54 witsch
#more corrections for the ID_switch_undulator macro and the enable_disable
#function.
#
#Revision 1.12 2012/10/18 12:05:22 witsch
#bug fix in motor_par disable during config.
#
#Revision 1.11 2012/10/16 13:18:46 witsch
#more bug fixing for revolver undulators.
#
#Revision 1.10 2012/10/12 13:39:15 witsch
#changes found during tests with the ID18 revolvers.
#
#Revision 1.9 2012/08/21 09:23:27 witsch
#as demanded by Jens in jira ticket IDCO-49, added :
#tango_io(dev, "source", value) with value=0 (source=DEVICE only) .
#
#some local variable clean up.
#
#Revision 1.8 2012/05/09 13:41:04 witsch
#revised the part where ds and spec velocity are synchronised.
#
#Revision 1.7 2012/05/03 12:04:26 witsch
#moved id_state() from ID_tango.mac to machinfo.mac
#
#Revision 1.6 2012/05/03 07:58:36 witsch
#added function id_state(), which is needed on some beam lines. used to be in
#id.mac.
#
#Revision 1.5 2012/03/26 11:24:26 witsch
#Berruyer added function __ID_ismoving
#
#Revision 1.4 2012/03/05 09:54:00 witsch
#Commented out the acceleration part of the __ID_par() function, as it is
#going to be imprecise, due to the storage of the acceleration time in spec
#as integer milliseconds.
#
#Revision 1.3 2012/03/01 10:01:56 witsch
#more testing done on real undulators on ID21 (helas without any revolvers)
#and also to make sure everything works after a fresh start.
#Documentation has been looked through.
#
#Revision 1.2 2011/12/21 15:40:20 witsch
#new version with thorough testing on test device id/id/00.
#
#Revision 1.1 2011/04/15 14:33:12 witsch
#Initial revision
#
need spec_utils
if (!(whatis("__id_debug") & 2)) rdef __id_debug \'#$*\'
if (SPEC == "holg") {
if (!(whatis("__id_verbose") & 2)) rdef __id_verbose \'eprint "ID >>> "\'
} else {
if (!(whatis("__id_verbose") & 2)) rdef __id_verbose \'#$*\'
}
#%IU%
#%MDESC% toggle debug mode for the present macros.
def ID_debug '{
if ((whatis("__id_debug") >> 16) <= 5) { # just a # sign -> off
rdef __id_debug "eprint \"ID: \", "
eprint "ID debug is now ON"
} else {
rdef __id_debug \'#$*\'
eprint "ID debug is now OFF"
}
}
'
#%IU%
#%MDESC% called after each tango_[get|io] call. Checks for errors.
def __id_tango_catch_error '{
if (TANGO_ERR != "0") {
tty_cntl("md")
#__errstr = what "(\"" addr "\", \"" attr "\")"
__errstr = __ID["macfname"] " - " func " - ERROR: " what \
"(\"" addr "\", \"" attr "\"" add ")\n" \
TANGO_ERR_STACK["0"]["desc"] "\n"
__ID_error(__errstr)
__id_debug TANGO_ERR_STACK
tty_cntl("me")
return ".error."
}
}
'
#%IU%(mnum, type, unit, mod, chan)
#%MDESC%
# The macro motor configuration function
def __ID_config(mnum, type, unit, mod, chan) '{
# chan will be used for counters to designate the tag address. Leave alone.
__id_debug "Configuring ID", mnum, type, unit, mod, chan
# take the old macros out
local oldmacs, aux[], mac, str
local stepsmm
oldmacs = "id_config id_cmd idunsetup id_poll
id_axedef id_getallinfo id_showinfo id_getallpos id_getpos
id_moveone id_prompt id_premove id_moveall"
split(oldmacs, aux)
for (mac in aux) {
if (whatis(aux[mac]) & 2) {
str = "undef " aux[mac]
eval(str)
}
}
# we still need this one, but empty.
rdef idsetup \'#$*\'
unglobal oldmacs, aux, mac, str
local func, attr, what, __errstr, mov
func = "__ID_config()"
addr = __ID_ADDR
what = "tango_get"
if (type == "ctrl") {
# delete all the associative arrays to do a clean job.
unglobal __IdDev[], __ID[], __IdRevolvers[]
unglobal __IdRevo[], __IdUndi[], __IdSrPos[]
global __IdDev[], __ID[], __IdRevolvers[]
global __IdRevo[], __IdUndi[], __IdSrPos[]
local x # can`t unglobal and reconstitute right afterwards.
__ID["savedir"] = SPECD "/../../../local/spec/userconf"
__ID["macfname"] = "ID_tango.mac"
local und, undnum, movnum, loop, Undi, mov, aux[]
attr = "UndulatorRevolverCarriage"
loop = tango_get(addr, attr, aux)
__id_tango_catch_error
for (und = 0; und < loop; und ++) {
__IdRevo[und] = aux[und]
}
attr = "UndulatorSrPositions"
loop = tango_get(addr, attr, aux)
__id_tango_catch_error
for (und = 0; und < loop; und ++) {
__IdSrPos[und] = aux[und]
}
attr = "MovableNames"
movnum = tango_get(addr, attr, aux)
__id_tango_catch_error
for (mov = 0; mov < movnum; mov ++) {
__IdDev[mov] = aux[mov]
}
# Revolvers
attr = "UndulatorNames"
loop = tango_get(addr, attr, aux)
__IdUndi["number"] = loop
__id_tango_catch_error
for (und = 0; und < loop; und ++) {
__IdUndi[und] = aux[und]
__IdUndi[aux[und]] = und
if (__IdRevo[und]) { # isa revolv.
local pos
pos = __IdSrPos[und]
# stuff all unds on same position in here
if (__IdRevolvers[pos]) {
__IdRevolvers[pos] = __IdRevolvers[pos] " " __IdUndi[und]
} else {
__IdRevolvers[pos] = __IdUndi[und]
}
}
}
__ID["unit"] = __ID_ADDR # saving for the spec bug in _par
if (whatis("__IdRevolvers") & 0x05000000) {
__ID["norevolvers"] = 0
} else {
unglobal __IdRevolvers
__ID["norevolvers"] = 1
}
# Add tango_io(dev, "source", value) with value=0 (source=DEVICE only).
# Avoids reading from the device cache when polling the
# device.
attr = "source"
__id_debug "tango_io(\"" addr "\", \"" attr "\", 0)"
tango_io(addr, attr, 0)
__id_tango_catch_error
}
else if (type == "mot") {
__ID[chan] = __IdDev[chan]
__ID["und"][chan] = substr(__IdDev[chan], 1, index(__IdDev[chan], "_")-1)
local str, i, n, bla[], mne
mne = motor_mne(mnum)
__ID[__ID[chan]] = mne
__ID["mot"][chan] = mne
__ID["motors"] = __ID["motors"] " " mne
motor_par(mnum, "backlash", 0) # is handled by server
# spec will not write the velocity, unless it is different from a stored value.
# This will set the velocity in the tango DS to the value set in the
# Spec config!!!!
# Read the velocity from server and use motor_par to set it.
local dsvelo
addr = __ID["unit"]
what = "tango_get"
attr = __IdDev[chan] "_Velocity"
__id_debug "tango_get(\"" addr "\", \"" attr "\")"
TANGO_ERR = "-1"
stepsmm = motor_par(mnum, "step_size")
dsvelo = tango_get(addr, attr) * stepsmm
# do we want to flood the user with error messages?
#__id_tango_catch_error
if (! TANGO_ERR ){
__id_verbose mne, "dsvelo", dsvelo
motor_par(mnum, "velocity", dsvelo) # set spec velocity to ds velocity
}
__id_verbose "Motor", motor_name(mnum), "(mne", mne ") handles", __ID[chan]
delete __IdDev[chan]
local undnum, state
state = __ID_mstate(__ID["unit"], chan)
__id_debug "state of", mne, state
if (state == "DISABLE") {
motor_par(mnum, "disable", 1) # disable
} else {
motor_par(mnum, "disable", 0) # enable
}
}
}
'
#%IU%(mnum, cmd, p1, p2, p3)
#%MDESC%
# The macro motor command function
def __ID_cmd(mnum, cmd, p1, p2, p3) '{
local unit, chan, retval, attr
__id_debug "Command ID", mnum, cmd, p1, p2, p3, "(" __ID["unit"] ")"
local func, attr, what, __errstr
func = "__ID_cmd()"
addr = __ID["unit"]
if (!addr) { # when macro has just been loaded and a reconfig is performed
addr = __ID_ADDR
}
what = "tango_io"
if (mnum == "..") { # can`t remember, why I do this
unit = p1
if (cmd == "preread_all") {
__ID_state(addr)
return
}
}
else {
unit = motor_par(mnum, "unit")
chan = motor_par(mnum, "channel")
if (cmd == "start_one") {
local state
attr = __ID[chan] "_Position"
state = __ID_mstate(__ID["unit"], chan)
__id_debug "*********", state, attr, p1
if (state == "DISABLE") {
local str
str = "Undulator " __ID[__ID[chan]]"/" __ID[chan] " disabled! (start_one)\n"
__ID_error(str)
return ".error."
}
__ID[attr]["started"] = 1
addr = __ID["unit"]
what = "tango_put"
__id_debug "tango_put(\"" addr "\", \"" attr "\", " p1 ")"
tango_put(addr, attr, p1)
__id_tango_catch_error
}
else if (cmd == "position") {
local position, state
state = __ID_mstate(__ID["unit"], chan)
__id_debug "position:", chan, __ID[chan], __ID[__ID[chan]], __ID[__ID[__ID[chan]]], "state is", state
if (state == "DISABLE") {
local str
str = "Undulator " __ID[__ID[chan]]"/" __ID[chan] " disabled (in position) !\n"
__ID_error(str)
return NaN #-1
}
what = "tango_get"
attr = __ID[chan] "_Position"
__id_debug "tango_get(\"" addr "\", \"" attr "\")"
retval = tango_get(addr, attr)
__id_tango_catch_error
return retval
}
else if (cmd == "get_status") {
local state, xtra[], x
attr = __ID[chan] "_Position"
state = __ID_state(addr)
__id_debug "get_status:", chan, __ID[chan], __ID[__ID[chan]], __ID[__ID[__ID[chan]]], "state is", state
if (state == "MOVING") {
return 2
} else
if (state == "ON") { # all good
return 0
} else { # anything else is bad
return 0x20
}
}
else if (cmd == "abort_one") {
attr = "Abort"
__id_debug "tango_io(\"" addr "\", \"" attr "\")"
tango_io(addr, attr)
__id_tango_catch_error
}
}
}
'
#%IU%(und)
#%MDESC%Get state of all undulators%BR%
#%END%
# Info from J. Meyer 12/12/11 on states%BR%
# Von ON = 0 bis UNKNOWN=13%BR%
# enum DevState { ON, OFF, CLOSE, OPEN, INSERT, EXTRACT, MOVING,%BR%
# STANDBY, FAULT, INIT, RUNNING, ALARM, DISABLE,%BR%
# UNKNOWN };
def __ID_state(addr) '{
__id_debug "state from", addr
local state, aux[], stenum[], str
str = "ON OFF CLOSE OPEN INSERT EXTRACT MOVING STANDBY FAULT INIT
RUNNING ALARM DISABLE UNKNOWN"
if (!addr) { # when macro has just been loaded and a reconfig is performed
return -1
}
split(str, stenum)
local func, attr, what, __errstr
func = "__ID_state()"
what = "tango_get"
attr = "State"
__id_debug "tango_get(\"" addr "\", \"" attr "\", aux)"
state = tango_get(addr, attr)
__id_tango_catch_error
return stenum[state]
}
'
#%IU%(mov)
#%MDESC%Get state of one movable%BR%
#%END%
# Von ON = 0 bis UNKNOWN=13%BR%
# enum DevState { ON, OFF, CLOSE, OPEN, INSERT, EXTRACT, MOVING,%BR%
# STANDBY, FAULT, INIT, RUNNING, ALARM, DISABLE,%BR%
# UNKNOWN };
def __ID_mstate(addr, mov) '{
__id_debug "state from", addr, mov
local state, aux[], stenum[], str
str = "ON OFF CLOSE OPEN INSERT EXTRACT MOVING STANDBY FAULT INIT
RUNNING ALARM DISABLE UNKNOWN"
if (!addr) { # when macro has just been loaded and a reconfig is performed
return "UNKNOWN"
}
split(str, stenum)
local func, attr, what, __errstr
func = "__ID_mstate()"
what = "tango_get"
attr = "MovableStates"
# get states of all movables on the beam line (up to 6 if revolvers everywhere)
__id_debug "tango_get(\"" addr "\", \"" attr "\", aux)"
state = tango_get(addr, attr, aux)
__id_tango_catch_error
#__id_debug aux
if (mov == -1) { # be able to grab the entire ass array.
local x, tmp[] # for some reason I can`t return aux
for (x in aux) {# nor can I copy it.
tmp[x] = stenum[aux[x]] # so, profit of this problem and return readable strings
}
return tmp # but I can return a one by one copy
} else {
state = aux[mov]
__id_debug "for movable", und, "return", stenum[state]
return stenum[state]
}
}
'
#%IU%(und)
#%MDESC%Get state of one undulator%BR%
#%END%
# Info from J. Meyer 12/12/11 on states%BR%
# Von ON = 0 bis UNKNOWN=13%BR%
# enum DevState { ON, OFF, CLOSE, OPEN, INSERT, EXTRACT, MOVING,%BR%
# STANDBY, FAULT, INIT, RUNNING, ALARM, DISABLE,%BR%
# UNKNOWN };
def __ID_ustate(addr, und) '{
__id_debug "state from", addr, und
local state, aux[], stenum[], str
str = "ON OFF CLOSE OPEN INSERT EXTRACT MOVING STANDBY FAULT INIT
RUNNING ALARM DISABLE UNKNOWN"
if (!addr) { # when macro has just been loaded and a reconfig is performed
return "UNKNOWN"
}
split(str, stenum)
local func, attr, what, __errstr
func = "__ID_ustate()"
what = "tango_get"
attr = "UndulatorStates"
# get states of all unds on the beam line (up to 6 if revolvers everywhere)
__id_debug "tango_get(\"" addr "\", \"" attr "\", aux)"
state = tango_get(addr, attr, aux)
__id_tango_catch_error
#__id_debug aux
if (und == -1) { # be able to grab the entire ass array.
local x, tmp[] # for some reason I can`t return aux
for (x in aux) {# nor can I copy it.
tmp[x] = stenum[aux[x]] # so, profit of this problem and return readable strings
}
return tmp # but I can return a one by one copy
} else {
state = aux[und]
__id_debug "for undulator", und, "return", stenum[state]
return stenum[state]
}
}
'
#%IU%(und)
#%MDESC%return 1 if ondulator und_mne is moving 0 otherwise%BR%
#%END%
def __ID_ismoving(und_mne) '{
local chan, state
chan = motor_par(und_mne, "channel")
state = __ID_mstate(__ID["unit"], chan)
if (state != "ON") {
return 1
}
return 0
}
'
#%IU%(mnum, key, action, p1, p2)
#%MDESC%
# Called by spec with different keys to handle motor_par(mot, "key", new_value)
# actions.
#%BR%
# %UL%
# %LI%Velocity - The velocity of a movable in mm/s %BR%
# Spec: velocity - steps per second.
# %LI%FirstVelocity - The first velovity of the movable in mm/s%BR%
# Spec: base_rate - steps per second.
# %LI%Acceleration - The movable acceleration in mm/s2%BR%
# Spec: acceler. - time in milliseconds for the motor to accelerate to
# full speed
# %XUL%
def __ID_par(mnum, key, action, p1, p2) '{
# be very carful with the use of motor_par with other than the SPEC provided
# key words, as __ID_ADDR gets lost at the end of this macro function.
local unit, channel, stepsmm
__id_debug "Parameters ID", mnum, key, action, p1, p2
channel = motor_par(mnum, "channel")
stepsmm = motor_par(mnum, "step_size")
local addr, func, attr, what, __errstr, xtra[]
func = "__ID_par()"
addr = __ID["unit"]
# if (key == "acceleration") {
# attr = __ID[channel] "_Acceleration"
# if (action == "set") {
# what = "tango_get"
# tango_get(addr, attr, xtra)
# __id_tango_catch_error
# if (("quality" in xtra) && (xtra["quality"] == 0)) { #quality valid!
# what = "tango_put"
# local base_rate, slew_rate
# base_rate = motor_par(mnum, "base_rate")
# slew_rate = motor_par(mnum, "slew_rate")
# # this is going to be inherently imprecise, as the acceleration time is
# # stored in integer milliseconds and the acceleration time will use fractions
# # of milliseconds a lot.
# # Not sure, we should be dealing with that! So no action taken!!!!!
## tango_put(__ID["unit"], attr, (slew_rate - base_rate) / p1)
## __id_tango_catch_error
# }
# else {
# return ".error."
# }
# }
# } else
if ((key == "velocity") || (key == "slew_rate")) {
attr = __ID[channel] "_Velocity"
if (action == "set") {
what = "tango_get"
tango_get(addr, attr, xtra)
# do we want to flood the user with messages?
#__id_tango_catch_error
if (("quality" in xtra) && (xtra["quality"] == 0)) { #quality valid!
what = "tango_put"
TANGO_ERR = "-1"
tango_put(__ID["unit"], attr, p1 / stepsmm)
if (TANGO_ERR == "API_AttrNotAllowed" ) {
local errstr
errstr = "Setting velocity for " motor_mne(mnum) " is not allowed!\n"
__ID_error(errstr)
return ".error."
} else {
__id_tango_catch_error
}
}
else {
return ".error."
}
}
}
else if (key == "base_rate") {
attr = __ID[channel] "_FirstVelocity"
if (action == "set") {
what = "tango_get"
tango_get(addr, attr, xtra)
__id_tango_catch_error
if (("quality" in xtra) && (xtra["quality"] == 0)) { #quality valid!
what = "tango_put"
tango_put(__ID["unit"], attr, p1 / stepsmm)
__id_tango_catch_error
}
else {
return ".error."
}
}
}
else if (key == "isRevolver") {
# this keyword can only be used, while the motor is enabled !!!!!!!!
if (action == "get") {
return __IdRevo[__IdUndi[__ID["und"][channel]]]
}
}
}
'
#%UU% undulator_name [keep-enabled]
#%MDESC%
# Switch undulator within a revolver setup. Without arguments, the macro will
# display the possible choices.%BR%
# If the second argument is present, all undulator motors will enabled,
# otherwise, only the motors on enabled undulators will be enabled.
#%END%
# this was specified by G. Berruyer in Feb. 2012! but I felt like there should
# be a choice for the user.
def ID_switch_undulator '{
if (!(whatis("__ID") & 0x01000000)) {
eprint "There seem to be no insertion devices in use."
eprint "If you just did a fresh start, you might have to reconfig !"
exit
}
local x
# any revolvers at all, no, if __IdRevolvers is Unset
if (__ID["norevolvers"]) {
eprint "There seem to be no revolvers in your insertion devices!"
exit
}
if (($# < 1) || ($# > 2)) {
eprint "Usage: ID_switch_undulator undulatorname [keep-enabled]"
for (x in __IdRevolvers) {
print "\tIn Position", x, "there are", __IdRevolvers[x]
}
print "Please use one of those names"
exit
}
local und, undnum, kenab, state
und = "$1"; undnum = __IdUndi[und]; kenab = "$2" ? 1: 0
local addr, func, attr, what, __errstr
func = "ID_switch_undulator"
addr = __ID["unit"]
if ( ! __ID["TeStInG"] ) { # we do need some testing!
if ((__ID_state(addr) != "ON") && (__ID_state(addr) != "ALARM")) {
eprint "The revolver is not in a state, where it can be turned!"
exit
}
}
__id_debug "ID_switch_undulator", und, undnum
# check if that exists at all
for (x in __IdUndi) {
if (__IdUndi[x] == und) {
break
}
}
__id_debug x, __IdUndi["number"]
if (x == __IdUndi["number"]) {
eprint "There seems to be no undulator named", und, "available."
for (x in __IdRevolvers) {
print "\tIn Position", x, "there are", __IdRevolvers[x]
}
print "Please use one of those names"
exit
}
state = __ID_ustate(__ID["unit"], undnum)
__id_debug "state of undulator", und, undnum, state
if (state != "DISABLE" ) {
eprint "Undulator", und, "isn`t disabled. Thus you can`t enable it!"
for (x in __IdRevolvers) {
print "\tIn Position", x, "there are", __IdRevolvers[x]
}
print "Please use one of those names"
exit
} else {
cdef("cleanup_once", "print \"Please do not abort this macro\"", "ID_switch_undulator")
attr = "Enable"
what = "tango_io"
add = ", \"" und "\"" #
__id_debug " ----------------- > switching", attr, und
tango_io(addr, attr, und)
__id_tango_catch_error
add = ""
local now, then, state
now = time()
while (1) {
state = __ID_ustate(__ID["unit"], undnum)
__id_tango_catch_error
if ((state != "MOVING") && (state != "DISABLE")) {
break
}
then = time() - now
if (then > 120) { # just in case !
__ID_error("Aborting after 120 seconds")
break
}
printf("ID: Moving carriage %#2d\r", then)
}
__ID_enable_disable_motors(kenab)
sleep(1) # temp solution to avoid getting position read errors :-(
sync # if Hardware read mode is not set to NQ, user will be asked
# multiple times for position discrepancy confirmation!
}
}
'
#%IU%(und)
#%MDESC% enables motors on enabled undulators and disables motors on disabled
# undulators.
def __ID_enable_disable_motors(kenab) '{
# disable the motors associated with the disabled undulator, if wished.
local mnum, aux[], channel, states[]
split(__IdRevolvers[pos], aux)
# get the states at once, to avoid multiple tango_gets
states = __ID_mstate(__ID["unit"], -1)
__id_debug states
for (mnum = 0; mnum < MOTORS; mnum++) {
if (motor_par(mnum, "device_id") != "__ID") {
continue # not an ID motor
}
channel = motor_par(mnum, "channel")
__id_debug "motor", motor_mne(mnum), "channel", channel, "state", states[channel]
motor_par(mnum, "disable", 0)
if (!motor_par(mnum, "isRevolver")) {
__id_debug motor_mne(mnum), " not a revo"
continue # not a revolver
}
if ((states[channel] != "DISABLE") || (kenab)) {
; # print "enable motor", mnum, motor_mne(mnum)
} else {
#print "disable motor", mnum, motor_mne(mnum)
motor_par(mnum, "disable", 1)
}
}
}
'
#%IU%(mesg)
#%MDESC% Writes indentical error message only once in a while.
def __ID_error(errstr) '{
global __IDERR[]
local expiry, now, str, x
str = "ID: " errstr
expiry = 5 # seconds before message is redisplayed
now = time()
if (errstr in __IDERR) {
if ((time() - __IDERR[errstr]) > expiry) {
__IDERR[errstr] = now
cprint(str, 3, 1, 1)
}
} else {
__IDERR[errstr] = now
cprint(str, 3, 1, 1)
}
for (x in __IDERR) {
if ((time() - __IDERR[x]) > 20) {
delete __IDERR[x]
}
}
}
'
#%IU%()
#%MDESC% Returns "YES" for a Insertion Devices TANGO server, otherwise "NO".
def __ID_isatango() '{
if (!(whatis("__ID") & 0x05000004) ) {
__ID_error("No controller defined yet. Please call your BCU contact!")
return ".error."
}
# taco_io allows an error message free call, when ESRF_ERR is preset to -1
# tango_io doesn`t do that :-(
# Update 4 Feb 2012: Gerry is going to allow for a TANGO_ERR = "-1" to do
# the job!
TANGO_ERR="-1"
if (__ID_state(__ID["unit"]) == -1) {
return "NO"
} else {
return "YES"
}
}
'
#%UU%
#%MDESC% Print some information about the Insertion Devices to the screen.
#%BR%If you deem necessary to issue more information from this macro, please
#contact the author.
def ID_show '{
local fmt, und, ustates[]
ustates = __ID_ustate(__ID["unit"], -1)
fmt = "%10s | %10s | %8s | %8s\n"
# header
print "Insertion devices information for", SPECBL
printf(fmt, "Undulator", "Revolver", "Slot", "State")
printf(fmt, "----------", "----------", "--------", "--------")
for (und = 0; und < __IdUndi["number"]; und ++) {
__id_debug und, __IdUndi[und]
printf(fmt, __IdUndi[und], __IdRevo[und] ? "yes" : "no", __IdSrPos[und], \
ustates[und])
}
}
'
#%UU%
#%MDESC% Declares a new macro called "wid" to display ID positions.
def ID_makewid '{
if (__ID["motors"]) {
eval (sprintf("newwh wid %s", __ID["motors"]))
}
}
'
#%UU%
#%MDESC%Disable all ID motors.%BR%
def idoff '{
local mne
for(mne=0; mne < MOTORS; mne++) {
if (motor_par(mne, "device_id") == "__ID") {
motor_par(mne, "disable", 1)
}
}
__ID["ON"] = 0
}
'
#%UU%
#%MDESC%Enable all ID motors.%BR%
def idon '{
local mne
for(mne=0; mne < MOTORS; mne++) {
if (motor_par(mne, "device_id") == "__ID") {
motor_par(mne, "disable", 0)
}
}
if (whatis("blmenu") & 0x2){
blmenuadd("Insertion Device motors","ID info","idbody","_idblmenu_")
}
__ID["ON"] = 1
}
'
#%IU% use with blmenu
#%MDESC%
def idbody() '{
if (__ID["ON"]) {
idoff
} else {
idon
}
return(sprintf("%s",__ID["ON"]?"On":"Off"))
}
'
#%UU%
#%MDESC%Standalone ID position read routine. In case of connection problems
# with the server, the macro will remain silent
def idshow '{
local type, aux[], machinehost, bl, hasid, addr
local MovableNames[], MovableStates[], pos
local func, attr, what, __errstr, i
func = "idshow"
what = "tango_get"
machinehost = "acs.esrf.fr:10000"
bl = toupper(SPECBL)
split(bl, aux, "D") # SPECBL is consistently Dxx or IDyy
type = aux[0] == "I" ? "ID" : "D"
if (type == "ID") {
local standalone
addr = sprintf("//%s/id/master/id%d", machinehost, aux[1])
attr = "MovableNames"
TANGO_ERR = "-1"
movnum = tango_get(addr, attr, MovableNames)
standalone = (whatis("titles") & 0x40000000)
if (TANGO_ERR != "0") {
titles = "No connection to the Insertion device server"
values = addr
} else {
attr = "MovableStates"
TANGO_ERR = "-1"
tango_get(addr, attr, MovableStates)
for (i = 0; i < movnum; i ++) {
#print MovableStates[i], MovableNames[i]
FMT = "%s %9.4f"
if (!MovableStates[i]) { # State is ON
local mslen
if ((mslen = length(MovableNames[i])) > 9) {
FMT = "%s %" mslen ".4f"
}
titles = sprintf("%s %9s", titles, MovableNames[i])
# TANGO_ERR = "-1"
attr = MovableNames[i] "_Position"
pos = tango_get(addr, attr)
values = sprintf(FMT, values, pos)
}
}
}
if (!standalone) { # print, if not called from mi_Fheader
print titles
print values
}
}
}
'
if (whatis("_miwrite") == 2) { # empty macro _miwrite
rdef _miwrite \'idshow\'
}
#%MACROS%
#%IMACROS%
#%AUTHOR% H. Witsch, BLISS - ESRF
#$Revision: 1.27 $, $Date: 2022/03/11 12:28:59 $
#%TOC%
|