esrf

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

#%TITLE% WAGOCORE.MAC
#%NAME%
#  WAGOCORE.MAC - Basic macros and utilities for Wago I/O stations
#
#%OVERVIEW%
#  This macro set provides basic functionality and utilities to access
#  Wago I/O stations through the device server class Wagods. %BR%
#  It also provides a simple way to declare I/O channels as %B%spec%B%
#  pseudomotors or pseudocounters.
#  
#  %BR%
#  %BR%
#
#%EXAMPLE%
#  %DL%
#  %DT%wagosetup wcid33a wcid33d
#  %DD%Declares the Wago I/O stations named wcid33a and wcid33d as being
#      accessible through this macro set.
#  %DT%wagoshow
#  %DD%Displays the currently declared Wago stations
#  %DT%wread .
#  %DD%Reads and prints the values of all the channels currently declared.
#  %DT%wread temp[2:4] mirrordac
#  %DD%Reads and prints the values of temp[2:4] (3 channels) and mirrordac.
#  %DT%wwrite phscreen 1
#  %DD%Writes 1 into the output channel phscreen
#  %DT%wwrite dac[3,0] 4.6 2.1
#  %DD%Writes the value 4.6 in the output channel dac[3] and 2.1 in dac[0].
#  %DT%print wago_readch("phscreen")
#  %DD%prints the value of the channel phscreen
#  %DT%wago_writech("phscreen", 0)
#  %DD%Writes 0 into the output channel phscreen
#  %DT%wagopseudosetup cnt1 temp[3] cnt2 mirroradc mot3 mirrordac
#  %DD%Configures the spec counters cnt1 and cnt2 to be loaded with the Wago
#      channels temp[3] and mirroradc, and mot3 is configured as a pseudocounter
#      that drives the Wago output mirrordac.
#  %XDL%
#
#%DEPENDENCIES%
#

#=============================================================
#=============================================================
# 2018/05/03 - rh
#    __wagopseudocheck: included double check of mnemonic to solve
#    the issue associated to the new spec version (eval inside
#    cnt_num() and motor_num()
#=============================================================
#=============================================================

jtdo("stlist")

#%UU% <wagoname1> [<wagoname2> [...]]
#%MDESC%
# Declares new Wago I/O stations. The corresponding device server(s) must
# be running and the device names be in conformity with the BLISS convention
# (<blname>/<wagoname>/wc).
# %BR%
# All the logical names declared to the device server become accesible through
# this macro set.
# %BR%
# This line must be included in the %B%spec%B% setup file.%BR%
#
def wagosetup '{
   local retval

   if (retval = _dowagosetup("$*")) {
      if (SETUP) print "Error in line: $0 $*"
      if (retval < 0) {
         print "Usage:  $0 wagoname1 [wagoname2 [ ...]]"
         if (yesno("\nDisplay online help", 0)) eval("help local wagocore")
      }
   }
}'


def _dowagosetup(args) '{
   global WAGO[] WAGOKEYS[] WAGOPLC[]
   local dev ndev devlist[] gooddevs

   ndev = split(args, devlist)
   if (!ndev) {
      if (whatis("_wagosetup") != 0) {
         print "WARNING: You are probably using deprecated macros."
         print "         Check the \'wagocore\' help."
         print
      }
      return(-1)
   }

   wago__plcinit()
   list_test WAGO

   gooddev = ""
   for (dev in devlist) {
      if (_wagoadd(devlist[dev]) == 0)
         setup_tail("wago", devlist[dev])
   }
   return(0)
}'

def wagounsetup '{
   local dev ndev devlist[] gooddev

   dev = "$1"
   _wagoremove(dev)
   if (list_n(WAGO) == 0) {
      unglobal WAGO WAGOKEYS WAGOPLC
   }
}' 

