esrf

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

#%TITLE% MACHINFO.MAC
#
#%NAME%
#  Macros to access storage ring status information.
#
#%CATEGORY% X-ray beam, Accelerator
#
#%DESCRIPTION%
#
#This macro set allows you:
#%UL%
#%LI% Include machine information in the data file.
#%LI% Display machine information on the screen.
#%LI% Use a counter to know the electron current of the
#     Storage Ring.
#%LI% Wait for the beam refill some seconds before the refill (michkon/off).
#%XUL%
#
#The %B%machine\0information%B% consists of:
#%UL%
#%LI% The electron current of the Storage Ring.
#%LI% The status of the automatic mode: activated ('ON'),
# or not activated ('OFF'). And time left in automatic mode.
#%LI% The state of the front end: 'OPEN', 'CLOSED' or 'ERROR'.
#%LI% Readout of frontend interlocks and UHV valve (if present)
#%LI% Time for next refill
#%LI% The last message from the Control Room
#%XUL%
#%BR%
#Once the %B%misetup%B% macro has been executed:
#%UL%
#%LI% The above listed information is included in the headers of each scan.
#%LI% It can be displayed on the screen by executing %B%machinfo%B%.
#%LI% A counter named 'srcur' can be defined, which will display the storage
# ring current. If an error happens, the counter is loaded with -1.
#%LI% A counter named 'sbcur' can be defined, which will display the single
# bunch current. If an error happens, the counter is loaded with -1.
#%XUL%
#%BR%
#%EXAMPLE%
#%B%misetup%B% %BR%
#%BR%
#These lines will be included in your scan header:
#%PRE%
#  #UMI0 Current  AM  SH  U42_Gap  U42_Taper
#  #UMI1  130.00  OFF  FE Open  29.99  0.02
#  #UMI2  Refill in 7457 sec, Message: 17/10/2025-05:34 END OF RUN 25-8.
#%PRE%
#The previous information is an example for an insertion device beamline
#and varies accordingly for a bending magnet beam
#%BR%
#Version: $Revision: 4.38 $
#%END%


##### January 2012 !!!!
# some info about what has been changed from the old machinfo.mac.
# The idea is to move everything concerning the frontend to the new tango server
# and keep the gap and taper servers the old taco way, where the beam lines have
# not moved to new tango servers.
#
# * extract all on BPMs. They are not used.
# * check_id is no longer used. Just load id.mac on ids. nothing on bms.

##### October 2019 - EBS
# Changed the tango host from orion to acs and new syntax domain/family/member
# For Frontend:
# Old was tango://orion:10000/fe/D/1 
# New is tango://acs.esrf.fr:10000/fe/master/bm01
# For Insertion device control:
# Old was tango://orion:10000/id/id/1
# New is tango://acs.esrf.fr:10000/id/master/id01
#   Author: Staffan Ohlsson

need spec_utils
need blmenu
lfile = sprintf("%s/local/spec/macros/%s", BLISSADM, "id.mac")
if (file_info(lfile)) {
    need id.mac
}

if (!(whatis("__mi_debug")  & 2)) rdef __mi_debug \'#$*\'

#%UU%
#%MDESC% toggle debug mode for the present macros.
def MI_debug '{
    if ((whatis("__mi_debug") >> 16) <= 5) { # just a # sign -> off
        rdef __mi_debug "eprint \"MI>>> \" "
        eprint "MachInfo debug is ON"
    } else {
        rdef __mi_debug \'#$*\'
        eprint "MachInfo debug is OFF"
    }
}
'

if (!(whatis("idsetup")  & 2)) { # create if inexistent
    rdef idsetup \'#$*\'
}

# If ID_tango.mac is loaded afterwards, it should be overwritten. If it was
#loaded before, those lines should not redefine it!
if (!(whatis("__ID_isatango")  & 2)) { # create if inexistent
    rdef __ID_isatango() \'{ return "NO" }\'
}


