esrf

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

#%TITLE% MUSST.MAC
#
#%NAME%
#  Macros for operating and testing the MUSST module
#
#%CATEGORY% Detection, Other hardware, Isg
#
#%OVERVIEW%
#  This macro set allows to set MUSST isgdevices in %B%spec%B%, configure
#  their signal inputs as pseudocounters, edit and download programs,
#  and provide simple macros for basic operation.
#  %BR%
#  An interactive macro %B%musst%B% can be used for device configuration and
#  monitoring.
#  %BR%
#  More than one MUSST module can be operated through this macro set.
#
#%EXAMPLE%
#  %DL%
#  %DT%musstsetup mainsync 2
#  %DD%Configures a MUSST unit connected to the serial line #2 and assigns the
#      name \"mainsync\" to it.
#  %DT%musstsetup pitch id33/serlin/11
#  %DD%Configures a MUSST unit associated to the TACO device \"id33/serlin/11\"
#  %DT%musst
#  %DD%Starts the interactive test program with the default unit.
#  %XDL%
#
#%DEPENDENCIES%
#  These macros make use of the following macro sets:
#  %UL%
#  %LI%stlist.mac
#  %LI%isgdevice.mac
#  %LI%isg.mac
#  %XUL%

#%SETUP%
# A %B%musstsetup%B% macro must be included in the setup file for every MUSST
# unit configured. Each unit is identified by its name that must be a unique
# isgdevice identifier. 
#%END%

#%HISTORY%
#$Log: musst.mac,v $
#Revision 1.12  2023/03/28 09:08:35  claustre
#Fix a bug in muss_config with timer_mode
#
#Revision 1.11  2014/10/09 13:20:31  berruyer
#Add a fon;
#
#musst_get_timer return the musst CLOCK
#improve Macro counter to be used without MUSST is the master spec timer
#
#Revision 1.10  2011/03/09 09:24:25  homsrego
#*musst_list_files: didn't list all files in the directory - fixed
#
#Revision 1.9  2010/08/16 11:56:35  guilloud
#*musst_list_files : add a +1 to be able to see all files of the directory.
#*cosmetic.
#
#Revision 1.8  2009/11/20 00:00:00  ahoms
#Do not ask channel values while RUNCT is active; estime it for timer
#
#Revision 1.7  2008/10/09 13:46:14  beteva
#changes in musst_getdata - maxln fixed to 1K
#
#Revision 1.6  2008/08/12 14:24:31  rey
#doc change
#
#Revision 1.5  2008/08/12 14:16:24  rey
#documentation changes
#
#Revision 1.4  2008/07/29 08:43:59  ahoms
#Added basic macro counter/timer support
#
#Revision 1.3  2008/06/30 10:40:54  guijarro
#added extra argument to musst_getdata to specify buffer (0 by default = old behaviour-
#
#Revision 1.2  2005/08/17 14:22:59  guijarro
#new version, added fast update and getdata
#
#Revision 1.1  2004/03/10 17:24:26  rey
#Initial revision
#
#
#%END%

jtdo("isg.mac")

#%UU% <musst_name> <comdev> [<parameter>=<value> ...]
#%MDESC%
#  Configures a MUSST unit connected to <comdev> with the name <musst_name>.
#  The following optional parameters can be also set.
#  %UL%
#  %LI%Valid parameters:%DL%
#    %DT%address=<addr>
#    %DD%This parameter allows to address a particular unit when the serial 
#        line is shared by several isgdevices in dasychain.
#        If <addr> is a non-numerical value, it is treated as the isgdevice
#        address set by the ADDR command and stored internally in the unit.
#        On the other hand if <addr> is a numerical value it indicates the 
#        relative position of the module in the serial line chain starting
#        from 0.
#        If this parameter is not specified, %B%spec%B% looks for the 
#        first MUSST unit in the chain.
#    %DT%ch<n>=<cnt_mne>
#    %DD%If <cnt_mne> is a valid counter mnemonic, it gets configured as a
#        pseudocounter that uses the nth channel in MUSST (n = 1 to 6).
#        The \"Scale Factor\" value in the %B%spec%B% config file applies.
#    %DT%update=<update_time>
#    %DD%Sets the update time in seconds for the interactive macro (musst).
#        The default is 1 second.
#    %XDL%
#  %XUL%
def musstsetup '{
   local retval

   if ($# == 0 || (retval = _musstsetup("$*"))) {
      if (SETUP) print "Error in line: $0 $*"
      if (retval < 0) {
         print "Usage:  $0 musst_name comm_device [parameter=value ...]"
         if (yesno("\nDisplay online help", 0)) eval("help local musst")
      }
   } 
}'


