esrf

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

#%TITLE% pi_e753.mac $Revision: 1.14 $
#%NAME%
# Macros to control a piezo PI E753 controller connected via TCP/IP.
#
#%DESCRIPTION%
# the PI E-753 is a single channel controller for piezo actuator.
#
#
#  For debug purpose mainly, it is also possible to configure a macro
#  counter that will read the position of a piezo axis. But to avoid
#  duplicating parameters, the counter has no parameter and takes them
#  from its corresponding macro motor. In order to guess the motor
#  to use, the name of the counter must be the same as the motor with
#  %B%\"_c\"%B% added at the end.
#  For instance: %B%\"nnn_x_c\"%B% for %B%\"nnn_x\"%B%
#
#%END%
#
#%SETUP%
#  In order to configure a macro motor:
#  %DL%
#    %DT% 1)
#         You must have a "MOTORS" defined with:
#         %BR%
#         %DL%
#           %DT% DEVICE
#           set to "pi_e753"
#           %DT% TYPE
#           set to "Macro Motor"
#           %DT% ADDR
#           set to the TCP Address of the controller
#           %DT% NUM
#           must be set to 1
#         %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 0 the piezo axis of the controller
#         %XDL%
#  %XDL%
#
#  In order to configure a macro counter:
#  %DL%
#    %DT% 1)
#         You must have a "SCALERS" defined with:
#         %BR%
#         %DL%
#           %DT% DEVICE
#           set to "pi_e753_c"
#           %DT% TYPE
#           set to "Macro Counter"
#           %DT% NUM
#           must be set also
#         %XDL%
#    %DT% 2)
#         Per piezo axis you must define a counter with:
#         %BR%
#         %DL%
#           %DT% Device
#           set to "MAC_CNT"
#           %DT% Mnemonic
#           must the motor mne plus "_c"
#           %DT% Unit
#           do not mind
#           %DT% Chan
#           do not mind
#         %XDL%
#
#  %XDL%


#%END%
#
#%HISTORY%
#$Log: pi_e753.mac,v $
#Revision 1.14  2020/02/28 09:26:11  witsch
#function pi_e753_get_model finding the substring E-753 in the IDN string, 
#was done with substr. That failed, when the controller changed the return
#string. Now, it is using index(), which will find it, if it there.
#..
#
#Revision 1.13  2018/09/27 09:25:40  mauro
#Correct the message connection with the address during the reconfig
#In the info macro TMX and TMN were inverted during the print screen - Corrected
#
#Revision 1.12  2017/03/23 10:17:51  perez
#Add waveform generation macros
#
#Revision 1.11  2017/03/20 14:57:38  guilloud
#fix bug in argument testing in info fuinction
#
#Revision 1.10  2014/10/24 14:47:46  guilloud
#error number for TAD command (broken cable ???)
#
#Revision 1.9  2014/02/13 16:50:51  guilloud
#some debugs...
#
#Revision 1.8  2014/02/04 09:29:07  guilloud
#*added traces for tcp_put calls
#*+pi_e753_get_real_pos(mot_num) position given by the capacitive captor
#*+i_e753_set_open_loop_pos(mot_num, position) ends "SVA 1 <position>"
#*Fixed "pz1" hardcoded bug in pi_e753_stop(mot_num)
#*more parameters in info
#*+ set velocity according to spec config value
#*open loop mode available
#
#Revision 1.7  2013/09/25 08:08:01  guilloud
#*need hg
#*err test on sock_put
#*fix if (pi_e753_get_closed_loop_status(mot_num) == 1){
#*refine config message
#*add closed loop status display in config
#
#Revision 1.6  2013/09/11 14:16:57  guilloud
#change messages for HG.mac
#simplify some messages
#removed PI_E753[idx]["model"]
#
#Revision 1.5  2013/08/30 16:57:58  homsrego
#fix macro counter
#
#Revision 1.4  2013/08/29 12:10:40  homsrego
#fix print error
#
#Revision 1.3  2013/07/11 14:36:53  guilloud
#* removed sleeps
#* use setpoint rather than real position in "get_pos"
#* status test ONT only in closed loop mode now
#* infos macro improved for useful parameters display (and descriptions)
#* Auto zero calibration procedure removed (useless if controller is well configured)
#
#Revision 1.2  2012/07/26 14:05:20  guilloud
#error codes
#
#Revision 1.1  2012/07/26 13:59:07  guilloud
#Initial revision
#
#
#%END%
#

need hg
need spec_utils
need pi_gcs_errorcodes