# this should eventually move to FE_config or be called by that
#%UU%
#%MDESC%
# No arguments!%BR%
#  Initializes parameters to access the information from the machine.
def misetup '{
    # get rid of old variables and the old cdefs
    minfounsetup
    unglobal MI_ON MI_PAR MI_ERROR MI_XBPM1 __MI_XBPM2
    unglobal MI_XPOS1 MI_XPOS2 MI_IDPOS
    unglobal MI_CHK MI_CHKT MI_WAITT MI_WAITING
    unglobal MI_LASTREAD MI_LASTSR MI_LASTSB MI_UPDATE_T
    MI_FEDEV = "misetup: don`t use old macros!"

    local oldmacs, aux[], mac, str, nb_param
    nb_param = $#
    oldmacs =   "mi_checkid _mi_state _mibmread _mibpmread \
                _mibmwrite _mibpmwrite _mibpmwrite _mibpmshow \
                mi_getxbpm misetgapcounter mi_getcounts mi_getsbcounts"
    split(oldmacs, aux)
    for (mac in aux) {
        if (whatis(aux[mac]) & 2) {
            str =   "undef " aux[mac]
            eval(str)
        }
    }
    local func, attr, what, __errstr

    # I very much dislike that many variables. rather use an asso array
    global __MI[] __MI_IDPOS[]


    # From ${TANGO_HOME}/cclient/tango-common.c :
    # static const char *anon_subnames_array296[] = {"ON", "OFF", "CLOSE", \
    #       "OPEN", "INSERT", "EXTRACT", "MOVING", "STANDBY", "FAULT", "WARMUP", \
    #       "RUNNING", "ALARM", "DISABLE", "UNKNOWN"};
    # Looks like the order comes from there.

    __MI["states"][2]   =   "CLOSE"     # The Front End is closed
    __MI["states"][3]   =   "OPEN"      # The Front End is open
    __MI["states"][10]  =   "RUNNING"   # The Front End is open with automatic opening mode on
    __MI["states"][8]   =   "FAULT"     # Fault on Front End
    __MI["states"][12]  =   "DISABLE"   # No operation permission for the Front End
    __MI["states"][13]  =   "UNKNOWN"   # Communication error on Front End
    # Issued by attribute "SR_Mode"
    __MI["srmodes"][1]  =   "USM"
    __MI["srmodes"][2]  =   "MDT"
    __MI["srmodes"][3]  =   "Shutdown"
    __MI["srmodes"][4]  =   "Safety Test"
    __MI["srmodes"][5]  =   "ID Test"

    # issued by "Mode" - Mode of the frontend:
    __MI["modes"][0]    = "No mode validated"
    __MI["modes"][1]    = "Injection"
    __MI["modes"][2]    = "Beam delivery"

    __MI["machinehost"] =   "acs.esrf.fr:10000"

    __MI["lastread"]    =   0
    __MI["lastsr"]      =   -1
    __MI["lastsb"]      =   -1
    __MI["updatetime"]  =   5
    __MI["timeout"]     =   15

    if (SPECBL == "") {
      tty_cntl("md")
      local str
      str = "FATAL: machinfo needs BL info in SPECBL variable."
      str = str " Check with your SPEC admin!"
      print str
      tty_cntl("me")
      exit
    }

    local type, aux[]

    if(nb_param == 1){
        __MI["par"]["BL"] = toupper("$1")
    }
    else{
        __MI["par"]["BL"] = toupper(SPECBL)
    }

    printf("using %s as BL name\n", __MI["par"]["BL"])

    split(__MI["par"]["BL"], aux, "D") # SPECBL is consistently Dxx or IDyy
    type    =   aux[0] == "I" ? "ID" : "D"
    # Check if ID or BM
    if (type == "ID") {
        __MI["hasid"]   = 1
        __MI["device"]  = sprintf("//%s/fe/master/id%s", __MI["machinehost"], aux[1])
        # find out, which ds type is used for undulators
        rdef _miread  \'\'
        rdef _miwrite \'\'
        rdef _mishow  \'_miIDshow\'
    } else {
        __mi_debug "this is a bending magnet bl"
        __MI["hasid"]   = 0
        __MI["device"]  = sprintf("//%s/fe/master/bm%s", __MI["machinehost"], aux[1])
        rdef idsetup \'#\$*\'
        rdef _miread  \'\'
        rdef _miwrite \'\'
        rdef _mishow  \'\'
    }

    # check for UHV valve validity
    local attr, addr
    addr    =   __MI["device"]
    attr    =   "UHV_Valve_State"

    TANGO_ERR   =   "-1"
    __MI["UHVValve"]    =   tango_get(addr, attr)
    # in case of absence of that valve, we`ll get an exception
    if (TANGO_ERR == "API_EmptyDeviceAttribute") { # no such attr
        __MI["UHVValve"]    =   -1
    }

    mistart

    __MI["lastread"]    =   0
    __MI["lastsr"]      =   -1
    __MI["lastsb"]      =   -1
    __MI["updatetime"]  =   5
    __MI["waiting"] = 0 # indicating if we have waited at the beams return

    setup_tail("minfo")
    if (whatis("blmenu") & 0x2){
        blmenuadd("Machine Info in Spec file","MachInfo","mibody","_miblmenu_")
    }

    __MI["macfname"] =   "machinfo.mac"
}
'


#%IU%
def minfounsetup '{
    cdef("","","mi","delete")
    cdef("","","srcur","delete")
    cdef("","","sbcur","delete")
    cdef("","","xbpm1x","delete")
    cdef("","","xbpm2x","delete")   # keep the following, to clean out
    cdef("","","idgap1","delete")   # old macros
    cdef("","","idgap2","delete")
    cdef("","","idgap3","delete")
    unglobal __MI
}
'

#%IU%
#%MDESC%
#  Adds machine info at the header of each scan
#
def mistart '{
    __MI["on"] = 1
    cdef("Fheader","mi_Fheader;","mach",0)
}
'


#%UU%
#%MDESC%
#    After this command spec will not longer read the machine info
#    and it will not be included in the scan header anymore.
#    With %B%mistart%B% you can switch the reading on again.
#
def mistop '{
    __MI["on"] = 0
    cdef("","mach","delete")
}
'


#%UU%
#%MDESC%
#   Display machine information on the SPEC screen
def machinfo 'mishow'
def mishow '{
    if (__MI["par"]["BL"] == 0 || __MI["par"]["BL"] == "") {
       print "You must run first \"misetup\" first"
       exit
    }

    miread

    if (!__MI["error"]) {
        local FMT, remaining, s
        FMT =   "%30s      %-30s\n"

        print
        printf(FMT, "Machine information", __MI["par"]["BL"])
        print
        printf(FMT, "Current :",        sprintf("%.2f mA", __MI["par"]["CURR"]))
        s = __MI["par"]["LifeTime"]
        printf(FMT, "Lifetime :",       sprintf("%d:%d", int(s / 3600), int((s % 3600) / 60)))
        printf(FMT, "Sgl Bunch Cur :",  sprintf("%.2f mA", __MI["par"]["SBCURR"]))

        printf(FMT, "AutoMode :", sprintf("%s ", __MI["par"]["FEAUTO"]) )
        printf(FMT, "Shutter :", sprintf("%s", __MI["par"]["SHUTTER"]))
        _mishow
        print
        printf(FMT, "Ilocks :", sprintf("FE(%s)  PSS(%s)   EXP(%s)", \
                __MI["par"]["FEILOCK"], __MI["par"]["PSSILOCK"], \
                __MI["par"]["EXPILOCK"]))
        printf(FMT, "", sprintf("HQPS(%s)", \
                __MI["par"]["HQPSILOCK"]))

        print
        if (__MI["UHVValve"] != -1) {
            printf(FMT, "UHV Valve :", __MI["par"]["UHVVALVE"])
        }
        s = __MI["par"]["REFILL"]
        remaining   =  sprintf("%d:%02d", int(s / 3600), int((s % 3600) / 60))
        printf(FMT, "Count down for refill :", __MI["par"]["REFILL"] ? \
                remaining : "n.a.")
        print
        printf(FMT, "Operation Mode :", sprintf("%s", __MI["par"]["MODE"]))
        printf(FMT, "Mode :",           sprintf("%s", __MI["par"]["SRMODE"]))
        printf(FMT, "FillMode :",       sprintf("%s", __MI["par"]["FILLMODE"]))
        printf(FMT, "Latest message :", sprintf("%s", __MI["par"]["MESS"]))
    } else {
        printf("\nError getting machine information\n")
    }
}
'