def _musstsetup(args) '{
   global MUSST[]
   global MUSST_AUX[]
   global MUSST_TIMER[]

   local i nparam auxlist0[] auxlist1[] larr 
   local comdev isgdev isgname 
   local chmne[] nchan macrodef

   MUSST_TIMER["1MHZ"]= 1e6
   MUSST_TIMER["1KHZ"]= 1e3

   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) && (1 in auxlist0))) {
      printf("Bad parameters\n")
      return(-1)
   }
   isgname = auxlist0[0]
   comdev = auxlist0[1]

   if (("address" in auxlist1) && !(index(comdev, ":"))){
      if (auxlist1["address"] + 0 == auxlist1["address"]) 
         isgdev = isgdevice_add(isgname, comdev , 0, auxlist1["address"])
      else 
         isgdev = isgdevice_add(isgname, comdev , 1, auxlist1["address"])
      delete auxlist1["address"]
   } else 
      isgdev = isgdevice_add(isgname, comdev , 3, "MUSST")

   if (isgdev <= 0 || isgdevice_check(isgname, 3, "MUSST") == 0) {
      printf("No MUSST device found at %s: %s\n", \
            index(comdev, ":")? "gpib address" : "serial port", comdev)
      return(1)
   }

   if (MUSST_AUX["setup_n"] != SETUP_N) {
      list_init MUSST
      MUSST_AUX["setup_n"] = SETUP_N
   }

   if (list_add(MUSST, isgname) <= 0) {
      printf("Invalid musst name: %s\n", isgname)
      return(-1)
   }

   MUSST[isgname]["setup"] = "musstsetup " args

   nchan = 0
   macrodef = ""
   for(i = 0; i <= 6; i++) {
      local chname mne num

      if (i == 0)
         chname = "timer"
      else
         chname = "ch" i

      num = (chname in auxlist1)? cnt_num(mne = auxlist1[chname]) : -1
      delete auxlist1[chname]
      if (num >=0 && counter_par(num, "controller")=="NONE") {
         chmne[i] = mne
         nchan++
      } else
         chmne[i] = -1

      MUSST[isgname][chname] = chmne[i]
      macrodef = macrodef ", " chmne[i]
   }

   if (nchan) {
      macrodef = sprintf("musst_getcounts(\"%s\"%s)\n", isgname, macrodef)
      cdef("user_getcounts", macrodef, isgname)
   } else {
      cdef("user_getcounts", "", isgname, "delete")
   }

   MUSST[isgname]["update"] = (auxlist1["update"] + 0)
   delete auxlist1["update"]

   setup_tail("musst", isgname)

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

def musstunsetup '{
   local isgname

   isgname = "$1"
   cdef("", "", isgname, "delete")
   isgdevice_remove(isgname)

   list_remove(MUSST, isgname)
   if (list_n(MUSST) <= 0)
      unglobal MUSST MUSST_AUX
}'

def musst_setdefault(isgname) '{
   if (MUSST[0] <= 0) {
      print "No MUSST devices configured. Use first \`musstsetup\'."
      return(0)
   }
   if (isgname != "") {
      MUSST_AUX["default"] = isgname
      if (!list_check(MUSST, isgname)) {
         print "\`" isgname "\' is not a valid MUSST unit."
      }
   } 
   if (!list_check(MUSST, MUSST_AUX["default"])) {
      MUSST_AUX["default"] = MUSST[1]
      print "Setting \`" MUSST_AUX["default"] "\' as default MUSST unit."
   }
   return(1)
}'

def musst_comm(comm, isgname) '{
   isgname = isgname? isgname : ""
 
   if (!musst_setdefault(isgname)) {
      print "Bad musst unit."
      exit
   }
   isgname = MUSST_AUX["default"]

   if (index(comm, "?") == 1) 
      return (isgdevice_comm(isgname, comm))
   else
      return (isgdevice_comm_ack(isgname, comm))
}'

