esrf

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

#%TITLE% ISG.MAC
#
#%NAME%
#  Utility macros for interactive communication with isgdevices.
#
#%CATEGORY% Tools, Isg
#
#%OVERVIEW%
#  This macro set provides utilities to build macros to talk interactively
#  to isgdevices. Most of the internals are implemented by 
#  the macro function %B%isg_main()%B% that can be customised by redefinition
#  of the macros %B%isg_prompt%B%, %B%isg_update%B%, %B%isg_show%B%
#  and %B%isg_process%B%.
#  In this way the functionality may be extended and adapted to specific
#  features of certain isgdevices.
#  %BR%
#  The macro %B%isg%B% is an example of use of %B%isg_main()%B% and can be
#  used as a generic interactive macro to dialogue with any isgdevice.
#
#%EXAMPLE%
#  %DL%
#  %DT%isg 0
#  %DD%Starts the interactive test program to talk to the first isgdevice
#      connected to the serial line port 0.
#  %XDL%
#  %DT%isg isg/serlin109/6 type=VSCANNER
#  %DD%Talks to the first isgdevice connected to the device isg/serlin109/6.
#  %XDL%
#
#%END%

#$Log: isg.mac,v $
#Revision 1.8  2019/11/20 15:40:35  homsrego
#removed toupper and tolower, are builtin in SPEC >= 6.08. for other cases loaded spec_utils.mac
#
#Revision 1.7  2008/07/17 14:40:37  rey
#*** empty log message ***
#
#Revision 1.6  2008/07/17 14:39:25  rey
#documentation changes
#
#Revision 1.5  2004/03/10 17:21:47  rey
#isg_path command added#
#
#Revision 1.4  2003/12/08 17:39:16  fajardo
#some bugs fixed
#
#Revision 1.3  2003/07/03 06:37:30  perez
#Add userconf for blissadm
#
#Revision 1.2  2003/02/21 16:34:05  fajardo
#Support for several isgdevices.
#

jtdo("spec_utils")
jtdo("isgdevice")

#%UU% <device_name> [<parameter>=<value> ...}]
#%MDESC%
#  Macro that runs the interactive loop. %B%isg%B% canrefresh certain
#  information on the screen and at the same time accepts commands 
#  from the standard input.
#  The following optional parameters can be also set.
#  %UL%
#  %LI%Valid parameters:%DL%
#    %DT%position=<pos>
#    %DD%This parameter allows to address a particular unit when the serial
#        line is shared by several isgdevices in dasychain.
#        <pos> is a numerical value it indicates the relative position of
#        the module in the serial line chain starting from 0.
#    %DT%address=<addr>
#    %DD%This parameter allows to address a particular unit when the serial
#        line is shared by several isgdevices in dasychain.
#        <addr> is is treated as the isgdevice address set by 
#        the ADDR command and stored internally in the unit.
#    %DT%type=<type>
#    %DD%This parameter allows to address a particular unit when the serial
#        line is shared by several isgdevices in dasychain.
#        <type> must be an uppercase string that identifies the functional
#        type of isgdevice.
#    %XDL%
#    %DT%isgname=<name>
#    %DD%This is the private name used by spec to identify the device.
#        If not set, the name is forced to %"isg%".
#  %XUL%
#  If no addressing parameter is specified, %B%spec%B% looks for the
#        first unit in the chain.
def isg '{
   global ISG[]
   local retval isgname

   ISG["name"] = "isg"

   if ($# == 0 || (retval = _isginit("$*"))) {
      if (SETUP) print "Error in line: $0 $*"
      if (retval < 0) {
         print "Usage:  $0 device_name [parameter=value ...]"
         if (yesno("\nDisplay online help", 0)) eval("help local isg")
      }
      exit
   }

   rdef isg_prompt  "_isg_prompt"
   rdef isg_update  "_isg_update"
   rdef isg_show    "_isg_show"
   rdef isg_process ""
   
   isg_main(ISG["name"])
}' 