def __mi_tango_catch_error '{
    if (TANGO_ERR != "0") {
        tty_cntl("md")
        __errstr = __MI["macfname"] " - " func "- ERROR: " what "(" addr ", " attr ")"
        __MI_error(__errstr)
        __mi_debug  TANGO_ERR
        tty_cntl("me")
        __MI["error"]   =   1
    } else {
        __MI["error"]   =   0
    }

}
'

#%IU%(mesg)
#%MDESC% Attempts to write indentical error message only once in a while
def __MI_error(errstr) '{
    global __MIERR[]
    local expiry, now, str
    str = "MI: " errstr
    expiry  =   5   # seconds before message is redisplayed
    now     = time()
    if (errstr in __MIERR) {
        if ((time() - __MIERR[errstr]) > expiry) {
            __MIERR[errstr] = now
            eprint(str)
        }
    } else {
        __MIERR[errstr] = now
        eprint(str)
    }
    local x
    for (x in __MIERR) {
        if ((time() - __MIERR[x]) > 20) {
            delete __MIERR[x]
        }
    }
}
'



#%UU% <mode?>
#%MDESC%
# Reads information from machine FE tango DS (as of April 2012)
def miread '{
    local addr, func, what, __tab[], x
    func    =   "miread"
    __MI["error"]    =   1
    addr    =   __MI["device"]
    # __MI member           # tango ds attribute to get
    __tab["SHUTTER"]        =   "FE_State"
    __tab["FEAUTO"]         =   "Automatic_Mode"
    __tab["FEILOCK"]        =   "FE_Itlk_State"
    __tab["PSSILOCK"]       =   "PSS_Itlk_State"
    __tab["EXPILOCK"]       =   "EXP_Itlk_State"
    __tab["HQPSILOCK"]      =   "HQPS_Itlk_State"
    __tab["UHVVALVE"]       =   "UHV_Valve_State"
    __tab["SRMODE"]         =   "SR_Mode"
    __tab["CURR"]           =   "SR_Current"
    __tab["SBCURR"]         =   "SR_Single_Bunch_Current"
    __tab["REFILL"]         =   "SR_Refill_Countdown"
    __tab["MESS"]           =   "SR_Operator_Mesg"
    __tab["FILLMODE"]       =   "SR_Filling_Mode"
    __tab["MODE"]           =   "Mode"
    __tab["LifeTime"]       =   "SR_Lifetime"
    what    =   "tango_get"
    for (;__MI["error"];) {
        local srmode, mode
        for (x in __tab) {
            if (x == "UHVVALVE" && ! __MI["hasid"]) {
                continue
            }
            attr = __tab[x]

            __MI["par"][x] = tango_get(addr, attr)
            __mi_debug "__MI[\"par\"][" x "] = ", __MI["par"][x], TANGO_ERR
            __mi_tango_catch_error
        }
        __MI["error"] =   0

        __MI["par"]["FEAUTO"]   = __MI["par"]["FEAUTO"]     ? "ON":"OFF"
        __MI["par"]["FEILOCK"]  = __MI["par"]["FEILOCK"]    ? "ACTIVE":"INACTIVE"
        __MI["par"]["PSSILOCK"] = __MI["par"]["PSSILOCK"]   ? "ACTIVE":"INACTIVE"
        __MI["par"]["EXPILOCK"] = __MI["par"]["EXPILOCK"]   ? "ACTIVE":"INACTIVE"
        __MI["par"]["HQPSILOCK"]= __MI["par"]["HQPSILOCK"]  ? "ACTIVE":"INACTIVE"
        __MI["par"]["UHVVALVE"] = __MI["par"]["UHVVALVE"]   ? "CLOSE":"OPEN"
        srmode                  = __MI["par"]["SRMODE"]
        __MI["par"]["SRMODE"]   = __MI["srmodes"][srmode]
        if (__MI["par"]["SRMODE"] == "") {
            __MI["par"]["SRMODE"]   =   "UNKNOWN" srmode
        }
        mode                    = __MI["par"]["MODE"]
        __MI["par"]["MODE"]     = __MI["modes"][mode]
    }
    if (__MI["hasid"]) {
        _miread
    }
}
'


#%IU%
#%MDESC%
#    Macro that formats information for including it in the scan headers.
def mi_Fheader '{
    miread

    if (!__MI["error"]) {
        local titles values
        _miwrite

        if (___fe_filling_mode() == "1 bunch") {
          titles = titles" SB_CURR"
          values = sprintf("%s %7.3f",values,__MI["par"]["SBCURR"])
        }
        local FMT
        printf ("#UMI0     Current AutoM      Shutter    %s\n", titles)
        printf ("#UMI1     %7.2f   %3s %11s     %s\n", __MI["par"]["CURR"],\
                     __MI["par"]["FEAUTO"], __MI["par"]["SHUTTER"], values)
        printf ("#UMI2 Refill in %d sec, Fill Mode: %s / Message: %s\n",\
        __MI["par"]["REFILL"],__MI["par"]["FILLMODE"], __MI["par"]["MESS"])

    } else {
        printf("#UMI0 Error reading machine information\n")
    }
}
'

# motor positions should be correct in the scan header 
##%IU%
##%MDESC%
##   scan header output for ID beamlines
#def _miIDwrite '{
#    if (__MI["hasid"]) {
#        local x
##        sync # avoid any error messages in the spec file.
#        for ( x = 0; x < MOTORS; x++) {
#            if (motor_par(x, "device_id") != "ID") {
#                continue    # not an ID motor
#            }
#            if (motor_par(x, "disable")) {
#                continue    # motor is disabled
#            }
#            titles = sprintf("%s %9s", titles, motor_mne(x))
#            values = sprintf("%s %9.4f", values, A[x])
#            __mi_debug titles, values
#        }
#    }
#}
#'