def musst_get_timer_scale(isgname) '{
    local cfg npar pars[]

    cfg= musst_comm("?TMRCFG", isgname)
    npar= split(cfg, pars)
    
    return(MUSST_TIMER[pars[0]])
}'

def musstcomm '{
   print musst_comm("$1", "$2")
}'

#%UU% [<musst_name>]
#%MDESC%
# Prints the current state (Sends the request ?STATE to MUSST).
def musststate 'musstcomm "?STATE RETCODE" $*'


#%UU% [<musst_name>]
#%MDESC%
# Starts program execution (Sends the command RUN to MUSST).
def musstrun 'musst_comm("RUN", "$*")'

#%UU% [<musst_name>]
#%MDESC%
# Stops program execution (Sends the command STOP to MUSST).
def musststop 'musst_comm("STOP", "$*")'


def musst_getstate(isgname) '{
   local state
   
   if ((state = isgdevice_comm(isgname, "?STATE")) == ISGDEV_ERR)
      state = "???"

   MUSST_AUX["nextT"] = time() + 30 

   if (state == "IDLE" || state == "STOP" || state == "BREAK") {
      if ((state = isgdevice_comm(isgname, "?STATE RETCODE")) == ISGDEV_ERR)
         state = "???"
   }
   if (state != MUSST[isgname]["state"]) {
      MUSST[isgname]["state"] = state
      return(1)
   } else
      return(0)
}'

def musst_getcounts(isgname, _timer, _ch1, _ch2, _ch3, _ch4, _ch5, _ch6) '{
   local answ i j
   local chlist[] chliststr

   if (_timer >= 0 && !counter_par(_timer, "disable")) {
      chlist[0] = _timer
      chliststr = "TIMER"
   } else {
      chlist[0] = -1
      chliststr = ""
   }

   chlist[1] = _ch1
   chlist[2] = _ch2
   chlist[3] = _ch3
   chlist[4] = _ch4
   chlist[5] = _ch5
   chlist[6] = _ch6
   for (i = 1; i <= 6; i++) {
      if (chlist[i] >= 0 && !counter_par(chlist[i], "disable"))
         chliststr = chliststr " CH" i
      else
         chlist[i] = -1
   }
  
   if (chliststr != "") {
      local value[]

      answ = isgdevice_comm(isgname, sprintf("?VAL %s", chliststr))
      split(answ, value)

      for (i = j = 0; i <= 6; i++) {
         if (chlist[i] >= 0)
             S[chlist[i]] = value[j++] / counter_par(chlist[i], "scale")
      }
   }
}'

#the value maxnl has been set to 1K instead of the calculated value to permit
#the transfer of all the data, as it seems that it overruns some of the data
#and unfortunately delivers bad data!!!!
def musst_getdata(isgname, nlines, npts, mdat, buffer) '{
  local maxnl

  maxnl = 1024 #int(((0x10000 / 4) - 1) / npts)
  long array ldat[maxnl][npts]

  for (nl = 0; nl < nlines; nl += maxnl) {
    nn = nlines - nl
    if (nn > maxnl)
      nn = maxnl
    comm = sprintf("?*EDAT %d %d %d", nn * npts, buffer, nl * npts)
    ndat = isgdevice_getdata(isgname, comm, ldat)
    if (ndat != nn * npts) 
      return(0)
    mdat[nl:nl+nn-1][] = ldat
  }
  return(nlines)
}'

#%UU% [<device_name>]
#%MDESC%
#  Macro that runs the interactive loop. %B%musst%B% refresh certain
#  information on the screen and at the same time accepts commands 
#  from the standard input. 
#  %BR%
#
def musst '{
   local isgname options

   isgname = $#? "$1": ""
   if (!$# || list_check(MUSST, isgname) > 0){
      if (musst_setdefault(isgname)) {
         isgname = MUSST_AUX["default"]
         MUSST[isgname]["prompt"] = "(" MUSST_AUX["default"] ")"
      }
   } else if (isgdevice_add("Musst", "$1", 3, "MUSST") > 0) {
      isgname = "Musst"
      MUSST[isgname]["prompt"] = ""
   } else {
      print "No MUSST unit found."
      isgname = ""
   }
   if (!isgname) {
      print "Usage: $0 musstname" 
      print "  or   $0 comm_device (serial or GPIB)"
      if (yesno("\nDisplay online help", 0))
         eval("help local musst")
   } else {
      rdef isg_prompt  "musst_prompt"
      rdef isg_update  "musst_update"
      rdef isg_show    "musst_show_comm"
      rdef isg_process "musst_process_comm"
   
      isg_main(isgname, options, 0, MUSST[isgname]["update"])
   }
}'