#%UU% {<mne> <wagoch>} ...
#%MDESC%
# Declares channels in Wago I/O stations as %B%spec%B% pseudomotors or
# pseudocounters.
# If <mne> is a %B%spec%B% mnemonic and <wagoch> a valid single-channel
# wago name, the channel gets configured as a pseudomotor or pseudocounter
# depending on the type of mnemonic.%BR%
# Pseudomotors only can be assigned to output channels. Pseudocounters can
# be assigned to either input or output channels.%BR%
# In the case of pseudocounters, the scale factor in the config file is applied.%BR%
#
def wagopseudosetup '{
   global WAGOPS[] WAGOPSCHAN[]
   local parlist[] npars i
   local warr[]
   local error

   if (SETUP && (SETUP_N != WAGOPS["setup_n"])) {
      unglobal WAGOPS WAGOPSCHAN
      global WAGOPS WAGOPSCHAN
      WAGOPS["setup_n"] = SETUP_N
   }

   if ((npars = split("$*", parlist)) && !(npars % 2)) {
      for (i = 0; i < npars; i += 2) {
         if (!__wagopseudocheck(parlist[i], parlist[i + 1], warr))
            error = 1
      }
   } else
      error = 1

   if (error) {
      print "Usage: $0 {<mne> <wagoch>} ..."
   } else {
      local wname macro cdefkey
      n = list_n(warr)
      for (i = 1; i <= n; i++) {
         wname = warr[i]
         if (!list_check(WAGOPS, wname)) {
            list_add(WAGOPS, wname)
            WAGOPS[wname]["ds"] = warr[wname]["ds"]
            WAGOPS[wname]["key"] = warr[wname]["key"]
         }
         cdefkey = sprintf("wago_%s", wname)

         if ("counters" in warr[wname]) {
            if ("counters" in WAGOPS[wname])
               WAGOPS[wname]["counters"] = \
                         WAGOPS[wname]["counters"] " " warr[wname]["counters"]
            else
               WAGOPS[wname]["counters"] = warr[wname]["counters"]
            macro = sprintf("wago_getcounts(\"%s\", \"%s\", \"%s\", %d, \"%s\")\n", \
                   WAGOPS[wname]["counters"], warr[wname]["type"], warr[wname]["ds"], warr[wname]["key"], wname)
            cdef("user_getcounts", macro, cdefkey)
            setup_tail("wagocounter", wname)
         }

         if ("motors" in warr[wname]) {
            if ("motors" in WAGOPS[wname])
               WAGOPS[wname]["motors"] = \
                         WAGOPS[wname]["motors"] " " warr[wname]["motors"]
            else
               WAGOPS[wname]["motors"] = warr[wname]["motors"]
            macro = sprintf("wago_move(\"%s\", \"%s\", \"%s\", %d, \"%s\")\n", \
                   WAGOPS[wname]["motors"], warr[wname]["type"], warr[wname]["ds"], warr[wname]["key"], wname)
            cdef("user_checkall", macro, cdefkey)
            macro = sprintf("wago_getangles(\"%s\", \"%s\", \"%s\", %d, \"%s\")\n", \
                   WAGOPS[wname]["motors"], warr[wname]["type"], warr[wname]["ds"], warr[wname]["key"], wname)
            cdef("user_getpangles", macro, cdefkey)
            setup_tail("wagomotor", wname)
         }
      }
   }
}'

def __wagopseudocheck(mne, wchan, warr) '{
    local charr[] wname iscntr typestr _num _mne

    if (mne in WAGOPSCHAN) {
        print "Mnemonic \`" mne "\' is already configured."
        return(0)
    }

    typestr = ""
    _num = cnt_num(mne)

    if ((_num >= 0) && (mne == cnt_mne(_num))){
        typestr = "counters"    
    }

    if(typestr == "") {
        _num = motor_num(mne)
        if ((_num >= 0) && (mne == motor_mne(_num))){
            typestr = "motors"    
        }
    }

    if(typestr == "") {
        print "\`" mne "\' is not a valid mnemonic."
        return(0)
    }

    if (wago__chanrange(wchan, charr) != 1) {
        print "Bad Wago channel identifier: \`" wchan "\'"
        return(0)
    }

    wname = charr["name"]
    if (!list_check(warr, wname)) {
        list_add(warr, wname)
        warr[wname]["ds"] = charr["ds"]
        warr[wname]["key"] = charr["key"]
        warr[wname]["type"] = charr["type"]
    }

    if (typestr in warr[wname]) 
        warr[wname][typestr] = warr[wname][typestr] " " mne
    else
        warr[wname][typestr] = mne

    WAGOPSCHAN[mne] = charr[0]
    return(1)
}'

def wago_move(motors, wtp, wds, wkey, wname) '{
   local vals[] motlist[]
   local i j motmne motnum
   local scale val

   split(motors, motlist)

   vals[0] = wkey
   j = 1
   for (i in motlist) {
      motmne = motlist[i]
      motnum = motor_num(motmne)
      if (motnum >= 0 && !motor_par(motnum, "disable")) {
         vals[j++] = WAGOPSCHAN[motmne]
         val       = A[motnum]
         scale     = motor_par(motnum, "scale")
         if(scale > 0) {
           val *= scale
         }
         vals[j++] = val
      }
   }
   if(j > 1) {
      if (wago_io(wtp, wds, "DevWritePhys", vals) < 0) {
         printf("Error writing Wago logical device \'%s\'.\n", wname)
      }
   }

}'

def wago_getangles(motors, wtp, wds, wkey, wname) '{
   local vals[] motlist[]
   local i motmne motnum error
   local scale val

   if (error = (wago_io(wtp, wds, "DevReadNoCachePhys", wkey, vals) < 0)) {
      printf("Error reading Wago logical device \'%s\'.\n", wname)
   }
   
   split(motors, motlist)

   for (i in motlist) {
      motmne = motlist[i]
      motnum = motor_num(motmne)
      if (motnum >= 0) {
         if (!error && !motor_par(motnum, "disable")) {
            val = vals[WAGOPSCHAN[motmne]]
            scale     = motor_par(motnum, "scale")
            if(scale > 0) {
               val /= scale
            }
            A[motnum] = val
         } else {
            A[motnum] = 0
         }
      }
   }
}'

