esrf

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

#%TITLE% SMARACT.MAC
#
#%NAME%
# Macro motor for SmarAct piezo controller used through a serial line.
#
#%CATEGORY% Positioning
#
#
#%DESCRIPTION%
#%DL%
#%DT%Configuring macro motors %DD%
# %DL%
#  %DT% 1)
#       You must have an entry in "MOTORS" table for each icepap MASTER
#       %DL%
#         %DT% -
#         The "DEVICE" field must be set to string "smaract"
#         %DT% -
#         The "ADDR" is set to the serial line entry in the config 
#         %DT% -
#         The "NUM" should be set to 10
#         %DT% -
#         The "TYPE" field must be set to "Macro Motors"
#       %XDL%
#  %DT% 2)
#       For each axis you must define a motor with:
#       %DL%
#         %DT% -
#         The "Controller" field set to "MAC_MOT"
#         %DT% -
#         The "Unit" must be set to the above MOTORS entry
#         %DT% -
#         The "Module" field, must be set to 0
#         %DT% -
#         The "Chan" field, numbered from 0,
#         must be set to the SmartAct axis channel
#         %DT% -
#         The "Linear/Rotary" must be set to 1 for rotation stages
#         %DT% -
#         The "Readback slop" has probably to be set also (typically 100)
#       %XDL%
# %XDL%
#%XDL%
#
#%END%
#

#%UU%
#%MDESC%
# Setup smaract controller 
# (NOTE MP: utility of this macro ??????)
#
def smaract_setup '{
    global SMARACT_PAR[]

    SMARACT_PAR["setup_done"] = 1
    
    print "smaract_setup"
}'

#%UU%
#%MDESC%
# Unsetup smaract controller
# (NOTE MP: utility of this macro ??????)
#
def smaract_unsetup '{
    unglobal SMARACT_PAR
    
    print "smaract_unsetup"
}'


#%IU% (mne, type, unit, module, channel)
#%MDESC%
# 
def smaract_config(mot_num, type, unit, module, channel) '{
    global SMARACT_PAR


    SMARACT_PAR["first"] = 1

    if (mot_num == "..") {
        # called for each macro hardware controller unit.
        # mot_num = ".."
        # type    = "ctrl"
        # unit    = unit number of the controller.
        # module  = number of channels (as specified
        #             in the device screen of config)

        SMARACT_PAR[unit]["addr"] = smaract_ADDR+0
        
        rep = _smaract_query(unit, "GIV")
        
        if (index(rep, "IV") == 0) {
            _smaract_perror("no answer from controller")
            return ".error."
            # Spec will now consider the CONTROLLER unresponsive.
        }
        
        # set smaract controller in asynchronuous mode
        _smaract_set(unit, "SCM1")

        # check closed-loop on all channels
        rep = _smaract_query(unit, "GSE")
        if (substr(rep, 1, 4) != ":SE1") {
            _smaract_set(unit, "SSE1")
        }
        
        _smaract_print("Found SmarAct controller")
    }
    else{

        if (type == "mot") {
            local  comm rep
            local  stype
            local  str
            local  nval vals[]

            # Get sensor type
            comm  = sprintf("GST%d", channel)
            rep   = _smaract_query(unit, comm)

            nval  = split(rep, vals, "," )
            stype = substr(vals[1], 0, length(vals[1])-1)
            
            str = sprintf("motor:%-8s  type:%s", \
                motor_mne(mot_num), _smaract_gettype(stype))
            _smaract_print(str)

            comm = sprintf("GPPK%d", channel)
            rep  = _smaract_query(unit, comm)
            
            nval  = split(rep, vals, "," )
            stype = substr(vals[1], 0, length(vals[1])-1)

            if (stype != 1) {
                str = sprintf("motor:%-8s Unknown physical position", motor_mne(mot_num))
                _smaract_perror(str)
                str = sprintf("Use chg_dial(%s, \"home\") to find home ref mark", \
                      motor_mne(mot_num))
                _smaract_print(str)
            }
	    SMARACT_PAR[motor_mne(mot_num)]["movend"]= 0
	    SMARACT_PAR[motor_mne(mot_num)]["moving"]= 0
	    SMARACT_PAR[motor_mne(mot_num)]["askpos"]= 0

            return 
        }
    }
    return 
}'