#%IU%
#%MDESC%
#    Screen formatting for ID beamlines.
def _miIDshow '{
    if (__MI["hasid"]) {
        getangles
        local FMT, x, any, aux[]
        FMT =   "%30s      %-30s\n"
        print
        for ( x = 0; x < MOTORS; x++) {
            if (motor_par(x, "device_id") != "__ID") {
                continue    # not an ID motor
            }
            if (motor_par(x, "disable")) {
                continue    # motor is disabled
            }
            any += 1
            aux[motor_mne(x)] = A[x]
        }
        if (any) {
            printf(FMT, "Enabled insertion devices :", "Motors Positions")
            for (x in aux) {
                printf(FMT, sprintf("%s :", x), sprintf("%10g", aux[x]))
            }
        }
    }
}
'

#%IU%
#%MDESC%
#    Return positions as a formatted string.
# as demanded by ID1/2
def _miIDpositions() '{
    if (__MI["hasid"]) {
        getangles
        local retstr, x, aux[]
        print
        for ( x = 0; x < MOTORS; x++) {
            if (motor_par(x, "device_id") != "__ID") {
                continue    # not an ID motor
            }
            if (motor_par(x, "disable")) {
                continue    # motor is disabled
            }
            retstr = retstr sprintf("%s=%05.2fmm,", motor_mne(x), A[x])
        }
	return retstr
    }
}
'


#%IU%
#%MDESC%
#  macro to include start/stop on blmenu
def mibody(mode) '{
    if (mode==1) {
      if (__MI["on"]) {
         mistop
      } else {
         mistart
      }
    }

    if (mode==2) {
        machinfo
    }

    return(sprintf("%s", __MI["on"]?"Active":"Not active"))
}
'


#%UU% [<refill-time>] [<waittime>]
#%MDESC%
#  Activates refill feature on chk_beam.
#  <refill-time> is the time before a refill whithin scans will wait for refill
#  if no parameter is given, a default of 15 seconds is set. Minimum value is
#  10 seconds.
#  <waittime> is the time to wait once the beam is back. Allows to wait for the
#  undulators to close, optic to heat up ...
def michkon '{
   if (__MI["par"]["BL"] == 0 || __MI["par"]["BL"] == "") {
      print "michkon: You must run first \"misetup\" first"
      exit
   }

   if ($# < 1) {
      __MI["checktime"] =   15
      __MI["waittime"]  =   15
   } else if ($# == 1) {
      __MI["checktime"] =   $1
      __MI["waittime"]  =   15
   } else if ($# == 2) {
      __MI["checktime"] =   $1
      __MI["waittime"]  =   $2
   }
   __MI["check"]    =   1

   cdef("measure0",  "mi_measure0;","_chkb_")
}
'


#%UU%
#%MDESC
#  Deactivates refill feature on chk_beam
#  previously activated by michkon
def michkoff '{
   __MI["check"] = 0
   cdef("","","_chkb_","delete")
}
'



######################################################################
###########################                ###########################
###########################  FE BEHAVIOUR  ###########################
###########################                ###########################
######################################################################


#%UU%
#%MDESC
#    Enables the automatic mode : FE will automatically re-open after a
# refill.
def mi_set_automatic_mode()'{
    local macro, func, addr, cmd, what, __errstr

    func    =   "mi_set_automatic_mode()"
    attr    =   "Automatic"
    what    =   "tango_io"
    addr    =   __MI["device"]

    tango_io(addr, attr)
    __mi_tango_catch_error

}
'



######################################################################
####################                              ####################
####################  MACHINE PARAMETERS READING  ####################
####################                              ####################
######################################################################


#%IU% ()
#%MDESC%
#    Returns the remaining time to refill.
def mi_refilltime() '{
    local macro, func, attr, what, addr, __errstr, remaint, srmode
    func    =   "mi_refilltime()"
    attr    =   "SR_Mode"
    what    =   "tango_get"
    addr    =   __MI["device"]

    srmode  =   tango_get(addr, attr)
    __mi_tango_catch_error
    if (__MI["srmodes"][srmode] != "USM") { # nothing further to do
        return -1
    }

    attr    =   "SR_Refill_Countdown"
    remaint =   tango_get(addr, attr)
    __mi_tango_catch_error

   return remaint
}
'


#%UU% ()
#%MDESC%
#    Returns the synchrotron ring current in mA.
def mi_SR_Current() '{
    local macro, func, attr, what, addr, __errstr, _attr_value, srmode

    func    =   "mi_SR_Current()"
    attr    =   "SR_Mode"
    what    =   "tango_get"
    addr    =   __MI["device"]

    srmode  =   tango_get(addr, attr)
    __mi_tango_catch_error

    if (__MI["srmodes"][srmode] != "USM") {
        # nothing further to do
        return -1
    }

    attr       = "SR_Current"
    __mi_debug "attr=" attr
    __mi_debug "addr=" addr

    _attr_value = tango_get(addr, attr)
    __mi_tango_catch_error

   return _attr_value
}
'


#%UU% ()
#%MDESC%
#    Returns the synchrotron ring current in mA. (fast but less safe version)
def mi_SR_Current_fast() '{
    return tango_get(__MI["device"], "SR_Current")
}'


#%IU% ()
#%MDESC%
#    Returns the operator message.
def mi_message() '{
    local func, attr, what, addr, msg
    func    =   "mi_message()"
    attr    =   "SR_Operator_Mesg"
    what    =   "tango_get"
    addr    =   __MI["device"]

    msg     =   tango_get(addr, attr)
    __mi_tango_catch_error
    return msg
}
'

#%UU% ()
#%MDESC%
#    Returns the front end state as a string
def mi_festate() '{
    return ___fe_state()
}'


#%UU% ()
#%MDESC%
#    Returns non zero if PSS interlock is active
def mi_intlck_pss() '{
    local func, attr, what, addr, msg
    func    =   "mi_intlck_pss()"
    attr    =   "PSS_Itlk_State"
    what    =   "tango_get"
    addr    =   __MI["device"]

    msg     =   tango_get(addr, attr)
    __mi_tango_catch_error
    return msg
}'

