esrf

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

#%TITLE% nhq_mac_hdw.mac.mac
#$Revision: 1.4 $
#%NAME% Macros for a NHQ high quality power supply
#%DESCRIPTION%
# The macros provide users with an interface using macro motors/counters between
# SPEC and a NHQ high quality power supply.
#%INTERNALS%
# You may declare a motor and a counter, even for the same
# device! The macro motor/counter macros can handle that. Possible channel
# numbers are %B%1%B% and %B%2%B%.
#%BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR%
#%SETUP%
#%BR% %BR%Declare a %B%motor\0controller%B%:
#%BR% %BR%
#%PRE%
#MOTORS\0\0\0\0DEVICE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ADDR\0\0<>MODE\0\0NUM\0\0\0\0\0\0\0\0\0\0\0\0\0<>TYPE%BR%
#\0\0\0YES\0\0\0\0\0\0\0NHQ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\04\0\0\0\0\0\0\0\0\0\0\0\03\0\0\0\0\0\0\0Macro Motors
#%PRE%

# %BR%
# Please put the index number of your serial line into the ADDR field (alphanum,
# hit "'" first).
# %BR%
#Then create the macro motor:
#%PRE%
#Number:\0<>Controller\0\0\0\0\0\0\0\0\0\0\00:\0MAC_MOT
#%BR%
#Unit/[Module/]Channel\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00/1
#%BR%
#Name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NHQ1
#%BR%
#Mnemonic\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0nhq1
#%BR%
#Steps per degree/mm\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01\0\0\0<------
#%BR%
#Sign of user * dial\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#%BR%
#Backlash [steps]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00\0\0\0<------
#%BR%
#Steady-state rate [Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\02\0\0\0<------
#^^^^^^^^^^^^^^^^^used as ramping value: between 2 and 255 V/s
#%BR%
#Base rate [Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#%BR%
#Acceleration time [msec]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#%BR%
#Motor accumulator\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00
#%BR%
#Restrictions <>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NONE
#%BR%
#
#%BR%
#Dial = accumulator / steps
#%BR%
#\0\0High limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01000.0000
#%BR%
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#%BR%
#\0\0Low limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0-1000.0000
#%BR%
#User = sign * dial + offset
#%BR%
#\0\0Offset\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#%BR%
#\0\0`High' limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01000.0000
#%BR%
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#%BR%
#\0\0`Low' limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0-1000.0000
#%PRE%
#%BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR% %BR%
# Now hit `m` twice to move to the third motor config screen
#%BR% %BR%
#%PRE%
#\0\0Number:\0<>Controller\0\0\0\0\0\00:\0MAC_MOT
#Unit/[Module/]Channel\0\0\0\0\0\0\0\0\0\0\0\0\0\00/1
#Name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NHQ1
#Mnemonic\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0nhq1
#
#Encoder\0steps\0per\0deg/mm
#Step\0mode\0(0=full,1=half)
#Linear/Rotary\0(1=rotary)
#Disable\0limit\0checks
#Readback\0slop\0[steps]
#Hardware\0read\0mode\0<>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NQ\0\0\0<------------
#%PRE%
#%BR% %BR%Now declare a %B%scaler\0controller%B%:
#%PRE%
#SCALERS\0\0\0\0\0\0\0\0\0DEVICE\0\0\0\0\0\0\0\0\0\0ADDR\0\0<>MODE\0\0NUM\0\0\0\0\0\0\0\0\0\0\0\0\0<>TYPE%BR%
#\0\0\0YES\0\0\0\0\0\0\0\0\0\0\0\0\0NHQ\0\0\0\0\0\0\0\0\0\0\0\0\04\0\0\0\0\0\0\0\0\0\0\0\03\0\0\0\0\0\0Macro Counter
#%PRE%%BR%
#%BR% %BR%
# The %B%counter%B% is then configured as :
#%PRE%
#Number\0\0\0\0\0\0\0\0Name\0\0Mnemonic\0\0<>Device\0\0Unit\0\0Chan\0\0\0<>Use\0As\0\0Scale\0Factor
#\0\0\0\0\04\0\0\0\0\0\0nhq1_c\0\0\0\0nhq1_c\0\0\0MAC_CNT\0\0\0\0\00\0\0\0\0\01\0\0\0\0counter\0\0\0\0\0\0\0\0\0\0\0\0\01
#%PRE%%BR%
#%END%
#%HISTORY%
#$Log: nhq_mac_hdw.mac,v $
#Revision 1.4  2011/02/03 10:56:12  witsch
#move the colored text macros before their first usage.
#
#Added more decriptive text to the steps_mm config value.
#
#.,
#
#Revision 1.3  2010/09/28 11:46:44  witsch
#some minor errors, which had not been seen, testing with the simulation
#device, have been corrected. Testing with a real device.
#
#Revision 1.2  2010/09/22 13:41:59  witsch
#little changes for th simulation.
#
#Revision 1.1  2010/09/20 15:42:58  witsch
#Initial revision
#

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

