esrf

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

#%TITLE% ICE_VDATA.MAC
#
#%NAME%
#  Macros to handle IcePAP data vectors, used for instance for 
#  defining embedded trajectories.
#
#%DESCRIPTION%
#  These macros allow to create a data vector from one to several
#  data arrays of parameter, position or slope values.
#
#%EXAMPLE%
#  short array myparam[4]
#  short array mypos[4]
#  array_op("fill", myparam)
#  array_op("fill", mypos, 10)
#  icepap_vdata_build vdata1 POSITION mypos 6
#  icepap_vdata_build vdata1 PARAMETER myparam 6
#  icepap_vdata_info vdata1
#
#%END%
#

# Mandatory standard IcePAP macros
need ice

# Constant definitions
unglobal ICEPAP_VDATA_DTYPECODE
ICEPAP_VDATA_DTYPECODE["BYTE"  ]    = 0x00
ICEPAP_VDATA_DTYPECODE["WORD"  ]    = 0x01
ICEPAP_VDATA_DTYPECODE["DWORD" ]    = 0x02
ICEPAP_VDATA_DTYPECODE["LWORD" ]    = 0x03
ICEPAP_VDATA_DTYPECODE["FLOAT" ]    = 0x04
ICEPAP_VDATA_DTYPECODE["DFLOAT"]    = 0x05
ICEPAP_VDATA_DTYPECODE["UBYTE" ]    = 0x10
ICEPAP_VDATA_DTYPECODE["UWORD" ]    = 0x11
ICEPAP_VDATA_DTYPECODE["UDWORD"]    = 0x12
ICEPAP_VDATA_DTYPECODE["ULWORD"]    = 0x13

unglobal ICEPAP_VDATA_VTYPECODE
ICEPAP_VDATA_VTYPECODE["PARAMETER"] = 0x1000
ICEPAP_VDATA_VTYPECODE["POSITION" ] = 0x2000
ICEPAP_VDATA_VTYPECODE["SLOPE"    ] = 0x4000

ICEPAP_VDATA_SIGNATURE = 0xCAFE



#%UU% vector type array addr
#%MDESC%
# Update an IcePAP data vector with the content of the given 
# data array. If the vector does not exist, it will be created, 
# otherwise it will be enlarged with the new data.
#
# The type describe the kind of vector data which could be
# "PARAMETER", "POSITION", "SLOPE"
#
# The address given is the destination DRIVER address.
#
# Example:
#   icepap_vdata_build vdata1 POSITION mypos 6
#
def icepap_vdata_build '{
  local silent

  if($# != 4) {
    p "Usage: $0 vector type array addr"
    p "   Ex: $0 vdata1 POSITION posarray 7"
    exit
  }

  silent = 1
  if(_icepap_vdata_build("$1", "$2", $3, $4, silent)) {
    exit
  }
}'


