esrf

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

#%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%