#%UU%
#%MDESC% toggle debug mode for the present macros.
def nhq_debug '{
    if ((whatis("__nhqdebug")>>16) <= 4) { # just a # sign -> off
        rdef __nhqdebug "eprint"
        print "NHQ debug is ON"
    } else {
        rdef __nhqdebug \'#\$*\' # that are three bytes
        print "NHQ debug is OFF"
    }
}
'

#%UU%
#%MDESC% toggle debug mode for the present macros.
def nhq_simu '{
    if ((whatis("__nhqsimu")>>16) <= 4) { # just a # sign -> off
        rdef __nhqsimu "__nhq_simu_func"
        print "NHQ simu is ON"
    } else {
        rdef __nhqsimu \'#\$*\' # that are three bytes
            print "NHQ simu is OFF"
    }
}
'

def _nhq_put(addr, str) '{
    local _str, _str_len
    __nhqdebug "_nhq_put(" addr ", " str ")"
    _str = str "\r\n"
    _str_len = length(_str)
    __nhqsimu _str ; return(0)
    if (ser_put(addr, _str) != _str_len) {
        tty_cntlred
        eprint "Communication error with NHQ!"
        tty_cntlnotred
        return(".error.")
    }
    # controller echoes input !!!!
    local answer
    answer = _nhq_get(addr)
    # is answer equal to _str
    if (index(answer, _str) != 1) {
        tty_cntlred
        eprint "Communication error with NHQ! Sent and received strings differ!", answer, _str
        eprint length(_str), length(answer)
        tty_cntlnotred
        return(".error.")
    }
}
'

def _nhq_get(addr) '{
    local str
    __nhqsimu ; return(__nhq["ANSWER"])
    str = ser_get(addr, 0)
    __nhqdebug "answer = " str
    return(str)
}
'

# except for the command G[12] every command should yield a number. Numbers are
# like +0022.  Without proper treatment, this value will be interpreted as octal.
# treat the result with a sscanf to make it decimal!
def _nhq_io(addr, str) '{
    local answer, val
    if(_nhq_put(addr, str) == ".error.") {
        tty_cntlred
        eprint "Error in function _nhq_put!"
        tty_cntlnotred
        return(".error.")
    }
    answer = _nhq_get(addr)
    # evaluate contents
    if (sscanf(answer,  "%d", val)) { # most likely a value like +0022
        return(val)
    }
    else if (sscanf(answer,  "%g", val)) { # for cmd I1 could be xeyyy
        return val
    }
    else {
        if (answer == "?WCN\r\n") {
            tty_cntlred
            eprint "Wrong Channel"
            tty_cntlnotred
            return ".error."
        }
        else if(answer == "????\r\n") {
            tty_cntlred
            eprint "Syntax error"
            tty_cntlnotred
            return ".error."
        }
        else if(answer == "?TOT\r\n") {
            tty_cntlred
            eprint "Timeout error"
            tty_cntlnotred
            return ".error."
        }
        else if (index(answer, "? UMAX=")) {
            sscanf(substr(answer, index(answer, "=") +1), "%d", val)
            tty_cntlred
            eprint "Set voltage exceeds voltage limit", val, "!!!"
            tty_cntlnotred
            return ".error."
        }
        return(answer)
    }
}
'

