esrf

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

#%TITLE% DEEPDEVICE.MAC
#
#%NAME%
#  DEEPDEVICE.MAC - Macros for operating deep devices though ethernet
#  (serial line still to be implemented)
#
#%OVERVIEW%
#  These macros simplify scommunication with
#  a family of electronic modules that follows certain communication
#  conventions defined in the Detyector & Electronics Group and adopted in a
#  number of in-house developements at ESRF.
#  This kind of modules are called deep devices in the following description.
#  %BR%%BR%
#  To use these macros, a name (a non-numeric string) must be first
#  assigned to each isgdevice with %B%deepdev_add()%B% macro function.%BR%
#  Once a isgdevice has been succesfully configured, commands and request can 
#  be sent to it by means of %B%deepdev_comm()%B% or %B%deep_comm_ack()%B%
#  macros.%BR%
#  Communication can be flushed by %B%deepdev_flush()%B%.%BR%
#  Other macros are %B%deepdevice%B% that shows a list of devices
#  currently configured, and %B%deepdebug%B% that toggles a special debug mode.
#
#%EXAMPLE%
#  %DL%
#  %DT%deepdev_add(\"mydev\", 2, 0, 0)
#  %DD%Configures the first device connected IP ... and assigns 
#      the name \"mydev\" to it.
#  %DT%print deepdev_comm(\"mydev\", \"?APPNAME\")
#  %DD%Sends the command \"?APPNAME\" to the device and prints the answer
#      on the screen.
#  %DT%deepdevice
#  %DD%Displays a table with the currently configured devices.
#  %XDL%
#
#%DEPENDENCIES%
#  These macros make use of the following macro sets:
#  %UL%
#  %LI%stlist.mac
#  %XUL%
#%END%

#$Log: deepdevice.mac,v $
#Revision 1.5  2020/02/26 16:17:50  guilloud
#remove definition of toupper/tolower/tocase as the are now built-in (use spec_utils for compatibility)
#
#Revision 1.4  2016/06/01 13:53:12  homsrego
##Since SPEC version 6.03.07 HDW_ERR is a built-in variable and it no longer has to be created at user level to take effect / copied from isgdevice.mac (claustre)
#
#Revision 1.3  2013/12/02 06:42:04  perez
#Allow to pass port device port number to deepdev_scan()
#
#Revision 1.2  2013/11/29 11:45:07  perez
#Small cosmetic improvements
#
#Revision 1.1  2012/03/26 12:05:07  perez
#Add deepdev_setdefault() + fix bug on fresh restart
#
#Revision 1.0  2011/03/18 12:17:45  perez
#Initial revision, used on ID26 March2011
#
#

constant DEEPDEV_OK      "\775"
constant DEEPDEV_ERR     "\776"
constant DEEPDEV_ERRANSW "\777"
constant DEEPDEV_SCRIPT  BLISSADM"/bin/deep"

need spec_utils # for toupper

#%IU%
def deepdev_scan(comdev) '{
   local dev_id
   local mode answ app prefix ndev indx

   prefix = ""
   ndev = 0

   mode = deepdev__commode(comdev)
   if (mode == -1)
      return(0)

   dev_id = "#" comdev
   list_add(DEEP_INT, dev_id)

   if (mode == "eth") {
      indx = dev_id "_" ndev
      if(index(comdev,":") == 0) { 
         comdev = comdev ":5001" 
      }

      app = deepdev_lowcomm("?APPNAME", mode, comdev, prefix)
      if (app == DEEPDEV_ERR)
         return(0)

      DEEP_DEV[indx]["app"] =  app
      DEEP_DEV[indx]["addr"] = deepdev_lowcomm("?ADDR", mode, comdev, prefix)
      DEEP_DEV[indx]["name"] = deepdev_lowcomm("?DEVNAME", mode, comdev, prefix)
      ndev = 1

   } else {
      print "Not implemented communication mode"
      return(0)
   }
   DEEP_INT[dev_id]["comdev"] = comdev
   DEEP_INT[dev_id]["ndev"] = ndev
   DEEP_INT[dev_id]["mode"] = mode
   DEEP_INT[dev_id]["setup_n"] = SETUP_N
   return(ndev)
}'