#%IU%(vector_name, type, array, addr, silent)
#%MDESC%
# Update an IcePAP data vector, given by its name.
#
# Returns non null if an error occured.
#
# TODO:
#   -check if the given vector type is not already present in the vector
#
# Example:
#   short array myparam[4]
#   short array mypos[4]
#   array_op("fill", myparam)
#   array_op("fill", mypos, 10)
#   _icepap_vdata_build("vdata1", "PARAMETER", myparam, 7)
#   _icepap_vdata_build("vdata1", "POSITION",  mypos,   7)
#
#   _icepap_send_array("eutmp1", 7, "*PARDAT", vdata1)
#   p _icepap_query("eutmp1","7","?pardat 0")
#
def _icepap_vdata_build(vdata_name, vdatat_str, data, addr, silent) '{
  local vdata
  local data_sz data_ln data_n
  local full_sz
  local i j k
  local header_sz header_ver header_zip
  local datat_str datat_cod vdatat_cod
  local vdata_append full_old_sz

  # From DSP source code:
  # typedef struct {
  #   uint16_t    signature;  // Signature must be VDAT_SIGNATURE (0xCAFE)
  #   uint8_t     version;    // Version of the data vector format. Currently=0
  #   uint8_t     data_offset;// Data offset in dwords = 6  (0x006)
  #   uint32_t    vdat_size;  // Full vector size in dwords (4 bytes)
  #   uint32_t    n_values;   // number of values in the vector
  #   uint8_t     data_type;  // Data type
  #   uint8_t     compression;// Compression algorithm: 0=uncompressed, 1=lzapp
  #   uint16_t    flags;      // coding flags and board address
  #   extdfloat_t firstvalue; // first vector value if incremental
  # } vdatheader_t;

  # some hardcoded information to be adapted if vector header signature changes
  header_sz  = 12  # header size in words
  header_ver = 0   # version of data vector
  header_zip = 0   # compression algo (0==uncompressed)
  
  # check argins validity
  if(whatis("data") & 0x0001ffff != 0x00010004) {
    _icepap_err
    print "Bad data array type"
    return(-1)
  }

  # guess the data size in words
  if((data_sz = _icepap_array_size(data)) == -1) {
    return(-1)
  }
  data_n    = array_op("rows", data) * array_op("cols", data)
  data_ln   = data_n * data_sz

  # encode data type
  datat_str = _icepap_array_type(data)
  if(!(datat_str in ICEPAP_VDATA_DTYPECODE)) {
    _icepap_err
    print "Data array type not supported"
    return(-1)
  }
  datat_cod = ICEPAP_VDATA_DTYPECODE[datat_str]

  # encode the vector type
  if(!(vdatat_str in ICEPAP_VDATA_VTYPECODE)) {
    _icepap_err
    printf("Wrong vector type, must be: ")
    for(i in ICEPAP_VDATA_VTYPECODE) {
      printf("\"%s\" ",i)
    }
    print ""
    return(-1)
  }
  vdatat_cod = ICEPAP_VDATA_VTYPECODE[vdatat_str]

  # calculate the full vector size in words
  full_sz   = data_ln + header_sz

  # check if destination vector is not empty and must be appended
  # NOTE MP Jan/2015: the whatis() function does not work 
  #if(whatis("@vdata") & 0x0001ffff == 0x00010004) {
  #  if(!index(whatis("dataw", "info"), "ushort")) { 
  #    _icepap_err
  #    printf("invalid data type for array \"%s\", must be ushort\n", @vdata)
  #    return(-1)
  #  }
  #  vdata_append = 1
  #}

  vdata_append = (@vdata_name[0] == ICEPAP_VDATA_SIGNATURE) 

  
  # allocate vector
  ushort array vdata[full_sz]
  i = 0

  # debug information
  if(!silent) {
     printf("\tVector name    : %s (%s)\n",   \
       vdata_name, vdata_append?"APPENDING":"CREATED")
     printf("\tVector type    : %s\n",        vdatat_str)
     printf("\tData type      : %s\n",        datat_str)
     printf("\tData size      : %4d words\n", data_sz)
     printf("\tNumber of data : %4d\n",       data_n)
     printf("\tData length    : %4d words\n", data_ln)
     printf("\tVector length  : %4d words\n", full_sz)
  }

  # prepare vector header
  # signature
  vdata[i++] = ICEPAP_VDATA_SIGNATURE

  # version + data_offset in dwords
  vdata[i++] = header_ver + ((header_sz / 2) << 8)

  # full vector size in dwords
  vdata[i++] = ((full_sz/2) & 0xffff)
  vdata[i++] = ((full_sz/2) >> 16)

  # number of values in the vector
  vdata[i++] = (data_n & 0xffff)
  vdata[i++] = (data_n >> 16)

  # data_type + compression
  vdata[i++] = datat_cod + (header_zip << 8)

  # format + address
  vdata[i++] = addr + vdatat_cod

  # first double value if incremental
  vdata[i++] = 0
  vdata[i++] = 0
  vdata[i++] = 0
  vdata[i++] = 0

  # minium check
  if(i != header_sz) {
    _icepap_err
    print "Internal error: wrong header size"
    return(-1)
  }
 
  # finally add the data
  # NOTE MP 2015Sep: bug of array_copy(subarray, ...) on release < 6.02
  #array_copy(vdata[i:], data)
  ushort array vdata_one[data_sz]
  for(j=0;j<data_n;j++) {
    array_copy(vdata_one, data[j])
    for(k=0;k<data_sz;k++) {
      vdata[i++] = vdata_one[k]
    }
  }
  
  # update vector to return
  if(!vdata_append) {

    # creating a new vector
    ushort array @vdata_name[full_sz]
    array_copy(@vdata_name, vdata)

  } else {

    # appending an existing one
   
    # calculate the full vector size in words
    full_old_sz = array_op("rows", @vdata_name) * array_op("cols", @vdata_name)
    ushort array vdata_tmp[full_old_sz]
    array_copy(vdata_tmp, @vdata_name)

    # create a new vector
    ushort array @vdata_name[full_sz + full_old_sz]
    array_copy(@vdata_name, vdata_tmp, vdata)
  }

  # normal end
  if(0) {
  if(!silent) {
    _icepap_vdata_dump(@vdata_name)
  }
  }
  return(0)
}'


#%IU%(vector_data)
#%MDESC%
# Print given data vector as a byte array. 
# For debug purpose only
#
def _icepap_vdata_dump(vdata) '{
  local data_sz data_ln data_n

  # vector data type length in words (should be always words array)
  data_sz   = _icepap_array_size(vdata)

  # calculate vector length in bytes
  data_n    = array_op("rows", vdata) * array_op("cols", vdata)
  data_ln   = data_n * data_sz * 2

  # dump vector data array as a byte array
  local ubyte array tmp[data_ln]
  array_copy(tmp, vdata)
  printf("\tVector as bytes: ")
  for(i=0;i<data_ln;i++) {
    printf("\\x%02x", tmp[i])
  }
  print ""
}'