def wago_getcounts(counters, wtp, wds, wkey, wname) '{
   local vals[] cntlist[]
   local i cntmne cntnum error

   if (error = (wago_io(wtp, wds, "DevReadNoCachePhys", wkey, vals) < 0)) {
      printf("Error reading Wago logical device \'%s\'.\n", wname)
   }
   
   split(counters, cntlist)

   for (i in cntlist) {
      cntmne = cntlist[i]
      cntnum = cnt_num(cntmne)
      if (cntnum >= 0) {
         if (!error && !counter_par(cntnum, "disable"))
            S[cntnum] = vals[WAGOPSCHAN[cntmne]] / counter_par(cntnum, "scale")
         else
            S[cntnum] = 0
      }
   }
}'

def wagomotorunsetup '{
   local wname cdefkey

   wname = "$1"
   cdefkey = sprintf("wago_%s", wname)
   cdef("user_getpangles", "", cdefkey, "delete")
   cdef("user_checkall", "", cdefkey, "delete")
   delete WAGOPS[wname]["motors"]
   if (!("counters" in WAGOPS[wname]))
      list_remove(WAGOPS, wname)
   if (list_n(WAGOPS) <= 0)
      unglobal WAGOPS WAGOPSCHAN
}' 

def wagocounterunsetup '{
   local wname cdefkey

p "UNSETUP!!"
   wname = "$1"
   cdefkey = sprintf("wago_%s", wname)
   cdef("user_getcounts", "", cdefkey, "delete")
   if (!("motors" in WAGOPS[wname]))
      list_remove(WAGOPS, wname)
   if (list_n(WAGOPS) <= 0)
      unglobal WAGOPS WAGOPSCHAN
}' 

def _wagoremove(wcname) '{
   local ds e

   if (list_check(WAGO, wcname)>0) {
      ds = WAGO[wcname]["ds"]
      for (e in WAGOKEYS){
         if (WAGOKEYS[e]["ds"] == ds) {
            delete WAGOKEYS[e]
            delete WAGOKEYS[e]["ds"]
            delete WAGOKEYS[e]["nch"]
            delete WAGOKEYS[e]["type"]
         }
      }
      list_remove(WAGO, wcname)
   }
}'

def _wagoadd(wcname) '{
   local i n devlist
   local mode ds state id 
   local typ

   if (SETUP && (SETUP_N != WAGO["setup_n"])) {
      n = list_n(WAGO)
      while (n >= 0) {
         list_remove(WAGO, n--)
      }
      WAGO["setup_n"] = SETUP_N
      unglobal WAGOKEYS
      global WAGOKEYS
   }

   if (wcname in WAGO) {
      if (SETUP) {
         print "Wago I/O station " dev " is already configured."
         return(0)
      } else {
         _wagoremove(wcname)
      }
   }
   ds = SPECBL "/" wcname "/wc"
   tp = "TACO"
   ESRF_ERR = -1
   if (int(state = esrf_io(ds, "DevState")) < 0) {
      ds = SPECBL "/" wcname "/tg"
      tp = "TANGO"
      TANGO_ERR = "-1"
      if (int(state = tango_io(ds, "State")) < 0) {
         print "Wago device server does not respond: " ds
         return(-1)
      }
   }


   # State values depends on the DS type
   # with TANGO: ON==0 FAULT==8
   # with TACO : ON==2 FAULT==11
   if (int(state) != (tp=="TACO"?2:0)) {
      print "Wago device " ds ": Wrong device state"
      return(-1)
   }

   if ((devlist = wago__getkeys(ds, tp)) == -1)
      return(-1)


   list_add(WAGO, wcname)
   WAGO[wcname]["ds"]    = ds
   WAGO[wcname]["type"]  = tp
   WAGO[wcname]["ldevs"] = devlist

   WAGO[wcname]["isgmain"] = wc_getversion(wcname)

   return(0)
}'

def wago_io(tp, ds, cmd, argin, argout) '{
   local ret

   if(tp=="TACO" || tp==0) {
     ESRF_ERR=-1
     if(whatis("argout") & 0x08000000)
        ret = esrf_io( ds, cmd, argin)
     else
        ret = esrf_io( ds, cmd, argin, argout)
   } else if(tp=="TANGO") {
     TANGO_ERR="-1"
     if(whatis("argout") & 0x08000000)
        ret = tango_io(ds, cmd, argin)
     else
        ret = tango_io(ds, cmd, argin, argout)
   } else {
     ret = -1
   }

   return(ret)
}'