global PI_E753[]

######################################################################
###########################                ###########################
###########################  DEBUG & INFO  ###########################
###########################                ###########################
######################################################################

hg_config_style("pi_e753", 0, 0, 0)
hg_config_indent("pi_e753",  0, 0, 1)
hg_config_headers("pi_e753", "PI_E753", "PI_E753", "PI_E753")
hg_generate("pi_e753")



######################################################################
###########################                 ##########################
###########################  COMMUNICATION  ##########################
###########################                 ##########################
######################################################################


#%IU%(<tcp_dev>, <cmd>)
#%MDESC%
#    Sends <cmd> to a tcp device.
# Adds \n at end of <cmd>
# Uses tcp/ip device : <tcp_dev>.
def pi_e753_tcp_put(tcp_dev, cmd, from) '{
    local _ans
    local _cmd  _cmd_woec

    # Purges <cmd>.
    # woec : without ending char...
    _cmd_woec = removeEndingChar(cmd, "\n")
    _cmd_woec = removeEndingChar(_cmd_woec, "\r")

    # Adds terminators.
    _cmd = sprintf("%s\n", _cmd_woec)

    # Flush ?
    #sock_par(tcp_dev, "flush")
    #sock_par(tcp_dev, "timeout", 3)

    pi_e753_dbg(sprintf("tcp_put():\ntcp_dev=%s\ncmd=\"%s\"\nfrom=%s",    \
                        tcp_dev, _cmd_woec, from))

    if(sock_put(tcp_dev, _cmd) == -1){
        pi_e753_err(sprintf("sock_put error in pi_e753_tcp_put(tcp_dev=%s, _cmd=%s) from=%s", \
                            tcp_dev, _cmd, from))
    }
}'


#%IU%(<tcp_dev>)
#%MDESC%
#    Returns ???
def pi_e753_tcp_get(tcp_dev) '{
    local _ans

    _ans = sock_get(tcp_dev)
    return(_ans)
}'


#%IU%(<mot_num>, <cmd>)
#%MDESC%
#    Send <cmd> to controller of motor <mot_num> and returns the answer.
# <mot_num> : SPEC motor number.
# <cmd> : String with or without terminator.
def pi_e753_send(mot_num, cmd, from) '{
    local _tcp_dev

    _tcp_dev = motor_par(mot_num, "tcp_dev")
    # re printed in pi_e753_tcp_put pi_e753_dbg(sprintf("_tcp_dev = %s", _tcp_dev))

    if(_tcp_dev == "0"){
        pi_e753_err( " pi_e753_send() :  bad motor name or undefined motor (_tcp_dev == \"0\")")
        return( -1)
    }

    sock_par(_tcp_dev, "flush")

    pi_e753_dbg(sprintf("SEND \"%s\" to %s (from=%s)",cmd, _tcp_dev, from))

    if(pi_e753_tcp_put(_tcp_dev, cmd, 214) == -1){
        return( -1)
    }

    if ((_ans = pi_e753_tcp_get(_tcp_dev)) == -1){
        return( -1)
    }
    else{
        local _ans_woec
        _ans_woec = removeEndingChar(_ans, "\n")
        _ans_woec = removeEndingChar(_ans_woec, "\r")

        pi_e753_dbg(sprintf("GET \"%s\" from %s ",  _ans_woec, _tcp_dev))

        _ans = removeEndingChar(_ans, "\n")
        return(_ans)
    }
}'


#%IU%(<mot_num>, <cmd>)
#%MDESC%
#    Sends <cmd> to controller corresponding to motor <mot_num> do not
# wait for an answer.
def pi_e753_send_no_ans(mot_num, cmd, from) '{

    local _tcp_dev

    _tcp_dev = motor_par(mot_num, "tcp_dev")

    pi_e753_dbg(sprintf("_tcp_dev = %s", _tcp_dev))

    sock_par(_tcp_dev, "flush")

    pi_e753_dbg(sprintf("SEND %s to %s (from=%s)", cmd, _tcp_dev, from))

    if(pi_e753_tcp_put(_tcp_dev, cmd, 246) == -1){
        return( -1)
    }
}'



#######################################################################
#######################                         #######################
#######################  CONTROLLER MANAGEMENT  #######################
#######################                         #######################
#######################################################################