#%UU% vector_name
#%MDESC%
# Print information on given data vector
#
def icepap_vdata_info '{
  local vdata_name
  local i n idx

  vdata_name = "$1"
  # update information first
  if(_icepap_vdata_info(vdata_name)) {
    exit
  }

  n = ICEPAP_VDATA_INFO[vdata_name]["vectors"]
  printf("\tVector name       : %s\n", vdata_name)
  printf("\tContained vectors : %d\n", n)

  for(i=1;i<=n;i++) {
    idx = sprintf("%s_%d", vdata_name, i)
    printf("\n")
    printf("\tVector number     : #%d\n", i)
    printf("\tDriver address    : %d\n", ICEPAP_VDATA_INFO[idx]["address"])
    printf("\tType              : %s\n", ICEPAP_VDATA_INFO[idx]["type"])
    printf("\tNumber of values  : %d\n", ICEPAP_VDATA_INFO[idx]["values"])
    printf("\tValues type       : %s\n", ICEPAP_VDATA_INFO[idx]["dtype"])
  }
}'


#%IU%(vector_name, silent)
#%MDESC%
# Update stored information on given data vector.
#
# Returns non null if an error occured.
#
def _icepap_vdata_info(vdata_name, silent) '{
  global ICEPAP_VDATA_INFO[]
  local flags flags_offset vdatat_cod vdatat_str
  local offset
  local array_sz full_sz full_sz_offset
  local data_n data_n_offset
  local datat_cod datat_offset datat_str
  local i n idx

  # check that the given array is a data vector
  # NOTE MP Jan/2015: the whatis() function does not work 
  if(@vdata_name[0] != ICEPAP_VDATA_SIGNATURE) {
    _icepap_err
    print "Not an IcePAP data vector"
    return(-1)
  }

  # remove any previous information
  _icepap_vdata_cleanup(vdata_name)

  # some hardcoded information to be adapted if vector header signature changes
  full_sz_offset = 2
  data_n_offset  = 4
  datat_offset   = 6
  flags_offset   = 7

  # at least one data vector present
  array_sz = array_op("rows", @vdata_name) * array_op("cols", @vdata_name)
  for(n=1, offset=0;;n++) {

    # new entry
    ICEPAP_VDATA_INFO[vdata_name]["vectors"] = n
    idx = sprintf("%s_%d", vdata_name, n)

    # get vector type
    flags = @vdata_name[flags_offset + offset]
    vdatat_cod = flags & 0xff00
    vdatat_str = ""
    for(i in ICEPAP_VDATA_VTYPECODE) {
      if(ICEPAP_VDATA_VTYPECODE[i] == vdatat_cod) {
        vdatat_str = i
        break
      }
    }
    if(vdatat_str == "") {
      _icepap_vdata_cleanup(vdata_name)
      _icepap_err
      print "Invalid data vector data type"
      return(-1)
    }
    ICEPAP_VDATA_INFO[idx]["type"] = vdatat_str

    # get DRIVER address
    ICEPAP_VDATA_INFO[idx]["address"] = flags & 0xff

    # get number of values
    data_n   =  @vdata_name[data_n_offset + offset]
    data_n  += (@vdata_name[data_n_offset + offset + 1] << 16)
    ICEPAP_VDATA_INFO[idx]["values"] = data_n

    # get values type
    datat_cod=  @vdata_name[datat_offset + offset] & 0xff
    datat_str= ""
    for(i in ICEPAP_VDATA_DTYPECODE) {
      if(ICEPAP_VDATA_DTYPECODE[i] == datat_cod) {
        datat_str = i
        break
      }
    }
    if(datat_str == "") {
      datat_str = "UNKNOWN"
    }
    ICEPAP_VDATA_INFO[idx]["dtype"] = datat_str

    # get full vector size in words
    full_sz  =  @vdata_name[full_sz_offset + offset]
    full_sz += (@vdata_name[full_sz_offset + offset + 1] << 16)
    full_sz *= 2
    ICEPAP_VDATA_INFO[idx]["full_sz_words"] = full_sz

    # check if there is another data vector to process
    offset  += full_sz
    if(offset >= array_sz) {
      break
    }
    if(@vdata_name[offset] != ICEPAP_VDATA_SIGNATURE) {
      break
    }
  }

  # normal end
  return(0)
}'


#%IU%(vector_name)
#%MDESC%
# Remove any stored information on given data vector
#
def _icepap_vdata_cleanup(vdata_name) '{
  global ICEPAP_VDATA_INFO[]
  local i j idx

  j = ICEPAP_VDATA_INFO[vdata_name]["vectors"]
  for(;j>0;j--) {
    idx = sprintf("%s_%d", vdata_name, j)
    for(i in ICEPAP_VDATA_INFO[idx]) {
      delete ICEPAP_VDATA_INFO[idx][i]
    }
  }

  for(i in ICEPAP_VDATA_INFO[vdata_name]) {
    delete ICEPAP_VDATA_INFO[vdata_name][i]
  }
}'


#%MACROS%
#%IMACROS%
#%AUTHOR% MP BLISS (Original 02/2015).
# %BR%$Revision: 1.1 $ / $Date: 2017/02/24 11:58:32 $
#%TOC%