esrf

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

#%TITLE% PEPU.MAC 
#
#%NAME%
# Macros to control ESRF PePU device over ethernet
#
#%DESCRIPTION%
#%DL%
#%DT%Configuring counters %DD%
#
# Implements macro counters to read encoder outputs and inputs.
# Implements also the control of embedded acquisition, so called
# streams.
#
# Configure first a macro motor controller with DEVICE field set to
# string "pepu" and ADDR field set to the hostname of PePU device.
# Therefore there must be ONE macro motor controller per PePU.
# The "NUM" field must be set to 9%BR%
#
# Then configure a counter using the previous controller, the "channel" 
# is used to select the output channel (from 1 to 8)%BR%
#
# An optional parameter "type" can be set to select what to read
# on the given channel ("IN", "OUT", "AUX"). By default, the input
# will be read%BR%
#
#%DT%Configuring motors %DD%
#
# Macro motors can be configured to control the encoder outputs.
#
# Configure first a macro motor controller with DEVICE field set to
# string "pepu" and ADDR field set to the hostname of PePU device.
# Therefore there must be ONE macro motor controller per PePU.
# The "NUM" fielt must be set to 9%BR%
#
# Then configure a motor using the previous controller, the "channel" 
# is used to select the output channel (from 5 to 8)%BR%
# An optional parameter "type" can be set to select what to read
# on the given channel ("IN", "OUT", "AUX"). By default, the output
# will be read%BR%
#
#%XDL%
#
#%END%
#


#%UU% hostname
#%MDESC%
# Launch an interactive command line interface.
# This allows for intance to check/setup the instrument configuration
# using direct command like ?HELHP
#
def pepu '{
  local cmd
  local pyc

  if($# != 1) {
    print "Usage: $0 hostname"
    exit
  }

  pyc = "pepuconsole.py"

  p sprintf("%s/bin/%s", BLISSADM, pyc)
  if(!file_info(sprintf("%s/bin/%s", BLISSADM, pyc), "-x")) {
    printf("Missing \"%s\" script, hint use blissinstaller\n", pyc)
    exit
  }

  cmd = sprintf("%s %s", pyc, "$1")
  unix(cmd)
}'


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

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

    # get hostname from config
    dev = pepu_ADDR

    # minimum check on controller
    ans = pepu_comm(dev, "?APPNAME")
    if(!index(ans, "PEPU")) {
      _pepu_err
      printf("not a PePU instrument: \"%s\"\n", dev)
      return ".error."
    }

    ans = pepu_comm(dev, "?VERSION")
    printf("Using \"%s\" for PePU instrument (version %s)\n", dev, ans)

    # normale end
    return
  }


  #
  # p1==unit p2==0 p3==channel
  #
  if(type == "cnt") {
    local typ

    # check type parameter
    typ = counter_par(num, "type")
    if(typ == 0) {
      # by default, read encoder inputs
      counter_par(num, "type", "IN", "add")
    }
 
    #TODO: add checks on type string
 
    # needed to distinguish counters from motors
    counter_par(num, "pepu_type", "counter", "add")

    # normale end
    return
  }


  #
  # p1==unit p2==0 p3==channel
  #
  if(type == "mot") {
    local typ

    # check type parameter
    typ = motor_par(num, "type")
    if(typ == 0) {
      # by default, read encoder inputs
      motor_par(num, "type", "OUT", "add")
    }

    #TODO: add checks on type string
 
    # needed to distinguish counters from motors
    motor_par(num, "pepu_type", "motor", "add")

    # normale end
    return
  }

}'


#%IU%()
#%MDESC%
# Called by spec on motor or counter operations
# 
def pepu_cmd(num, key, p1, p2) '{
  local mne
  local dev
  local cha
  local typ
  local cmd ans val


  #
  # Handle actions at controller level
  #
  if(num == "..") { 
    return
  }


  #
  # Handle actions at counter level
  #
  if (key == "counts") {
    dev = counter_par(num, "address")
    cha = counter_par(num, "channel")
    typ = counter_par(num, "type")
    cmd = sprintf("?CHVAL %s%d", typ, cha)
    ans = pepu_comm(dev, cmd)
    if(sscanf(ans, "%lf", val) != 1) {
      _pepu_err
      print "unable to parse channel value"
      return ".error."
    }

    return val
  }

  # get rid of any other counter requests
  if(counter_par(num, "pepu_type") == "counter") {
    return
  }


  #
  # Handle actions at motor level
  #
  mne = motor_mne(num)
  dev = motor_par(num, "address")
  cha = motor_par(num, "channel")
  typ = motor_par(num, "type")


  #
  # return the current motor position in mm or deg
  #
  if (key == "position") {
    cmd = sprintf("?CHVAL %s%d", typ, cha)
    ans = pepu_comm(dev, cmd)
    if(sscanf(ans, "%lf", val) != 1) {
      _pepu_err
      print "unable to parse channel value"
      return ".error."
    }

    return val

  }


  #
  # start a motion (p1==abs pos, p2==rel pos, with pos in mm or deg)
  #
  if (key == "start_one") {
    cmd = sprintf("CHVAL %s%d %g", typ, cha, p1)
    pepu_comm(dev, cmd)
    return
  }


  #
  # called on set_dial (p1==abs pos in mm or deg)
  #
  if (key == "set_position") {
    cmd = sprintf("CHSET %s%d %g", typ, cha, p1)
    pepu_comm(dev, cmd)
    return
  }

}'