#%IU%(<mot_num>)
#%MDESC%
#    Returns the setpoint read from the controller.
#
def pi_e753_get_pos(mot_num) '{
    local _ans  _pos _cmd  _mne

    _mne  = motor_mne(mot_num)

    # _cmd = sprintf("POS?")  <--- position read by sensor
    # We prefer to read the set point value.
    _cmd = sprintf("MOV?")

    _ans = pi_e753_send(mot_num, _cmd)
    # _ans should looks like "1=-8.45709419e+01\n"
    _ans = removeEndingChar(_ans , "\n")
    _pos = substr(_ans, 3) + 0.0
    pi_e753_dbg( sprintf("in pi_e753_get_pos  _ans=%s  _pos=%s", _ans, _pos))

    return (_pos)
}'

#%IU%(<mot_num>)
#%MDESC%
#    Returns the position given by the capacitive captor.
#
def pi_e753_get_real_pos(mot_num) '{
    local _ans  _pos _cmd  _mne

    _mne  = motor_mne(mot_num)

    # position read by sensor
    _cmd = sprintf("POS?")

    _ans = pi_e753_send(mot_num, _cmd)
    # _ans should looks like "1=-8.45709419e+01\n"
    _ans = removeEndingChar(_ans , "\n")
    _pos = substr(_ans, 3) + 0.0
    pi_e753_dbg( sprintf("in pi_e753_get_real_pos  _ans=%s  _pos=%s", _ans, _pos))

    return (_pos)
}'



#%IU%(<mot_num>, <position>)
#%MDESC%
#    Sends to controller of axis corresponding to <mot_num> a
# move to <position> command.
# Sends "MOV 1 <position>"
def pi_e753_set_pos(mot_num, position) '{
    local _mne  _cmd

    pi_e753_dbg( sprintf("in pi_e753_set_pos mot_num=%s position=%s ", mot_num, position))

    _cmd = sprintf("MOV 1 %g", position)

    pi_e753_send_no_ans(mot_num, _cmd)

    # return( ?)
}'



#%IU%(<mot_num>, <position>)
#%MDESC%
#    Sends to controller of axis corresponding to <mot_num> a
# move to <position> command.
# Sends "SVA 1 <position>"
def pi_e753_set_open_loop_pos(mot_num, position) '{
    local _mne  _cmd

    pi_e753_dbg( sprintf("in pi_e753_set_open_loop_pos mot_num=%s position=%s ", mot_num, position))

    _cmd = sprintf("SVA 1 %g", position)

    pi_e753_send_no_ans(mot_num, _cmd)

    # return( ?)
}'



#
#
#
def test_status '{

    local ii
    printf("MOTION STATUS  |   ONT STATUS")

    for (ii=0 ; ii<30; ii++){
        printf("%s           |   %s  ",  \
               pi_e753_get_motion_status(pz),  \
               pi_e753_get_ont_status(pz)      \
            )
        print ""
        sleep(0.01)
    }
}'


#%IU%(<mot_num>)
#%MDESC%
#    Returns the error code read from the controller.
# ERR? command clean the last error code seen.
#
def pi_e753_get_err(mot_num) '{
    local _ans  _cmd  _mne _error _error_str

    pi_e753_dbg( sprintf("in pi_e753_get_err mot : %s(%s)",     \
                         motor_mne(mot_num), mot_num))

    _mne  = motor_mne(mot_num)

    _cmd = sprintf("ERR?")
    _ans = pi_e753_send(mot_num, _cmd)

    _error = removeEndingChar(_ans , "\n")
    _error_str = pi_gcs_get_error(_error)

    pi_e753_dbg(  sprintf("in pi_e753_get_err _error=%s", _error))
    pi_e753_dbg(  sprintf("in pi_e753_get_err _error_str =%s", _error_str))

    return (_error)
}'




######################################################################
##############################          ##############################
##############################  STATUS  ##############################
##############################          ##############################
######################################################################



#%IU%(<mot_num>)
#%MDESC%
#
#
def pi_e753_get_status(mot_num) '{
    local _ans  _pos    _status

    pi_e753_dbg(sprintf( "in pi_e753_get_status  mot_num=%s ", mot_num))

    # First, checks if closed-loop is active.
    if (pi_e753_get_closed_loop_status(mot_num) == 1){
        pi_e753_dbg( "closed loop active")

        # get ON TARGET status : only usable in closed loop mode.
        _ans = pi_e753_get_ont_status(mot_num)


    }
    else{
        # not in closed loop => ON_TARGET status not usable.
        pi_e753_dbg( "closed loop disabled")
        return(1)
    }


    return(_status)
}'