#%UU%(<name>, <comdev>, <type>, <id>)
#%MDESC%
# Checks for the presence of a deep device. If succesful the device gets 
# configured and <name> is internally assigned to it.%BR%
# This macro must be invoked before any other access can be done to the device.
# The communication device <comdev> must be either a number (a serial
# interface in the config file), a string representing a ethernet device
# or an ESRF device name recognised as a Serial Line Device Server.
# %BR%
# The parameters <type> and <id> allow to identify a specific deep device when
# more than one are chained in the same physical communication interface.
# The value of <type> selects the meaning of the identifier <id> as follows:
# %LI%<type>=0: <id> indicates the position of the deep device in the communication chain. The first position is 0.
# %LI%<type>=1: <id> corresponds to the address of the device as returned by the %B%?ADDR%B% command.
# %LI%<type>=2: <id> indicates the name of the device as returned by the %B%?DEVNAME%B% command.
# %LI%<type>=3: <id> indicates the functional type of module as returned by the %B%?APPNAME%B% command.
#
def deepdev_add(name, comdev, type, id) '{
   global ESRF_ERR
   # Since SPEC version 6.03.07 HDW_ERR is a built-in variable and
   # it no longer has to be created at user level to take effect
   if (!(whatis("HDW_ERR") & 0x2000000)) {
     global HDW_ERR
   }
   global DEEP_INT[]
   global DEEP_DEV[]
   global DEEP_CONF[]
   global DEEPDEV_ERRMSG
   local  dev_id ndev i indx prefix mode

   list_test DEEP_INT
   list_test DEEP_CONF

   dev_id = "#" comdev
   if ((ndev = DEEP_INT[dev_id]["ndev"]) <= 0 || !SETUP || \
               DEEP_INT[dev_id]["setup_n"] != SETUP_N) { 
      ndev = deepdev_scan(comdev)
   }

   for (i = 0; i < ndev; i++) {
      indx = dev_id "_" i
      if (type == 0 && id == i  || \
          type == 1 && id == DEEP_DEV[indx]["addr"] || \
          type == 2 && id == DEEP_DEV[indx]["name"] || \
          type == 3 && id == DEEP_DEV[indx]["app"]) {
         if((ndev = list_add(DEEP_CONF, name)) <= 0)
            break;

         DEEP_CONF[name]["index"] = indx
         DEEP_CONF[name]["comdev"] = DEEP_INT[dev_id]["comdev"]
         DEEP_CONF[name]["pos"] = i
         DEEP_CONF[name]["prefix"] = ""
         DEEP_CONF[name]["mode"] = DEEP_INT[dev_id]["mode"]
         DEEP_CONF[name]["app"] = DEEP_DEV[indx]["app"]
         return(ndev);
      }
   }
   return(0)
}'

#%UU%(<name>)
#%MDESC%
# Removes a currently configured deep device from the internal list.
#
def deepdev_remove(name) '{
   if (list_check(DEEP_CONF, name) <= 0)
      return(-1)

   list_remove(DEEP_CONF, name)

   if (list_n(DEEP_CONF) <= 0) {
      unglobal DEEP_INT DEEP_DEV DEEP_CONF
   }
}'

#%UU%(<name>, <type>, <id>)
#%MDESC%
# Checks if <name> is a valid deep device identified by the parameters 
# <type> and <id>. Returns -1 if the device is not a registered device, 0
# if it does not match the identification parameters and 1 otherwise.
# The meaning of  <type> and <id> is the same that in the 
# macro function %B%deepdev_add()%B%. 
#
def deepdev_check(name, type, id) '{
   local indx

   if (list_check(DEEP_CONF, name) <= 0)
      return(-1)

   indx = DEEP_CONF[name]["index"]
   if (type == 0 && id == DEEP_CONF[name]["pos"]  || \
       type == 1 && id == DEEP_DEV[indx]["addr"] || \
       type == 2 && id == DEEP_DEV[indx]["name"] || \
       type == 3 && id == DEEP_DEV[indx]["app"]) {
      return(1)
   } else
      return(0)
}'