#%IU%(hostname, command)
#%MDESC%
# Send a command to the given PePU device and returns the answer if any
# TODO: port to deepdevice.mac
#
def pepu_comm(hn, cmd) '{
  local ans
  local ln lc 
  local n args

  _dance_debug(sprintf("WR: \"%s\"", cmd))

  dev = sprintf("%s%s", hn, index(hn,":")?"":":5000")
  pepu_wr(dev, cmd)
  if(index(cmd, "?") || index(cmd, "#")) {
    ans = pepu_rd(dev)
  } else {
    ans = ""
  }
  
  # 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)
  }
  _dance_debug(sprintf("RD: \"%s\"", ans))

  # bad answer from device
  n = split(ans, args)
  if(n < 1) {
    # TODO: print out error?
    return ans
  }

  # TODO: handle error returned by device

  # remove command echoed by device
  return substr(ans, index(ans, " ")+1)
}'


#%IU%(device, command)
#%MDESC%
# Send a command to the given PePU device
# TODO: port to deepdevice.mac
#
def pepu_wr(dev, cmd) '{
  cmd = cmd "\n"
  sock_par(dev, "flush")
  sock_put(dev, cmd)
}'


#%IU%(device)
#%MDESC%
# Waits for an answer from the given PePU device and returns it
# TODO: port to deepdevice.mac
#
def pepu_rd(dev) '{
  local ret
  local ans
  local mline

  sock_par(dev, "timeout", 2)
  for(mline=0, ret="";;) {
    ans = sock_get(dev, "\n")

    if(_pepu_lastchar(ans) == "\$") 
      mline = (mline == 0)

    ret = ret ans

    if(mline == 0) 
      break
  }

  return(ret)
}'

#%IU%
#%MDESC%
# Return the last significant character (non terminator) 
#
def _pepu_lastchar(ans) '{
  local lc
  local ln

  if((ln = length(ans)) == 0) {
    return("")
  }
  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
    }
  }

  return(substr(ans, ln+1, 1))
}'


#%IU%
#%MDESC%
# Pure cosmetic macro
#
def _pepu_err '{
 tty_cntl("md")
 printf("PEPU ERROR: ")
 tty_cntl("me")
}'

#%IU%(src_arrname, dst_arrname)
#%MDESC%
# Convert the given byte array to double array assuming 
# PePU SFIXED data (40bits mantise + 8bits frac) is encoded over 64bits. 
# The destination array is created with the given name.
# Retunrs the number of doubles converted or -1 in case of error.
#
def _pepu_sfixed_40_8_to_double(src_arrname, dst_arrname) '{
    local sz_b sz_f
    local sz_u
    local i j val

    
    # data is given over 64bits values (==8bytes)
    sz_u = 8

    # TODO: check array type
    # TODO: support any type of integer array, remapping to byte array
    
    # check number of data
    sz_b = array_op("cols", @src_arrname)
    if(sz_b % sz_u) {
        _pepu_err
        print "wrong array size"
        return -1
    }
    sz_f = sz_b / sz_u

    double array @dst_arrname[sz_f]
    for(i=0;i<sz_f;i++) {
        for(j=0, val=0.0;j<sz_u;j++) {
            val += (@src_arrname[(i*sz_u)+j])<<(j*8)
        }
        #if(val > ((1<<47)-1)) {
        if(@src_arrname[(i*sz_u)+5] & 0x80) {
            val = val - (1<<48)
        }
        val /= 256.0
        @dst_arrname[i] = val
    }

    return sz_f
}'


#
# ---------------------------- data streams -------------------------------
#                   TODO: move this to deepdevice.mac 
#                if streams implemented generic in DANCE
#