def wago__getkeys(ds, tp) '{
   local nkeys keys[] name nch i namelist
   local dummy[]
   local ch[] hw[] j
   local idx

   if ((nkeys = wago_io(tp, ds, "DevGetKeys", keys)) < 0)
         return(-1)
 
   namelist = ""
   for (i = 0; i < nkeys; i++) {
      if ((name = wago_io(tp, ds, "DevKey2Name", keys[i])) == -1 || \
          (nch  = wago_io(tp, ds, "DevReadPhys", keys[i], dummy)) < 0) 
         return(-1)

      if (!(name in WAGOKEYS)) {
         WAGOKEYS[name] = keys[i]
         WAGOKEYS[name]["ds"] = ds
         WAGOKEYS[name]["nch"] = nch
         WAGOKEYS[name]["type"] = tp
         namelist = namelist? (namelist " " name) : name
      } else {
         print "Duplicated logical name: " name \
               "in " WAGOKEYS[name]["ds"] " and " ds
         return(-1)
      }

      # Get hardware information on logical device/channel
      for(j = 0; j < nch;j++) {
         ch[0] = keys[i]
         ch[1] = j
         if (wago_io(tp, ds, "DevLog2Hard", ch, hw) < 0)
            return(-1)
         idx = sprintf("%s[%d]", name, j)

         WAGOKEYS[idx]["modbus_addr"] = hw[0]  # offset in wago memory
         WAGOKEYS[idx]["modbus_type"] = hw[1]  # Ex: 0x4957 = ("I"<<8)+"W")
         WAGOKEYS[idx]["module_name"] = hw[2]  # module reference
         WAGOKEYS[idx]["module_num"]  = hw[3]  # module number, 1st==0
         WAGOKEYS[idx]["module_ch"]   = hw[4]  # channel of the module, 1st==0

         WAGOKEYS[name]["module_name"] = hw[2]
      }

   }
   return(namelist)
}'

#%UU% 
#%MDESC%
# Lists the currently declared Wago I/O stations and the associated logical
# names.%BR%
#
def wagoshow '{
   local wcname
   local nwago i ldevs[] lname nch lkey
   local nd ndevs
   local module_name

   nwago = list_n(WAGO)
   printf("\n%s Wago I/O station(s) declared\n", nwago? nwago : "No")
   if (nwago) {
      print "--------------------------------\n"
      for (i = 1; i <= nwago; i++) {
         wcname = WAGO[i]
         ndevs = split(WAGO[wcname]["ldevs"], ldevs)
         printf("%-10s  -  %s logical device(s)\n", wcname, ndevs?ndevs:"No")
         for (nd = 0; nd < ndevs; nd++) {
            lname = ldevs[nd]
            nch = WAGOKEYS[lname]["nch"]
            lkey  = sprintf("%s%s", lname, (nch > 1)?"[0]":"")
            module_name = "750-" WAGOKEYS[lkey]["module_name"]
            printf("%5s%s%s  %s\n", "", lname, nch? sprintf("(%d)", nch):"", module_name)
         }
         print
      }
   }
}'


#%UU%
#%MDESC%
# Prints the status of currently declared Wago I/O stations. Usefull to get
#  hardware info about wago box and modules%BR%
#
def wagoinfo '{
    local wcname
    local nwago i ldevs[] lname nch
    local nd ndevs
    local module_name

    nwago = list_n(WAGO)
    printf("\n%s Wago I/O station(s) declared\n", nwago? nwago : "No")
    if (nwago) {
        for (i = 1; i <= nwago; i++) {
            wcname = WAGO[i]
            printf( "\n---------------- %s ----------------\n", wcname)
            ds =  WAGO[wcname]["ds"]
            print tango_get(ds, "Status")
        }
    }
}'

def __wread(chlbl) '{
   local nchan charr[] name key ds chname tp
   local i nval vals[]

   if ((nchan = wago__chanrange(chlbl, charr)) > 0) {
      name = charr["name"]
      key  = charr["key"]
      ds   = charr["ds"]
      tp   = charr["type"]
      nval = wago_io(tp, ds,"DevReadNoCachePhys", key, vals) 
      if (nval > 0) {
         for(i = 0; i < nchan; i++) {
            if(WAGOKEYS[name]["nch"] == 1)
               chname = name
            else
               chname = sprintf("%s[%d]", name, charr[i])
            printf("%15s - %.10g\n", chname, vals[charr[i]])
         }
      } else
         print "ERROR"
   }
}'

#%UU% <logname1> [<logname2> [...]]
#%MDESC%
# Reads the current values of the Wago channels corresponding to the logical 
# names in the parameter list.
# The logical names accept subarray syntax.
# A single dot character ('.') represents all the available channels.%BR%
#
def wread '{
   local rlist[] nlist nchan

   if (!(nlist = split("$*", rlist))) {
      print "Usage: $0 <name> <name> ..."
      print "       $0 ."
      exit
   }
   for (i = 0; i < nlist; i++){
      if (rlist[i] == ".") {
         for (dev in WAGOKEYS) {
            if (WAGOKEYS[dev]["nch"] > 0)
               __wread(dev)
         }
      } else 
         __wread(rlist[i])
   }
}'