def _isginit(args) '{
   local i nparam auxlist0[] auxlist1[] larr
   local comdev

   nparam = split(args, auxlist0)
   for (i = 0; i < nparam; i++) {
      if (split(auxlist0[i], larr, "=") > 1){
         delete auxlist0[i]
         auxlist1[larr[0]] = larr[1]
      } else if (i > 1) {
         delete auxlist0[i]
         auxlist1[larr[0]] = "yes"
      }
   }
   if (!(0 in auxlist0)) {
      printf("Bad parameters\n")
      return(-1)
   }
   comdev = auxlist0[0]


   if ("isgname" in auxlist1){
      ISG["name"] = auxlist1["isgname"]
      delete auxlist1["isgname"]
      ISG["prompt"] = ISG["name"]
   } else
      ISG["prompt"] = ""

   if ("position" in auxlist1){
      isgdev = isgdevice_add(ISG["name"], comdev , 0, auxlist1["position"])
      if (!ISG["prompt"])
         ISG["prompt"] = "#" auxlist1["position"]
      delete auxlist1["position"]

   } else if ("address" in auxlist1){
      isgdev = isgdevice_add(ISG["name"], comdev , 1, auxlist1["address"])
      if (!ISG["prompt"])
         ISG["prompt"] = "&" auxlist1["address"]
      delete auxlist1["address"]

   } else if ("type" in auxlist1){
      isgdev = isgdevice_add(ISG["name"], comdev , 3, auxlist1["type"])
      if (!ISG["prompt"])
         ISG["prompt"] = auxlist1["type"]
      delete auxlist1["type"]

   } else {
      isgdev = isgdevice_add(ISG["name"], comdev , 0, 0)
   }

   if (isgdev <= 0) {
      printf("No isgdevice found at : %s\n", comdev)
      return(1)
   }

   i = ""
   for (i in auxlist1) {
      print "Invalid parameter: " i "=" auxlist1[i]
   }
   if (i != "")
      return(-1)
   else
      return(0)
}'

def isgserver 'runforever("isg $*")'

def _isg_prompt  '{
if (ISG["prompt"])
   printf("ISG(%s)", ISG["prompt"])
else
   printf("ISG")
}'
def _isg_update  'update = 0'
def _isg_show    ''
def _isg_process 'return(-1)'

#%UU%(<isgdev> [, <options> [, <minT>, <maxT>]])
#%MDESC%
# This macro function runs a loop to provide interactive communication to an
# isgdevice. Among the available features :
# %LI%Checks for keyboard input and compares with the list commands recognised
#     by the isgdevice. If the command is recognised by the device then it sends
#     it through the serial line. If not, the macro tries execution by the eval()
#     function.
# %LI%Can update information on the screen, like the status of the device.
# %LI%Whith the option 'S' implements a TCP server for isgdevices.
# %LI%Implements special commands (starting by a dot character \".\" )
# %LI%It can be customised by macro redefinition.
#
def isg_main(isgname, options, minT, maxT) '{
   global ISG_NCOMM
   local updateT update_flag line remcomm
   local line time0 devcomm answer
   local commlist[]

   if (minT <= 0) minT = .02
   if (maxT <= 0) maxT = 2
   updateT = maxT
   update_flag = 0

   ISG_NCOMM++

   if (isg_get_commands(isgname, commlist) != ISGDEV_OK) {
      print "Error talking to ISGdevice \`" isgname "\'."
      return(1)
   }
   
   isg_update
   isg___process(isgname, ".", 0)

   while(1){
      time0 = time()
      line = ""
      devcomm = 0
      printf("\n")
      isg___prompt
      while(1) {
	 sleep(0.05)
         c = input(-1)
         if (c == "\n") {
            if (line && !devcomm)
               devcomm = isg_check_devcomm(devcomm, line, commlist)
            printf("\n")
            tty_cntl("cd")
            break
         } else if ((c == "\b" || c == "\177") && line) {
            line = substr(line, 0, length(line)-1)
            printf("\b \b")
            if (devcomm)
               devcomm = isg_check_devcomm(devcomm, line, commlist)
         } else if (asc(c) == 27) {
            while(input(-1));
         } else if (c >= " " && c <= "z") {
            if (c == " " && devcomm == 0)
               devcomm = isg_check_devcomm(devcomm, line, commlist)
            line = line c
            printf(c) 
         }
         if ((time()-time0) > updateT) {
            isg_update
            if (update_flag){
               printf("\n")
               isg___prompt
               updateT = minT
            } else {
               updateT *= 2
               if (updateT > maxT)
                  updateT = maxT
            }
            time0 = time()
         }
      }
      if (devcomm > 0) {
         isg___command(isgname, line)
         updateT = minT
         ISG_NCOMM++
      } else if (devcomm < 0) {
         answer = isg___process(isgname, line, commlist)
         if (answer == ISGDEV_ERR)
            return(0)
         ISG_NCOMM++
      } 
      isg_update
   }
}'