#%IU% (motnum, key, action, p1, p2)
#%MDESC%
#    The smaract_par function is called when various motor parameters
# are set and when the motor_par() function is used to retrieve a
# user-defined parameter.
def smaract_par(motnum, key, action, p1, p2) '{
    local unit channel comm rep
    local val nval vals[]

    unit    = motor_par(motnum, "unit")
    channel = motor_par(motnum, "channel")

    if (action=="get") {
        # should return a value for the parameter named as key for
        # motor number motnum. It is never called for an internal spec param.
        # return 0

        if (key == "acceleration"){
        }
        else if (key == "backlash") {
        }
        else if (key == "backlash_rate") {
        }
        else if (key == "base_rate") {
        }
        else if (key == "disable") {
        }
        else if (key == "slew_rate" || key == "velocity") {
        }
        else if (key == "step_size") {
        }
        else if (key == "limits") {
        }
        else if (key == "offset") {
        }
        else if (key == "misc_par_1") {
        }
    }
    else if (action == "set"){
        if (key == "acceleration"){
        }
        else if (key == "backlash") {
        }
        else if (key == "backlash_rate") {
        }
        else if (key == "base_rate") {
        }
        else if (key == "disable") {
        }
        else if (key == "slew_rate" || key == "velocity") {
        }
        else if (key == "step_size") {
        }
        else if (key == "chan0") {
        }
        else if (key == "read_mode") {
        }
        else if (key == "slop") {
        }
        else if (key == "limits") {
        }
        else if (key == "offset") {
        }
    }
}'