#%UU% ()
#%MDESC%
#    Returns non zero if EXP interlock is active
def mi_intlck_exp() '{
    local func, attr, what, addr, msg
    func    =   "mi_intlck_exp()"
    attr    =   "EXP_Itlk_State"
    what    =   "tango_get"
    addr    =   __MI["device"]

    msg     =   tango_get(addr, attr)
    __mi_tango_catch_error
    return msg
}'

#%UU% ()
#%MDESC%
#    Returns non zero if FE interlock is active
def mi_intlck_fe() '{
    local func, attr, what, addr, msg
    func    =   "mi_intlck_fe()"
    attr    =   "FE_Itlk_State"
    what    =   "tango_get"
    addr    =   __MI["device"]

    msg     =   tango_get(addr, attr)
    __mi_tango_catch_error
    return msg
}'

#%UU% ()
#%MDESC%
#    Returns non zero if HQPS interlock is active
def mi_intlck_hqps() '{
    local func, attr, what, addr, msg
    func    =   "mi_intlck_hqps()"
    attr    =   "HQPS_Itlk_State"
    what    =   "tango_get"
    addr    =   __MI["device"]

    msg     =   tango_get(addr, attr)
    __mi_tango_catch_error
    return msg
}'

#%UU% ()
#%MDESC%
#    Returns non zero if automatic front end mode is on
def mi_automode() '{
    local func, attr, what, addr, msg
    func    =   "mi_automode()"
    attr    =   "Automatic_Mode"
    what    =   "tango_get"
    addr    =   __MI["device"]

    msg     =   tango_get(addr, attr)
    __mi_tango_catch_error
    return msg
}'

#%UU% ()
#%MDESC%
#    Returns the remaining time for automatic front end mode
def mi_autotime() '{
    return "autotime no more available"
}'

#%UU% ()
#%MDESC%
#    Returns the remaining time for automatic front end mode
def mi_autotime_str() '{
    return "autotime str no more available"
}'


#%IU% ()
#%MDESC%
#    Returns the lifetime.
def mi_lifetime() '{
    local func, attr, what, addr, lifet
    func    =   "mi_lifetime()"
    attr    =   "SR_Lifetime"
    what    =   "tango_get"
    addr    =   __MI["device"]

    lifet  =   tango_get(addr, attr)
    __mi_tango_catch_error
    return lifet
}
'

#%IU% ()
#%MDESC%
#    Returns the operation mode as text: No mode validated, Injection, Beam Delivery
def mi_mode() '{
    local func, attr, what, addr, value
    func    =   "mi_mode()"
    attr    =   "Mode"
    what    =   "tango_get"
    addr    =   __MI["device"]

    value  =   tango_get(addr, attr)
    __mi_tango_catch_error

    return __MI["modes"][value]
}
'


#%IU% ()
#%MDESC%
#    Returns the mode as text: USM, MDT etc.
def mi_srmode() '{
    local func, attr, what, addr, value
    func    =   "mi_srmode()"
    attr    =   "SR_Mode"
    what    =   "tango_get"
    addr    =   __MI["device"]

    value  =   tango_get(addr, attr)
    __mi_tango_catch_error

    return __MI["srmodes"][value]
}
'




#%IU% (<flag>)
#%MDESC% Check if the accelerator filling mode.
def ___fe_filling_mode() '{
    local func, attr, what, addr, __errstr, fefm
    func    =   "___fe_filling_mode"
    addr    =   __MI["device"]
    attr    =   "SR_Filling_Mode"
    what    =   "tango_get"

    fefm    =   tango_get(addr, attr)
    __mi_tango_catch_error

    return fefm
}
'


######################################################################
############################              ############################
############################  CHECK BEAM  ############################
############################              ############################
######################################################################


#%IU%
#%MDESC
#  chk_beam stuff
def mi_chkbeam '{

   local refill

   if (__MI["check"]) {
      refill = mi_refilltime()
      if (refill != -1) {
          if (refill < __MI["checktime"]) {
              printf("Waiting for Beam refilling (%d sec left)\r",refill)
              __MI["waiting"] = __MI["waittime"] # waiting needed

              sleep(1)
              return(0)
          }
          else if (__MI["waiting"] > 0) {
              printf("Waiting after Beam is back (%d sec left)\r",__MI["waiting"])
              __MI["waiting"] -= 5 # doing waiting

              sleep(5)
              return(0)
          }
      }
   }
}
'


#%UU% [<check_time> [<wait_time>]]
#%MDESC%
#    waitforreffill can be used directly without a parameter,
# behaviour will then depend on previous michkon / michkoff.
# With a parameter, on/off is ignored and the wait is done with the
# value in the first argument. An additional parameter can be used to
# wait for <wait_time> seconds after beam is back (Ex: to wait for
# heating or end of cleanup).
def waitforrefill '{
    local check_time  wait_time
    local nvals vals
    local do_stabilization_wait  sleep_time  refill_duration
    local already_waiting

    already_waiting = 0

    if ($# > 0 ) {
        check_time = $1
        if ($# > 1){
            wait_time = $2
        }
    }
    else {
        if (__MI["check"]) {
            check_time = __MI["checktime"]
        }
        else {
            check_time = -1
        }

        if (__MI["waiting"]) {
            wait_time = __MI["waiting"]
        }
        else {
            wait_time = -1
        }
    }

    sleep_time = 5
    do_stabilization_wait = 0
    refill = mi_refilltime()
    refill_duration = 0
    # printf("WFR: checktime=%g  waittime=%g  refill=%g\n", check_time, wait_time, refill)

    while (refill < check_time) {
        # A refill will occur before check_time seconds.

        if (already_waiting == 0){
            already_waiting = 1
            user_wfr_enter_wait
        }

        refill = mi_refilltime()

        if ( (refill == -1) && (__MI["error"] == 0) ) {
            # MDT or ShutDown => do not wait.
            break
        }

        if(refill > 1){
            printf("Waiting for Beam refilling (%d sec left) (%gs requiered)\r", refill, check_time)
        }
        else{
            printf("Waiting for end of refill (for %ds) \r", refill_duration)
            sleep_time = 1 # to reduce useless overhead.
            refill_duration += sleep_time
        }

        sleep(sleep_time)
        do_stabilization_wait = 1
    }

    if(do_stabilization_wait && wait_time){
        printf("Beam is back ! Now waiting stabilization for %d seconds...\n", wait_time)
        countdown(wait_time)
    }

    already_waiting = 0
    user_wfr_exit_wait

}'