#%UU% <logname> <value1> <value2> [...]
#%MDESC%
# Writes the channels corresponding to the logical name <logname>.
# The logical name accepts subarray syntax and must only include output
# channels.
# One must specify values for all the channels.%BR%
#
def wwrite '{
   local npar nchan name plist[] charr[] vals[]

   npar = split("$*", plist) - 1
   if (npar <= 0) {
      print "Usage: $0 <name> val0 val1 val2 ... valN"
      exit
   }
   if ((nchan = wago__chanrange(plist[0], charr)) <= 0) {
      exit
   }
   if (npar != nchan) {
      print "Wrong number of values to write in " charr["rname"] ": " \
                  npar " instead of " nchan
      exit
   }
   vals[0] = charr["key"]
   for (i = 1; i <= nchan; i++) {
      vals[2 * i - 1] = charr[i - 1]
      vals[2 * i] = plist[i] + 0
   }
   wago_io(charr["type"], charr["ds"], "DevWritePhys", vals)
}'

#%UU% (<chid> [, <out_array>])
#%MDESC%
# This macro function reads the current values of the Wago channels
# identified by <chid>.
# <chid> can be either a string or an associative array. %BR%
# In the case of a string, <chid> must be a Wago logic name with optional
# subarray syntax (i.e. "temp[1,4]"). %BR%
# If <chid> is an array, the elements chid[0], ..., chid[n] must contain
# strings with the corresponding logical names of the n channels to read.
# If a logical name is associated to several the physical channels, the
# channel index within the logical name must be specified in chid[i]["ch"].
# %BR%
# If <chid> is a string and refers to only one channel, the function
# returns the corresponding value.
# If <chid> is an associative array or a string that refers to more than
# one channel, the function returns the number of channels actually read
# into the output array <out_arr> that is mandatory in that case.
# If there is an error, the function returns an emty string ("").
# %BR%
def wago_readch(chid, retval) '{
   local whischid nch ds key all i tp
   local name prevname chan
   local chinfo[] vals[]

   whischid = whatis("chid")

   if (whischid & 0x00200000) {
      if (chid in WAGOKEYS) {
         nch = WAGOKEYS[chid]["nch"]
         key = WAGOKEYS[chid]
         ds = WAGOKEYS[chid]["ds"]
         tp = WAGOKEYS[chid]["type"]
         all = 1
      } else {
         if ((nch = wago__chanrange(chid, chinfo)) <= 0){
            return("")
         }
         ds = chinfo["ds"]
         tp = chinfo["type"]
         key = chinfo["key"]
      }
      if (wago_io(tp, ds, "DevReadNoCachePhys", key, vals) < 0)
         return("")

      if (nch == 1)
         return(vals[all? 0 : chinfo[0]])

      if (whatis("retval") & 0x01010000) {
         for (i = 0; i < nch; i++)
            retval[i] = vals[all? i : chinfo[i]]
         return(nch)
      } else {
         print "  Missing or wrong output array."
         return("")
      }

   } else if (whischid & 0x01000000){
      if (!(whatis("retval") & 0x01010000)) {
         print "  Missing or wrong output array."
         return("")
      }
      i = 0
      while(i in chid) {
         name = chid[i]
         if (name == "" || name != prevname) {
            if (!(name in WAGOKEYS)) {
               print "  Bad Wago logic name: \'" name "\'."
               return("")
            }
            nch = WAGOKEYS[name]["nch"]
            key = WAGOKEYS[name]
            ds = WAGOKEYS[name]["ds"]
            tp = WAGOKEYS[name]["type"]
            if (wago_io(tp, ds, "DevReadNoCachePhys", key, vals) < 0)
               return("")
            prevname = name
         }
         if ("ch" in chid[i]) {
            chan = chid[i]["ch"]
            if (chan < 0 || chan >= nch) {
               print "  Wago channels out of bounds: \'" name "[" chan "]\'."
               return("")
            }
         } else {
            if (nch != 1) {
               print "  Wago channel index missing for logic name: \'" name "\'."
               return("")
            }
            chan = 0
         }
         retval[i] = vals[chan]
         i++
      } 
      if (i == 0) {
         print "  Missing or bad input channel list."
         return("")
      }
      return(i)
   } else {
      print "  Bad Wago channel(s) identifier."
      return("")
   }
}'