#%IU%(mot_num)
#%MDESC%
#    Returns 1 if closed loop is active
#    Returns 0 if closed loop is NOT active
#    Returns -1 otherwise
def pi_e753_get_closed_loop_status(mot_num) '{
    local  _ans

    pi_e753_dbg(sprintf("in pi_e753_get_closed_loop_status mot_num=%s", mot_num))

    _ans = removeEndingChar(pi_e753_send(mot_num, "SVO?"))

    if ( _ans == "1=1" ){
        # closed loop active
        return(1)
    }
    else if(_ans == "1=0"){
        # closed loop NOT active
        return(0)
    }
    else{
        print "ERROR in pi_e753_get_closed_loop_status"
        return(-1)
    }

}'


#%IU%(<mot_num>)
#%MDESC%
#    Returns the individual "on-target status" of axis corresponding
# to motor <mot_num>
# Sends "ONT?
def pi_e753_get_ont_status(mot_num) '{
    local _ans  _pos    _status

    pi_e753_dbg( "in pi_e753_get_ont_status ")


    _ans = pi_e753_send(mot_num, "ONT?")
    _ans = removeEndingChar(_ans , "\n")
    pi_e753_dbg(sprintf( "in pi_e753_get_status _ans=%s", _ans))

    _chan = substr(_ans, 1, 1)

    _status = substr(_ans, 3, 1)
    return(_status)
}'


#%IU%(<mot_num>)
#%MDESC%
#    Returns the "motion status" of axis corresponding
# to motor <mot_num>
# Sends "#5"
def pi_e753_get_motion_status(mot_num) '{
    local _ans  _pos    _status

    pi_e753_dbg( "in pi_e753_get_motion_status " )

    _ans = pi_e753_send(mot_num, "#5")
    _ans = removeEndingChar(_ans , "\n")
    pi_e753_dbg( sprintf("in pi_e753_get_motion_status _ans=%s", _ans))

    #_chan = substr(_ans, 1, 1)
    _status = _ans
    return(_status)
}'


#%IU%(<mot_num>)
#%MDESC%
#    Sends to controller of motor <mot_num> a stop command.
# Sends "STP"   (no HLT in e-753...)
#
def pi_e753_stop(mot_num) '{

    pi_e753_dbg( "in pi_e753_stop " )

    pi_e753_send_no_ans(mot_num, "STP")

    # return( ?)
}'


# ERROR CODES SEEN :
#
#  1 : Syntax error
#  2 : Unknown command
#  5 : Unallowable move attempted on unreferenced axis,
#      or move attempted with servo off
#  7 : position out of limits
# 10 : controller was stopped by command
# 17 : parameter out of range
# 24 : wrong number of parameters
# 88 : ??? not found in manual...


#%UU% <mot_num>
#%MDESC%
#
def pi_e753_infos '{
    local _mot_num
    local _sys_ans
    local _tmp_arr[]
    local _ip_addr[]
    local _err

    if ($# == 0){
        print "pi_e753_infos : please give a motor number"
        exit
    }

    _mot_num = motor_num("$1")
    pi_e753_dbg(sprintf("_mot_num=%s",  _mot_num))
    print date()
    printf(" %25s   IDN?: %s\n", "Controller Identification", pi_e753_send(_mot_num, "IDN?"))
    printf(" %25s   IFC?: %s\n", "Communication parameters",  removeChars(pi_e753_send(_mot_num, "IFC?"), "\n"))
    printf(" %25s   ERR?: %s\n", "Error code",                _err = pi_e753_send(_mot_num, "ERR?"))
    printf(" %25s       : %s\n", "Error string",              pi_gcs_get_error(_err))

    # Parsing of IFC answer to get ip address and hostname
    split(pi_e753_send(_mot_num, "IFC?"), _tmp_arr, "\n")
    split( substr(_tmp_arr[1], 7), _ip_addr, ":")
    printf(" %25s       : %s\n", "ip address", _ip_addr[0])
    unix (sprintf("host %s | cut -d\" \" -f5", _ip_addr[0]), _hostname)
    printf(" %25s       : %s","hostname", _hostname)

    printf(" %25s   CCL?: %s\n", "command level",       pi_e753_send(_mot_num, "CCL?"))
    printf(" %25s   POS?: %s\n", "Real Position",       removeChars(pi_e753_send(_mot_num, "POS?"), "\n"))
    printf(" %25s   MOV?: %s\n", "Setpoint Position",   removeChars(pi_e753_send(_mot_num, "MOV?"), "\n") )
    printf(" %25s POS?-MOV?: %s\n", "SP RP diff",       pi_e753_get_real_pos(_mot_num) - pi_e753_get_pos(_mot_num)  )
    printf(" %25s   TMN?: %s\n", "Position low limit",  pi_e753_send(_mot_num, "SPA? 1 0x07000000"))
    printf(" %25s   TMX?: %s\n", "Position High limit", pi_e753_send(_mot_num, "SPA? 1 0x07000001"))

    printf(" %25s   TAD?: %s\n", "ADC value of analog input", removeChars(pi_e753_send(_mot_num, "TAD?"), "\n"))