cdef("user_wfr_enter_wait", "#$*\n", "_wfr_")
cdef("user_wfr_exit_wait", "#$*\n", "_wfr_")

#%UU%
#%MDESC%
#    Waits machine not to be in Injection mode and frontend open.
def wait_for_beam '{
    local beam_status  front_end_status

    beam_status = mi_mode()
    front_end_status=___fe_state()

    while (beam_status == "Injection") {
        print "wait_for_beam : still in injection, checking every minute"
        sleep(60)
        beam_status = mi_mode()
    }

    if (beam_status == "Beam delivery") {
        print "wait_for_beam : beam is on, ckecking for front end status"

        front_end_status = ___fe_state()

        while (front_end_status == "FE closed") {
            print "wait_for_beam : front end closed, trying to open it every minute"
            feopen
            sleep(60)
            front_end_status = ___fe_state()
        }

        print "wait_for_beam : front end is open, the experiment can continue"
    }
}'


cdef("user_michk_prewait", "#$*\n", "_michk_")
cdef("user_michk_postwait", "#$*\n", "_michk_")


#%IU%
#%MDESC%
#    mi_measure0 replaces mi_chkbeam.
# It loops until refill is finished. This is done without counting
# which avoids to trig counting cards and detectors.
def mi_measure0 '{
    local chkt, refill, iwaswaiting, prstr, srmode, prewaitcalled

    __MI["waiting"]  = 0
    iwaswaiting = 0
    prewaitcalled = 0

    # Save the srmode at the beginning of the scan
    if ( NPTS == 0 ) {
        __MI["saved_srmode"] = mi_srmode()
    }

    # Reads time to refill. (ttr)
    refill = mi_refilltime()

    # last counting time + security time set by user.
    chkt = _ctime + __MI["checktime"]

    __mi_debug   "refill", refill, "chkt", chkt, (refill < chkt)

    while (refill < chkt) {
"""
HW: 2013/3/18
check the SR mode at the start and the current one:
* If they are the same, but it`s not USM, then the scan was started outside user mode. Thus
  is can run.
* If they are different and the current mode is NOT user mode, then we went from USM to to
  MDT or Shutdown, in which case the scan should be suspended, the moment it changes.
* Obviously, when neither is true, we need to proceed to the loop, which awaits the return
  of the beam after an injection.
hope that works!
"""
        srmode  = mi_srmode()
        if ((srmode == __MI["saved_srmode"]) && \
            (srmode != "USM")) {
            # not in user mode, break loop.
            break
        } else
        if ((srmode != __MI["saved_srmode"]) && \
            (__MI["saved_srmode"] == "USM")) {
            # changed from USM to MDT/Shutdown, suspend scan.
            prstr = ": SR mode is " srmode
        } else 
        {
            # Injection during USM, suspend scan until beam comes back
            prstr = " - " refill > 0 ? refill : 0 " sec left  "
        }
        printf("Waiting for Beam%s\r", prstr)
        __MI["waiting"] = __MI["waittime"] # Waiting needed after the beam return.

        if (!prewaitcalled) {
            user_michk_prewait
            prewaitcalled = 1
        }

        sleep(2)

        refill = mi_refilltime()
    }

    if (__MI["waiting"] > 0) {
        print
    }

    while(__MI["waiting"] > 0) {
        __mi_debug "__MI[\"waiting\"] > 0"
        iwaswaiting = 1
        printf("Waiting after Beam is back (%d sec left)\r", __MI["waiting"])
        __MI["waiting"] -= 5
        # doing waiting
        sleep(5)
    }

    if (prewaitcalled) {
        user_michk_postwait
    }

    if (iwaswaiting) {
        print "\nBeam is back. Scan resumed."
    }
}
'

#%UU%
#%MDESC%
# Open the frontend shutter from SPEC
#
def feopen '{
    ___feopen
}'

#%IU%
#%MDESC%
# Open the frontend shutter from SPEC
#
def ___feopen '{
    if (!__MI["device"]) {
        eprintf("Don`t forget to run ")
        tty_cntl("md"); print "misetup"; tty_cntl("me")
        exit
    }
    local func, attr, what, addr, __errstr
    func    =   "feopen"
    addr    =   __MI["device"]
    attr    =   "Open"
    what    =   "tango_io"

    tango_io(addr, attr)
    __mi_tango_catch_error
    ___fe_pollfe("open")
}
'


#%UU%
#%MDESC%
# Close the frontend shutter from SPEC
#
def feclose '{
    if (!__MI["device"]) {
        eprintf("Don`t forget to run ")
        tty_cntl("md"); print "misetup"; tty_cntl("me")
        exit
    }
    local func, attr, what, addr, __errstr
    func    =   "feclose"
    addr    =   __MI["device"]
    attr    =   "Close"
    what    =   "tango_io"

    tango_io(addr, attr)
    __mi_tango_catch_error

   ___fe_pollfe("close")
}
'

def idopen  'print "Please use feopen!";  feopen'
def idclose 'print "Please use feclose!"; feclose'

#%IU% ()
#%MDESC%
def ___fe_state() '{
    local func, attr, what, addr, __errstr, festate
    func    =   "___fe_state"
    addr    =   __MI["device"]
    attr    =   "FE_State"
    what    =   "tango_get"
    if (!addr) {
        return -1
    }

    festate =   tango_get(addr, attr)
    __mi_tango_catch_error

    return festate
}
'



