esrf

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

#%TITLE% PI_E712 $Revision: 1.2 $
#%NAME%
# Macros to control a piezo PI controller connected via serial line or
# ethernet.
#
#%DESCRIPTION%
#  This macro file allows to connect a piezo to a macro motor in SPEC.
#
#  %BR%Controllers currently supported are:
#%DL%
#%DT%PI E-712 (SERIAL)%DD%
# ID01
#%DT%PI E-712 (ETHERNET)%DD%
# ID13
# ID16
#%XDL%
#
#
#%END%
#
#%SETUP%
#%DL%
#%DT%Configuring macro motors %DD%
#  %DL%
#    %DT% 1)
#         You must have a "MOTORS" defined with:
#         %BR%
#         %DL%
#           %DT% DEVICE
#           set to "pi_e712"
#           %DT% TYPE
#           set to "Macro Motor"
#           %DT% ADDR
#           set to the address of the controller, examples:
#           %DL%
#               %DT% for SERIAL: "1"
#               %DT% for ETHERNET: "id13e712"
#           %XDL%
#
#           %DT% NUM
#           must be set also to at least 6
#         %XDL%
#    %DT% 2)
#         Per piezo axis you must define a motor with:
#         %BR%
#         %DL%
#           %DT% Controller
#           set to "MAC_MOT"
#           %DT% Unit
#           field must be set to the "MOTOR" entry.
#           %DT% Chan
#           field must be set to the piezo axis of the controller
#           starting from 1 for the first channel
#         %XDL%
#  %XDL%
#
#%DT%Extra motor_par() parameters %DD%
#  %DL%
#    %DT% motor_par(motor,"servo")%DD%
#       Returns non zero if the piezo closed loop is active
#    %DT% motor_par(motor,"servo",1|0)%DD%
#       Activates or open the piezo closed loop
#  %XDL%
#
#
#%XDL%
#


#%END%
#
#%HISTORY%
#$Log: pi_e712.mac,v $
#Revision 1.2  2013/07/17 09:38:16  guilloud
#*changed error code file from 712 to gcs common one.
#
#Revision 1.1  2012/05/24 14:35:28  perez
#Add error code handling + motor_par("servo")
#
#Revision 1.0  2012/05/22 14:40:02  perez
#Initial release, tested only with socket interface
#
#
#%END%
#

constant PI_E712_HEAD "PI E712"

need pi_gcs_errcode.mac

global PI_E712[]


#%UU% [personal msg]
#%MDESC%
# Switch on or off the print of debug messages
#
def piezogcsdebug '{
 global PI_E712[]

 if(PI_E712["debug"]) {
  rdef pi_e712__debug \'#\$#\'
  piezogvs_prn("debug mode is OFF")
  PI_E712["debug"]=0
 } else {
  rdef pi_e712__debug \'print "PIEZO GCS:","$*"\'
  piezogvs_prn("debug mode is ON")
  PI_E712["debug"]=1
 }
}'

# No debug by default
def pi_e712__debug \'#\$#\'

#%IU%
#%MDESC%
def pi_e712_perr(msg) '{
    tty_cntl("md")
    printf("%s: ERROR: ",PI_E712_HEAD)
    tty_cntl("me")
    printf("%s\n", msg)
}'

#%IU%
#%MDESC%
def pi_e712_prn(msg) '{
    tty_cntl("md")
    printf("%s: ",PI_E712_HEAD)
    tty_cntl("me")
    printf("%s\n", msg)
}'