####  TAD[1]==131071  => broken cable ??
# 131071 = pow(2,17)-1

    printf(" %25s   VEL?: %s\n", "Velocity?",           removeChars(pi_e753_send(_mot_num, "VEL?"), "\n"))
    printf(" %25s   ONT?: %s\n", "On target",           removeChars(pi_e753_send(_mot_num, "ONT?"), "\n"))
    printf(" %25s       : %s\n", "target tolerance",    pi_e753_send(_mot_num, "SPA? 1 0X07000900"))
    printf(" %25s       : %s\n", "settling time",       pi_e753_send(_mot_num, "SPA? 1 0X07000901"))
    printf(" %25s       : %s\n", "Sensor Offset",       pi_e753_send(_mot_num, "SPA? 1 0x02000200"))
    printf(" %25s       : %s\n", "Sensor Gain",         pi_e753_send(_mot_num, "SPA? 1 0x02000300"))
    printf(" %25s     #5: %s\n", "Motion status",       removeChars(pi_e753_send(_mot_num, "#5"), "\n"))
    printf(" %25s   SVO?: %s\n", "Closed loop status",  removeChars(pi_e753_send(_mot_num, "SVO?"), "\n"))
    printf(" %25s   SVA?: %s\n", "Open loop setpoint",  removeChars(pi_e753_send(_mot_num, "SVA?"), "\n"))
    printf(" %25s   TSP?: %s\n", "Analog Setpoints",    removeChars(pi_e753_send(_mot_num, "TSP?"), "\n"))
    printf(" %25s   AOS?: %s\n", "Analog Input Offset", removeChars(pi_e753_send(_mot_num, "AOS?"), "\n"))
    printf(" %25s   ATZ?: %s\n", "Auto Zero Calibration ?", removeChars(pi_e753_send(_mot_num, "ATZ?"), "\n"))
    printf(" %25s       : %s\n", "Low  Voltage Limit", removeChars(pi_e753_send(_mot_num, "SPA? 1 0x07000A00"), "\n"))
    printf(" %25s       : %s\n", "High Voltage Limit", removeChars(pi_e753_send(_mot_num, "SPA? 1 0x07000A01"), "\n"))

}'



#%UU%(<tcp_dev>)
#%MDESC%
#    Returns the controller model name (should be E-753)
def pi_e753_get_model(tcp_dev) '{
    local _model  _idn  _ans

    sock_par(tcp_dev, "flush")
    _ans = pi_e753_tcp_put(tcp_dev, "IDN?\n", 589)
    _idn = pi_e753_tcp_get(tcp_dev)

    pi_e753_dbg( sprintf("IDN=%s",_idn))

    _model = index(_idn, "E-753")

    if (_model) {
        return("E-753")
    }
    else {
        return("UNKNOWN")
    }
}'


#%IU%
#%MDESC%
#    Checks if controller is responding
# Sends "IDN?"
def pi_e753_check_ctrl(mot_num) '{
    local _tcp_dev

    _tcp_dev = motor_par(mot_num, "tcp_dev")

    if(pi_e753_tcp_put(_tcp_dev, "IDN?", 614) == -1){
        return (-1)
    }

    if ((_ans = pi_e753_tcp_get(_tcp_dev)) == -1){
        return (-1)
    }
    else{
        print _ans
    }

    return (1)
}'



######################################################################
############################               ###########################
############################  MACRO MOTOR  ###########################
############################               ###########################
######################################################################