#%UU% (<chid>, <data>])
#%MDESC%
# This macro function writes into the output Wago channels identified
# by <chid>.
# <chid> can be either a string or an associative array.%BR%
# In the case of a string, <chid> must be a Wago logic name with optional
# subarray syntax (i.e. "dac[1,4]").
# %BR%
# If <chid> is an array, the elements chid[0], ..., chid[n] must contain
# strings with the corresponding logical names of the n output channels
# to write.
# If a logical name is associated to several physical channels, the channel
# index within the logical name must be specified in chid[i]["ch"].
# %BR%
# If <chid> is a string and refers to a single output channel, the parameter
# <data> can be a simple numerical value. Otherwise <data> must be an array
# containing all the values to be written.
# The function returns the number of channels written if it completes
# succesfully. It returns 0 if there is an error in the channel
# specification or in the data format, and -1 if the error happens in the
# access to the device server.
# %BR%
def wago_writech(chid, data) '{
   local whischid nch ds i j chan tp
   local name prevname
   local chinfo[] vals[]

   whischid = whatis("chid")

   if (whischid & 0x00200000) {
      if (chid in WAGOKEYS) {
         nch = WAGOKEYS[chid]["nch"]
         vals[0] = WAGOKEYS[chid]
         ds = WAGOKEYS[chid]["ds"]
         tp = WAGOKEYS[chid]["type"]
         for (i = 0; i < nch; i++)
            vals[2 * i + 1] = i
      } else {
         if ((nch = wago__chanrange(chid, chinfo)) <= 0){
            return(0)
         }
         ds = chinfo["ds"]
         tp = chinfo["type"]
         vals[0] = chinfo["key"]
         for (i = 0; i < nch; i++)
            vals[2 * i + 1] = chinfo[i]
      }

      if (whatis("data") & 0x09010000) {
         for (i = 0; i < nch; i++)
            vals[2 * (i + 1)] = data[i]
      } else {
         if (nch > 1) {
            print "  Missing data array."
            return(0)
         }
         vals[2] = data
      }
      if (wago_io(tp, ds, "DevWritePhys", vals) < 0)
         return(-1)
      else
         return(nch)

   } else if (whischid & 0x01000000){
      i = 0
      prevname = ""
      while(i in chid) {
         name = chid[i]

         if (name == "" || name != prevname) {
            if (!(name in WAGOKEYS)) {
               print "  Bad Wago logic name: \'" name "\'."
               return(0)
            }
            if (prevname != "") {
               local e

               if (wago_io(tp, ds, "DevWritePhys", vals) < 0)
                  return(-1)

               for (e in vals) delete vals[e]
            }
            nch = WAGOKEYS[name]["nch"]
            vals[0] = WAGOKEYS[name]
            ds = WAGOKEYS[name]["ds"]
            tp = WAGOKEYS[name]["type"]
            j = 1
            prevname = name
         }
         if ("ch" in chid[i]) {
            chan = chid[i]["ch"]
            if (chan < 0 || chan >= nch) {
               print "  Wago channels out of bounds: \'" name "[" chan "]\'."
               return(0)
            }
            vals[j] = chan
         } else {
            if (nch != 1) {
               print "  Wago channel index missing for logic name: \'" name "\'."
               return(0)
            }
            vals[j] = 0
         }
         vals[j + 1] = data[i]
         j += 2
         i++
      } 
      if (i == 0) {
         print "  Missing or bad input channel list."
         return(0)
      }
      if (wago_io(tp, ds, "DevWritePhys", vals) < 0)
         return(-1)
      else
         return(i)
   } else {
      print "  Bad Wago channel(s) identifier."
      return(0)
   }
}'

# taken from /segfs/dserver/include/BlcDsNumbers.h
constant WAGODS_BASE ((5<<26)+(47<<18))

def wago__plcinit() '{
   global WAGOPLC[]

   WAGOPLC["#addr0"] = 256
   WAGOPLC["#max_par"] = 32
   WAGOPLC["#max_res"] = 32

   WAGOPLC["#err"]["base"]  = WAGODS_BASE + 19
   WAGOPLC["#err"][1]  = "Communication timeout"
   WAGOPLC["#err"][2]  = "Bad command"
   WAGOPLC["#err"][3]  = "Bad parameter(s)"
   WAGOPLC["#err"][4]  = "Bad instance number"
   WAGOPLC["#err"][5]  = "Instance is not enabled"
   WAGOPLC["#err"][6]  = "No more instances available"
   WAGOPLC["#err"][7]  = "Bad Function"
   WAGOPLC["#err"][8]  = "Bad channel"
   WAGOPLC["#err"][9]  = "No more channels available"

   WAGOPLC["NAME"]    = 0x0001
   WAGOPLC["ACTIVE"]  = 0x0002
   WAGOPLC["RESET"]   = 0x0003
   WAGOPLC["VERSION"] = 0x0004

   WAGOPLC["#func"]["INTERLOCK"]   = 0x0100

   WAGOPLC["ILCK_CREATE"]   = 0x0101
   WAGOPLC["ILCK_DELETE"]   = 0x0102
   WAGOPLC["ILCK_ADDCHAN"]  = 0x0103
   WAGOPLC["ILCK_DELCHAN"]  = 0x0104
   WAGOPLC["ILCK_GETCONF"]  = 0x0105
   WAGOPLC["ILCK_SETNAME"]  = 0x0106
   WAGOPLC["ILCK_GETNAME"]  = 0x0107
   WAGOPLC["ILCK_GETSTAT"]  = 0x0108
   WAGOPLC["ILCK_SETFLGS"]  = 0x0109
   WAGOPLC["ILCK_CLRFLGS"]  = 0x010A
   WAGOPLC["ILCK_SETTHR"]   = 0x010B
   WAGOPLC["ILCK_RESET"]    = 0x010C
}'