#%IU% (<what>)
#%MDESC%
# Some complementary info:%BR%
# Attribute FE_State can deliver the following answers:%BR%
#"FE open", "FE closed", "FE fault", "FE disabled", "FE unknown"
def ___fe_pollfe(what) '{
    local festate st_time festate

    st_time = time()

    for(;;) {
       festate =  ___fe_state() # string like "FE Open"
       if (festate == "FE closed") {
           # FE closed
           if ( what == "close" ) {
               return(0)
           }
       }
       else if (festate == "FE open") {
           # FE open
           if ( what == "open" ) {
               return(0)
           }
       }
       else {
           printf("Problem with frontend command \"%s\". festate is: ", what)
           tty_cntl("md"); printf("%s\r", festate); tty_cntl("me")
       }

       sleep(0.2)

       if ((time() - st_time) > __MI["timeout"]) {
           printf("\nTimeout polling on frontend. Giving up.\n")
           return(-1)
       }
    }
}
'

#%IU% (<what>)
#%MDESC% <what> : state in  {"close"; "open"}%BR%
# macro to replace possibly needed macro from the old id.mac
def id_pollfe(what) '{
    return ___fe_pollfe(what)
}
'


#%IU% (<flag>)
#%MDESC% Check if the front end is open. Open it if %B%flag%B% set.
def ___fe_check(flag) '{
    local stat

    stat = ___fe_state()

    if (flag == 0) {
        if (stat != "FE open"){
            eprintf ("Front End not open, exiting...\n")
            return (-1)
        }
    }
    else if (stat == "FE closed"){
        feopen
    }

    return(stat)
}
'


#%IU% (<flag>)
#%MDESC% VINTAGE function to check if the front end is open. Open it if
# %B%flag%B% set.
#%BR%Only this old version is being used in other macros and they expect a
# numeric value to be returned according to the old taco DS.
#%BR%Old state values of the frontend were: 0 (unknown), 3 (close), 4(open),
# 17 (automatic), 23 (fault), 46 (disabled) or -1 iferror.
def _check_fe(flag) '{
    local stat

    stat = ___fe_check(flag)
    if (stat == "FE open") {
        return  4
    }
    else if (stat == "FE closed") {
        return  3
    }
    else if (stat == "FE fault") {
        return 23
    }
    else if (stat == "FE disabled") {
        return 46
    }
    else {
        return  0
    }

}
'



###################################################################################
#
#   Macro Counter macros, used for reading Storage Ring and Single Bunch currents
#
###################################################################################

#%IU%(cnum, type, unit, mod, chan)
#%MDESC%
#%BR% %BR%Declare a %B%counter\0controller%B%:
#%BR% %BR%
#%PRE%
#SCALERS\0\0\0DEVICE\0\0\0ADDR\0\0\0\0\0\0\0<>MODE\0\0NUM\0\0\0\0\0\0\0\0\0\0\0\0<>TYPE
#\0\0\0\0YES\0\0\0\0SRCUR\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\02\0\0\0\0\0Macro\0Counter
#%PRE%
# No address is necessary, as it will be taken from misetup.
#%BR% %BR%
#Then create %B%macro\0counters%B% in a similar manner:
#%BR% %BR%Unit is index num of controller, channel 0 is for the storage ring current, 1 for single bunch current.%BR%
#Number\0\0\0Name\0\0Mnemonic\0\0\0Device\0\0Unit\0\0Chan\0\0\0<>Use\0As\0\0Scale\0Factor
#\0\0\0\0\03\0\0srcur\0\0\0\0\0srcur\0\0MAC_CNT\0\0\0\0\02\0\0\0\0\00\0\0\0\0counter\0\0\0\0\0\0\0\0\0\0\0\0\01
#\0\0\0\0\04\0\0sbcur\0\0\0\0\0sbcur\0\0MAC_CNT\0\0\0\0\02\0\0\0\0\01\0\0\0\0counter\0\0\0\0\0\0\0\0\0\0\0\0\01

# %BR%Called by spec
# %BR%
#Note: the internal variable SRCUR_addr will not be used. Please use misetup.
def SRCUR_config(cnum, type, unit, mod, chan) '{
    # chan will be used for counters to designate the tag address. Leave alone.
    __mi_debug "Configuring SRCUR", cnum, type, unit, mod, chan
    __mi_debug "No action here!"
    if (type == "ctrl") {
        if (!__MI["device"]) {
            eprintf("Don`t forget to run ")
            tty_cntl("md"); print "misetup"; tty_cntl("me")
            __MI["device"]  =   SRCUR_ADDR
        }
    }
}
'

#%IU%(cnum, attr, p1, p2, p3)
#%MDESC% Channel 0 will return the SR current and channel 1 the single bunch
# current.%BR%
# Called by spec%BR%
#Note: the internal variable SRCUR_addr will not be used. Please use misetup.
#%END%
# I`d like to check the mode to find out about the validity of the single bunch
# current, but that`s not yet possible.
#%BR%From the file
#/segfs/tango/cppserver/machine/machstat_wrapper/MachStatWrapClass.cpp
# we get the possible SR filling modes:%BR%
#	vect_data.push_back("uniform multibunch");%BR%
#	vect_data.push_back("1/3 multibunch");%BR%
#	vect_data.push_back("2/3 multibunch");%BR%
#	vect_data.push_back("2*1/3 multibunch");%BR%
#	vect_data.push_back("7/8 multibunch");%BR%
#	vect_data.push_back("1 bunch");%BR%
#	vect_data.push_back("4 bunch");%BR%
#	vect_data.push_back("16 bunch");%BR%
#	vect_data.push_back("32 bunch");%BR%
#	vect_data.push_back("24*8+1bunch");%BR%
#	vect_data.push_back("4GeV");%BR%
#	vect_data.push_back("5GeV");%BR%
#	vect_data.push_back("Empty");%BR%
# There is a single bunch current in several modes. Don't bother right now.
def SRCUR_cmd(cnum, cmd, p1, p2, p3) '{
    local unit, chan, retval, attr
    __mi_debug "Command SRCUR", cnum, cmd, p1, p2, p3
    if (cnum != "..") {
        if (cmd == "counts") {
            channel = counter_par(cnum, "channel")
            local func, attr, what, addr, __errstr, srmode
            func    =   "SRCUR_cmd"
            what    =   "tango_get"
            addr    =   __MI["device"]

            if (channel == 0) {
                attr=   "SR_Current"
                if (!__MI["device"]) {
                    eprintf("\nPlease run ")
                    tty_cntl("md"); print "misetup"; tty_cntl("me")
                }
            }
            else if (channel == 1) {
                attr=   "SR_Single_Bunch_Current"
            }
            else if (channel == 2) {
                attr=   "SR_Refill_Countdown"
            }
            else if (channel == 3) {
                attr=   "SR_Mode"
            }

            if (!__MI["device"]) {
                return (1e1000/1e1000)
            }
            what    =   "tango_get"
            __mi_debug what "(" addr "," attr ")"
            TANGO_ERR = "-1"
            retval  =   tango_get(addr, attr)
            # do not pick up errors, as we will just return the -1 resulting from
            # the tango_get call, if we`re not in USM, Then the server will set the 
            # quality of the attr to INVALID and spec gives an error. --> return -1
            #__mi_tango_catch_error

            # return count
            return(retval)
        }
    }