#%UU%(<name>, <comm>, [<bindata>])
#%MDESC%
# Sends the command <comm> to the device identified by <name> with 
# acknowledge request.
# If the command is sent succesfully, this macro function returns the
# constant DEEPDEV_OK.
# If and error happens in the device, DEEPDEV_ERRANSW is returned.%BR%
# If there is a communication error, this macro function returns the 
# constant DEEPDEV_ERR.
#
def deepdev_comm_ack(name, comm, bindata) '{
   local answ

   if (index(comm, "#") != 1) {
      comm = "#" comm
   }
   answ = deepdev_comm(name, comm, bindata)
   return(answ)
}'

#%UU%(<name>, <comm>, [<bindata>])
#%MDESC%
# Sends the command <comm> to the device identified by <name>.
# If there is any answer from the device it is returned. If there is no answer
# the macro function returns either DEEPDEV_OK (if the command was sent with no
# error), DEEPDEV_ERR in case of error ot DEEPDEV_ERRANSW.
#
def deepdev_comm(name, comm, bindata) '{
   local comdev dev_id prefix mode answer

   if (DEEPDEBUG) print "deepdev_comm[" name"]: >" comm "<"

   if (!(whatis("DEEP_CONF") & 0x01000000))
      return(DEEPDEV_ERR)

   if (!(name in DEEP_CONF))
      return(DEEPDEV_ERR)

   comdev = DEEP_CONF[name]["comdev"]
   dev_id = "#" comdev
   prefix = DEEP_CONF[name]["prefix"]
   mode   = DEEP_CONF[name]["mode"]

   if (DEEP_INT[dev_id]["flush"]) {
      deepdev__flush(mode, comdev, 0)
   }

   DEEP_INT[dev_id]["flush"] = 1
   answer = deepdev_lowcomm(comm, mode, comdev, prefix, bindata)
   if (answer != DEEPDEV_ERR) {
      DEEP_INT[dev_id]["flush"] = 0

      if (answer == DEEPDEV_ERRANSW)
         print "ERROR answer from " comdev ": ", DEEPDEV_ERRMSG
      else if (DEEPDEBUG) {
         local answmsg
         answmsg = (answer == DEEPDEV_OK)? "--ok--" : answer
         print "deepdev_comm[" name"]: <" answmsg ">"
      }
   }
   return(answer)
}'


#%IU%
def deepdev__flush(mode, comdev, prflag) '{
   if (mode == "eth") {
      sock_par(comdev, "flush")
      # this does not really work, we need something smarter
      while(sock_par(comdev, "queue")) {
        sock_par(comdev, "flush")
        sleep(0.1)
      }
   }
}'

#%IU%
def deepdev_read_line(mode, comdev) '{
   if (mode == "eth") {
      answ = sock_get(comdev, 0)
      if (!answ)
         return(DEEPDEV_ERR)
      else {
         return(answ)
      }
   } else {
      return(DEEPDEV_ERR)
   }
}'

#%IU%
def deepdev__read_array(mode, dev, myarray) '{
   if (mode == "eth") {
      if (sock_get(dev, myarray) <= 0)
         return(DEEPDEV_ERR)
   } else
      return(DEEPDEV_ERR)
}'