#%UU% 
#%MDESC%
#  
def wc_comm '{
   local i comm npar par[] station
   local nres res[]

   if ($# < 2) {
      print "Usage: $0 <station> <command> <p0> [<p1> <p2> ...]"
      exit
   }

   station = "$1"

   npar = $# - 2
   par[0] = "$3"
   par[1] = "$4"
   par[2] = "$5"
   par[3] = "$6"
   par[4] = "$7"
   par[5] = "$8"

   if ((nres = wc_sendcomm(station, "$2", npar, par, res)) < 0) {
      exit
   } else if (!nres) {
      if (!silent)
         print "OK."
   } else {
      if (!silent) {
         printf("OK. : ")
         for (i = 0; i < nres; i++)
            printf(" 0x%04X", res[i] & 0xFFFF)
         print
      }
   }
}'

#UU (<wcname>, <command>, <npar>, <par> [, <silent>])
#%MDESC%
#  
def wc_getstring(device, comm, npar, par, silent) '{
   local nres i c str d
   local short array res[WAGOPLC["#max_par"]]

   nres = wc_sendcomm(device, comm, npar, par, res, silent)
   if (nres < 0)
      return(-1)

   str = ""
   for (i = 0; i < nres; i++) {
      if (!(c = sprintf("%c", res[i] >> 8)))
         return(str)
      str = str c
      if (!(c = sprintf("%c", res[i] & 0xFF)))
         return(str)
      str = str c
   }
   return(str)
}'

#%UU% (<wcname>, <command>, <npar>, <par>, <res> [, <silent>])
#%MDESC%
#  
def wc_sendcomm(device, comm, npar, par, res, silent) '{
   local wname i ass_wcd[]
   local nres  nerr
   local lastpar npar_org

   if ((wname = list_item(WAGO, device)) < 0) {
      if (!silent)
         print "  Undeclared Wago I/O station: " device
      return(-.1)
   }
   if (int(comm) != comm){
      if (comm in WAGOPLC)
         comm = WAGOPLC[comm]
      else {
         if (!silent)
            print "  Unknown Wago PLC command: " comm
         return(-.1)
      }
   }
   ass_wcd[0] = comm

   # can not use par because its dimension is passed by spec to esrf_io()
   npar_org = npar
   for (i = 1, npar = 1; i <= npar_org; i++) {
      lastpar = par[i - 1]

      # check if a parameter is string
      if (int(lastpar) == lastpar) {
         ass_wcd[npar++] = int(lastpar)
      } else {
         local slen c j

         slen = length(lastpar)

         for (j = 0; j < slen; j += 2) {
            c = asc(substr(lastpar, j + 1, 1)) << 8
            c |= asc(substr(lastpar, j + 2, 1))
            ass_wcd[npar++] = c
         }
      }
   }

   # consider commands that have no parameter
   if(npar == 0) {
      npar = 1
   }

   local ushort array wcd[npar]
   for (i = 0; i < npar; i++)
      wcd[i] = ass_wcd[i] & 0xffff

   ESRF_ERR = -1
   nres = wago_io(WAGO[wname]["type"], WAGO[wname]["ds"], "DevWcComm", wcd, res)
   if (nres < 0 && !silent) {
      if(WAGO[wcname]["type"] == "TACO") {
         nerr = ESRF_ERR - WAGOPLC["#err"]["base"]
         printf("  ERROR %d: %s.\n", nerr, WAGOPLC["#err"][nerr])
      } else {
         nerr = 1
         printf("  ERROR : %s.\n", TANGO_ERR)
      }
      return(-nerr)
   }
   return(nres)
}'


#%UU% (<wcname>)
#%MDESC%
# Loads and returns the current version of the %B%isgmain%B% program running
# in the controller <wcname>.
# Returns 0 if there is no response.
#
def wc_getversion(wcname) '{
   local par[] res[]

   if (wc_sendcomm(wcname, "VERSION", 0, par, res, 1) != 1)
      return(0)
   else
      return(res[0])
}'