#%IU%
#%MDESC%
# MACRO MOTOR:
# Called by spec after reading the config file
#
def pi_e712_config(num,type,p1,p2,p3) '{
    global PI_E712[]
    local  i
    local  dev itf

    # p1==controller p2==number of motors supported
    if(type=="ctrl") {

        # erase any previous configuration
        if(p1 == 0) { for(i in PI_E712) { delete PI_E712[i] } }

        # try to guess the interface to use
        dev = pi_e712_ADDR
        if(int(pi_e712_ADDR)!=pi_e712_ADDR) {
            # the port to use is optional
            if(!index(pi_e712_ADDR,":")) {
                dev = dev":50000"
            }
            if(!sock_par(dev,"connect")) {
                pi_e712_perr("controller not on ethernet")
                return ".error."
            }
            itf="SOCKET"
            sock_par(dev, "timeout", 0.2)
        } else {
            # serial interface by default
            if(ser_par(dev,"device_id") == -1) {
                pi_e712_perr("wrong serial line for controller")
                return ".error."
            }
            itf="SERIAL"
        }

        # keep a record of the full interface
        PI_E712[pi_e712_ADDR]["itf"] = itf
        PI_E712[pi_e712_ADDR]["dev"] = dev


        # at this point, we should be able to work
        PI_E712[pi_e712_ADDR]["on"]  = 1

        # but check that is true
        if((ans = pi_e712_putget(dev, itf, "IDN?")) == ".error.") {
            pi_e712_perr("no answer from controller")
            return ".error."
        }
        pi_e712_prn(sprintf("found %s controller",itf))
        print ans
    }


    # p1==unit p2==module p3==channel
    if(type=="mot") {
        local mne

        # keep a record of the full interface
        motor_par(num,"itf", PI_E712[pi_e712_ADDR]["itf"], "add")
        motor_par(num,"dev", PI_E712[pi_e712_ADDR]["dev"], "add")

        # minimum check
        mne = motor_mne(num)
        if(motor_par(num,"channel")<1) {
            pi_e712_perr(sprintf("invalid channel for motor: %s", mne))
            return ".error."
        }
        if(motor_par(num,"module")) {
            pi_e712_perr(sprintf("invalid module for motor: %s", mne))
            return ".error."
        }
     }

}'




#%IU%
#%MDESC%
# MACRO MOTOR:
# Called by spec on motor operation.
#
def pi_e712_cmd(num,key,p1,p2) '{
    local mne
    local ans
    local cha
    local cmd

    if(num == "..") { return }
    mne = motor_mne(num)
    cha = motor_par(num, "channel")

    #
    # return the current motor position in mm or deg
    #
    if (key == "position") {
        local pos

        cmd = sprintf("POS? %d",cha)
        if((ans = pi_e712_motputget(num, cmd)) == ".error.") { return(0) }

        if(sscanf(ans,"%d=%f", cha, pos) != 2) {
            pi_e712_perr(sprintf("unable to get postion for motor: %s", mne))
            return ".error."
        }
        return(pos)
    }


    #
    # start a motion (p1==abs pos, p2==rel pos, with pos in mm or deg)
    #
    if (key == "start_one") {
        local err

        # request the motion (valid only if servo is on)
        cmd = sprintf("MOV %d %f", cha, p1)
        pi_e712_motput(num, cmd)

        # check for errors
        if((ans = pi_e712_motputget(num, "ERR?")) == ".error.") {
            return ".error."
        }
        if(sscanf(ans,"%d",err) != 1) {
            pi_e712_perr(sprintf("unable to get last error for motor: %s", mne))
            return ".error."
        }
        if(err != 0) {
            pi_e712_perr(sprintf("for motor: %s\n%s\n", mne, pi_gcs_get_error(err)))
            return ".error."
        }

        # optional parameter, should not be needed for E712
        if((t=motor_par(num,"deadtime")) != 0) {
            sleep(t)
        }
    }


    #
    #
    #
    if (key == "get_status") {

        # get the bitmask string
        ans = pi_e712_motputget(num, sprintf("%c",5))
        if(ans == ".error.") { return(ans) }

        # get the bitmask
        if(sscanf(ans,"%d",mask) != 1) {
            pi_e712_perr(sprintf("unable to get status for motor: %s", mne))
            return ".error."
        }

        # moving
        if(mask & (1<<(cha-1))) {
            return(0x02)
        }

        # alles klarr
        return(0)
    }


    #
    # stop a single motor
    #
    if (key == "abort_one") {

        # in fact, stop all motions
        pi_e712_motput(num, sprintf("%c",24))
    }

}'


#%IU%
#%MDESC%
# MACRO MOTOR:
# Called by spec on motor operation.
#
def pi_e712_par(num,key,todo,p1) '{
    local mne
    local ans
    local cha
    local cmd

    # return new motor_par() argins handled
    if (key == "?" && todo == "get") {
        return("servo")
    }

    mne = motor_mne(num)
    cha = motor_par(num, "channel")

    # handle the piezo closed loop
    if (key == "servo") {
        local srv

        if (todo == "set") {
            cmd = sprintf("SVO %d %d",cha,(p1?1:0))
            pi_e712_motput(num, cmd)
        }

        cmd = sprintf("SVO? %d",cha)
        if((ans = pi_e712_motputget(num, cmd)) == ".error.") { return(0) }

        if(sscanf(ans,"%d=%f", cha, srv) != 2) {
            pi_e712_perr(sprintf("unable to get servo for motor: %s", mne))
            return ".error."
        }

        return(srv)
    }
}'