#%IU%
def deepdev__read_binary(mode, comdev, myarray) '{
   local answ fstr nchar1 nchar2

   long array header[3]

   if (deepdev__read_array(mode, comdev, header) == DEEPDEV_ERR) {
      print "Error reading binary header"
      return(DEEPDEV_ERR)
   } else if ((header[0] & 0xffff0000) != 0xa5a50000) {
      print "Wrong binary header"
      return(DEEPDEV_ERR)
   } else {
      little_endian = !(header[0] & 0x00000020)
      usechksum  = !(header[0] & 0x00000010)
      units = header[0] & 0x0000000f
      size  = header[1]
      checksum = header[2]

      if (!little_endian) {
         printf("BIG ENDIAN not supported\n")
         return(DEEPDEV_ERR)
      } else if (units == 1) {
         ubyte array myarray[size]
      } else if (units == 2) {
         ushort array myarray[size]
      } else if (units == 4) {
         ulong array myarray[size]
      } else if (units == 8) {
         double array myarray[size]
      } else {
         printf("%d bit data types not supported\n", units * 8)
         return(DEEPDEV_ERR)
      }
      if (deepdev__read_array(mode, comdev, myarray) == DEEPDEV_ERR) {
         print "Error reading binary data"
         return(DEEPDEV_ERR)
      }
      if (usechksum) {
         calcsum = (array_op("sum", myarray) & 0xffffffff)
         if (calcsum != checksum) {
            printf("Checksum mistmach: calculated=0x%08x vs 0x%08x\n", calcsum, checksum)
            return(DEEPDEV_ERR)
         }
      }
   }
   return(size * units)
}'



#%IU%
def deepdev__read_ascii(mode, comdev, naked) '{
   local answ fstr nchar1 nchar2
   local full_answer

   answ = deepdev_read_line(mode, comdev)

   if (answ == DEEPDEV_ERR) {
      print "Communication error with " comdev
      return(DEEPDEV_ERR)
   } else {
      answ = substr(answ, 1, length(answ) - 1)
      if (answ == naked) {
         return("")
      }
      fstr = naked " %n ERROR %n"
      sscanf(answ, fstr, nchar1, nchar2)
      if (nchar1 == 0) {
         print "Wrong answer from " comdev
         return(DEEPDEV_ERR)
      } else if (nchar2 > 0) {
         answ = substr(answ, nchar2 + 1)
         DEEPDEV_ERRMSG = answ
         return(DEEPDEV_ERRANSW)
      } else {
         answ = substr(answ, nchar1 + 1)
      }
   }
   if (index(answ, "\$")) {
      full_answer = ""
      while((answ = deepdev_read_line(mode, comdev)) != DEEPDEV_ERR) {
         if (index(answ, "\$"))
            break
         else
            full_answer = full_answer answ
      }
   } else
      full_answer = answ

   return(full_answer)
}'


#%IU%
def deepdev__read(mode, comdev, naked, bindata) '{
   local answ

   answ = deepdev__read_ascii(mode, comdev, naked)
   if ((answ != DEEPDEV_ERR)  && (answ != DEEPDEV_ERRANSW)) {
      if ((whatis("bindata") & 0x00ffffff) == 0x00010004) {
         if (deepdev__read_binary(mode, comdev, bindata) < 0) {
            print "Binary transfer failed"
            return(DEEPDEV_ERR)
         } else if (DEEPDEBUG)
            print "deepdev_bincomm[" comdev "]: binary block read"
      }
   }
   return(answ)
}'

#%IU%
def deepdev__print_binary_header(header) '{

printf ("0x%08x\n", header[0])
printf ("0x%08x\n", header[1])
printf ("0x%08x\n", header[2])
   little_endian = !(header[0] & 0x00000020)
   usechksum  = !(header[0] & 0x00000010)
   units = header[0] & 0x0000000f
   size  = header[1]
   checksum = header[2]
   print "  binary header:"
   printf("   byte order: %s ENDIAN\n", little_endian? "LITTLE" : "BIG")
   printf("   checksum  : %s\n", usechksum? sprintf("YES : 0x%08x", checksum):"NO")
   printf("   data type : %d byte%s\n", units, (units > 1)? "s":"")
   printf("   size      : %d\n", size)
}'