#%UU% logical_name host_name
#%MDESC%
# Create a new logical name to acces the PePU instrument given
# by its name on the network.
#
def pepu_setup '{
    global PEPU[]

    # retrieve the PEPU instrument hostname
    if ($# != 2) {
        print "Usage: $0 <logical_name> <host_name>"
        exit
    }

    if(_pepu_setup("$1", "$2")) {
        exit
    }

    printf("Using \"%s\" as PePU instrument (version %s)\n", \
        PEPU["$1"]["device"], PEPU["$1"]["version"])
}'


#%IU%(logical_name, hostname)
#%MDESC%
# If the logical name already exists, it will be overwritten.
# Returns non null in case of error.
#
def _pepu_setup(name, dev) '{
    global PEPU[]
    local  cmd ans

    # minimum check on controller
    ans = pepu_comm(dev, "?APPNAME")
    if(!index(ans, "PEPU")) {
        _pepu_err
        printf("not a PePU instrument: \"%s\"\n", dev)
        return(-1)
    }

    ans = pepu_comm(dev, "?VERSION")

    # remove any previous configuration
    list_remove(PEPU, name)

    # new logical device
    list_add(PEPU, name)
    PEPU[name]["device"]  = dev
    PEPU[name]["version"] = ans

    return(0)
}'


#%UU% logical_name stream_name
#%MDESC%
# Create a new stream, identified by its name, on a PePU instrument.
#
def pepu_stream_add '{
    global PEPU[]

    # retrieve the PEPU instrument hostname
    if ($# != 2) {
        print "Usage: $0 <logical_name> <stream_name>"
        exit
    }

    if(_pepu_stream_add("$1", "$2")) {
        exit
    }

    printf("Stream \"%s\": created on instrument \"%s\"\n", \
        "$2", PEPU["$1"]["device"])

}'

#%IU%(logical_name, stream_name)
#%MDESC%
#
def _pepu_stream_add(name, sid) '{
    global PEPU[]
    global PEPU_STREAMS[]
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU, name) == -1) {
        _pepu_err
        printf("wrong PePU logical name: \"%s\"\n", name)
        return(-1)
    }
    dev = PEPU[name]["device"]

    # remove any previous configuration
    _pepu_stream_del(sid)

    # create an empty dstream
    ans = pepu_comm(dev, sprintf("#DSTREAM %s GLOBAL", sid))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to create stream: \"%s\"\n", sid)
        printf("\"%s\"\n", ans)
        return(-1)
    }

    # by default the stream is ON at creation
    pepu_comm(dev, sprintf("DSTREAM %s OFF", sid))

    # at this point we  have a valid dstream
    list_add(PEPU_STREAMS, sid)
    PEPU_STREAMS[sid]["device"]  = dev
    PEPU_STREAMS[sid]["pepu"]    = name
    PEPU_STREAMS[sid]["sources"] = ""

    return(0)
}'

#%UU% stream_name
#%MDESC%
# Remove any previous configuration for the given stream.
# The corresponding instrument will also be updated.
#
def pepu_stream_del '{
    global PEPU[]

    # retrieve stream name
    if ($# != 1) {
        print "Usage: $0 <stream_name>"
        exit
    }

    if(_pepu_stream_del("$1")) {
        exit
    }

    printf("Stream \"%s\": deleted\n", "$1")
}'

#%IU%(stream_name)
#%MDESC%
# No error returned.
#
def _pepu_stream_del(sid) '{
    global PEPU[]
    global PEPU_STREAMS[]
    local  dev

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        # no need to print error, just do nothing
        return(0)
    }

    dev = PEPU_STREAMS[sid]["device"]
    pepu_comm(dev, sprintf("DSTREAM %s DEL GLOBAL", sid))

    list_remove(PEPU_STREAMS, sid)

    return(0)
}'


#%UU% stream_name start clock
#%MDESC%
# Configure the signal used to start the acquistion and
# the signal used as acquistion clock.
# The signals could be "SOFT" "DI1" "DI2" "FREQ"
#
def pepu_stream_trig '{

    # minimum checks
    if ($# != 3) {
        print "Usage: $0 <stream_name> <start_signal> <clock_signal>"
        exit
    }

    if(_pepu_stream_trig("$1", "$2", "$3")) {
        exit
    }

    printf("Stream \"%s\": start:\"%s\" clock:\"%s\"\n", "$1", "$2", "$3")
}'

#%IU%(stream_name, start, clock)
#%MDESC%
#
def _pepu_stream_trig(sid, start, clock) '{
    global PEPU_STREAMS[]
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    # check signal names
    local sig_s sig_c sig_a
    sig_a = "DI1 DI2 SOFT FREQ"
    sig_s = _dance_toupper(start)
    if(!index(sig_a, sig_s)) {
        _pepu_err
        printf("invalid start signal: \"%s\"\n", start)
        return(-1)
    }
    sig_c = _dance_toupper(clock)
    if(!index(sig_a, sig_c)) {
        _pepu_err
        printf("invalid clock signal: \"%s\"\n", clock)
        return(-1)
    }

    # update stream configuration 
    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("#DSTREAM %s TRIG %s %s", sid, sig_s, sig_c))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to configure the acquisition triggering\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }
    PEPU_STREAMS[sid]["start"] = sig_s
    PEPU_STREAMS[sid]["clock"] = sig_c

    return(0)
}'