#%UU% (<logname>, <chlist>)
#%MDESC%
# This macro function parses the logical name in the string <logname> and
# stores the information so obtained in the associative array <chlist>.
# Individual channels or channel ranges within the logical name can be
# specified by using the %B%spec%B% subarray syntax (i.e. "vin[0,4:7]"). %BR%
# If there is an error, the function returns -1, otherwise it returns the
# total number of physical channels n that is also stored in chlist["n"].%BR
# chlist["key"] contains the device server key, and the array elements
# chlist[0] to chlist[n] contain the indexes of the channels.
# Other information available is the device server name in chlist["ds"]
# and the logical name in chlist["name"].
#
def wago__chanrange(rngname, arr) '{
   local lname idxrng endstr nch

   if (index(rngname, "[")) {
      if (sscanf(rngname, " %[^[] [ %[^]] %s", lname, idxrng, endstr) == 1) {
         idxrng = ""
         sscanf(rngname, " %[^[] [ %s", lname, endstr)
      }
      if (endstr != "]" || 0) {
         print "  Wrong Wago channel range syntax: \'" rngname "\'"
         return(-1)
      }
   } else {
      sscanf(rngname, " %s", lname)
      idxrng = ""
   }
   if (!(lname in WAGOKEYS)) {
      print "  Bad Wago logic name: \'" lname "\'"
      return(-1)
   }
   nch = WAGOKEYS[lname]["nch"]

   nch = wago__parseindex(idxrng, arr, nch)
   if (nch == 0) {
      nch = -1
      print "  Wago channels out of bounds: \'" rngname "\'"
   } else if (nch < 0)
      print "  Wrong Wago subarray syntax: \'" rngname "\'"

   arr["n"] = nch
   arr["name"] = lname
   arr["rname"] = rngname
   arr["ds"] = WAGOKEYS[lname]["ds"]
   arr["key"] = WAGOKEYS[lname]
   arr["type"] = WAGOKEYS[lname]["type"]

   return(nch)
}'

def wago__parseindex(str, arr, maxidx) '{
   local pos n

   n = 0
   while(pos = index(str, ",")) {
      n = wago__parseindex0(substr(str, 1, pos - 1), arr, n, maxidx)
      str = substr(str, pos + 1)
      if (n <= 0)
         return(n)
   } 
   n = wago__parseindex0(str, arr, n, maxidx)
   return(n)
}'


def wago__parseindex0(str, arr, n, maxidx) '{
   local pos ch ch1 str1

   if (str == "") {
      ch = 0
      ch1 = maxidx - 1
   } else {
      if (pos = index(str, ":")) {
         str1 = substr(str, pos + 1)
         str  = substr(str, 1, pos - 1)
      }

      ch = int(str)
      if (str != 0 && str != "" && ch != str)
         return(-1)
      if (ch < 0) ch = maxidx + ch
      if (ch < 0 || ch >= maxidx) return(0)

      if (!pos) {
         ch1 = ch
      } else {
         ch1 = int(str1)
         if (str1 != 0 && str1 != "" && ch1 != str1)
            return(-1)
         if (str1 == "") ch1 = maxidx - 1
         if (ch1 < 0) ch1 = maxidx + ch1
         if (ch1 < 0 || ch1 >= maxidx) return(0)
      }
   }
   while (1) {
      arr[n++] = ch
      if (ch == ch1)
         return(n)
      else if (ch < ch1)
         ch++
      else
         ch--
   }
}'

def wago__type2code(type) '{return((asc(type) << 8) + asc(substr(type, 2, 1)))}'
def wago__code2type(code) '{return sprintf("%c%c", code >> 8, code & 0x00ff)}'

def wago__hard2log(wcname, channel, type) '{
   local short array sqin[2] sqout[2]
   local name ds tp

   ds = WAGO[wcname]["ds"]
   tp = WAGO[wcname]["type"]
   sqin[1] = channel
   sqin[0] = wago__type2code(type)
   if (wago_io(tp, ds, "DevHard2Log", sqin, sqout) != 2) {
      print "  Warning: can\'t get logical name from \'"  wcname "\'."
      return(-1)
   } else {
      name = wago_io(tp, ds, "DevKey2Name", sqout[0])
      if (WAGOKEYS[name]["nch"] > 1) {
         return(sprintf("%s[%d]", name, sqout[1]))
      } else {
         return(name)
      }
   }
}'


def wago__log2hard(chname) '{
   local short array sqin[2] sqout[5]
   local ds arr[] nch tp

   if (int(chname) == chname)
      return(chname)

   nch = wago__chanrange(chname, arr)
   if(nch == 1) {
      sqin[0] = arr["key"]
      sqin[1] = arr[0]
      if (wago_io(arr["type"], arr["ds"],"DevLog2Hard", sqin, sqout) >= 0)
         return((sqout[1] << 16) + sqout[0])

   } else if (nch > 1)
      print "  More than one channel in: " chname

   return(-1)
}'


def wago__log2scale(chname) '{
   local short array sqin[2] sqout[5]
   local ds arr[] nch tp

   nch = wago__chanrange(chname, arr)
   if(nch == 1) {
      if(!index(chname, "[")) {
         chname = sprintf("%s[0]", chname)
      }
      if((module = WAGOKEYS[chname]["module_name"])) {
         # Default case: 10V over 15bits
         scale  = 10 

         # Handle here all exotic modules
         if(module == "483")
            scale = 30 

         return(scale / 0x7fff)
      }

   } else if (nch > 1)
      print "  More than one channel in: " chname

   print " ERROR: missing information on \""chname"\""
   print " Hint:  run \"wagosetup\""
   return(-1)
}'


#%MACROS%
#%SETUP%

#%AUTHOR% M. Perez, P.Fajardo, (Original 3/2005).
#  $Revision: 3.6 $ / $Date: 2018/09/24 13:23:31 $
#%TOC%