#%IU%
def deepdev__send_binary(mode, comdev, bindata) '{
   local checksum flags unit timeout
   local typ[] 
   ulong array header[3]

   split(whatis("bindata", "info"), typ)
   type = typ[2]
   if (type == "ubyte" || type == "byte")
      units = 1
   else if (type == "ushort" || type == "short")
      units = 2
   else if (type == "ulong" || type == "long" || type == "float")
      units = 4
   else if (type == "double")
      units = 8

   size = array_op("rows", bindata) * array_op("cols", bindata)
   flags = 0  # little endian, and checksum
   checksum = (array_op("sum", bindata) & 0xffffffff)

   header[0] = (0xa5a50000 | flags | units)
   header[1] = size
   header[2] = checksum

   if (DEEPDEBUG) deepdev__print_binary_header(header)

   timeout = sock_par(comdev, "timeout")
   sock_par(comdev, "timeout", 10)
   sock_put(comdev, header)
   sock_put(comdev, bindata)

#   sock_par(dev, "timeout", timeout)
}'

#%IU%
def deepdev__send_ascii(mode, comdev, comm) '{
   comm = comm "\n"

   deepdev__flush(mode, comdev, 0)
   if (mode == "eth") {
      if (sock_put(comdev, comm) < 0)
         return(DEEPDEV_ERR)
   } else {
      print "Wrong communication mode"
      return(DEEPDEV_ERR)
   }
   return(DEEPDEV_OK)
}'

#%IU%
def deepdev__send(mode, comdev, comm, myarray) '{
   deepdev__flush(mode, comdev, 0)
   if (deepdev__send_ascii(mode, comdev, comm) != DEEPDEV_ERR) {
      if ((whatis("myarray") & 0x00ffffff) == 0x00010004) {
         if (deepdev__send_binary(mode, comdev, myarray) < 0) {
            print "Binary transfer failed"
            return(DEEPDEV_ERR)
         }
      }
      return(DEEPDEV_OK)
   } else
      return(DEEPDEV_ERR)
}'

#%IU%
def deepdev_lowcomm(comm, mode, comdev, prefix, bindata) '{
   local firstc 
   local naked ack query binary

   sscanf(comm, " %s ", naked)
   firstc = substr(naked, 1, 1)
   if (firstc == "#") {
      ack = 1
      naked = substr(naked, 2)
      firstc = substr(naked, 1, 1)
   } else
      ack = 0
   if (firstc == "?") {
      query = 1
      binary = (substr(naked, 2, 1) == "*")
   } else {
      query = 0
      binary = (firstc == "*")
   }
   if (binary ) {
      if ((whatis("bindata") & 0x00ffffff) != 0x00010004) {
         print "deepdevice: needs binary data array"
         return(DEEPDEV_ERR)
      }
   }

   naked = toupper(naked)
   comm = prefix comm

   if (deepdev__send(mode, comdev, comm, (binary && !query)? bindata : 0) == DEEPDEV_ERR)
      return(DEEPDEV_ERR)

   if (query || ack) {
      answ = deepdev__read(mode, comdev, naked, (binary && query)? bindata : 0)
      if (!query && answ == "OK")
         return(DEEPDEV_OK)
      else
         return(answ)
   } else
      return(DEEPDEV_OK)
}'


#%IU%
def deepdev__commode(comdev) '{
   local state_str serpar[]

   if (comdev + 0 == comdev) {
      if (ser_par(comdev, "timeout", 0.5) < 0)
         return(-1)
      else
         return("spec")
   } else if (index(comdev, "/") != 0) {
      ESRF_ERR = -1
      if ((state_str = esrf_io(comdev, "DevStatus")) < 0)
         return(-1)
      else if (index(state_str, "DEEP")) {
         esrf_io(comdev, "DevSetDebug", 0)
         return("deepDS")
      } else {
         serpar[0]  = 3; serpar[1] = 256     ; # 1 sec
         serpar[2]  = 4; serpar[3] = 0       ; # No parity
         serpar[4]  = 5; serpar[5] = 0       ; # 8 data bits
         serpar[6]  = 6; serpar[7] = 0       ; # 1 stop bit
         serpar[8]  = 7; serpar[9] = 9600    ; # baud rate
         serpar[10] = 8; serpar[11] = 0xa    ; # newline 
         ESRF_ERR = -1
         if (esrf_io(comdev, "DevSerSetParameter", serpar) < 0)
            return(-1)
         else 
            return("serialDS")
      }
   } else {
      return("eth")
   }
}'