def musst_update '{
   update_flag = musst_getstate(isgname);
}'

#%IU%
#%MDESC%
#  Displays the prompt on the screen. If the "update" flag is set also
#  displays the board state.
#
def musst_prompt '
   printf("MUSST%s: %s - %s", MUSST[isgname]["prompt"], date(), \
                     MUSST[isgname]["state"])
'

def musst_show_comm '
   isg_show_comm(".p, .program",  "Upload program")
   isg_show_comm(".e, .edit",     "Edit program file")
   isg_show_comm(".t, .trace",    "Trace program execution")
   isg_show_comm(".c, .config",   "Display spec setup")
'

#%IU%
#%MDESC%
#
def musst_process_comm '
    if (comm[0] == ".program" || comm[0] == ".p") {
        local filepath line clear  _msg

        filepath = musst_choose_file(isgname)
        if (getline(filepath, "open") < 0){
            printf("Can\'t open file \`%s\'.\n", filepath)
        }
        else {
            _msg = sprintf("\nDisplay file content(%s)", filepath)
            if (yesno(_msg, 0)) {
                print "\nBegin ::::::::::::::::::::::::::: Program code\n\n"
                getline(filepath, "open")
                while ((line = getline(filepath)) != -1){
                    print line
                }
                print
                print "\nEnd ::::::::::::::::::::::::::::: Program code\n\n"
            }

            if (yesno("\nUpload program into the device", 1)) {
                clear = yesno("\nDelete existing  program", 1)
                print
                if (musst_upload_program(isgname, filepath, clear)) {
                    print "\nProgram succesfully loaded."
                }
            }
        }
        return(1)

    }
    else if (comm[0] == ".edit" || comm[0] == ".e") {
        local filepath line clear

        filepath = musst_choose_file(isgname)
        if (!file_info(filepath, "isreg")){
            if (yesno(sprintf("File \`%s\' does not exist. Cancel", filepath), 1))
                return(1)
        }
        unix(sprintf("touch %s", filepath))
        if (getline(filepath, "open") < 0)
            printf("\nCan\'t open file.")
        else {
            getline(filepath, "close")
            unix(sprintf("%s %s &", EDITOR, filepath))
        }

        return(1)
    }
    else if (comm[0] == ".trace" || comm[0] == ".t") {
        musst_trace_program(isgname)
        return(1)

    }
    else if (comm[0] == ".config" || comm[0] == ".c") {
        local aux

        print

        if (ISGDEV_CONF[isgname]["mode"] == "gpib") {
            printf("\tGPIB address:  %s\n", ISGDEV_CONF[isgname]["comdev"])
        }
        else {
            printf("\tSerial line: %s (%s)\n", ISGDEV_CONF[isgname]["comdev"], \
                   ISGDEV_CONF[isgname]["mode"])
            printf("\tPosition   : %d\n", ISGDEV_CONF[isgname]["pos"])
        }

        print

        aux = MUSST[isgname]["foutbeam"]

        printf("\tPseudocounters:\n")
        musst_cfg_show_cnt(isgname, "ch1")
        musst_cfg_show_cnt(isgname, "ch2")
        musst_cfg_show_cnt(isgname, "ch3")
        musst_cfg_show_cnt(isgname, "ch4")
        musst_cfg_show_cnt(isgname, "ch5")
        musst_cfg_show_cnt(isgname, "ch6")
        musst_cfg_show_cnt(isgname, "timer")
        print
        printf("\tSetup line: %s\n", MUSST[isgname]["setup"])
        return(1)
    }
'


def musst_cfg_show_cnt(isgname, chname) '{
   local mne

   printf("\t%20s: ", chname)
   mne = MUSST[isgname][chname]
   if (mne < 0)
      printf("---")
   else
      printf("%s [x%g]", mne, counter_par(cnt_num(mne), "scale"))
   print
}'