#%IU%()
#%MDESC%
#    Called by spec after reading the config file
#
def pi_e753_config(mot_num, type, p1, p2, p3) '{
    local _msg

    if(type=="ctrl") {
        local _tcp_dev _model

        # p1==controller number (0..N)
        # p2==number of motors supported (1..M) (should be 1 for e753)

        if (p2 != 1){
            pi_e753_err("Wrong number of motors in config (must be 1)")
        }

        # Gets address from SPEC config.
        _tcp_dev = sprintf("%s:50000", pi_e753_ADDR)
        pi_e753_dbg( sprintf("pi_e753_config(%s, %s, %s) _tcp_dev = %s", \
                             mot_num, type, p1, _tcp_dev))

        # Opens a socket to the specified host and port : _tcp_dev
        # Returns true (1) for success and false (0) for failure.
        sock_par(_tcp_dev, "close")

        if (sock_par(_tcp_dev, "connect") == 1){
            _model = pi_e753_get_model(_tcp_dev)

            # Just checks if controller is really a 753.
            if (_model == "E-753"){
                printf("[PI_E753] Using controller piezo PI ")
                cprint(_model, 4, 8)
                printf("  address = %s\n", pi_e753_ADDR)
            }
            else{
                pi_e753_err( sprintf("WRONG MODEL : %s ", _model))
        return ".error."
            }
        }
        else{
            local _cmd
            _cmd = sprintf("Controller piezo E753 on address %s is not responding", \
                           _tcp_dev)
            pi_e753_err(_cmd)

            # -> will not configure motors attached to this controller.
            return (".error.")
        }
    }
    else if(type=="mot") {
        # p1==unit p2==module p3==channel
        local _tcp_dev
        local _mot_mne
        local _velocity

        # print "-------------------ctrl-------------------------"
        # print pi_e753_CONPAR
        # print "addr= " pi_e753_ADDR
        # print "--------------------------------------------"

        _tcp_dev = sprintf("%s:50000", pi_e753_ADDR)

        _mot_mne = motor_mne(mot_num)

        pi_e753_dbg(sprintf("pi_e753_config(),_tcp_dev=%s ",_tcp_dev))

        # Saves tcp dev in a custom parameter.
        pi_e753_dbg(sprintf("_tcp_dev saved in custom param : %s", _tcp_dev))
        motor_par(mot_num, "tcp_dev", _tcp_dev, "add")

        if (_err = pi_e753_get_err(mot_num) != 0 ) {
            print ""
            pi_e753_err(sprintf("ERR : %s", _err))
            print ""
        }

        # pi_e753_infos mot_num

#         _ans =  pi_e753_send(mot_num, "ATZ?")
#         print ""
#         sleep(0.1)
#         if (_ans == "1=0"){
#             print "auto zero calibration not done"
#             ### no need to do an Auto zero calibration if the controller is well configured.
#         }

        # Enables SERVO (Closed-loop) mode:
        pi_e753_send_no_ans(mot_num, "SVO 1 1")

        # Displays status of closed loop.
        printf("[PI_E753] piezo motor \"%s\" : closed loop is ",_mot_mne)
        cprint_on_off( pi_e753_get_closed_loop_status(mot_num))
        print ""

        # Set velocity
        _velocity = motor_par(mot_num, "velocity")
        printf("[PI_E753] piezo motor \"%s\" : velocity set to %d\n", \
            _mot_mne, _velocity)
        pi_e753_send_no_ans(mot_num, sprintf("VEL 1 %s", _velocity))

    }
    else {
        pi_e753_err(sprintf("pi_e753_config() : unknown type : %s ", type ))
    }
}'


#%IU%()
#%MDESC%
# Called by spec on motor operation.
def pi_e753_par(mot_num, key, todo, p1) '{
    local _srv  _cmd  _ans  _mne

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

    _mne = motor_mne(mot_num)

    # Handles the piezo closed loop activation.
    if (key == "servo") {
        local _srv

        if (todo == "set") {
            _cmd = sprintf("SVO 1 %d",(p1?1:0))
            pi_e753_send_no_ans(mot_num, _cmd)
        }

        # Checks if CL well enabled.
        # ??? pk ca pourrai valoir ".error." ????
        if ((_ans = pi_e753_get_closed_loop_status(mot_num)) == ".error."){
            return(0)
        }
        else{
            return(_ans)
        }
    }

}'