#%IU%(<motnum>, <cmd>, <p1>, <p2>)
#%MDESC%
#    This function is called to control *hardware* macro_motors.
# <p1> and <p2> signification depends on cmd.
#
def smaract_cmd(motnum, cmd, p1, p2, p3) '{
    global SMARACT_PAR[]
    
    local unit comm channel rep _notreasy pos motmne
    local nval vals[]

    if (SMARACT_PAR["first"]){
        SMARACT_PAR["first"] = 0
    }

    if (motnum == "..") {
        # cmd apply to all motors.

        # !! no motor_par(motnum,...) here -> crash.

        # print "in smaract_cmd motnum=", motnum, "cmd=", cmd

        if (cmd == "preread_all") {
            # Sent prior a "position" command.

            # Can be the place for an "Update all positions"  here ???

        }
        else if (cmd == "prestart_all") {
            # ?
        }
        else if (cmd == "start_all") {
            # ?
        }
    }
    else{

        # "cmd" applies to individual motor.
        # "motnum" is the motor number.

        # A minimal implementation requires to recognize the commands:
        # *start_one
        # *get_status
        # *position
        # *set_position.

        unit    = motor_par(motnum, "unit")
        channel = motor_par(motnum, "channel")
        motmne  = motor_mne(motnum)
        
        if (cmd == "get_status") {
            local sta staold

            staold= -1
            sta= -2
            while (sta!=staold) {
              comm = sprintf("GS%d", channel)
              rep = _smaract_query(unit, comm)
            
              # Get the status code
              staold= sta
              sta = substr(rep, 5, length(rep)-5)
            }
            
            # The controller status is very verbose:
            # 0 == stopped
            # 1 == stepping, open-loop motion (MST)
            # 2 == scanning, (MSCA or MCSR)
            # 3 == holding, target or reference position (MPA MAA FRM)
            # 4 == targeting, closed-loop motion (MPA MAA)
            # 5 == move delay
            # 6 == calibrating, (CS)
            # 7 == finding reference, (FRM)
            # 9 == locked, emergency stop
            # Inform SPEC that there is no motion
            if((sta == 0) || (sta == 3) || (sta==9)) {
                if (SMARACT_PAR[motmne]["moving"]) {
		    SMARACT_PAR[motmne]["movend"]= 1
		    SMARACT_PAR[motmne]["moving"]= 0
                }
                return (0)
            }

            # Inform SPEC that there the motor is not ready
            return (0x02)
        }
        else if (cmd == "start_one") {
            local pos

            # p1 : target position in dial units for absolute motion.
            # p2 : magnitude in dial units for relative motion.

            # Calculate target position in udeg or um
            pos =  p1*motor_par(motnum, "step_size")

            if (motor_par(motnum, "rotary")) {

                # Mininum check to avoid controller failure
                if((pos<0) || (pos>360e6)) {
                    _smaract_perror("invalid target position, out of [0-360]")
                    return
                }

                # Move to absolute angle with infinite hold time (=60s)
                comm= sprintf("MAA%d,%d,0,60000", channel, pos)

            } else {

                # Move to absolute position with infinite hold time (=60s)
                comm= sprintf("MPA%d,%d,60000", channel, pos)
            }
            rep = _smaract_set(unit, comm)
            SMARACT_PAR[motmne]["movend"]= 0
            SMARACT_PAR[motmne]["moving"]= 1
            SMARACT_PAR[motmne]["askpos"]= p1
        }
        else if (cmd == "position") {
            if (SMARACT_PAR[motmne]["movend"]==1) {
                pos= SMARACT_PAR[motmne]["askpos"]
            } else {
                # Must return the current real motor position in dial units.
                if (motor_par(motnum, "rotary")) {
                    comm= sprintf("GA%d", channel)
                } else {
                    comm= sprintf("GP%d", channel)
                }
                rep = _smaract_query(unit, comm)
           
                nval= split(rep, vals, "," )
                pos = vals[1]
                pos = pos / motor_par(motnum, "step_size")
            }

            return pos
        }
        else if (cmd == "set_position") {
            comm = sprintf("SP%d,%d", channel, p1)
            rep = _smaract_set(unit, comm)            
        }
        else if (cmd == "base_rate") {
            # p1 is the base_rate defined in spec config.
        }
        else if (cmd == "slew_rate") {
            comm = sprintf("SCLS%d,%d", channel,p1)
            rep  = _smaract_set(unit, comm)
        }
        else if (cmd == "acceleration") {
            # p1 is the acceleration defined in spec config.
        }
        else if (cmd == "home_base_rate") {
            #
        }
        else if (cmd == "home_slew_rate") {
            #
        }
        else if (cmd == "home_acceleration") {
            #
        }
        else if (cmd == "preread_all") {
            # Can be use to read a controller ?
        }
        else if (cmd == "preread_one") {
            # Can be use to read a controller ?
        }
        else if (cmd == "prestart_one") {
            #
        }
        else if (cmd == "magnitude") {
            # p1 is the relative quantity of movement of the move.
        }
        else if (cmd == "flush_all") {
            #
        }
        else if (cmd == "flush_one") {
            #
        }
        else if (cmd == "abort_one") {
            if (SMARACT_PAR[motmne]["moving"]) {
                comm = sprintf("S%d", channel)
                rep = _smaract_set(unit, comm)
            }
            SMARACT_PAR[motmne]["moving"]= 0
            SMARACT_PAR[motmne]["movend"]= 0
        }
        else if (cmd == "abort_all") {
            #
        }
        else if (cmd == "search") {
            # Initiates a home or limit search.
            # A prestart_one call will precede this call.
            # The parameter p1 indicates the type of search.
            # The parameter p2 contains the position in dial units
            # that corresponds to the home or limit switch.

            if((p1 == "home") || (p1 == "home-")){
                # find home position by moving in negative direction.
                comm = sprintf("FRM%d,1,60000,0", channel)
                _smaract_set(unit, comm)
            }
            else if(p1 == "home+"){
                # find home position by moving in positive direction.
                comm = sprintf("FRM%d,0,60000,0", channel)
                _smaract_set(unit, comm)
            }
            else if(p1 == "lim+"){
                # find the positive limit.
                _smaract_perror("not implemented, use \"home+\" or \"home-\"")
            }
            else if(p1 == "lim-"){
                # find the negative limit.
                _smaract_perror("not implemented, use \"home+\" or \"home-\"")
            }
        }
        else if (cmd == "diff_position") {
            #
            print "SMARCAT--smaract_cmd()--cmd=", cmd
        }
        else {
            #
            print cmd, " -> not implemented ?"
        }
    }
}'


#%IU%
#%MDESC%
#
def _smaract_set(unit, comm) '{
    local dev scomm
    
    dev = SMARACT_PAR[unit]["addr"]
    
    scomm = sprintf(":%s\n", comm)
    
    ser_put(dev, scomm)
}'