#%IU%(mnum, type, unit, mod, chan)
#%MDESC%
# Called by spec
def NHQ_config(mnum, type, unit, mod, chan) '{
    __nhqdebug "Configuring NHQ", mnum, type, unit, mod, chan
    if ((type == "..") && !whatis("__nhq")) {
        global __nhq[] # used for detection if config has been run.
        ser_par(NHQ_addr, "flush", 2)
        if (ser_put(NHQ_addr, "\r\n") != 2) {
            return ".error."
        }
        return(0)
    }
    if (type == "mot") {
        if ((chan = motor_par(mnum, "channel")) == 0) {
            tty_cntlred
            eprint "Channel number 0 is not possible for motor", \
                motor_mne(mnum)
            tty_cntlnotred
            return ".error."
        }
        NHQ_par(mnum, "velocity", "set", motor_par(mnum, "velocity"))
    }
    else if (type == "cnt") {
        if ((chan = counter_par(mnum, "channel")) == 0) {
            tty_cntlred
            eprint "Channel number 0 is not possible for counter", \
                cnt_mne(mnum)
            tty_cntlnotred
            return ".error."
        }
    }
}
'

#%IU%
#%MDESC%
# Called by spec
def NHQ_cmd(mnum, cmd, p1, p2, p3) '{
    local chan, tension, str, addr
    __nhqdebug "*** NHQ_cmd: mnum", mnum, "cmd", cmd, "p1", p1, "p2", p2, "p3", p3
    if (mnum != "..") {
        if (cmd == "start_one") {
            # start counter, when it is one
            if (p2 == 0) {
                return
            }
            # must be a motor then
            chan = motor_par(mnum, "channel")
            addr = motor_par(mnum, "address")
            # send command "D1=xxx" : Set voltage for channel 1
            str = "D" chan "=" int(p1)
            _nhq_io(addr, str)
            # send command "G1" : Start Voltage change for channel 1
            str = "G" chan
            str = _nhq_io(addr, str)
            __nhqdebug "*** ramp command, answer:", str
        }
        else if ((cmd == "position") || (cmd == "counts")) { # counts is for the counter
            # dirty trick: if p1 is 16, it`s a counter
            if (cmd == "counts") {
                chan = counter_par(mnum, "channel")
                addr = counter_par(mnum, "address")
            } else {
                chan = motor_par(mnum, "channel")
                addr = motor_par(mnum, "address")
            }
            str     = "U" chan
            tension = _nhq_io(addr, str)
            __nhqdebug "tension channel", chan, "is:", tension
            return(tension)
        }
        else if (cmd == "get_status") {
            local aux[]
            chan = motor_par(mnum, "channel")
            addr = motor_par(mnum, "address")
            str  = "S" chan
            str  = _nhq_io(addr, str)
            if (index(str, "H2L\r\n") || index(str, "L2H\r\n")) {
                return(2)
            }
            return(0)
        }
    }
    # Documentation sais synching with a "\r\n" is necessary. The
    # multiple flush commands seem unnecessary.
#    else {
#        if (cmd == "prestart_all") {
#            ser_par(NHQ_ADDR, "flush", 2)
#            __nhqdebug "flushed serial line", NHQ_ADDR
#        }
#    }
}
'

#%IU%
#%MDESC%
# Called by spec
def NHQ_par(mnum, cmd, p1, p2) '{
    if (mnum == ".." ) {
        return
    }
    __nhqdebug "*** NHQ_par",mnum, cmd, p1, p2
    local chan, addr, str
    chan = motor_par(mnum, "channel")
    addr = motor_par(mnum, "address")
    if (cmd == "velocity" && p1 == "set") {
        local str
        str = "V" chan "=" p2
        _nhq_io(addr, str)
    }
}
'