#%IU%()
#%MDESC%
# MACRO MOTOR:
# Called by spec on motor operation.
#
def pi_e753_cmd(mot_num, key, p1, p2) '{
    local _mne

    if(mot_num == "..") {
        return
    }

    _mne     = motor_mne(mot_num)
    _tcp_dev = motor_par(mot_num, "tcp_dev")

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

        pi_e753_dbg( sprintf("position() : pos    :  %s micron", pos))
        return(pos)
    }


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

        pi_e753_dbg(sprintf( "start_one(): pos     :  %s micron", p1))


        if (pi_e753_get_closed_loop_status(mot_num)){
            pi_e753_set_pos(mot_num, p1)
        }
        else{
            pi_e753_set_open_loop_pos(mot_num, p1)
        }

        # ???
        if((t=motor_par(mot_num, "deadtime")) != 0) {
            pi_e753_dbg( sprintf("start_one(): sleeping:  %s seconds", t))
            sleep(t)
        }
    }

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

        # First check if closed-loop is active.
        if (pi_e753_get_closed_loop_status(mot_num)){

            _pos = pi_e753_get_real_pos(mot_num)
            pi_e753_dbg(sprintf("_pos in get_status (mne=%s) :%s \n",_mne, _pos))

            # ON TARGET status : only usable in closed loop mode.
            _status = pi_e753_get_ont_status(mot_num)
        }
        else{
            if (pi_e753_get_motion_status(mot_num) == 0){
                # no motion
                _status = 1
            }
        }

        if (_status == 0) {
            # moving.
            return (0x02)
        }
        else {
            # on-target or movement finished.
            return(0)
        }
    }

    # Stops a single motor.
    if (key == "abort_one") {
        pi_e753_dbg( "pi_e753_cmd abort_one()")
        pi_e753_stop(mot_num)

        return 1
    }
}'



######################################################################
###########################                 ##########################
###########################  MACRO COUNTER  ##########################
###########################                 ##########################
######################################################################

#%IU%()
#%MDESC%
# Called by spec after reading the config file
#
def pi_e753_c_config(num,type,p1,p2,p3) '{

    #
    if(type=="ctrl") {
        # ?
    }

    #
    if(type=="cnt") {
        local mot_mne mot_num cmne suffix, l, mmne
        cmne = cnt_mne(num)
        l = length(cmne)
        suffix = substr(cmne, l-1)

        if(suffix != "_c") {
            pi_e753_err(sprintf("\nE753 ERROR invalid counter [%s] (bad suffix [%s])\n", \
                                cmne, suffix))
            return ".error."
        }

        mmne = substr(cmne, 1, l-2)
        mot_num = motor_num(mmne)
        if(mot_num == -1) {
            pi_e753_err(sprintf("\nE753 ERROR invalid counter [%s] (invalid motor [%s])\n", \
                                cmne, mmne))
             return ".error."
        }

        #_msg = "motor num = " mot_num
        #cprint(_msg , 1)
        PI_E753[num]["cnt/mot_num"]= mot_num
    }
}'


#%IU%()
#%MDESC%
# Called by spec on counter operation.
#
def pi_e753_c_cmd(num,key,p1,p2) '{

    if (key == "counts") {
        local pos
        local mot_num
        local mne

        mne = cnt_mne(num)

        mot_num = PI_E753[num]["cnt/mot_num"]

        # print "           " mot_num

        # pos = pi_e753_get_pos(mot_num)
        pos = pi_e753_get_real_pos(mot_num) # <--- we want the real position

        return(pos)
    }
}'

######################################################################
###########################                 ##########################
###########################    WAVEFORM     ##########################
###########################                 ##########################
######################################################################

#%UU% <motor> <hz> <amplitude>
#%MDESC%
#   Start waveform generation on given motor.
#%BR%<motor>  : SPEC motor.
#%BR%<hz>     : frequency of the wave in Hz.
#%BR%<ampl>   : wave amplitude in microns.
#
def pi_e753_wav_start '{
    local mot_num
    local hz
    local ampl

    if($# < 3) {
        print "Usage: $0 motor hz amplitude"
        exit
    }

    if((mot_num = motor_num("$1")) == -1) {
        exit
    }

    hz   = $2
    ampl = $3

    _pi_e753_wav_start(mot_num, hz, ampl)
    pi_e753_wav_status $1

}'