#%UU% stream_name hz
#%MDESC%
# Configure the acquisition clock, given in Hz.
# Useless if clock will be an external signal.
#
def pepu_stream_fsample '{

    # minimum checks
    if ($# != 2) {
        print "Usage: $0 <stream_name> <hz>"
        exit
    }

    if(_pepu_stream_fsample("$1", $2)) {
        exit
    }

    printf("Stream \"%s\": acquisition clock:%dHz\n", "$1", int($2))
}'


#%IU%(stream_name, hz)
#%MDESC%
#
def _pepu_stream_fsample(sid, hz) '{
    global PEPU_STREAMS[]
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    # minimum checks
    if((hz < 1) || (hz>10e6)) {
        _pepu_err
        printf("invalid frequency\n")
        return(-1)
    }
    hz = int(hz)

    # update stream configuration 
    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("#DSTREAM %s FSAMPL %dHz", sid, hz))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to configure the acquisition clock\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }
    PEPU_STREAMS[sid]["fsample"] = hz

    return(0)
}'


#%UU% stream_name nsamples
#%MDESC%
# Configure the number of samples
#
def pepu_stream_nsample '{

    # minimum checks
    if ($# != 2) {
        print "Usage: $0 <stream_name> <nsamples>"
        exit
    }

    if(_pepu_stream_nsample("$1", $2)) {
        exit
    }
    printf("Stream \"%s\": number of samples:%d\n", "$1", int($2))
}'


#%IU%(stream_name, nsample)
#%MDESC%
#
def _pepu_stream_nsample(sid, nsample) '{
    global PEPU_STREAMS[]
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    # minimum checks
    if(nsample < 1) {
        _pepu_err
        printf("invalid number of samples\n")
        return(-1)
    }
    nsample = int(nsample)

    # update stream configuration 
    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("#DSTREAM %s NSAMPL %d", sid, nsample))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to configure the number of samples\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }
    PEPU_STREAMS[sid]["nsample"] = nsample

    return(0)
}'


#%IU%(stream_name, nsample_limit)
#%MDESC%
#
def _pepu_stream_nsample_limit(sid, n_limit) '{
    global PEPU_STREAMS[]

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    PEPU_STREAMS[sid]["n_limit"] = n_limit
    return(0)
}'


#%UU% stream_name channels
#%MDESC%
# The given channel will be acquired.
#
def pepu_stream_source_add '{

    # minimum checks
    if ($# != 2) {
        print "Usage: $0 <stream_name> <channel>"
        exit
    }

    if(_pepu_stream_source_add("$1", "$2")) {
        exit
    }
    printf("Stream \"%s\": channel added:\"%s\"\n", "$1", "$2")

}'

#%IU%(stream_name, channel)
#%MDESC%
# Allowed channels "IN1/6" "CALC1/8"
def _pepu_stream_source_add(sid, chname) '{
    global PEPU_STREAMS[]
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    # check channel name
    local n ch
    ch = _dance_toupper(chname)
    if(index(ch, "IN") == 1) {
    	n  = int(substr(chname, 3, 1))
    	if((n<1) || (n>6)) {
            _pepu_err
            printf("invalid channel name number: \"%s\"\n", chname)
            return(-1)
       }
    } else if(index(ch, "CALC") == 1) {
    	n  = int(substr(chname, 5, 1))
    	if((n<1) || (n>8)) {
            _pepu_err
            printf("invalid calc channel number: \"%s\"\n", chname)
            return(-1)
       }
    } else {
        _pepu_err
        printf("invalid channel name: \"%s\"\n", chname)
        return(-1)
    }

    # check if channel already configured
    local src
    src = PEPU_STREAMS[sid]["sources"]
    if(index(src, ch)) {
        return(0)
    }

    # update stream configuration 
    src = _dance_trim(sprintf("%s %s", src, ch))
    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("#DSTREAM %s SRC %s", sid, src))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to add channel: \"%s\"\n", chname)
        printf("\"%s\"\n", ans)
        return(-1)
    }
    PEPU_STREAMS[sid]["sources"] = src

    return(0)
}'


#%IU%(stream_name)
#%MDESC%
# Returns the current number of sources
#
def _pepu_stream_nsources(sid) '{
    local tt[]

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    return(split(PEPU_STREAMS[sid]["sources"], tt))
}'