def isg___command(isgname, line) '{
   local answer

   answer = isgdevice_comm_ack(isgname, line)
   if(answer == ISGDEV_ERR)
      printf("Error sending command [%s].\n", line)
   else if (answer != "")
      printf("%s\n", answer)

   return(answer)
}'

#%IU%
#
def isg_check_devcomm(devcomm, line, commlist) '{
   local linelen ucline commlen comm i c
   
   linelen = length(line)
   commlen = (devcomm >= 0)? devcomm : -devcomm
   
   if (commlen) {
      if (linelen >= commlen)
         return(devcomm)
      else {
         for(i = 0; i < linelen; i++) printf("\b")
         printf("%s", line)
         return(0)
      }
   } else {
      ucline = ""
      for(i = 1; i <= linelen; i++) {
         c = asc(substr(line, i, 1))
         ucline = sprintf("%s%c", ucline, (c >= 0x60)? c - 0x20 : c)
      }
      sscanf(ucline, " %s", comm)
      if (comm == "")
         return(0)
      if (comm in commlist) {
         for(i = 1; i <= linelen; i++) printf("\b")
         printf("%s", ucline)
         return(linelen)     
      } else
         return(-linelen)     
   }
}'


#%IU%
#%MDESC%
#  Prints the user prompt.
#
def isg___prompt '
  printf("%d.", ISG_NCOMM)
  isg_prompt
  printf(" > %s", line)
'


#%IU%
#%MDESC%
#  Macro function that analyzes and execute special commands
#  It returns '0' if it is not an internal command, '1' if the command
#  is recognized and `-1' if it is not.
#
def isg___process(isgname, line, commlist) '{
   local comm[] npar info[] i sl_id

   if ((npar = split(line, comm) - 1) >= 0){
      if (substr(comm[0], 1, 1) != ".")
         return(eval(line))

      if (comm[0] == "." || comm[0] == ".h" || comm[0] == ".help") {
         print
         if (whatis("commlist") & 0x01000000) {
            print "\tDevice commands: " isgdevice_comm_ack(isgname, "?VER") 
            print "\t---------------"
            isg_show_commlist(commlist)
            print
         }
         print "\tInternal commands:"
         print "\t------------------"
         isg_show_comm("., .h, .help", "Display available commands")
         isg_show_comm(".i, .info",    "Display device information")
         isg_show
         isg_show_comm(".s, .save",    "Save configuration to file")
         isg_show_comm(".l, .load",    "Load configuration from file")
         isg_show_comm(".d, .debug",   "Switch debug mode")
         if (ISGDEV_CONF[isgname]["mode"] != "gpib") {
            isg_show_comm(".f, .flush",   "Flush communication interface")
         }
         isg_show_comm(".q, .quit",    "Quit interactive macro")

      } else if (comm[0] == ".info" || comm[0] == ".i") {
         print
         print "\tSPEC info"
         print "\t---------"
         if (ISGDEV_CONF[isgname]["mode"] == "gpib")
            print "\tGPIB address: " ISGDEV_CONF[isgname]["comdev"]
         else
            print "\tSerial line : " ISGDEV_CONF[isgname]["comdev"] \
                  "   Position " ISGDEV_CONF[isgname]["pos"]
         sl_id = "#" ISGDEV_CONF[isgname]["comdev"] "_" \
                     ISGDEV_CONF[isgname]["pos"]
         print "\tModule type : " ISGDEV_DEV[sl_id]["app"]
         print "\t    address : " ISGDEV_DEV[sl_id]["addr"]
         print "\t       name : " ISGDEV_DEV[sl_id]["name"]
         if ("?INFO" in commlist) {
            npar = split(isgdevice_comm_ack(isgname, "?INFO"), info, "\n")
            print
            print "\tDevice info"
            print "\t-----------"
            for (i = 0; i < npar; i++)
               printf("\t%s\n", info[i])
         }

      } else if (comm[0] == ".exec" || comm[0] == ".ex") {
         line = substr(line, length(comm[0]) + 1)
         isg___command(isgname, line)

      } else if (comm[0] == ".debug" || comm[0] == ".d") {
         isgdebug

      } else if (comm[0] == ".flush" || comm[0] == ".f") {
         print "Flushing communication interface..."
         isgdevice_flush(isgname)

      } else if (comm[0] == ".quit" || comm[0] == ".q") {
         stopforever()
         return(ISGDEV_ERR)
      } else {
         isg_process

         if (comm[0] == ".save" || comm[0] == ".s") {
            isg_save(isgname)

         } else if (comm[0] == ".load" || comm[0] == ".l") {
            isg_upload(isgname)
            
         } else {
            printf("Internal command [%s] not valid.\n", comm[0])
         }
      }
   }
   return(ISGDEV_OK)
}'