def musst_list_files(path, lastfilepath) '{

   local aux filelist[] opminfo[] file selected
   local ii, n, n0

   uxcom = "ls " path
   unix(uxcom, aux)
   n0 = n = split(aux, filelist, "\n")
   for (ii in filelist) {
      filepath = path filelist[ii]
      if (!file_info(filepath, "isreg")) {
         delete filelist[ii]
         n--
      }
   }

   if (!n) {
      printf("No files found in \`%s\'.\n", path)
      return("")
   }

   print
   print "Directory: " path
   print
   print "File                      Date"
   print "------------------------  ------------------------"
   selected = 0
   for (ii = 0; ii < n0; ii++) {
      local filepath lastfound mtime lastmtime

      if (!(ii in filelist))
         continue

      file = filelist[ii]
      filepath = path file

      if (filepath == lastfilepath) {
         lastfound = 1
         selected = ii
         tty_cntl("so")
      }
      mtime = file_info(filepath, "mtime")
      printf("%-25s %s\n", file, date(mtime))
      tty_cntl("se")
      if (mtime > lastmtime) {
         lastmtime = mtime
         if (!lastfound)
           selected = ii
      }
   }
   return(filelist[selected])
}'

def musst_choose_file(isgname) '{
   local file path

   path = isg_path("musst/")
   file = musst_list_files(path, MUSST[isgname]["lastfilepath"])
   file = getval("\nFile to load", file)
   if (index(file, "/"))
      return(file)
   else {
      file = path file
      return(file)
   }
}'

def musst_slow_upload_program(musst_name, filepath, clear) '{
   local line answer

   if (getline(filepath, "open") < 0)
      return(0)

   MUSST[musst_name]["lastfilepath"] = filepath

   if (clear)
      isgdevice_comm_ack(musst_name, "CLEAR")

   while ((line = getline(filepath)) != -1) {
      answer = isgdevice_comm_ack(musst_name, sprintf("+%s", line))
      if(answer == ISGDEV_ERR) {
         printf("\nError sending command [%s].\n", line)
         printf("Upload aborted.\n")
         break;

      } else if (answer != "OK") {
         printf("Error in line: %s", line)
         printf("  %s\n", answer)
      }

   }
   return (1)
}'

def musst_upload_program(musst_name, filepath, clear) '{
   local line answer prog

   if (getline(filepath, "open") < 0) {
      print "Cannot open program file:", filepath
      return(0)
   }
   MUSST[musst_name]["lastfilepath"] = filepath

   if (clear)
      isgdevice_comm(musst_name, "CLEAR")

   prog = ""
   while ((line = getline(filepath)) != -1) {
      prog = prog "+" line
   }
   answer = isgdevice_comm(musst_name, prog)
   if(answer == ISGDEV_ERR) {
      printf("\nError sending command [%s].\n", line)
      printf("Upload aborted.\n")
      return(0);
   }
   answer = isgdevice_comm_ack(musst_name, "?list err")
   if (answer != "") {
      print "Program contains errors:"
      printf("%s\n", answer)
      return(0)
   } else
     return (1)
}'


def musst_trace_program(isgname, step) '{
   local state waitflg stepcomm

   if (!step)
      step = 1

   if ((state = isgdevice_comm(isgname, "?STATE")) == ISGDEV_ERR)
      return

   if (state == "RUN" || state == "NOPROG" || state == "PROG") {
      print "Cannot step program"
      return
   }
   stepcomm = sprintf("STEP %d", step)
   instrcomm = "?INSTR TIME CODE"
   if (step < 0)
      instrcomm = instrcomm " ASM"

   print "\nSTEPPING: pulse the space bar to continue. \'Q\' to finish."
   while(1) {
      if (!waitflg) {
         isgdevice_comm(isgname, stepcomm)
         if ((state = isgdevice_comm(isgname, "?STATE")) == ISGDEV_ERR)
            return
         if (state == "IDLE") {
            local retval
            retval = isgdevice_comm(isgname, "?RETCODE")
            printf("Program exit");
            if (retval != "")
               printf(" - return value : %s", retval);
            print
            return
         } else if (state == "ERROR") {
            print "Program ERROR - ", isgdevice_comm(isgname, "?RETCODE")
            return
         } else if (state == "BREAK"){
            printf("Breakpoint #%d reached\n", \
                    isgdevice_comm(isgname, "?RETCODE"))
            waitflg = 1
         } else {
            print "  >> " isgdevice_comm(isgname, instrcomm)
            waitflg = 0
         }
      }

      while((time() - t0) < 0.3)
         sleep(.01)
      t0 = time()
      if ((a = input(-1)) != "") {
         if (a == " ") {
            waitflg = !waitflg
            if (waitflg)
               print "PAUSED: pulse the space bar to continue. \'Q\' to finish"
         } else if (a == "q" || a == "Q") {
            print "Stepping aborted."
            return
         }
      }
   }
}'