#%UU% mne ramp
#%MDESC%
#    Set ramp (2 to 255 V/s) for motor mne to ramp
def nhq_ramp '{
    local mnum
    if ($# != 2) {
        eprint "Please give motor mnemonic and ramp value as argument!"
        eprint "Ramp value can be 2 to 255 V/s."
        eprint "Exit!"
        exit
    }
    if ((mnum = motor_num($1)) == -1) {
        print "Invalid mnemonic - no such motor :", $1
        exit
    }
    if (motor_par($1, "device_id") != "NHQ") {
        print "This is not a NHQ motor :", $1
        exit
    }
    if (int($2) > 255) }
        print "Value too big. Device will use 255 V/s."
    } else if (int($2) < 2) }
        print "Value too small. Device will use 2 V/s."
    }
    motor_par(mnum, "velocity", $2)
}'

#%UU% mne
#%MDESC%
#    Prints Status, Voltage, Ramp of NHQ for motor mne
def nhq_info '{
    if ($# == 0) {
        eprint "Please give motor mnemonic as argument!"
        eprint "Exit!"
        exit
    }
    local mnum
    if ((mnum = motor_num($1)) == -1) {
        eprint "Invalid mnemonic - no such motor :", $1
        exit
    }
    _nhq_info(mnum)
}'


#%IU% (mnum)
#%MDESC%
# Prints Status, Voltage, Ramp of NHQ (bench -> ~1.8s / channel)
def _nhq_info(mnum) '{
    local str, val, chan, addr
    chan = motor_par(mnum, "channel")
    addr = motor_par(mnum, "address")
    print "- NHQ - ask status of", mnam = motor_name(mnum)
    print "Id (unit nb; software version; U max ; Imax) :", _nhq_id(addr)
    print "SerialLine   :", addr , "    \tChannel       :", chan
    print "Status       :", _nhq_status(addr, chan), "    \tmodule status :", _nhq_mod_status(addr, chan)
    str = "M" chan
    print "Voltage      :", NHQ_cmd(mnum, "position") " V"  "   \tU limit :", _nhq_io(addr, str) "%"
    # do we really need polarity, the value returned has a sign
    #print "Polarity     :", (fabs(A[mnum]) == A[mnum]) ? "positiv" : "negative"
    local curr, currlim, currtrip
    str = "I" chan; curr     = _nhq_io(addr, str)
    str = "N" chan; currlim  = _nhq_io(addr, str)
    str = "L" chan; currtrip = _nhq_io(addr, str)
    print "Current      :", curr " uA"  "   \tI limit : " currlim "%", \
        "     Current Trip :", currtrip
    str = "V" chan
    print "Ramp speed   :", _nhq_io(addr, str), " V/s"
    print ""
}'


#%IU% (mnum)
#%MDESC%
# Returns status of one NHQ channel.   "S" command
def _nhq_status(addr, chan) '{
    local str
    #              "S1"
    str = "S" chan
    if((str = _nhq_io(addr, str)) == ".error.") {
        return ""
    }
    local aux[]
    # before this would be substr(str,4)
    # but there might be simply an "ON\r\n"
    if (split (str, aux, "=") > 1) {
        str = aux[1]
    }
    split(str, aux, "\r")
    return (aux[0])
}'

#%IU% (mnum)
#%MDESC%
#    Returns Module Status of one NHQ module.      "T" command
def _nhq_mod_status(addr, chan) '{
    local str, _s
    #              "T1"
    str = "T" chan
    if((str = _nhq_io(addr, str)) == ".error.") {
        return ""
    }
    __nhqdebug "Module Status:", str
    _s = ""
    if (val >> 8) {
        _s = "QUA"
    }
    if (val >> 7) {
        _s = _s " ERR"
    }
    if (val >> 6) {
        _s = _s " INH"
    }
    if (val >> 5) {
        _s = _s " KILL_ENA"
    }
    if (val >> 4) {
        _s = _s " OFF"
    }
    if (val >> 3) {
        _s = _s " POL"
    }
    if (val >> 2) {
        _s = _s " MAN"
    }
    if (val >> 1) {
        _s = _s " T?"
    }

    return (_s)
}'


#%IU% (mnum)
#%MDESC%
#    Returns ID of one NHQ channel.
def _nhq_id(addr) '{
    return (_nhq_io(addr, "#"))
}'


# some cosmetics
def tty_cntlred '{
    printf("")
}
'

def tty_cntlnotred '{
    printf("")
}
'