#%IU%(stream_name)
#%MDESC%
# Returns the current data array name
#
def _pepu_stream_arrname(sid) '{
    local tt[]

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    return PEPU_STREAMS[sid]["arr"]
}'


#%UU% stream_name
#%MDESC%
# Switch on the given stream and launch the acquistion.
# Handle also the software start if configured.
#
def pepu_stream_start '{

    # minimum checks
    if ($# != 1) {
        print "Usage: $0 <stream_name>"
        exit
    }

    if(_pepu_stream_start("$1")) {
        exit
    }

    printf("Stream \"%s\": acquisition launched\n", "$1")
}'


#%IU%(stream_name)
#%MDESC%
#
def _pepu_stream_start(sid) '{
    global PEPU_STREAMS[]
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("#DSTREAM %s APPLY", sid))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to apply configuration\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }

    if(0) {
    ans = pepu_comm(dev, sprintf("#DSTREAM %s FLUSH", sid))

    ans = pepu_comm(dev, sprintf("#DSTREAM %s ON", sid))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to launch acquisition\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }
    }


    # create the array that will store data
    local bidon[] nch nsample arrname
    nch     = split(PEPU_STREAMS[sid]["sources"], bidon)
    arrname = sprintf("pepu_%s_data", sid)
    nsample = PEPU_STREAMS[sid]["nsample"]
    double array @arrname[nsample][nch]
    PEPU_STREAMS[sid]["arr"] = arrname 


    # handle the software start of acquistion
    local start 
    start = list_getpar(PEPU_STREAMS, sid, "start")
    if((start == -1) || (start != "SOFT")) {
        return(0)
    }
    ans = pepu_comm(dev, "#STRIG")
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to handle the software satrt of acquisition\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }

    return(0)
}'


#%UU% stream_name
#%MDESC%
# Switch off the given stream and stop the acquistion.
#
def pepu_stream_stop '{

    # minimum checks
    if ($# != 1) {
        print "Usage: $0 <stream_name>"
        exit
    }

    if(_pepu_stream_stop("$1")) {
        exit
    }

    printf("Stream \"%s\": acquisition stopped\n", "$1")
}'


#%IU%(stream_name)
#%MDESC%
#
def _pepu_stream_stop(sid) '{
    global PEPU_STREAMS[]
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("#DSTREAM %s STOP", sid))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to stop acquisition\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }

    ans = pepu_comm(dev, sprintf("#DSTREAM %s OFF", sid))
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("unable to switch off the stream\n")
        printf("\"%s\"\n", ans)
        return(-1)
    }

    return(0)
}'


#%IU%(stream_name)
#%MDESC%
# Returns the number of samples ready to be read
#
def _pepu_stream_nsample_cur(sid) '{
    local  dev cmd ans
    local  n n_cur

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("?DSTREAM %s NSAMPL", sid))
    n   = sscanf(ans, "%d", n_cur)
    return(n_cur)
}'

#%IU%(stream_name, nsamples, arrname)
#%MDESC%
# Read the given number of samples and stores them in a array.
# The array will be created and named as given.
# It will be of type double
#
def _pepu_stream_read(sid, n_cur, subarrname) '{
    local  dev cmd ans

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    dev = PEPU_STREAMS[sid]["device"]

    # get raw data from PEPU
    cmd = sprintf("?*DSTREAM %s READ %d", sid, n_cur)
    _dance_get_array(dev, cmd, "ldat")

    # convert to float value data
    _pepu_sfixed_40_8_to_double("ldat", subarrname)
}'


#%IU%(stream_name)
#%MDESC%
#
def _pepu_stream_poll_read(sid) '{
    global PEPU_STREAMS[]
    local  dev cmd ans
    local  n n_cur n_tot nsample
    local  arrname

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        return(-1)
    }

    # TODO: add tests if acquisition has been launched
    dev     = PEPU_STREAMS[sid]["device"]
    arrname = PEPU_STREAMS[sid]["arr"]
    nsample = PEPU_STREAMS[sid]["nsample"]

    #TODO: handle timeout
    for(n_tot=0;n_tot<nsample;) {
        #ans = pepu_comm(dev, sprintf("?DSTREAM %s NSAMPL", sid))
        #n   = sscanf(ans, "%d", n_cur)
        n_cur = _pepu_stream_nsample_cur(sid)
        if(n_cur == 0) {
            # TODO: add timeout here
            sleep(.01)
            continue
        }
    
        # NOTE MP 21thNov: bug in binary transfer commands (any binary request)
        # with large (well, not so large) data. Bug to be fixed
        # by FLM. Meantime, limit transfer size.
        if((n_bug = PEPU_STREAMS[sid]["n_limit"]) != 0) {
            n_cur = (n_cur>n_bug)?n_bug:n_cur
        }

        # get raw data from PEPU
        #cmd = sprintf("?*DSTREAM %s READ %d", sid, n_cur)
        #_dance_get_array(dev, cmd, "ldat")

        # convert to value data
        #_pepu_sfixed_40_8_to_double("ldat", "fdat")

        _pepu_stream_read(sid, n_cur, "fdat")

        # resort data in columns and
        # store partial data in global full data
        array_copy(@arrname[n_tot:(n_tot+n_cur-1)][], fdat)

        n_tot += n_cur
    }
}'