def musst_config(cnum, type, unit, module, chan) '{

   global MUSST_CT[]

   local mne cmd factor
   if (type == "ctrl") {
      printf("Using MUSST \"%s\" counters\n", musst_ADDR)
      MUSST_CT[musst_ADDR]["timer_mode"] = 0
      return
   }

   mne = cnt_mne(cnum)
   if (chan == 0) {
      printf("Configuring \"%s\" as timer\n", mne)
      factor = counter_par(cnum, "scale")
      cmd = sprintf("TIMER %d", factor)
      musst_comm(cmd, musst_ADDR)

      MUSST_CT[musst_ADDR]["running"]  = 0
      MUSST_CT[musst_ADDR]["start_ts"] = 0

      MUSST_CT[musst_ADDR] = cnum
      MUSST_CT[musst_ADDR]["timer_mode"] = 1
   }

}'

def musst_cmd(cnum, key, p1, p2) '{

   global MUSST_CT[]

   local chan name cmd ans val is_running

   if (cnum != "..") {
      chan = counter_par(cnum, "channel")
      name = (chan == 0) ? "TIMER" : sprintf("CH%d", chan)
   }

   if (key == "get_status") {

      ans = musst_comm("?STATE", musst_ADDR)
      is_running = (ans == "RUN")
      MUSST_CT[musst_ADDR]["running"] = is_running
      return is_running

   } else if (key == "prestart_all") {
     
      if (!MUSST_CT[musst_ADDR]["timer_mode"]) {
         MUSST_CT[musst_ADDR]["factor"] = musst_get_timer_scale(musst_ADDR)
         cmd = sprintf("RUNCT %d", p1 * MUSST_CT[musst_ADDR]["factor"])
         musst_comm(cmd, musst_ADDR)
         MUSST_CT[musst_ADDR]["running"]  = 1
         MUSST_CT[musst_ADDR]["start_ts"] = time()
      }

   } else if ((key == "start_one") && (chan == 0)) {

      MUSST_CT[musst_ADDR]["factor"] = counter_par(cnum, "scale")
      cmd = sprintf("RUNCT %d", p1 * MUSST_CT[musst_ADDR]["factor"])
      musst_comm(cmd, musst_ADDR)
      MUSST_CT[musst_ADDR]["running"]  = 1
      MUSST_CT[musst_ADDR]["start_ts"] = time()

   } else if (key == "counts") {

      if (MUSST_CT[musst_ADDR]["running"]) {
        if (chan == 0)
          val = (time() - MUSST_CT[musst_ADDR]["start_ts"]) * MUSST_CT[musst_ADDR]["factor"]
        else
	      val = 0
      } else {
        cmd = sprintf("?VAL %s", name)
        ans = musst_comm(cmd, musst_ADDR)
        sscanf(ans, "%d", val)
      }
      return val

   } else if (key == "halt_all") {

      musst_comm("ABORT", musst_ADDR)
      musst_comm("BTRIG 0", musst_ADDR)
      MUSST_CT[musst_ADDR]["running"] = 0

   } else if (key == "halt_one") {

      cmd = sprintf("%s%s STOP", (chan == 0) ? "" : "CH ", name)
      musst_comm(cmd, musst_ADDR)

   }

}'


#%MACROS%

#%AUTHOR% P.Fajardo, (Original 2/04).
#  $Revision: 1.12 $ / $Date: 2023/03/28 09:08:35 $
#%TOC%