#%UU%(<name>)
#%MDESC%
#
def deepdev_flush(name) '{
   local comdev dev_id errcode

   comdev = DEEP_CONF[name]["comdev"]
   errcode = deepdev__flush(DEEP_CONF[name]["mode"], comdev, DEEPDEBUG)
   if (errcode != ISGDEV_OK) {
      dev_id = "#" comdev
      DEEP_INT[dev_id]["flush"] = 1
   }
   return(errcode)
}'


#%UU%
#%MDESC%
# Displays a table with the isgdevices currently configured
#
def deepdevice '{
   local ndev comdev dev_id

   if (whatis("DEEP_CONF") & 0x01000000) {
      ndev = list_n(DEEP_CONF)
      printf(" #  %-9s %-20s %-8s %-8s %s\n", "ID", "Interface", \
                                         "Addr", "Module", "Name")
      printf("--- --------- -------------------- -------- -------- -------------------\n")
      for (i = 1; i <= ndev; i++) {
         name = DEEP_CONF[i]
         comdev = DEEP_CONF[name]["comdev"]
         dev_id = DEEP_CONF[name]["index"]
         printf("%2d  %-9s %-20s %-8s %-8s %s\n", i, name, comdev, \
                 DEEP_DEV[dev_id]["addr"], \
                 DEEP_DEV[dev_id]["app"], DEEP_DEV[dev_id]["name"])
      }
   } else {
      print "No isgdevices currently configured."
   }
}'

#%UU%
#%MDESC%
# Switches debug mode for communication with isgdevices
#
def deepdebug '{
   DEBUG ^= 0x40000000
   printf("DEEP debug mode is now: %s\n", DEEPDEBUG? "On":"Off")
}'

def DEEPDEBUG '(DEBUG & 0x40000000)'

def deep_getfile(name, remote_file, local_file) '{
   local dev command naked
   ubyte array dataarray[1]

   command = "?*PROG \'"  remote_file "\'"
   answer = deepdev_comm(name, command, dataarray)

   if (answer != DEEP_ERR) {
      print answer
      print "writting file: " local_file
      fmt_write(local_file, "raw", dataarray)
   }
   return(answer)
}'


#%UU% [hostname]
#%MDESC%
# Launch interactive communication program in a separated xterm.
# Requires the "deep" shell command installed on the current machine.
# 
def deep '{
   global DEEP_DEFAULT
   local  cmd
   local  dev



   if(!file_info(DEEPDEV_SCRIPT,"-x")) {
     print "ERROR: missing \"deep\" script, hint use blissinstaller"
     exit
   }

   dev = $#?"$1":""

   if(!dev && DEEP_DEFAULT) {
     dev = DEEP_DEFAULT
   }

   if(!dev) {
     print "Usage: $0 [hostname]"
     exit
   }

   DEEP_DEFAULT = dev

   cmd = sprintf("xterm -tn vt100 -e %s %s &",DEEPDEV_SCRIPT,dev)
   unix(cmd)
}'


#%IU%(appname, devicename)
#
#
def deepdev_setdefault(appname, argin) '{
  local dev
  local ndev
  local name
  local n
  local args[]

  # check if device name given
  n   = split(argin,args)
  dev = n?args[0]:""
 
  if (!(whatis("DEEP_CONF") & 0x01000000)) {
    print "No isgdevices currently configured."
    return("")
  }

  # try to guess device name
  if(!dev && (ndev=list_n(DEEP_CONF))>=1) {
    if(ndev>1) {
      print "ERROR: multi devices not implemented yet, hint: give hostname"
    }
    i = 1
    dev = DEEP_CONF[i]
  }
  
  # check if valid device type
  if(DEEP_CONF[dev]["app"] != appname) {
    print "ERROR: device not of type \""appname"\""
    dev = ""
  }

  # normal end
  return(dev)
}'


#%MACROS%
#%AUTHOR% P.Fajardo, (Original 6/10).
#  $Revision: 1.5 $ / $Date: 2020/02/26 16:17:50 $
#%TOC%