#%UU% stream_name
#%MDESC%
# Print out current stream configuration
#
def pepu_stream '{
    global PEPU_STREAMS[]
    local  dev cmd ans
    local  sid
    local  n_bug

    # minimum checks
    if ($# != 1) {
        print "Usage: $0 <stream_name>"
        exit
    }
    sid = "$1"

    # minimum checks
    if(list_item(PEPU_STREAMS, sid) == -1) {
        _pepu_err
        printf("non configured stream: \"%s\"\n", sid)
        exit
    }

    local start clock nsample fsample sources arrname
    start   = list_getpar(PEPU_STREAMS, sid, "start")
    clock   = list_getpar(PEPU_STREAMS, sid, "clock")
    nsample = list_getpar(PEPU_STREAMS, sid, "nsample")
    fsample = list_getpar(PEPU_STREAMS, sid, "fsample")
    sources = list_getpar(PEPU_STREAMS, sid, "sources")
    arrname = list_getpar(PEPU_STREAMS, sid, "arr")

    printf("Stream \"%s\":\n", sid)
    printf("\tStart  : %s\n",     (start  !=-1)?start:"????")
    printf("\tClock  : %s\n",     (clock  !=-1)?clock:"????")
    printf("\tNsample: %s\n",     (nsample!=-1)?nsample:"????")
    printf("\tFsample: %sHz\n",   (fsample!=-1)?fsample:"????")
    printf("\tSources: \"%s\"\n", (sources!=-1)?sources:"????")
    printf("\tArray  : %s[][]\n", (arrname!=-1)?arrname:"????")

    #TODO: check that conf in globals matches the conf in the instrument
    dev = PEPU_STREAMS[sid]["device"]
    ans = pepu_comm(dev, sprintf("?DSTREAM %s", sid))
}'

#
# ----------------------- binary transfers toolkit -------------------------
#                   TODO: move this to deepdevice.mac
#

#%IU%(hostname, query_cmd, array_name)
#%MDESC%
# Returns the number of data read or -1 in case of error.
#
def _dance_get_array(hn, cmd, arrname) '{
    local ulong array header[3]
    local dev
    local no_checksum  checksum bin_checksum
    local data_sz data_n n


    # check argin validity
    if(substr(cmd, 1, 2) != "?*") {
        _pepu_err
        printf("not a binary query command: \"%s\"\n", cmd)
        return -1
    }

    # full socket name
    dev = sprintf("%s%s", hn, index(hn,":")?"":":5000")
    
    # TODO: whats the data type in DANCE??
    #} else if(!index(whatis("arr", "info"), "ushort")) {
    #    _pepu_err
    #    printf("bad binary data array\n")
    #    return -1
    #}

    # get the ASCII part of the answer
    # TODO: check error 
    ans = pepu_comm(dev, cmd)
    if(index(ans, "ERROR")) {
        _pepu_err
        printf("can not get data: \"%s\"\n", ans)
        return -1
    }

    # now comes the binary part of the answer
    sock_get(dev, header)
    _dance_debug(sprintf("hearder : 0x%08x", header[0]))
    _dance_debug(sprintf("size    : 0x%08x", header[1]))
    _dance_debug(sprintf("checksum: 0x%08x", header[2]))


    # mimimum check on magic number
    if(((header[0]&0xffff0000)>>16) != 0xa5a5) {
        _pepu_err
        printf("not a DANCE/PEPU binary format, giving up\n")
        sock_par(dev, "flush")
        return -1
    }

    # check if checksum has to be handled or not
    no_checksum = (header[0]&0x00000010)
    checksum    =  header[2]
    _dance_debug(sprintf("using checksum: %s",(no_checksum)?"NO":"YES"))

    # data size of data in bytes
    data_sz = (header[0]&0x0000000f)

    # number of data
    data_n  = header[1]

    # redefine array given as argin
    if(data_sz == 1) {
        ubyte array @arrname[data_n]
    } else if(data_sz == 2) {
        ushort array @arrname[data_n]
    } else if(data_sz == 4) {
        ulong array @arrname[data_n]
    } else if(data_sz == 8) {
        ulong64 array @arrname[data_n]
    } else {
        _pepu_err
        printf("unsupported data type: %d \n", data_sz)
        sock_par(dev, "flush")
        return -1
    }

    # the binary data should be in the socket, waiting for us
    sock_par(dev, "timeout", 10)
    n = sock_get(dev, @arrname)
    _dance_debug(sprintf("bytes read: %d", (n*data_sz)))
    if(data_n != n) {
        _pepu_err
        printf("missing data\n")
        return -1
    }

    # check data validity
    # WARNING: when data size is large (see with long64) the array_op() 
    # may return a wrong value because SPEC stores the sum in float/double
    # with limited mantisse. Then it would be better to ignore checksum.
    #
    if(!no_checksum) {
        bin_checksum = array_op("sum", @arrname) & 0xffffffff
        if(bin_checksum != checksum) {
            _pepu_err
            printf("wrong checksum on binary data\n")
            return -1
        }
        _dance_debug("checksum  : OK")
    }

    # normal end
    return data_n
}'