#%IU%(<mot_num>, <hz>, <ampl>)
#%MDESC%
#   Start waveform generation on motor <mot_num>.
#%BR%<mot_num>: SPEC motor number.
#%BR%<hz>     : frequency of the wave in Hz.
#%BR%<ampl>   : wave amplitude in microns.
#
def _pi_e753_wav_start(mot_num, hz, ampl) '{
    local _cmd
    local _ans
    local _tt[]
    local _stime _wtr _npts

    # minimum checks
    if(!index(motor_par(mot_num, "device_id"), "e753")) {
        pi_e753_err("Not a PI E753 macro motor")
        return(-1)
    }
    # TODO: add some limits on values
    if(hz == 0) {
        pi_e753_err("Wrong frequency in Hz")
        return(-1)
    }
    # TODO: add some limits on values
    if(ampl == 0) {
        pi_e753_err("Wrong amplitude in microns")
        return(-1)
    }


    # get the servo update time
    _ans = removeEndingChar(pi_e753_send(mot_num, "SPA? 1 0x0e000200"))
    if(split(_ans, _tt, "=") != 2) {
        pi_e753_err("Unable to get servo update time")
        return(-1)
    }
    if(sscanf(_tt[1], "%f", _stime) != 1) {
        pi_e753_err("Unable to parse servo update time")
        return(-1)
    }
    pi_e753_dbg(sprintf("Servo update time: %fusec", _stime*1e6))


    # get wave table rate
    _ans = removeEndingChar(pi_e753_send(mot_num, "SPA? 1 0x13000109"))
    if(split(_ans, _tt, "=") != 2) {
        pi_e753_err("Unable to get WTR")
        return(-1)
    }
    if(sscanf(_tt[1], "%d", _wtr) != 1) {
         pi_e753_err("Unable to parse WTR")
        return(-1)
    }
    pi_e753_dbg(sprintf("Wave table rate  : %d", _wtr))


    # calculate the number of points to get the wanted period
    _npts = int(1 / (_wtr * _stime * hz))
    # TODO: add some limits on values
    pi_e753_dbg(sprintf("Wave total points: %d", _npts))


    # use first wave table and overwrite it
    _cmd = sprintf("WAV 1 X")
    # generate an inverted cosine curve
    _cmd = sprintf("%s SIN_P", _cmd)
    # wave length in points
    _cmd = sprintf("%s %d", _cmd, _npts)
    # wave amplitude
    _cmd = sprintf("%s %d", _cmd, ampl)
    # wave offset
    _cmd = sprintf("%s 0", _cmd)
    # wave length
    _cmd = sprintf("%s %d", _cmd, _npts)
    # wave start point
    _cmd = sprintf("%s 0", _cmd)
    # wave center
    _cmd = sprintf("%s %d", _cmd, int(_npts/2))

    pi_e753_dbg(sprintf("Waveform command : %s\n", _cmd))
    pi_e753_send_no_ans(mot_num, _cmd)
    if (_err = pi_e753_get_err(mot_num) != 0 ) {
        pi_e753_err(sprintf("ERR : %s", _err))
        return(-1)
    }


    # connect wave table #1 to wave generator #1
    pi_e753_send_no_ans(mot_num, "WSL 1 1")

    # launch wave generation
    _pi_e753_wav_action(mot_num, 1)
}'

#%UU% <motor>
#%MDESC%
#   Print out the waveform status for given motor.
#
def pi_e753_wav_stop '{
    if($# != 1) {
        print "Usage: $0 motor"
        exit
    }

    _pi_e753_wav_stop(motor_num("$1"))
    pi_e753_wav_status $1
}'

#%IU%(<mot_num>)
#%MDESC%
#   Stop waveform generation on motor <mot_num>.
#   Returns the state of waveform generator.
#
def _pi_e753_wav_stop(mot_num) '{
    local _ret

    _ret = _pi_e753_wav_action(mot_num, 0)

    # position discrepancies between spec and the motor hardware will
    #be silently resolved in favor of the hardware
    read_motors(0x06)

    return(_ret)
}'

#%IU%(<mot_num>, <action>)
#%MDESC%
#   Starts or stop waveform generation on motor <mot_num>.
#   Returns the state of waveform generator.
#
def _pi_e753_wav_action(mot_num, todo) '{

    # NOTE: the E753 has only one waveform generator
    pi_e753_send_no_ans(mot_num, sprintf("WGO 1 %d", (todo!=0)?1:0))

    return(_pi_e753_wav_status(mot_num))
}'

#%UU% <motor>
#%MDESC%
#   Print out the waveform status for given motor.
#
def pi_e753_wav_status '{
    if($# != 1) {
        print "Usage: $0 motor"
        exit
    }

    printf("Motor \"%s\" waveform generator is: %s\n", \
        "$1", \
        _pi_e753_wav_status(motor_num("$1"))?"ON":"OFF")
}'


#%IU%(<mot_num>)
#%MDESC%
#   Returns non null if a waveform is beeing generated on motor <mot_num>.
#
def _pi_e753_wav_status(mot_num) '{
    local ans
    _ans = removeEndingChar(pi_e753_send(mot_num, "WGO?"))
    return(!(_ans == "1=0"))
}'



#%MACROS%
#%IMACROS%
#%AUTHOR% BLISS 2006
#%BR%$Revision: 1.14 $  /  $Date: 2020/02/28 09:26:11 $
#%BR%$RCSfile: pi_e753.mac,v $
#%TOC%