#%IU%(motor_num, cmd)
#%MDESC%
# Send a string command to the controller using the
# interface configured for the motor specified
# and waits for the controller answer.
# Returns an empty string if no error, ".error." otherwise.
#
def pi_e712_motputget(num, cmd) '{
    local ans

    ans = pi_e712_motput(num, cmd)
    if(ans == ".error.") { return(ans) }

    ans = pi_e712_motget(num)
    return(ans)
}'



#%IU%(dev, interface, cmd)
#%MDESC%
# Send a string command to the controller using the
# interface specified
# and waits for the controller answer.
# Returns an empty string if no error, ".error." otherwise.
#
def pi_e712_putget(dev, itf, cmd) '{
    local ans

    ans = pi_e712_put(dev, itf, cmd)
    if(ans == ".error.") { return(ans) }

    ans = pi_e712_get(dev, itf)
    return(ans)
}'



#%IU%(motor_num, cmd)
#%MDESC%
# Send a string command to the controller using the
# interface configured for the motor specified.
# Returns an empty string if no error, ".error." otherwise.
#
def pi_e712_motput(num, cmd) '{
    local dev itf
    local msg


    # no, I am not paranoid
    if(!index(motor_par(num,"device_id"),"e712")) {
        pi_e712_perr("motor not a PI E712")
        return ".error."
    }

    # retrieve information on controller acces
    dev = motor_par(num,"dev")
    itf = motor_par(num,"itf")

    # chaud devant
    ans = pi_e712_put(dev, itf, cmd)

    # normal end
    return(ans)
}'



#%IU%(dev, interface, cmd)
#%MDESC%
# Send a string command to the controller using the
# interface specificied.
# Returns an empty string if no error, ".error." otherwise.
#
def pi_e712_put(dev, itf, cmd) '{
    local msg


    # the command terminator is optional
    msg = cmd
    if(!index(msg,"\n")) {
        msg = msg"\n"
    }

    # chaud devant
    if(itf == "SOCKET") {
        sock_par(dev,"flush")
        sock_put(dev,msg)
    } else if(itf == "SERIAL") {
        ser_par(dev,"flush")
        ser_put(dev,msg)
    } else {
        pi_e712_perr("wrong interface, hint use \"SOCKET\" or \"SERIAL\"")
        return ".error."
    }

    # normal end
    return ""
}'


#%IU%(motor_num)
#%MDESC%
# Waits for a valid string answer from controller using the
# interface configured for the motor specified.
# Returns the string ".error." in case of problem.
#
def pi_e712_motget(num) '{
    local dev itf
    local ans


    # no, I am not paranoid
    if(!index(motor_par(num,"device_id"),"e712")) {
        pi_e712_perr("motor not a PI E712")
        return ".error."
    }

    # retrieve information on controller acces
    dev = motor_par(num,"dev")
    itf = motor_par(num,"itf")

    # allo?
    ans = pi_e712_get(dev,itf)

    # normal end
    return(ans)
}'


#%IU%(dev, interface)
#%MDESC%
# Waits for a valid string answer from controller using the
# interface specified.
# Returns the string ".error." in case of problem.
#
def pi_e712_get(dev, itf) '{
    local ans

    # ask for a report
    HDW_ERR=0

    # allo?
    if(itf == "SOCKET") {
        ans = sock_get(dev,"\n")
    } else if(itf == "SERIAL") {
        ans = ser_get(dev,"\n")
    } else {
        pi_e712_perr("wrong interface, hint use \"SOCKET\" or \"SERIAL\"")
        return ".error."
    }

    # check if timeout
    if(HDW_ERR == 3) {
        pi_e712_perr("timeout waiting for answer")
        return ".error."
    }

    # normal end
    return(ans)
}'


#
#%MACROS%
#%IMACROS%
#%AUTHOR% BLISS MP+RH 2012
#%TOC%