"""
#%TITLE% PSEUDO.MAC
#%NAME%
# Utility macros to define pseudomotors
#%DESCRIPTION%
# Pseudo motors are a commonly used feature to implement motors in spec,
# which do not have actual hardware connected. An example could be
# the gap and offset motors for slits.
# In some cases, where you would like to use real hardware without
# writing c-code, you could also use the concept of pseudo motors.
# With the feature of %B%Macro\0Hardware%B% (type help mac_hdw at the spec
# prompt), there is no need to create new pseudo motors/counters. Please use
# the macro hardware feature for new implementations.
#%EXAMPLE%
#%DL%
#%DT% pseudodef piezo1 none piezo_move piezo_getangles none none none none 3 0
#%DD% Define a pseudo motor piezo1. Each time the user tries to move the
# piezo piezo_move is called. If the user wants to know the position
# of the piezo piezo_getangles is called. No action is taken on set
# ^c. The user parameters were 3 0 and could represent the channel
# on the corresponding DAC card.
# %DT% cpseudodef cnttim1 none cnt_move piezo_getangles none none none none 3 0
# %DD%
# Define a pseudo motor piezo1. Each time the user tries to move the
# piezo piezo_move is called. If the user wants to know the position
# of the piezo piezo_getangles is called. No action is taken on set
# ^c. The user parameters were 3 0 and could represent the channel
# on the corresponding DAC card.
#%XDL%
#
#%INTERNALS%
# As updated moves and counting used the c-function wait directly, these
# waits had to be replaced by the new waitcheckcount ,... macros.
# These changes have been made:
#%DL%
#%DT% IN UTIL.MAC %DD%
# config user_waitall user_waitmove user_waitcount
#%XDL%
#%ATTENTION%
# This version of pseudo.mac is to be used with SPEC version 6.00.07 and later!
# Many ESRF specific macros like mv, ct have been eliminated by improvements
# made to the standard macros in standard.mac.
# The effort to keep all ESRF functionality was quite big, but we need to be
# vigilant to eliminate errors. All old code has been kept in this file as
# comments, which should allow tracking errors.
#%END%
"""
# Changes to standard SPEC macros
# -------------------------------
#
global USER_WAITINGCOUNT USER_WAITINGMOVE
def chk_count '(USER_WAITINGCOUNT=(wait(0x22)||user_counters_run()))'
def chk_move '(USER_WAITINGMOVE=(wait(0x21)||user_motors_run()))'
def user_counters_run() '{user_countersrun; return(0)}'
def user_motors_run() '{user_motorsrun; return(0)}'
def chk_beam 'if (chk_beamf()) {break} '
def chk_beamf() '{
_chk_beamc
return 1
}'
def _chk_beamc ''
# ESRF uses traditionally its own names for the user_ functions with these
# cdef both should work
cdef ("user_precount", "user_prepcount; ", "pseudo")
cdef ("user_getangles", "user_getpangles; ", "pseudo")
cdef ("user_premove", "user_checkall; ", "pseudo")
cdef ("user_postmove", "user_moveall; ", "pseudo")
cdef ("user_cleanup", "user_cleanup2; ", "pseudo")
cdef ("cleanup1", "user_cleanup; ", "pseudo") #This definition could be erased !
def user_setpos '{local motor2set position2set;
motor2set = $1 ; position2set = $2 ; user_set }'
# user_wait is defined to poll pseudo motors and pseudo counters
def user_waitall 'user_waitcount; user_waitmove; user_wait2'
global MOTORSPOLLTIME
MOTORSPOLLTIME=.05
def user_waitmove '
while (chk_move) {
user_pollmove
sleep (MOTORSPOLLTIME)
}
'
global COUNTERSPOLLTIME
COUNTERSPOLLTIME=.01
def user_waitcount '
while (chk_count) {
user_pollcounts
sleep (COUNTERSPOLLTIME)
}
'
# In normal move commands, wait until motor stops and call user macro
def move_poll '
waitmove
user_finished
'
cdef("user_finished", "user_finished1; ", "pseudo")
# -------------------- Move multiple motors with std move commands -----------
## the position can an arbitrary expression only for the first motor.
#def mv 'if ($# == 2){_mv $*; move_poll} else _mmv(0x00, "$*")'
#def umv 'if ($# == 2){_mv $*; _update("$1") } else _mmv(0x02, "$*")'
#def mvr 'if ($# == 2){_mvr $*; move_poll } else _mmv(0x01, "$*")'
#def umvr 'if ($# == 2){_mvr $*; _update("$1") } else _mmv(0x03, "$*") '
#def mvd 'if ($# == 2){_mvd $*; move_poll } else _mmv(0x04, "$*")'
#def umvd 'if ($# == 2){_mvd $*; _update("$1")} else _mmv(0x06, "$*")'
#
#def _mmv(mode, args) '{
# local i N aux mot macname posl
# N = split(args, aux)
# if (mode & 0x01) {
# macname = "mvr"; posl="relative-"
# } else if (mode & 0x04) {
# macname = "mvd"; posl="dial-"
# } else {
# macname = "mv"; posl=""
# }
# macname = sprintf("%s%s", (mode & 0x02)?"u":"", macname)
# if (!N || N%2){
# printf("Usage: %s motor %sposition [[motor %sposition] ...]", macname, posl, posl)
# exit
# }
#
# for(i = 0; i < N; i += 2){
# mot[i] = motor_num(aux[i])
# if (mot[i] < 0){
# print "Invalid motor mnemonic: ", aux[i]
# exit
# }
# if (aux[i+1]+0 != aux[i+1]){
# print "Invalid motor position: ", aux[i+1]
# exit
# }
# }
# waitmove; get_angles
# mlist = ""
# for(i = 0; i < N; i += 2) {
# if (mode & 0x01)
# A[mot[i]] += aux[i+1]
# else if (mode & 0x04)
# A[mot[i]] = user(mot[i], aux[i+1])
# else
# A[mot[i]] = aux[i+1]
# mlist = sprintf("%s %s", mlist, aux[i])
# }
# move_em
# if (mode & 0x02){
# _update(mlist)
# } else {
# move_poll
# }
#}'
#
#def _ord_move 'move_em; move_poll; get_angles; calcHKL'
#def _upd_move '{
# local mlist
#
# move_em
# mlist = ""
# if (_stype&1) {
# for (i=0;i<_nm;i++)
# mlist = sprintf("%s %s", mlist, motor_name(_m[i]))
# _update(mlist, 9, -1)
# } else if (_stype&2) {
# _update("H K L", 10, -1)
# }
#}'
#
# Redefine update
# a.) have maximum number of columns on the screen (even on rescale)
# b.) guess a nice output format
# c.) have a common update function to poll user defined counters/motors
#def _upd_count '
# if ($1) for (;;) {
# local clist
#
# printf("\r%3d %s <counting ...>", NPTS,VPRNT)
# count_em $1
# for (i = 0, clist = ""; i < COUNTERS; i++) {
# if (cnt_name(i) != "unused")
# clist = sprintf("%s %s",clist,cnt_mne(i))
# }
# _update(clist, 0, 1)
# chk_beam
# }
#'
#def _update1 '_update("$*", 10)'
#def _update2 '_update("$*", 10)'
#def _update3 '_update("$*", 10)'
#def _update4 '_update("$*", 10)'
#def _update2hkl '_update("$* H K L", 9)'
#def _update3hkl '_update("$* H K L", 9)'
#def _update4hkl '_update("$* H K L", 9)'
#def _update5hkl '_update("$* H K L", 9)'
#def _update6hkl '_update("$* H K L", 9)'
#global NOENTERKEY
#def _update(mlist, width, scanning) '{
# local devarr devname iscounter
# local i j n update cols rows oldrows
# local tnext polltime labeldone
# local plines fmtth absc
#
# n = split(mlist, devarr)
# for (i = 0; i < n; i++) {
# iscounter[i] = ((c = cnt_num(devarr[i])) >= 0)
# if (iscounter[i]) {
# devarr[i] = c
# devname[i] = cnt_name(devarr[i])
# USER_WAITINGCOUNT = 1
# } else {
# c = motor_num(devarr[i])
# devname[i] = (c >= 0) ? motor_name(c) : devarr[i]
# devarr[i] = c
# USER_WAITINGMOVE = 1
# }
# }
#
# if (width <=0) width = 12
# fmtth = exp10(width-2)
#
# while(USER_WAITINGMOVE || USER_WAITINGCOUNT) {
# update = (time() > tnext)
#
# polltime = 0
# if (USER_WAITINGMOVE) {
# if (!chk_move) {
# user_finished
# update = 1
# }
# if (update) {
# get_angles
# calcHKL
# } else {
# user_pollmove
# if (!polltime || MOTORSPOLLTIME < polltime)
# polltime = MOTORSPOLLTIME
# }
# }
# if (USER_WAITINGCOUNT) {
# if (!chk_count) update = 1
# if (update) {
# get_counts
# } else {
# user_pollcounts
# if (!polltime || COUNTERSPOLLTIME < polltime)
# polltime = COUNTERSPOLLTIME
# }
# }
# if (update) {
# tty_cntl("resized?")
# cols = int((COLS-1)/width)
# rows = int((n-1)/cols) + 1
# if (scanning == -1)
# printf("\r%3d ", NPTS)
# else if (plines > 0) {
# tty_move(1000, 1000+plines)
# if (rows < oldrows) {
# printf("\r")
# tty_cntl("cd")
# }
# plines = 0
# }
# oldrows = rows
# plines = 0
# for (i = 0; i < n; i++) {
# if (!labeldone && !(i % cols) && scanning != -1) {
# if (scanning || i != 0) {
# if (i != 0) tty_cntl("ce")
# printf("\n")
# plines++;
# }
# tty_cntl("ce")
# printf("\n")
# for (j = 0; j < cols && i+j < n; j++) {
# printf("%*s", width, devname[i+j])
# }
# tty_cntl("ce")
# printf("\n")
## JK CHANGE : Global variable NOENTERKEY set to 0 to be able to press
## Enter to scroll up in updated moves.
# if (!scanning && !NOENTERKEY)
# labeldone = 1
# else
# plines += 2
# }
# if (devarr[i] >= 0)
# c = iscounter[i]?S[devarr[i]]:A[devarr[i]]
# else if (devname[i] == "H")
# c = H
# else if (devname[i] == "K")
# c = K
# else if (devname[i] == "L")
# c = L
# else
# c = 0
#
# if (tty_cntl("resized?")) break;
# if ((absc = fabs(c)) < 1 && absc >= 0.001)
# printf("% *.*f", width, width-3, c)
# else
# printf("% *.*G", width, width-2-4*(absc<0.001 || absc>fmtth), c)
# }
# tty_cntl("ce")
# printf("\r");
# tnext = time() + UPDATE
# } else
# sleep(polltime)
# }
# if (!scanning)
# print
# else if (plines > 0) {
# tty_move(1000, 1000+plines-1)
# tty_cntl("cd")
# tty_move(1000, 1001)
# }
#}'
# Redefine count macros:
# have user_handlecounts to save MCA spectra
# _cols = 0 to now that we are in ct
#def ct '{
# _cols = 0
# waitmove
# count_em $*
# rdef cleanup \'
# undef cleanup
# onp; show_cnts; offp
# user_handlecounts
# \'
# waitcount
# undef cleanup
# onp; show_cnts; offp
# user_handlecounts
#}'
cdef("user_ct", "user_handlecounts;", "user_handlecounts")
#def uct '{
# local clist
#
# _cols = 0
# waitmove
# count_em $*
# for (i = 0, _c2 = ""; i < COUNTERS; i++) {
# if (cnt_name(i) != "unused")
# _c2 = sprintf("%s %s",_c2,cnt_mne(i))
# }
# rdef cleanup \'
# undef cleanup
# _update(_c2)
# user_handlecounts
# \'
# _update(_c2)
# undef cleanup
# user_handlecounts
#}'
#----------------------------END CHANGES -------------------------------
#%UU% <motormne> <checkmacro> <movemacro> <getanglemacro>
# <cleanupmacro> < config> <setdial> <finished> <userdata1> <userdata2>
#%MDESC%
#
# This macro defines a new pseudo-motor. The motor has to be configured
# and the controller set to NONE. Whenever a move_all is called the
# userdefined macro <checkmacro> will be called before and the
# <movemacro> afterwards.
# These macros will get three parameters. The first parameter will
# be the motor mnemonic, the second the string userdata1 and the third
# the string userdata2.
# The macro is responsible to move the motor to the position
# given in the global variable A[motornumber]. The userdata can be used
# to give some information to the macro (for example the device server
# name).
# In the same way the <getanglemacro> will have to fill the A[]
# array with the actual position of the motor.
# The <cleanup macro> will be called if the user hits ^c or some
# error condition occures (A limit is hit for example). The <config>
# macro will be called after the user called config.
# The <setdial> and <finished> macros are not yet fully implemented
# and will server future additions.
# If you do not want to wait in your macros until your move has
# finished (and then allow updated move commands), you can
# hook code in user_motorsrun that executes `return(1)' in case your
# move is not yet finished. While waiting your getangles macro will
# be called.
def pseudodef '
{
#defines pseudomotor
#pseudodef motorname check_all move_all getangles cleanup user1 user2
global __p0 __p1 __p2 __p3 __p4 __p5 __p6 __p7 __p8 __p9 __p10 __p11
__p1="$1"; __p2="$2"; __p3="$3"; __p4="$4"; __p5="$5"; __p6="$6";
__p7="$7"; __p8="$8"; __p9="$9"; __p10="$10"; __p11="$11"
__p0=$1
_pseudodef
}
'
#%UU% <var-name>
#%MDESC%
# same as pseudodef , the input parameter is just a variable name
def pseudosdef '
{
#defines pseudomotor
#pseudosdef stringvariable
global __p0 __p1 __p2 __p3 __p4 __p5 __p6 __p7 __p8 __p9 __p10 __p11
{
local xx i
split ($1,xx)
__p1=xx[0]; __p2=xx[1]; __p3=xx[2]; __p4=xx[3]; __p5=xx[4]; __p6=xx[5];
__p7=xx[6]; __p8=xx[7]; __p9=xx[8]; __p10=xx[9]; __p11=xx[10]
_pseudodef
}
}
'
def _pseudodef '
{
global __p0 __p1 __p2 __p3 __p4 __p5 __p6 __p7 __p8 __p9 __p10 __p11
local flag
flag = 0
if (substr(__p1,1,1)=="_") {
flag = 0 #If a motor name starts with underscore then this is a hook to
}
else {
flag = 1
}
pseudo_hook "user_checkall" __p2 __p1 flag __p9 __p10
pseudo_hook "user_moveall" __p3 __p1 flag __p9 __p10
pseudo_hook "user_getpangles" __p4 __p1 flag __p9 __p10
pseudo_hook "user_cleanup1" __p5 __p1 flag __p9 __p10
pseudo_hook "user_config" __p6 __p1 flag __p9 __p10
pseudo_hook "user_setdial" __p7 __p1 flag __p9 __p10
pseudo_hook "user_finished1" __p8 __p1 flag __p9 __p10
}
'
def pseudo_hook '
{
local rstring
if (($2 != "") && ($2 != "none") && ($2 != "NONE")) {
rstring = sprintf ("%s %s %s %s; ",$2,$3,$5,$6)
cdef("$1",rstring,$3,$4)
}
}
'
#%UU% [motormne] : A pseudomotor can be deleted with this macro
#%MDESC%
def pseudodel 'cdef("","","$1","delete")'
def pseudosdel 'cdef("","",$1,"delete")'
def pseudoshow 'cdef("","","","?")'
def pseudohack '
pseudomotformula
'
#%UU% <formula> <mot-to-calc> <depend-mot1> <depend-mot2> ...
#%MDESC%
# This macro can be used to define simple pseudo motors very easily.
# A pseudomotor like tlaue := piezo/38 could be implemented
# by %BR% pseudomotformula "A[piezo]/38" laue piezo %BR%
# and pseudomotformula "A[tlaue]*38" piezo laue
def pseudomotformula '
global PSEUDO_OLDPOS
rdef pseudomform_$2 \' { local _p iu
_p[0]="$3";_p[1]="$4";_p[2]="$5";_p[3]="$6";_p[4]="$7";_p[5]="$8";_p[6]="$9"
for (iu=0;iu<($#-2);iu++) if (PSEUDO_OLDPOS[motor_num(_p[iu])] != A[motor_num(_p[iu])]) break
if (iu < ($#-2)) A[$2] = $1
} \'
rdef pseudomsave \'for (iu=0;iu<MOTORS;iu++) {PSEUDO_OLDPOS[iu]=A[iu]};\'
cdef("user_checkall","pseudomform_$2 ;","$2",0x21)
cdef("user_getpangles","pseudomsave ;" ,"$2",0x21)
'
# %DL%
# %XDL%
#%UU% <counter_mne> <precount> <postcount> <getcounts> <cleanup> <config> <userdata1> <userdata2>
#%MDESC%
# This macro defines a new pseudo-counter. The counter has to be
# configured and the controller set to NONE. Whenever a count_em
# macro is used (all the time spec wants to start a counter/timer)
# <precount> is called before starting the timer and <postcount>
# after.
# These macros will get three parameters. The first parameter will
# be the counter number, the second the string userdata1 and the third
# the string userdata2.
# Normally the <precount> or <postcount> macro will start your timer.
# In <getcounts> you have to fill the S[] array with the counts read
# from the counter/timer. S[sec := 0] should be the time in seconds.
# The userdata can be used to give some information to the
# macro (for example the device server name).
# The <cleanup> macro will be called if the user hits ^c or some
# error condition occures (A limit is hit for example). The <config>
# macro will be called after the user called config.
# If you do not want to wait in your macros until counting has
# finished (and in this allow updated count commands), you can
# hook code in user_countersrun that executes `return(1)' in case your
# count is not yet finished. While waiting your <pollcounts> macro will
# be called.
def cpseudodef '
{
#defines pseudocounter
#cpseudodef countername precount postcount getcounts cleanup config user1 user2
global __p0 __p1 __p2 __p3 __p4 __p5 __p6 __p7 __p8 __p9 __p10 __p11
__p1="$1"; __p2="$2"; __p3="$3"; __p4="$4"; __p5="$5"; __p6="$6"; __p7="$7"
__p8="$8"; __p9="$9"; __p10="$10" ; __p11="$11"; __p0=$1
_cpseudodef
}
'
#%UU% <var-name>
#%MDESC%
# same as cpseudodef , the input parameter is just a variable name
def cpseudosdef '
{
#defines pseudocounter
#cpseudosdef stringvariable
global __p0 __p1 __p2 __p3 __p4 __p5 __p6 __p7 __p8 __p9 __p10 __p11
{
local xx i
split ($1,xx)
__p1=xx[0]; __p2=xx[1]; __p3=xx[2]; __p4=xx[3]; __p5=xx[4]; __p6=xx[5];
__p7=xx[6]; __p8=xx[7]; __p9=xx[8]; __p10=xx[9]; __p11=xx[10]
for (i=0,__p0=0;i<COUNTERS;i++) {
if (cnt_mne(i) == __p1) {
__p0=i
}
}
}
_cpseudodef
}
'
def _cpseudodef '
{
global __p0 __p1 __p2 __p3 __p4 __p5 __p6 __p7 __p8 __p9 __p10 __p11
local flag
flag = 0
if (substr(__p1,1,1)=="_") {
flag = 0 #If a counter name starts with underscore then def in any case
}
else {
flag = 2
}
pseudo_hook "user_prepcount" __p2 __p1 flag __p7 __p8
pseudo_hook "user_postcount" __p3 __p1 flag __p7 __p8
pseudo_hook "user_getcounts" __p4 __p1 flag __p7 __p8
pseudo_hook "user_cleanup2" __p5 __p1 flag __p7 __p8
pseudo_hook "user_config" __p6 __p1 flag __p7 __p8
}
'
#%UU% [countermne]
#%MDESC%
# A pseudomotor/counter can be deleted with this macro
def cpseudodel 'pseudodel $* ; '
def cpseudosdel 'pseudosdel $* ; '
def cpseudoshow 'pseudoshow $* ; '
def pseudo_define_empty_stubs '
if (!(whatis("user_wait2")&2)) rdef user_wait2 ""
if (!(whatis("user_cleanup2")&2)) rdef user_cleanup2 ""
if (!(whatis("user_finished1")&2)) rdef user_finished1 ""
if (!(whatis("user_set")&2)) rdef user_set ""
if (!(whatis("user_checkall")&2)) rdef user_checkall ""
if (!(whatis("user_moveall")&2)) rdef user_moveall ""
if (!(whatis("user_getpangles")&2)) rdef user_getpangles ""
if (!(whatis("user_setdial")&2)) rdef user_setdial ""
if (!(whatis("user_finished")&2)) rdef user_finished ""
if (!(whatis("user_prepcount")&2)) rdef user_prepcount ""
if (!(whatis("user_pollcounts")&2)) rdef user_pollcounts ""
if (!(whatis("user_pollmove")&2)) rdef user_pollmove ""
if (!(whatis("user_config") & 2)) rdef user_config ""
if (!(whatis("user_countersrun") & 2)) rdef user_countersrun ""
if (!(whatis("user_motorsrun") & 2)) rdef user_motorsrun ""
if (!(whatis("user_handlecounts") & 2)) rdef user_handlecounts ""
'
pseudo_define_empty_stubs
#%MACROS%
#%IMACROS%
#%ATTENTION%
#%UL%
# %LI% Hitting ^c (so ^c twice) while the cleanup macro is running will undefine
# the cleanup macro (This will not erase your macro, it will just not be
# executed). Next time a motor is moved it is redefined again.
# %LI% If you want SPEC to check for software limits, all the motor
# positions must be calculated before the move. (If you calculate the
# gap only in getangles, SPEC will not check for limits on the gap)
# %XUL%
#%DEPENDENCIES%
# The file pseudo.mac has to be loaded. ! This is done in startup
#%AUTHOR%
# JK 3.94
# taken out all fprintf(PRINTER , Holger, 9.12.2004
#%TOC%
|