#%IU%(array_name)
#%MDESC%
# Work around to SPEC array_op("sum") loosing LSB bits.
# Much more slower than SPEC built-in of course.
# NOT VALIDATED 
#
def _dance_checksum(arrname) '{
    local ulong array sumval[1]
    local cols i

    cols = array_op("cols", @arrname)
    for(i=0, sumval[0]=0;i<cols;i++) {
        #sumval[0] += @arrname[i]
        sumval[0] += (@arrname[i] & 0xffffffff)
    }
    printf("0x%08x\n", array_op("sum", @arrname))
    printf("0x%08x\n", sumval[0])
}'


#%UU%
#%MDESC%
#
def dancedebug '{
    dance_debug $*
}'

def dance_debug '{
    global DANCE_DEBUG

    # TODO: add debug levels
    if(DANCE_DEBUG) {
        print "DANCE debug is OFF"
        DANCE_DEBUG = 0
    } else {
        print "DANCE debug is ON"
        DANCE_DEBUG = 1
    }
}'

#%IU%(msg)
#%MDESC%
#
def _dance_debug(msg) '{
    global DANCE_DEBUG

    if(DANCE_DEBUG == 0) { 
        return
    }

    printf("\t[DANCE]: %s\n", msg)
}'


#%IU%(string, case)
#%MDESC%
# Convert to lower (case==1) or to upper (case==0) case the string passed
#
def _dance_tocase(str, case) '{
  string array str_a[length(str)]
  local l

  str_a = str
  l = length(str)-1
  for(;l>=0;l--) {
   if(case)
    str_a[l]=str_a[l]+(str_a[l]>=asc("A") && str_a[l]<=asc("Z"))*0x20;
   else
    str_a[l]=str_a[l]-(str_a[l]>=asc("a") && str_a[l]<=asc("z"))*0x20;
  }
  return(sprintf("%s", str_a))
}'


#%IU%(string)
#%MDESC%
# Return the lower string of the string passed
#
def _dance_tolower(str) '{return((str!="")?_dance_tocase(str, 1):"")}'


#%IU%(string)
#%MDESC%
# Return the upper string of the string passed
#
def _dance_toupper(str) '{return((str!="")?_dance_tocase(str, 0):"")}'


#%IU%(string)
#%MDESC%
#
def _dance_trim(str) '{
  local  b l

  l = length(str)
  if(l == 0) {
     return ""
  }

  string array str_a[l]
  str_a = str
  l = l-1
  for(;(l>=0)&&(str_a[l]==0x20);l--);
  for(b=0;(b<l)&&(str_a[b]==0x20);b++);

  return(substr(str,b+1,(l-b)+1))
}'


#
# ----------------------------- zap toolkit --------------------------------
#                   TODO: move this to zappepu.mac
#



#%IU%(stream_name, column)
#%MDESC%
# For tests only, do not use
#
def pepu_test_array_plot(sid, col) '{
    global PEPU_STREAMS[]
    local v_min v_max
    local npts

    arrname = PEPU_STREAMS[sid]["arr"]
    npts    = PEPU_STREAMS[sid]["nsample"]-1


    if((npts == -1) || (!arrname)) {
        _pepu_err
        printf("nothing to plot\n")
        return -1
    }
    long array arr_x[npts+1]
    array_op("fill",arr_x)

    double array a1[npts+1]
    array_op("fill", a1)
    a1 = a1+@arrname[0][col]

    #v_min = array_op("min",  @arrname[:npts][col])
    #v_max = array_op("max",  @arrname[:npts][col])
    v_min = array_op("min",  a1)
    v_max = array_op("max",  a1)

    plot_cntl("erase")
    plot_range("auto", "auto",v_min, v_max)

    plot_cntl(sprintf("colors=%s",splot_col))
    array_plot(arr_x[:npts], @arrname[:npts][col])

    plot_cntl("colors=47:49:9:3:3")
    array_plot(arr_x[:npts], a1)

    # NOTE: the array_op() does not work on sub 2D arrays
    double array a2[npts+1]
    array_copy(a2, @arrname[][col])
    return array_op("max", fabs(a2-a1))
}'