def isg_show_comm(comm, descr) '{printf("\t%-20s -  %s\n", comm, descr)}'

def isg_show_commlist(commlist) '{
   local comm rcomm

   for (comm in commlist) 
      commlist[comm] = 0

   for (comm in commlist) {
      if (commlist[comm])
         continue
      commlist[comm] = 1
      if (substr(comm, 1, 1) != "?") {
         rcomm = sprintf("?%s", comm)
         if (rcomm in commlist) {
            commlist[rcomm] = 1
            comm = sprintf("%s, %s", comm, rcomm)
         }
      } else {
         sscanf(comm, "?%s", rcomm)
         if (rcomm in commlist) {
            commlist[rcomm] = 1
            comm = sprintf("%s, %s", rcomm, comm)
         }
      }
      printf("\t%-20s - \n", comm)
   }
}'

def isg_get_commands(isgname, commlist) '{
   local answ list[]
   
   answ = isgdevice_comm(isgname, "?HELP")
   if (answ == ISGDEV_ERR)
      return(answ)

   split(answ, list)
   for (i in list) {
      sscanf(list[i], "%s", answ)
      commlist[answ] = 0
   }
   return(ISGDEV_OK)
}'

def runonce(macro, key) '{
   local comm

   if (!key) key = "runatprompt"

   comm = macro ";runonce_cleanup " key "\n"
   cdef("prompt_mac", comm, key)

   comm = "runonce_cleanup " key "\n"
   cdef("cleanup_once", comm)
}'

def runonce_cleanup 'cdef("prompt_mac","","$1","delete")'

def runforever(macro) '{
   local file comm

   file = "/tmp/forever." SPEC
   if (file_info(file, "-r") == 1) {
      comm = "rm " file
      unix(comm)
   }
   comm = "echo \"do_forever\nqdo " file "\" > " file
   unix(comm)
   comm = "chmod a+rw " file
   unix(comm)

   rdef do_forever macro
   spec_par("keep_going", 1)
   qdofile(file)
   return(file)
}'

def stopforever() '{
   spec_par("keep_going", 0)
   rdef do_forever "if (0)"
}'

def isg_save(isgname, info, fext) '{
   local path file filepath indx aux

   path = isg_path("config/")

   if (!fext)
      fext = "." tolower(ISGDEV_CONF[isgname]["app"])
   aux = isg_ls(isgname, path, fext)

   file = getval("\nSave current configuration as", aux)

   if (indx = index(file, ".")) {
      if (substr(file, indx) != fext) {
         print "No a valid filename."
         return(-1)
      }
   } else
      file = file fext
    
   if (!info) {
      info = isgdevice_comm(isgname, "?INFO")
      if (info == ISGDEV_ERR)
         return(-1)
   }
   filepath = path file
   if (file_info(filepath, "-e")) {
      if (!yesno(sprintf("Overwrite %s", file), 0))
         return(-1)
      unix(sprintf("rm %s", filepath))
   }
   unix(sprintf("(umask 0; touch %s)", filepath))

   if (!file_info(filepath, "-w")) {
      print "You do not have rights to write " filepath
      return(-1)
   }
   fprintf(filepath, "#ISGDEVICE %s %d\n", isgdevice_comm(isgname, "?VER"), time())
   fprintf(filepath, info)
   close(filepath)
   print "Module configuration saved in: " filepath
   return(0)
}'