#%IU%(code)
#%MDESC%
# Return a string corresponding to the encoder type hardcoded in
# the controller, no way to set its resolution
#
def _smaract_gettype(typ) '{
    local str

    str="= unknown positioner and sensor"
    if(typ ==  1) { str="=\"S\"    linear positioner with nano sensor"  }
    if(typ ==  2) { str="=\"SR\"   rotary positioner with nano sensor"  }
    if(typ ==  4) { str="=\"MR\"   rotary positioner with micro sensor" }
    if(typ ==  5) { str="=\"SP\"   linear positioner with nano sensor"  }
    if(typ ==  6) { str="=\"SP\"   linear positioner with nano sensor"  }
    if(typ ==  7) { str="=\"M25\"  rotary positioner with micro sensor" }
    if(typ ==  8) { str="=\"SR20\" rotary positioner with nano sensor"  }
    if(typ ==  9) { str="=\"M\"    linear positioner with micro sensor" }
    if(typ == 10) { str="=\"GC\"   rotary positioner with micro sensor" }
    if(typ == 11) { str="=\"GD\"   goniometer with micro sensor"        }
    if(typ == 12) { str="=\"GE\"   goniometer with micro sensor"        }
    if(typ == 13) { str="=\"RA\"   rotary positioner with abs sensor"   }
    if(typ == 14) { str="=\"GF\"   rotary positioner with micro sensor" }
    if(typ == 15) { str="=\"RB\"   rotary positioner with micro sensor" }
    if(typ == 20) { str="=\"SR77\" rotary positioner with nano sensor"  }
    if(typ == 21) { str="=\"SD\"   linear positioner with nano sensor"  }

    return(sprintf("%2d%s",typ, str))
}'


#%IU%(code)
#%MDESC%
# Return a string corresponding to the specified error code
#
def _smaract_geterr(err) '{
    # No sense, but controller can return a no error error
    if(err ==   0) { return("")                         }

    # At this point, we are in trouble
    if(err ==   1) { return("Syntax error")             }
    if(err ==   2) { return("Invalid command")          }
    if(err ==   3) { return("Overflow error")           }
    if(err ==   4) { return("Parse error")              }
    if(err ==   5) { return("Too few parameters")       }
    if(err ==   6) { return("Too many parameters")      }
    if(err ==   7) { return("Invalid parameter")        }
    if(err ==   8) { return("Wrong mode")               }
    if(err == 129) { return("No sensor present")        }
    if(err == 140) { return("Sensor disable")           }
    if(err == 141) { return("Command overridden")       }
    if(err == 142) { return("End stop reached")         }
    if(err == 143) { return("Wrong sensor type")        }
    if(err == 144) { return("Could not find reference") }
    if(err == 145) { return("Wrong end effoctor type")  }
    if(err == 146) { return("Movement locked")          }
    if(err == 147) { return("Range limit reached")      }
    if(err == 148) { return("Physical position unknown")}

    return(sprintf("Unknown error: %d",err))
}'

#%IU%(answer)
#%MDESC%
# Return a non zero value if the controller answer contains an error
# and print out an explicit error message.
#
def _smaract_checkerr(rep) '{
    local nval vals[] err

    # Check if controller complains
    if(substr(rep,0,2) != ":E") { return(0) }

    # Handle controller errors
    nval = split(rep, vals, "," )
    str  = _smaract_geterr(vals[1])
    if(str == "") { return(0) }

    # Be friendly with the user
    _smaract_perror(sprintf("controller error: %s\n", str))
    return(-1)
}'


#%IU%
#%MDESC%
#
def _smaract_get(unit) '{
    local dev rep
    
    dev = SMARACT_PAR[unit]["addr"]
    
    rep = ser_get(dev, "\n")
    if(_smaract_checkerr(rep)) { rep="" }

    return rep
}'

#%IU%
#%MDESC%
#
def _smaract_query(unit, comm) '{
    local dev
    dev = SMARACT_PAR[unit]["addr"]

    ser_par(dev, "flush")

    _smaract_set(unit, comm)
        
    return _smaract_get(unit)
}'


#%IU%(string)
#%MDESC%
#
def _smaract_perror(str) '{
    tty_cntl("md")
    printf("SMARACT ERROR: ")
    tty_cntl("me")
    printf("%s\n", str)
}'

#%IU%(string)
#%MDESC%
#
def _smaract_print(str) '{
#    tty_cntl("md")
    printf("SMARACT: ")
#    tty_cntl("me")
    printf("%s\n", str)
}'

#%MACROS%
#%IMACROS%
#%AUTHOR% GB+EP+MP BLISS
# %BR%$Revision: 1.3 $ / $Date: 2014/01/10 09:17:32 $
#%TOC%