# this is an attempt to come around the problem with the macro motor macros
# being loaded at the end of the config/setup, thus not executing the
# NHQ_config function. Variable remain uninitialised. We need a reconfig
if (!whatis("__nhq")) {
    tty_cntlred
    eprint "Make sure to do a reconfig for the NHQ high quality power supply!!!"
    tty_cntlnotred
}

def __nhq_delete_old_macros() '{
    # delete all the old macros to avoid confusion
    x = "nhq_setup nhq_unsetup _nhq_write"
    x = x " nhq_set_voltage _nhq_set_voltage _nhq_get_voltage _nhq_get_voltage_limit"
    x = x " _nhq_get_current _nhq_get_current_limit _nhq_get_current_trip"
    x = x " _nhq_set_ramp _nhq_get_ramp nhq_pseudo_setup nhq_pseudo_unsetup _nhq_getpangles"
    x = x " _nhq_checkall _nhq_getcounts nhq_on nhq_off"

    local y[]
    global isthereone
    split( x, y)
    for (name in y) {
        bla = y[name]
        str = "if (whatis(\"" bla "\") & 2) { undef " bla "; isthereone = 1; }"
        eval(str)
    }
    if (isthereone) {
        eprint "nhq_mac_hdw.mac has deleted all macros contained in the nhq.mac file!"
    }
    unglobal isthereone
}
'
__nhq_delete_old_macros()
undef __nhq_delete_old_macros

def __nhq_simu_func '{
    if ($# == 0) {
        __nhqdebug "__nhq_simu_func: answer", __nhq["ANSWER"]
    }
    else if ($# > 1) {
        tty_cntlred
        eprint "SIMULATION: wrong number of args in __nhq_simu_func!"
        tty_cntlnotred
    }
    else {
        # take the string in $1 apart.
        local cmd, chan, val
        cmd  = substr($1, 1, 1)
        chan = substr($1, 2, 1)
        __nhqdebug "__nhq_simu_func: cmd", cmd, "chan", chan, "\$1", $1
        # store command
        if ((cmd == "U") || (cmd == "S")) {
            local tdiff, tneed
            tdiff = time() - __nhq[chan]["startt"]
            tneed = fabs(__nhq[chan]["oldval"] - __nhq[chan]["val"]) / \
                __nhq[chan]["rampspeed"]
            if (tdiff < tneed) {
                local sign
                sign = (__nhq[chan]["oldval"] < __nhq[chan]["val"]) ? 1 : -1
                val = __nhq[chan]["oldval"] + tdiff * \
                        sign * __nhq[chan]["rampspeed"]
            } else {
                val = __nhq[chan]["val"]
                __nhq[chan]["status"]   = "ON\r\n" # move terminated
            }
            if (cmd == "S") {
                __nhq["ANSWER"]         = __nhq[chan]["status"]
            }
            else {
                __nhq["ANSWER"]         = val + rand(val * .1)
            }
        }
        else if (cmd == "D") {
            local aux[]
            if (split($1, aux, "=") == 2) {
                __nhq[chan]["oldval"]   = __nhq[chan]["val"]
                __nhq[chan]["val"]      = int(aux[1])
                __nhq["ANSWER"]         = ""
            }
            else {
                __nhq["ANSWER"]         = __nhq[chan]["val"]
            }
        }
        else if (cmd == "G") {
            __nhq[chan]["status"]       = "H2L\r\n" # just for moving
            __nhq[chan]["startt"]       = time()
            __nhq["ANSWER"]             = "S" chan "=" __nhq[chan]["status"]
        }
        else if (cmd == "V") { # 2 to 255 V/s
            local aux[]
            if (split($1, aux, "=") == 2) {
                __nhq[chan]["rampspeed"] = int(aux[1])
                __nhq["ANSWER"]        = ""
            }
            else {
                __nhq["ANSWER"]        = __nhq[chan]["rampspeed"]
            }
        }
        else {
            __nhq["ANSWER"]        = ""
        }

    }
}
'


#%MACROS%
#%IMACROS%
#%AUTHOR%
#  nhq_mac_hdw.MAC  HW 9/2010
#  %BR%derived from nhq.mac
#%TOC%