#%IU% hostname [[[[npts] freq] loops] start_trig] clock_trig]
#%MDESC%
# For tests only, do not use
# %BR%
# Expects pulses on IN6 at the
# same frequency that the sampling one. Can use an IcePAP axis
# controlled in speed, with motor_par(..., "jog_speed", hz), and its
# DB25 "Outpos" configured to "AXIS" as "source".
#%BR% freq given in Hz
#%BR% loops -1 means infinite loop
#%BR% start_trig is "SOFT"(default) "DI1" "DI2"
#%BR% clock_trig is "FREQ"(default) "DI1" "DI2"
#
def pepu_icepap_test '{
    local pepuname
    local hostname
    local npts
    local loops i
    local tbeg
    local intrig

    if($# < 1) {
        p "Usage: $0 hostname [[[[npts] freq] loops] start_trig] clock_trig]"
        exit
    }

    pepuname  = "bidon"
    hostname  = "$1"
    sid       = "encs"

    npts  = ($#>=2)?$2:1000
    freq  = ($#>=3)?$3:1000
    loops = ($#>=4)?$4:1
    intg  = ($#>=5)?"$5":"SOFT"
    clktg = ($#>=6)?"$6":"FREQ"

    if(loops == -1) {
        loops = pow(2, 32)
    }

    if(_pepu_setup(pepuname, hostname))     { exit }
    if(_pepu_stream_add(pepuname, sid))     { exit }
    if(_pepu_stream_source_add(sid, "in1")) { exit }
    if(_pepu_stream_source_add(sid, "in6")) { exit }
    if(_pepu_stream_nsample(sid, npts))     { exit }
    if(_pepu_stream_fsample(sid, freq))     { exit }
    if(_pepu_stream_trig(sid, intg, clktg)) { exit }

    for(i=0;i<loops;i++) {
        printf("---------------Loop: %d\n", i+1)
        p date()
        printf("Npts : %d\n", npts)
        printf("Freq : %dHz\n", freq)
        if(intrig) {
            printf("Start: %s\n", intg)
            printf("Clock: %s\n", clktg)
        }

        if(_pepu_stream_start(sid)) {
            break
        }
    
        tbeg=time()
        _pepu_stream_poll_read(sid)
        printf("Elaps: %.1fsec\n", time()-tbeg)

        err = pepu_test_array_plot(sid, 1)
        printf("Error: %d\n", err)
        if(err > 100) {
            break
        }
    }
}'

#%IU% hostname
#%MDESC%
def pepu_test_renishaw '{
    local pepuname
    local hostname
    local cmd ans

    if($# < 1) {
        p "Usage: $0 hostname"
        exit
    }

    p $#

    hostname  = "$1"
    pepuname  = "bidon"
    printf("Usig  PePU: \"%s\"\n", hostname)
    if(_pepu_setup(pepuname, hostname))     { exit }

    printf("Configure IN1/2/3/4/5/6 to be RENISHAW compatible\n")
    for(i=1;i<=6;i++) {
        pepu_comm(hostname, sprintf("#CHCFG IN%d BISS", i))
        pepu_comm(hostname, sprintf("#BISSCFG IN%d 32BITS 250000HZ", i))
        pepu_comm(hostname, sprintf("#CHCFG IN%d ENABLE", i))
    }

    printf("Configure OUT7/8 to be RENISHAW compatible\n")
    for(i=7;i<=8;i++) {
        pepu_comm(hostname, sprintf("#CHCFG OUT%d BISS", i))
        pepu_comm(hostname, sprintf("#CHCFG OUT%d ENABLE", i))
    }

    printf("Configure OUT7/8 as average of IN1/2/3/4\n") 
    pepu_comm(hostname, "#CALCCFG calc7 (in1+in2+in3+in4)/4")
    pepu_comm(hostname, "#CHSRC out7 calc7")
    pepu_comm(hostname, "#CHSRC out8 calc7")
}'


#%MACROS%
#%IMACROS%
#%AUTHOR% MP+FLM+RH BLISS (Original 9/2015).
# %BR%$Revision: 1.3 $ / $Date: 2016/11/21 11:53:21 $
#%TOC%