#    else {
#        if (!__MI["device"]) {
#            eprintf("Please run ")
#            tty_cntl("md"); print "misetup"; tty_cntl("me")
#        }
#    }
}
'

#%IU%
#%MDESC%
#    Reading insertion devices from the old taco servers.
def _miidread '{
    if (__MI["hasid"]) {
        ESRF_ERR = -1
        if (esrf_io(IDDEV, "DevRead", __MI_IDPOS) == -1) {
            __MI["error"] = 1
        }
    }
}
'

#%IU%
#%MDESC%
#   scan header output with the old taco servers.
def _miidwrite '{
    if (__MI["hasid"]) {
     local naxis ax name
     naxis = list_n(IDAXIS)
     for (ax=0;ax<naxis;ax++) {
       name   = list_getpar(IDAXIS,sprintf("_%d",ax+1),"meca")
       titles = sprintf("%s %s_Gap  %s_Taper",titles,name,name)
       values = sprintf("%s %9.4f %9.4f",values,__MI_IDPOS[ax*2],__MI_IDPOS[ax*2+1])
     }
   }
}
'

#%IU%
#%MDESC%
#    Screen formatting for the old taco servers.
def _miidshow '{
    if (__MI["hasid"]) {
        local naxis ax name, FMT
        FMT =   "%30s      %-30s\n"
        naxis = list_n(IDAXIS)
        if (naxis > 0) {
            print
            printf(FMT, "ID :", sprintf("%10s %10s", "Gap(mm)", "Taper(mm)"))
        }
        for (ax=0;ax<naxis;ax++) {
            name = list_getpar(IDAXIS,sprintf("_%d",ax+1),"meca")
            printf(FMT, sprintf("%s :", name), sprintf("%9.4g %9.4g",__MI_IDPOS[ax*2],__MI_IDPOS[ax*2+1]))
        }
    } else {
        print
        printf("  ID (not avail. id.mac needed)\n")
    }
}
'


#%IU%()
#%MDESC%Get state of all undulators%BR%
# function which copies behaviour of the old id_state() from the old id.mac
#%BR% Return the state of the frontend -> 0 (unknown), 3 (close), 4(open),
#17 (automatic), 23 (fault), 46 (disabled) or -1 iferror.
def id_state() '{
    __mi_debug "id_state"
    local state
    if ((state = ___fe_state()) == -1 ) {
        local func, attr, what, __errstr
        func    =   "id_state()"
        __errstr    =   __MI["macfname"] " - " func " - ERROR\n"
        __MI_error(__errstr)
        return(-1)
    }
    else {
        if (state == "FE closed") {
            return 3
        }
        else  if (state == "FE open") {
            return 4
        }
        else  if (state == "FE fault") {
            return -1
        }
        else  if (state == "FE disabled") {
            return 46
        }
        else  if (state == "FE unknown") {
            return 0
        }
    }
}
'


###################################################################################
#
#   Macro Counter enable/disable
#
###################################################################################

#%UU%
#%MDESC%Disable all storage ring counters.%BR%
def mioff '{
    local cnt
    for(cnt=0; cnt < COUNTERS; cnt++) {
        if (counter_par(cnt, "device_id") == "SRCUR") {
            counter_par(cnt, "disable", 1)
        }
    }

}
'

#%UU%
#%MDESC%Enable all storage ring counters.%BR%
def mion '{
    local cnt
    for(cnt=0; cnt < COUNTERS; cnt++) {
        if (counter_par(cnt, "device_id") == "SRCUR") {
            counter_par(cnt, "disable", 0)
        }
    }

}
'




######################################################################
#########################                     ########################
#########################  MACHINE MODE INFO  ########################
#########################                     ########################
######################################################################


def mi_get_mode() '{
    local attr, addr

    addr = "//acs.esrf.fr:10000/sys/machstat/tango"
    attr = "Sr_mode"

    #   SR Mode
    # 1 /* USM */
    # 2 /* MDT */
    # 3 /* SHUTDOWN */
    # 4 /* SAFETY TEST*/
    # 5 /* ID TEST*/
    # Il y a aussi tous les parametres machines importants: (courant,
    # lifetime, countdown, message operatoro, état des frontends...

    TANGO_ERR   =   "-1"
    _mode    =   tango_get(addr, attr)

    if (TANGO_ERR == "API_EmptyDeviceAttribute") {
        # no such attr
        _mode = -1
    }

    return _mode
}'

def mi_mode_is_USM() '{
    return mi_get_mode() == 1
}'



#%MACROS%
#%IMACROS%
#%DEPENDENCIES%
#  Functions here might use functions from ID_tango.mac
#%AUTHOR%
#  MACHINFO.MAC - HW, Jan 2012
#%END%
#  $Revision: 4.38 $ / $Date: 2023/08/29 07:54:12 $
#%TOC%