def isg_path(lpath) '{
   local path

   if (whatis("BLISSADM") & 0x04000000) {
      path = BLISSADM "/local/isg/" lpath
   } else {
      path = SPECD "/../local/userconf/isg/" lpath
   }
   return(path)
}'

def isg_upload(isgname, file, fext) '{
   local finfo[] path filepath
   local cmdlst[] ncmd
   local i answ

   path = isg_path("config/")

   if (!file) {
      if (!fext)
         fext = "." tolower(ISGDEV_CONF[isgname]["app"])

      file = isg_ls(isgname, path, fext)
      file = getval("\nFile to load", file)
      if (!file || index(file, "*"))
         return(0)
      if (!index(file, "."))
         file = file fext
   }
   filepath = path file

   if (isg_file_header(filepath, finfo, 1) < 0) {
      print "Problem reading file: " file
      return(-1)
   } 
   printf("\nContent of \`%s\', %s %s - %s:\n", \
             file, finfo["type"], finfo["version"], date(finfo["date"]))
   for(ncmd = 0; (line = getline(filepath)) != -1; ) {
      local aux

      if (sscanf(line, " %s", aux) <= 0 || substr(aux, 1, 1) == "#")
         continue
      # a patch for the ADDR command
      if (sscanf(line, " ADDR \"%s", aux) == 1)
         line = substr(line, 1, index(line, "\"") - 1) substr(aux, 1, index(aux, "\"") - 1)

      cmdlst[ncmd++] = line
      print "  " line
   }
   if (yesno("\nUpload configuration into the device", 0)) {
      for (i = 0; i < ncmd; i++) {
         if (substr(cmdlst[i], 1, 1) == " ") {
            print ">>> " cmdlst[i]
            print isgdevice_comm_ack(isgname,  cmdlst[i])
         }
      }
      return(1)
   } else
      return(0)
}'

#!!! tolower and toupper are builtin in SPEC >= 6.08
# in other case is defined in spec_utils.mac
#def tolower(str) '{return(__tocase(str, 1))}'
#def toupper(str) '{return(__tocase(str, 0))}'

def __tocase(str, case) '{
  string array str_a[length(str)]

  str_a = str
  str_a = str_a + (case * (str_a >= asc("A")) - (str_a >= asc("a"))) * (asc("a") - asc("A"))

  return(sprintf("%s", str_a))
}'

def isg_ls(isgname, path, fext) '{
   local aux filelist[] filepath finfo[]
   local oldestd oldestf

   uxcom = "cd " path "; ls *" fext
   unix(uxcom, aux)
   nf = split(aux, filelist, "\n")
   for (i = 0; i < nf; i++){
      if (filelist[i])
         filepath = path  filelist[i]
      else
         continue
      if (isg_file_header(filepath, finfo) < 0) {
         print filelist[i] "  **BAD**"
      } else {
         printf("%-15s  %9s %s - Created: %s\n", \
              filelist[i], finfo["type"], finfo["version"],date(finfo["date"]))
         if (finfo["date"] > oldestd) {
            oldestf = filelist[i]
            oldestd = finfo["date"]
         }
      }
   }
   if (!oldestf)
      oldestf = "config" fext
   return(oldestf)
}'

def isg_file_header(filepath, finfo, verbose) '{
   local line unittype, version, cdate

   if(getline(filepath, "open") < 0) {
      return(-1)
   } 
   line = getline(filepath)
   if (sscanf(line, "#ISGDEVICE %s %s %d", unittype, version, cdate) != 3) {
      if (verbose) print "Not a isgdevice configuration file."
      return(-1)
   }

   finfo["type"] = unittype
   finfo["version"] = version
   finfo["date"] = cdate
   return(0)
}'

#%MACROS%
#%IMACROS%
#%AUTHOR% P.Fajardo, Revision: 8/00, (Original 8/00).
#  $Revision: 1.8 $ / $Date: 2019/11/20 15:40:35 $
#%TOC%