esrf

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

#%TITLE% ICE.MAC 
#
#%NAME%
# Macros to control ISG ICEPAP motor controller as a macro motor
# and to configure macro counters to read DRIVERs temperatures.
# Replacing the old "icepap.mac" set of macros.
#
#%CATEGORY% Positioning, Isg
#
#%DESCRIPTION%
#%DL%
#%DT%Configuring macro motors %DD%
# %DL%
#  %DT% 1)
#       You must have an entry in "MOTORS" table for each icepap MASTER
#       %DL%
#         %DT% -
#         The "DEVICE" field must be set to string "icepap"
#         %DT% -
#         The "ADDR" must be set to the MASTER network name.
#         The communication port can optionaly be specified also 
#         (ex: "isgtmp5:5000")
#         %DT% -
#         The "NUM" should be set to 159
#         %DT% -
#         The "TYPE" field must be set to "Macro Motors"
#       %XDL%
#  %DT% 2) 
#       For each axis you must define a motor with:
#       %DL%
#         %DT% -
#         The "Controller" field set to "MAC_MOT"
#         %DT% -
#         The "Unit" field, numbered from 0,
#         must be set to the MOTORS entry.
#         %DT% -
#         The "Module" field, numbered from 0, 
#         must be set to the ICEPAP rack number (seen on
#         the red 7 segments display of the left hand of each rack)
#         %DT% -
#         The "Chan" field, numbered from 1, 
#         must be set to the DRIVER address 
#         %DT% -
#         Example: "0/1/2" refers to the first icepap controller declared, 
#         the rack having the number "1" on its display,
#         the 2nd DRIVER of this rack
#       %XDL%
# %XDL%
#
#
#%DT%Configuring macro counters to read a DRIVER ENCODER POSITION%DD%
# %DL%
#  %DT% 1)
#       You must have a "SCALER" define with "icepapenc" as "DEVICE" and
#       "Macro Counter" as "TYPE".
#  %DT% 2)
#       You must define a counter with "MAC_CNT" as "Device". 
#       %DL%
#         %DT% -
#         The "Unit" field must be set to the SCALER entry.
#         %DT% -
#         The "Chan" field must be set to the DRIVER address (ex: 5 for
#         the 5th DRIVER of crate 0 or ex: 23 for the 3rd DRIVER of crate 2)
#         %DT% -
#         The "encoder" parameter can be set to one of the following values.
#         Typically this parameter is set in config "custom parameters"
#         screen for a counter. Hint: type "p" to get in.
#         %DL%
#            %DT% -
#            "abs" Read the SSI absolute encoder
#            %DT% -
#            "absuXX" Read the unsigned SSI XX bits absolute encoder
#            %DT% -
#            "inc" Read the incremental encoder  (DEFAULT)
#            %DT% -
#            "inpos" Read the DRIVER front panel input
#            %DT% -
#            "motor" Read the motor phase position
#         %XDL%
#       %XDL%
# %XDL%
#
#
#%DT%Configuring macro counters to read a single DRIVER TEMPERATURE %DD%
# %DL%
#  %DT% 1)
#       You must have a "SCALER" define with "icepapcnt" as "DEVICE" and
#       "Macro Counter" as "TYPE".
#  %DT% 2)
#       You must define a counter with "MAC_CNT" as "Device". 
#       %DL%
#         %DT% -
#         The "Unit" field must be set to the SCALER entry.
#         %DT% -
#         The "Chan" field must be set to the DRIVER address (ex: 5 for
#         the 5th DRIVER of crate 0 or ex: 23 for the 3rd DRIVER of crate 2)
#       %XDL%
# %XDL%
#
#
#%DT%Configuring macro counters to read several DRIVERs  TEMPERATUREs %DD%
# %DL%
#  %DT% 1)
#       You must have a "SCALER" define with "icepapcalc" as "DEVICE" and
#       "Macro Counter" as "TYPE"
#  %DT% 2)
#       For each counter declared:
#       %DL%
#         %DT% -
#         The "Device" field set to "MAC_CNT"
#         %DT% -
#         The "Unit" field must be set to the SCALER entry.
#         %DT% -
#         The "Chan" field is not used.
#         %DT% -
#         The "Misc 1" parameter (type "s" to access it) is the 
#         crate number (ex: 0)
#         %DT% -
#         The "Misc 2" parameter is the type of calculation
#         done on the DRIVERs temperatures of the crate. Possible values are
#         "max", "min", "avg"
#       %XDL%
# %XDL%
#
#
#%DT%Configuring macro counters to read a single DRIVER internal parameter %DD%
# %DL%
#  %DT% 1)
#       You must have an entry in "SCALER" table for each icepap MASTER
#       %DL%
#         %DT% -
#         The "DEVICE" field must be set to string "icepapmeas"
#         %DT% -
#         The "ADDR" must be set to the MASTER network name.
#         The communication port can optionaly be specified also 
#         (ex: "isgtmp5:5000")
#         %DT% -
#         The "NUM" should be set to 159
#         %DT% -
#         The "TYPE" field must be set to "Macro Counter"
#       %XDL%
#  %DT% 2)
#       You must define a counter with "MAC_CNT" as "Device". 
#       %DL%
#         %DT% -
#         The "Unit" field must be set to the SCALER entry.
#         %DT% -
#         The "Chan" field must be set to the DRIVER address (ex: 5 for
#         the 5th DRIVER of crate 0 or ex: 23 for the 3rd DRIVER of crate 2)
#         %DT% -
#         An extra parameter called "cmd" must be set to the command
#         to be used to get the DRIVER internal parameter (ex: "meas vm" 
#         "meas i" etc)
#       %XDL%
# %XDL%
#
#
#%DT%Extra motor_par() parameters %DD%
# %DL%
#  %DT% motor_par(motor,"power")%DD%
#       Return "1" if the power is enabled on the speficied motor
#  %DT% motor_par(motor,"power",1)%DD%
#       Try to enable the the power the speficied motor and return the result
#  %DT% motor_par(motor,"jog_speed")%DD%
#       Returns the current speed if the motor is in never ending motion
#       or returns "0" if it's stop or in normal operation mode.
#  %DT% motor_par(motor,"jog_speed",speed)%DD%
#       Sets the speed for the never ending motion. If the motor is
#       already moving in this mode, its speed will be changed on the fly.
#       If the new speed has not the same sign that the current one, the
#       motor will first deccelerate until stopping and then starts rotating
#       in the other direction with the new speed.
#  %DT% motor_par(motor,"jog_speed",0)%DD%
#       Cancel a never ending motion.
#  %DT% motor_par(motor,"closed_loop")%DD%
#       Returns nonzero if the closed loop is active
#  %DT% motor_par(motor,"closed_loop",1)%DD%
#       Activate or desactivate the closed loop for the specified motor
#  %DT% motor_par(motor,"home_active")%DD%
#       Returns nonzero if logical home signal is active
#  %DT% motor_par(motor,"high_lim_set")%DD%
#       Returns nonzero if positive limitswitch is active
#  %DT% motor_par(motor,"low_lim_set")%DD%
#       Returns nonzero if negative limitswitch is active
#  %DT% motor_par(motor,"keepusable",1)%DD%
#       Keep the motor usable even if power could not be switched on during
#       reconfig. Typically this parameter is set in config "additional
#       optional parameters" screen for a motor. Hint: type "p" to get in
# %XDL%
#
#%XDL%
#
#%END%
#



#
# ----------------------- MACRO MOTOR implementation -----------------------
#

constant ICEPAP_LOCKFILE  "/users/blissadm/local/spec/userconf/icepap_lock"
constant ICEPAP_TIMEOUT   3

#%IU% [personal msg]
#%MDESC%
# Switch on or off the print of debug messages
#
def icepapdebug '{
 global ICEPAP[]

 if(ICEPAP["debug"]) {
  rdef icepap__debug \'#\$#\'
  print "ICEPAP debug mode is OFF"
  ICEPAP["debug"]=0
 } else { 
  rdef icepap__debug \'print "ICEPAP[\"dev\"]: "\'
  print "ICEPAP debug mode is ON"
  ICEPAP["debug"]=1
 }
}'


#%IU%
#%MDESC%
# Reset socket communication and MASTER FIFOs
#
def icepap_fiforst(dev) '{
 _icepap_wr(dev,"","_FIFORST")
}'


#%IU%
#%MDESC%
# Called on <Ctrl-C>
#
def icepap_cleanup(dev) '{
  # give time to sockets
  sleep(.02)
  # very dangerous with multiple clients
  # icepap_fiforst(dev)
  sock_par(dev,"flush")
}'


#%IU%
#%MDESC%
# Needed to initialize debug print out macro the first time the 
# file is loaded.
def icepapdebug_init '{

 if(!(whatis("ICEPAP") & 0x05000000)) {
  global ICEPAP[]
  ICEPAP["debug"]=1
  icepapdebug
 }
}'
icepapdebug_init



#%IU%(string, case)
#%MDESC%
# Convert to lower (case==1) or to upper (case==0) case the string passed
#
def icepap__tocase(str, case) '{
  string array str_a[length(str)]
  local l 

  str_a = str
  l = length(str)-1
  for(;l>=0;l--) {
   if(case)
    str_a[l]=str_a[l]+(str_a[l]>=asc("A") && str_a[l]<=asc("Z"))*0x20;
   else
    str_a[l]=str_a[l]-(str_a[l]>=asc("a") && str_a[l]<=asc("z"))*0x20;
  }
  return(sprintf("%s", str_a))
}'


#%IU%(string)
#%MDESC%
# Return the lower string of the string passed
#
def icepap__tolower(str) '{return((str!="")?icepap__tocase(str, 1):"")}'


#%IU%(string)
#%MDESC%
# Return the upper string of the string passed
#
def icepap__toupper(str) '{return((str!="")?icepap__tocase(str, 0):"")}'



#%IU%(string)
#%MDESC%
#
def icepap_trim(str) '{
  string array str_a[length(str)]
  local  b l 

  str_a = str
  l = length(str)-1
  for(;(l>=0)&&(str_a[l]==0x20);l--);
  for(b=0;(b<l)&&(str_a[b]==0x20);b++);

  return(substr(str,b+1,(l-b)+1))
}'

#%IU%(x)
#%MDESC%
# Return a well rounded integer as opposed to int() which always
# round down
#
def icepap_round(x) '{ if (x>=0) return int(x+0.5); else return int(x-0.5) }'



#%IU%(ans)
#%MDESC%
# Check if the answer given contains an ERROR and if yes
# request the error message
#
def icepap_chkerr(dev,ans) '{
   if(index(ans,"ERROR") == 1) {
    ans= _icepap_wrrd(dev,0,"?ERR 1")
    print ans;
   }
}'



#
# ----------------------- IcePAP library access ----------------------------
#


#%IU%()
#%MDESC%
# Check if the IcePAP library can be used through the dedicated TANGO DS.
# Returns non null if usable.
#
def _icepaplib_check() '{
 global ICEPAP[]
 local ds
    
   
 # TODO: implement a custom TANGO DS name (cf new controler type?)
 ds = sprintf("%s/testicepap/1", SPECBL)

 # Check if the DS is out there
 TANGO_ERR = "-1"
 if(tango_io(ds, "State") == -1) {
   printf("ICEPAP WARNING: missing DS \"%s\", using old fashion control\n",\
             ds)
   return 0
 }

 # This will force to use the TANGO DS and its library
 ICEPAP["ds"] = ds

 # Normal end
 return 1
}'


#%IU%
#%MDESC%
# Macro to get access to the TANGO DS using the IcePAP library
#
def _icepaplib_access '
 if(!(ds = ICEPAP["ds"])) { return -1}
'


#%IU%(group_name)
#%MDESC%
# Creates a group with the specified name.
# If the group already exists, it will be emptied
# WARNING: this implies that groups can not be shared accross different
# SPEC sessions.
#
def _icepaplib_create_group(name) '{
 global ICEPAP[]
 local ds
 
 # Returns if library is not usable
 _icepaplib_access

 TANGO_ERR = "-1"
 if(tango_io(ds, "CreateGroup", name) == -1) {
   if(!index(TANGO_ERR_STACK[0]["desc"],"already defined group name")) {
     p TANGO_ERR_STACK["0"]["desc"]
     return -1
   }

   printf("ICEPAP WARNING: emptying existing group \"%s\"\n", name)
   tango_io(ds, "EmptyGroup", name)
 }


 # Update group list
 ICEPAP["ds"]["groups"] = sprintf("%s %s", ICEPAP["ds"]["groups"], name)

 # Normal end
 return 0
}'

#%IU%(mne, flags)
#%MDESC%
# Add a new axis to defaut group or to a given group if motor mne
# has a parameter "group" defined.
# Returns 0 if success otherwise -1
#
def _icepaplib_create_axis(mne, flags) '{
 global ICEPAP[]
 local  ds
 local  i argin[]
 local  dev idx 
 local  num grp
 

 # Returns if library is not usable
 _icepaplib_access

 # Remove host port if any
 dev = ICEPAP[mne]["dev"]
 if(idx=index(dev,":")) { dev=substr(dev, 0 , idx-1) }

 # By default use the session name as group name
 num = motor_num(mne)
 if(!(grp = motor_par(num,"group"))) { 
   grp = SPEC 
   motor_par(num, "group", grp, "add")
 } 

 if(!index(ICEPAP["ds"]["groups"], grp)) {
   _icepaplib_create_group(grp)
 }

 # An axis is identified by: name, group name, hostname, address, flags
 argin[i++] = mne
 argin[i++] = grp
 argin[i++] = dev
 argin[i++] = ICEPAP[mne]["addr"]

 # The flags are optional
 if(flags) { argin[i++] = flags }

 # Append the axis
 ds  = ICEPAP["ds"]
 if(tango_io(ds, "AddAxis", argin) == -1) {
   p TANGO_ERR_STACK["0"]["desc"]
   return -1
 }

 # Normal end
 return 0
}'


#%IU%()
#%MDESC%
#
def _icepaplib_prestart_all() '{
 global ICEPAP[]
 local  ds
 local  n grps[]

 # Returns if library is not usable
 _icepaplib_access

 ICEPAP["ds"]["tomove"]=""
 ICEPAP["ds"]["tostat"]=""

 n = split(ICEPAP["ds"]["groups"],grps)
 for(;n;n--) {
   ICEPAP["ds"][grps[n-1]] = ""
 }

 # Normal end
 return 0
}'


#%IU%(mne, pos_in_steps)
#%MDESC%
#
def _icepaplib_start_one(mne, pos) '{
 global ICEPAP[]
 local  ds
 local  num grp

 # Returns if library is not usable
 _icepaplib_access

 # Retrieve the group owning this motor
 num = motor_num(mne)
 grp = motor_par(num, "group")

 ICEPAP["ds"][grp] = sprintf("%s %s %d", ICEPAP["ds"][grp], mne, pos)

 # Normal end
 return 0
}'


#%IU%()
#%MDESC%
#
def _icepaplib_start_all() '{
 global ICEPAP[]
 local  ds
 local  n grps[]
 local  i argin[]

 # Returns if library is not usable
 _icepaplib_access

 # Each array element is "group mne pos mne pos..."
 # One array element per group to move
 n = split(ICEPAP["ds"]["groups"],grps)
 for(;n;n--) {
   tomove = icepap_trim(ICEPAP["ds"][grps[n-1]])
   if(length(tomove)) {
     argin[i++] = sprintf("%s:%s",grps[n-1], tomove)
   }
 }

 if(tango_io(ds, "MoveGroup", argin) == -1) {
   p TANGO_ERR_STACK["0"]["desc"]
   return -1
 }

 # Normal end
 return 0
}'



#%IU%(mne)
#%MDESC%
# Returns the move and limit status of a single motor (bit mask) or
# the string ".error.". If the library can not be used, returns -1
#
def _icepaplib_get_status(mne) '{
 global ICEPAP[]
 local  ds
 local  num grp
 local  i argin[] argout[]
 local  sta

 # Returns if library is not usable
 _icepaplib_access

 # Retrieve the group owning this motor
 num = motor_num(mne)
 grp = motor_par(num, "group")

 # Get the status of a unique motor
 argin[i++]  = sprintf("%s:%s", grp, mne)
 if(tango_io(ds, "IsMovingGroup", argin, argout) == -1) {
   p TANGO_ERR_STACK["0"]["desc"]
   return ".error."
 }


 # Get a numerical status as hexa bitmask
 sta = 0
 if(sscanf(argout[0],"%s %s %x", grp, mne, sta) != 3) {
   p "ICEPAP ERROR: unable to parse group status"
   return ".error."
 }

 # The TANGO DS bitmaks is identical to SPEC one (strange ;-)
 return sta
}'


#%IU%(mne)
#%MDESC%
# Returns the position single motor or
# the string ".error.". If the library can not be used, returns ".error."
#
def _icepaplib_get_position(mne) '{
 global ICEPAP[]
 local  ds
 local  num grp
 local  i argin[] argout[]
 local  pos

 # Returns if library is not usable
 if(!(ds = ICEPAP["ds"])) { return ".error."}

 # Retrieve the group owning this motor
 num = motor_num(mne)
 grp = motor_par(num, "group")

 # Get the status of a unique motor
 argin[i++]  = sprintf("%s:%s", grp, mne)
 if(tango_io(ds, "PositionGroup", argin, argout) == -1) {
   p TANGO_ERR_STACK["0"]["desc"]
   return ".error."
 }


 # Get the position in steps
 pos = 0
 if(sscanf(argout[0],"%s %s %d", grp, mne, pos) != 3) {
   p "ICEPAP ERROR: unable to parse group position"
   return ".error."
 }

 # Returns the position in steps
 return pos
}'




#%IU%(mne)
#%MDESC%
# Stops a single motor or
# the string ".error.". If the library can not be used, returns -1
#
def _icepaplib_stop(mne) '{
 global ICEPAP[]
 local  ds
 local  num grp
 local  i argin[]

 # Returns if library is not usable
 _icepaplib_access

 # Retrieve the group owning this motor
 num = motor_num(mne)
 grp = motor_par(num, "group")

 # Get the status of a unique motor
 argin[i++]  = sprintf("%s:%s", grp, mne)
 if(tango_io(ds, "StopGroup", argin) == -1) {
   p TANGO_ERR_STACK["0"]["desc"]
   return ".error."
 }

 # Normal end
 return 0
}'


#%IU%(mne, power)
#%MDESC%
# Change the power of the specified axis. The argument can be "ON" or "OFF".
# Returns 0 if success otherwise -1
#
def _icepaplib_power_axis(mne, power) '{
 global ICEPAP[]
 local  ds
 local  num grp
 local  i argin[] argout[]

 # Returns if library is not usable
 _icepaplib_access

 # Retrieve the group owning this motor
 num = motor_num(mne)
 grp = motor_par(num, "group")

 # Let the DS to the check on action string
 power = icepap__toupper(power)

 # Actuate on a unique motor
 argin[i++]  = sprintf("%s %s:%s", grp, power, mne)
 if(tango_io(ds, "PowerGroup", argin, argout) == -1) {
   p TANGO_ERR_STACK["0"]["desc"]
   return -1
 }

 # Check that things went well
 if(!index(argout[0], "ON")) {
   local dev addr

   #TODO: implement diagnostic through library
   dev=motor_par(num,"address")
   addr=ICEPAP[mne]["addr"]

   _icepap_err
   printf("unusable motor: \"%s\"\n",mne)
   _icepap_print_info(dev,addr,mne)
   motor_par(num,"disable",1)

   return -1
 }

 # Normal end
 return 0
}'



#
# ----------------------- MACRO MOTOR implementation -----------------------
#


#%IU%
#%MDESC%
# MACRO MOTOR: 
# Called by spec after reading the config file
#
def icepap_config(num,type,p1,p2,p3) '{
 global ICEPAP[]
 global ICEPAP_CAT[]
 local  dev
 local  i j
 local  ans
 local  hrev lrev
 local  n tt[]
 local  m aa[]
 local  mne
 local  silent
 


 # p1==controller index p2==number of motors supported
 if(type=="ctrl") {
  silent = 1

  icepap__debug "config(): new controller"
  if (p1 == 0) { for(i in ICEPAP) { delete ICEPAP[i] } }
  ICEPAP["on"] = 0
  ICEPAP["dev"]=""

  # Check if a TANGO DS is out there
  if (p1 == 0) {
    if(_icepaplib_check()) {
      # Create a group per spec session.
      _icepaplib_create_group(SPEC)
    }
  }


  # get the MASTER
  dev=icepap_ADDR
  if(dev=="") {
    _icepap_err
    printf("missing ADDR field\n")
    return 
  }
  if(index(dev,":") == 0) { dev=sprintf("%s:5000",dev) }
  ICEPAP["dev"][p1]=dev
  ICEPAP["dev"]=dev
  ICEPAP["on"] = 1

  # if the MASTER is switched off, disable all the associated motors
  ans=_icepap_query(dev,0,"?ID", silent)
  if(ans == "") { return ".error." }

  # get the system version
  hrev = 0
  lrev = 0
  ICEPAP[dev]["hrev"]=0
  ICEPAP[dev]["lrev"]=0
  ans=_icepap_query(dev,"","?VER")
  if(sscanf(ans,"%d.%d",hrev,lrev) == 2) {
    ICEPAP[dev]["hrev"]=hrev
    ICEPAP[dev]["lrev"]=lrev
  }
  
  # try to guess if the firmware supports group motions
  ICEPAP[dev]["groups"]=(((hrev>=1)&&(lrev >= 22)) || (hrev>=2))?"YES":"NO"

  # try to guess if the firmware supports linked axis
  ans=_icepap_query(dev,"","?LINKED",silent)
  n  =split(ans,tt,"\r\n")
  ICEPAP[dev]["linked"]=(!index(ans,"ERROR"))?"YES":"NO"

  # get linked axis configured if any (expecting a multi-line answer)
  if((ICEPAP[dev]["linked"] == "YES") && (n>=3)) {
   ICEPAP[dev]["linked_list"]=""

   for(i=0;i<n;i++) {
    if(tt[i] == "$") { 
      continue 
    }

    m   = split(tt[i],aa)
    mne = (m>0)?aa[0]:"???"

    if(m<1) {
      _icepap_err
      printf("bad configured linked axis \"%s\" on \"%s\"\n", \
        mne,dev)
      continue
    }
 
    ICEPAP[dev]["linked_list"]=ICEPAP[dev]["linked_list"] mne " "
    for(j=1;j<m;j++) {
      ICEPAP[mne]["linked_addr"]=ICEPAP[mne]["linked_addr"] aa[j] " "
    }
    ICEPAP[mne]["linked_addr"]=icepap_trim(ICEPAP[mne]["linked_addr"])
    ICEPAP[mne]["linked"]     ="YES"
   }

   ICEPAP[dev]["linked_list"]=icepap_trim(ICEPAP[dev]["linked_list"])
   
  }


 }


 # p1==unit p2==module p3==channel
 if(type=="mot") {
  local mne
  local dev
  local addr
  local i
  local ret
  local addr
  local silent
  
  # If the controller was not well configured then give up
  if(ICEPAP["on"] != 1) { return }

  # get the motor mnemonic string
  mne=motor_mne(num)

  # get the MASTER
  ICEPAP[mne]["dev"]=""
  dev=motor_par(num,"address")
  if(dev==0) { return }
  if(index(dev,":") == 0) { dev=sprintf("%s:5000",dev) }
  ICEPAP[mne]["dev"]=dev

  # initialize global settings
  ICEPAP[mne]["addr"]=p2*10+p3
  addr=ICEPAP[mne]["addr"]
  ICEPAP[mne]["jog_velocity"]=0
  ICEPAP[mne]["stop_code"]=0
  
  # for a linked axis the mnemonic is used as identification for the DSP
  if(addr==0){
   ICEPAP[mne]["addr"]=mne
  }


  # create a new library access and add it to a group
  if(!_icepaplib_create_axis(mne, flags)) {

   # try to switch on power on motors
   _icepaplib_power_axis(mne, "ON")

   # if the library is usable, nothing else to do
   return
  }



  # check if it is linked axis (address==0)
  if(addr==0) {
   if(ICEPAP[mne]["linked"] != "YES") {
    _icepap_err
    printf("unusable motor: \"%s\"\n",mne)
    printf("Hint: change address in config or configure linked axe\n")
    motor_par(num,"disable",1)
    return ".error."
   }

   # switch on, at once, the power on all concerned motors
   ans=_icepap_query(dev,"#",sprintf("POWER ON %s", mne))
   if(index(ans,"ERROR"))
   {
    _icepap_err
    printf("unusable motor: \"%s\"\n",mne)
    _icepap_print_info(dev,addr,mne)
    motor_par(num,"disable",1)
    return ".error."
   }

   # the afterward commands are not implemented in DSP for linked axis:
   #   ?ACTIVE
   #   ?CFG ACTIVE
   #   ?PCLOOP
   return
  } 


  # check if the DRIVER is configured and active 
  ans=_icepap_query(dev,addr,"?ACTIVE")
  if(ans != "NO") {
   if (ans != "YES")
   {
    _icepap_err
    printf("unusable motor: \"%s\"\n",mne)
    motor_par(num,"disable",1)
    return ".error."
   }
  }
  else
  {
   ans=_icepap_query(dev,addr,"?CFG ACTIVE")
   if(ans != "ACTIVE YES") {
    _icepap_err
    printf("not configured active driver for motor: \"%s\"\n",mne)
    printf("Hint: use \"icepapcms\" to activate this driver\n")
   } else {
    _icepap_err
    printf("not active driver for motor: \"%s\"\n",mne)
    printf("Hint: probably a hardware problem, crate off/on may help\n")
   }
   motor_par(num,"disable",1)
   return ".error."
  }

  # check if the DSP will handle a closed loop 
  ICEPAP[mne]["pcloop"]="OFF"
  ans=_icepap_query(dev,addr,"?PCLOOP")
  if(index(ans,"ON")) { ICEPAP[mne]["pcloop"]="ON" }

  # check if the DC dead band and settling time are well set in the config
  if(ICEPAP[mne]["pcloop"]=="ON")
  {
   if(motor_par(num,"dc_settle_time") != 0)
   {
    printf("ICEPAP WARNING: settling handled by IcePAP for motor: \"%s\"\n",mne)
    printf("ICEPAP WARNING: the \"DC settle time\" should be set to 0\n")
   }
   if(motor_par(num,"dc_dead_band") != 1)
   {
    printf("ICEPAP WARNING: settling handled by IcePAP for motor: \"%s\"\n",mne)
    printf("ICEPAP WARNING: the \"DC dead band\"   should be set to 1\n")
   }
  }

  
  # switch on the power on the motor, chaud devant
  silent = 1
  ans=_icepap_query(dev,"#" addr,"POWER ON", silent)
  if(index(ans,"ERROR") && motor_par(mne, "keepusable") != 1)
  {
   _icepap_err
   printf("unusable motor: \"%s\"\n",mne)
   _icepap_print_info(dev,addr,mne)
   motor_par(num,"disable",1)
   return ".error."
  }


 }
}'


#%IU%
#%MDESC%
# MACRO MOTOR: 
# Called by spec after reading the config file, after calling _config()
# and only if parameters are set in the config file for a motor.
#
def icepap_par(num,key,todo,p1) '{
 global ICEPAP[]
 local  mne
 local  ans
 local  sta
 local  dev

 # get the motor mnemonic string
 mne=motor_mne(num)

 # get the MASTER
 if(!("dev" in ICEPAP[mne])) { return }
 dev=ICEPAP[mne]["dev"]
 if(dev=="") { return }

 # get the address
 if(!("addr" in ICEPAP[mne])) return;
 addr=ICEPAP[mne]["addr"]

 # return new motor_par() argins handled 
 if (key == "?" && todo == "get") {
  return("power, jog_speed, closed_loop")
 }


 #
 #
 #
 if (key == "power") {
  
  # Change the power on the motor, chaud devant
  if(todo == "set") {
   if(ICEPAP[mne]["linked"] == "YES") {
    ans=_icepap_query(dev,"#",sprintf("POWER %s %s",(p1==1?"ON":"OFF"),addr))
   } else {
    ans=_icepap_query(dev,"#" addr,sprintf("POWER %s",(p1==1?"ON":"OFF")))
   }

   if(index(ans,"ERROR"))
   {
    _icepap_print_info(dev,addr,mne)
    _icepap_err
    printf("unable to power motor: \"%s\"\n",mne)
    return 
   }

  }

  # Return the current state in any case
  if(ICEPAP[mne]["linked"] == "YES") {
   ans=_icepap_query(dev,"",sprintf("?POWER %s",addr))
  } else {
   ans=_icepap_query(dev,addr,"?POWER")
  }
  return(index(ans,"ON")?1:0)
 }



 #
 #
 #
 if (key == "jog_speed") {

  if(ICEPAP[mne]["linked"] == "YES") {
   _icepap_err
   printf("JOG motion no implemented for linked axis\n")
   return 
  }

  # Change or start a JOG motion
  if(todo == "set") 
  {
   local vel
   local acc
   local cmd

   # Just in case the motor had never been moved previously
   acc = motor_par(num,"acceleration")
   icepap_cmd(num,"acceleration",acc)
 
   # If the rotation sense changes, we have to stop first the motor
   # NOTE MP 15Jul13: with firmware >=2.0 this is no more needed
   if(((ICEPAP[mne]["jog_velocity"] * p1) <0) && (ICEPAP[dev]["hrev"]<2))
   {
    # Stop the motion
    ans=_icepap_query(dev,"#" addr,"STOP")
    if(index(ans,"ERROR"))
    {
     _icepap_err
     printf("unable to stop JOG on motor: \"%s\"\n",mne)
     return 
    }

    # Wait for the end of the motion
    while(icepap_cmd(num,"get_status") == 0x02) sleep(.01);
  
    # Give some time to DRIVER  to refresh its status
    sleep(0.1)
   }

   # Treat the special case of null velocity
   # NOTE MP 15Jul13: with firmware >=2.0 a "JOG 0" will block the motor
   # in a never ending loop because the status shows an ongoing motion at
   # null velocity.
   if(p1 == 0) {
    cmd = "STOP"
   } else {
    cmd = sprintf("JOG %d",p1)
   }
   
   # Set the new velocity
   ICEPAP[mne]["jog_velocity"]=p1
   ans=_icepap_query(dev,"#" addr, cmd)
   if(index(ans,"ERROR"))
   {
    _icepap_err
    printf("unable to launch JOG on motor: \"%s\"\n",mne)
    return 
   }
  }

  # Avoid position discrepancy on motion stopped
  if((todo == "set") && (p1 == 0))
  {
   # NOTE MP 21Jun10: wait for the end of motion before reading position
   # can not call wait(0x21) because Spec does not know that we are moving
   while(icepap_cmd(num,"get_status") == 0x02) sleep(.01);

   # NOTE MP 26Oct09: on users request, clear the position to avoid
   # huge position values
   icepap_cmd(num,"set_position",0)

   # NOTE MP 21Jun10: give time to DSP to update DPM, seen with firmware 1.21
   sleep(.1)
   
   # NOTE: from "help motors": position discrepancies between spec and the
   # motor hardware will be silently resolved in favor of the hardware
   read_motors(0x06)
  }

  # Return the current jog velocity in any case
  # NOTE MP 15Oct09: ?V returns the current instantaneous speed which therefore
  # can not be the target one if the motor is still accelerating.
  # To avoid confusing the user, we return the target one
  if(todo == "set") 
   return(ICEPAP[mne]["jog_velocity"])

  # Return the current instantaneous velocity
  ans=_icepap_query(dev,addr,"?JOG")
  if(index(ans,"ERROR"))
  {
   _icepap_err
   printf("unable to get speed for motor: \"%s\"\n",mne)
   return 
  }
  if(sscanf(ans,"%d",vel) != 1)
  {
   _icepap_err
   printf("unable to read speed for motor: \"%s\"\n",mne)
   return 
  }

  # NOTE MP 15Oct09: ?JOG returns positive only values, little work-arround
  if(ICEPAP[dev]["hrev"]<2) {
   if(ICEPAP[mne]["jog_velocity"]<0) 
    vel *= -1
  }
  
  return(vel)
 }


 #
 #
 #
 if (key == "closed_loop") {
  if(todo == "set") {
   ans=_icepap_query(dev,"#" addr,sprintf("PCLOOP %s",(p1==1?"ON":"OFF")))
   if(index(ans,"ERROR"))
   {
    _icepap_err
    printf("unable to activate closed loop for: \"%s\"\n",mne)
    return 
   }
  }

  # Return the current state of closed loop
  ans=_icepap_query(dev,addr,"?PCLOOP")
  return(index(ans,"ON")?1:0)
 }


 #
 #
 #
 if (key == "home_active") {
  sta = 0
  ans=_icepap_query(dev,"",sprintf("?FSTATUS %s",addr))
  if(sscanf(ans,"%x", sta) != 1) 
  {
   _icepap_err
   printf("unable to get status for: \"%s\"\n",mne)
   return ".error."
  }

  # get home signal status
  return((sta & (1<<20))!=0)
 }


 #
 #
 #
 if (key == "high_lim_set") {
  sta = 0
  ans=_icepap_query(dev,"",sprintf("?FSTATUS %s",addr))
  if(sscanf(ans,"%x", sta) != 1) 
  {
   _icepap_err
   printf("unable to get status for: \"%s\"\n",mne)
   return ".error."
  }

  # get limitswtich status
  return(sta & (1<<18))
 }

 #
 #
 #
 if (key == "low_lim_set") {
  sta = 0
  ans=_icepap_query(dev,"",sprintf("?FSTATUS %s",addr))
  if(sscanf(ans,"%x", sta) != 1) 
  {
   _icepap_err
   printf("unable to get status for: \"%s\"\n",mne)
   return ".error."
  }

  # get limitswtich status
  return(sta & (1<<19))
 }

}'






#%IU%
#%MDESC%
# MACRO MOTOR: 
#
def _icepap_prestart_all(dev) '{
 global ICEPAP

 if(ICEPAP[dev]["groups"] != "YES") { return }

 ICEPAP[dev]["tomove"]=""
 ICEPAP[dev]["tostat"]=""
}'

#%IU%
#%MDESC%
# MACRO MOTOR: 
#
def _icepap_start_all(dev) '{
 global ICEPAP
 local  aux[]


 if(ICEPAP[dev]["groups"] != "YES") { return }

 # NOTE MP 24Jul12: the start_all should not be call if nothing to move
 # but SPEC version 5.10.02-16 do this
 if(ICEPAP[dev]["tomove"] == "")    { return }

 # NOTE MP 01Feb11: avoid group motion for single axis motion
 cmd=sprintf("MOVE %s %s",\
      (split(ICEPAP[dev]["tostat"],aux)>1)?"GROUP":"",\
      ICEPAP[dev]["tomove"])
 answ = _icepap_query(dev,"#",cmd)
 if (answ != "OK")
 {
  _icepap_err
  printf("unable to start grouped motion\n")
  return ".error."
 }

}'


#%IU%
#%MDESC%
# MACRO MOTOR: 
#
def _icepap_start_one(dev, addr, pos) '{
 global ICEPAP


 if(ICEPAP[dev]["groups"] != "YES") 
 { 
  local cmd

  # NOTE MP 31Jan11: even if multi-axes motion exists before group motion
  # their behavior as drastically changed with group implementation
  # therefore it is better to not use them
  cmd=sprintf("MOVE %d",pos)
  addr = "#" addr
  answ = _icepap_query(dev,addr,cmd)
  if (answ != "OK")
  {
   _icepap_err
   printf("unable to start motion for: \"%s\"\n",mne)
   return ".error."
  }

  return
 }
  
 ICEPAP[dev]["tomove"] = sprintf("%s %s %d",ICEPAP[dev]["tomove"],addr,pos)
 ICEPAP[dev]["tostat"] = sprintf("%s %s",   ICEPAP[dev]["tostat"],addr)

}'




#%IU%
#%MDESC%
# MACRO MOTOR: 
# Called by spec on motor operation.
# 
def icepap_cmd(num,key,p1,p2) '{
 local dev
 local mne
 local addr




 #
 # Handle actions at controller level
 #
 if(num == "..") { 

  dev = icepap_ADDR
  if(index(dev,":") == 0) { dev=sprintf("%s:5000",dev) }


  if ( key == "prestart_all" ) { 
    if(!_icepaplib_prestart_all(dev)) { return }

    return(_icepap_prestart_all(dev)) 
  }

  if ( key == "start_all" )    { 
    if(!_icepaplib_start_all(dev)) { return }

    return(_icepap_start_all(dev))    
  }

  return
 }




 # If the controller was not well configured then give up
 if(ICEPAP["on"] != 1) { return }

 # get the motor mnemonic string
 mne=motor_mne(num)

 # get the MASTER
 if(!("dev" in ICEPAP[mne])) { return }
 dev=ICEPAP[mne]["dev"]
 if(dev=="") { return }

 # NOTE MP 2Aug05: when starting from fresh, SPEC calls 
 # icepap_cmd("position") before calling icepap_config() and 
 # therefore nothing is usable at that moment 
 if(!("addr" in ICEPAP[mne])) return;
 addr=ICEPAP[mne]["addr"]


 #
 # return the current motor position in mm or deg
 #
 if (key == "position") {
  local pos 
  local pos_str
  local ans

  # Try to use the library 
  if((pos = _icepaplib_get_position(mne)) == ".error.") {

    # Oherwise use the direct access
    ans= _icepap_query(dev,"",sprintf("?FPOS %s",addr))
    if(sscanf(ans,"%d",pos) != 1) {
     _icepap_err
     printf("unable to get position for: \"%s\"\n",mne)
     return ".error."
    }
  }

  pos_str = sprintf("%.15g", pos / motor_par(num,"step_size"))
  icepap__debug sprintf("\"%s\": pos      %s",mne,pos_str)
  return(pos_str)
 }


 #
 # start a motion (p1==abs pos, p2==rel pos, with pos in mm or deg)
 #
 if (key == "start_one") {
  local pos 
  local cmd
  local ans



  
  # Convert position into steps
  pos=icepap_round(p1*motor_par(num,"step_size"))
  ICEPAP[mne]["target_steps"]=pos

  # Erase stop code condition to treat it once
  ICEPAP[mne]["stop_code"]=0



  # Try to use the library 
  if(!_icepaplib_start_one(mne, pos)) { return }




  # Otherwise use the direct access
  # minimum check that motion is possible
  sta = 0
  ans=_icepap_query(dev,"",sprintf("?FSTATUS %s",addr))
  if(sscanf(ans,"%x", sta) != 1) 
  {
   _icepap_err
   printf("unable to get status for: \"%s\"\n",mne)
   return ".error."
  }

  # get ready status
  if(!(sta & (1<<9)))
  {
   _icepap_err
   printf("driver is not ready for: \"%s\"\n",mne)

   # temporary: for debug purpose only
   _icepap_print_info(dev,addr,mne)
   return ".error."
  }

  return(_icepap_start_one(dev,addr,pos))
 }




 #
 # Check if the driver is powered and ready to use
 #
 if (key == "prestart_one") {
  local ret
  local ans
  local i

  return(0)
 }




 #
 # return the current motor status
 #
 if (key == "get_status") {
  local ret
  local ans
  local cms
  local sta sta_neg sta_pos


  # Try to use the library 
  if((ret = _icepaplib_get_status(mne)) != -1) { return(ret)}

  # Oherwise use the direct access
  ret = 0
  sta = 0
  ans=_icepap_query(dev,"",sprintf("?FSTATUS %s",addr))
  if(sscanf(ans,"%x", sta) != 1) 
  {
   _icepap_err
   printf("unable to get status for: \"%s\"\n",mne)
   return ".error."
  }


  # get moving status
  #
  # NOTE MP 07Jun10: check MOVING and SETTLING status bits
  #   -the MOVING   (10) goes down at the end of the trajectory
  #   -the SETTLING (11) goes down at the end of close loop algorythm
  #
  # NOTE MP 21Jun10: during a HOMING sequence, several motions take place,
  # therefore the MOVING/SETTLING pair can be unset. But the READY remains
  # unset during all the HOMING sequence. To distinguish a non READY axis
  # due to error condition from moving condition, the POWER bit 
  # must also be checked
  #   -the READY    (09) goes down on error or moving
  #   -the POWERON  (23) goes down on error
  #
  if((sta & (1<<10)) || (sta & (1<<11))) ret |= 0x02;
  else if(!(sta & (1<<9)) && (sta & (1<<23))) ret |= 0x02;

  # get limitswitches status
  if(sta & (1<<19)) ret |= 0x04;
  if(sta & (1<<18)) ret |= 0x08;


  # check stop code if not moving, of course
  #
  if(!(ret & 0x02)) {
    _icepap_stopcode(num, sta)
  }


  return(ret)
 }

 #
 # verify if settling is OK
 #
 if (key =="diff_position") {

  # the settling is handled by the DSP
  if(ICEPAP[mne]["pcloop"]=="ON")
  {
   local ans

   ans=_icepap_query(dev,"",sprintf("?FSTATUS %s",addr))
   if(sscanf(ans,"%x", sta) !=1) {
    _icepap_err
    printf("unable to get status for: \"%s\"\n",mne)
    return 0
   }

   # by default the motion is finished
   ret=0

   # get settling bit
   if(sta & (1<<11)) { ret=motor_par(num,"dc_dead_band") }

   return ret
  }

  # the settling is handled by SPEC
  if(ICEPAP[mne]["pcloop"]=="OFF")
  {
   local pos 
   local ans
   local poubelle


   ans= _icepap_query(dev,"",sprintf("?FPOS %s",addr))
   if(sscanf(ans,"%f%s",pos,poubelle) != 1) {
    _icepap_err
    printf("unable to get position for: \"%s\"\n",mne)
    return ".error."
   }
  
   # must return the position delta in steps
   return (pos-ICEPAP[mne]["target_steps"])
  }
  
 }


 #
 # stop a single motor
 #
 if (key == "abort_one") {
  local answ

  # Try to use the library 
  if(_icepaplib_stop(mne) != -1) { return }

  # Oherwise use the direct access
  cmd=sprintf("STOP %s",addr)
  answ = _icepap_query(dev,"#",cmd)
  if (answ == "OK")
   return;
  else
   _icepap_wr(dev,"","STOP");

  return
 }



 #
 # set position (p1=mm)
 #
 if (key == "set_position") {
  local fpos tbeg

  ans  = _icepap_query(dev,"#",\
      sprintf("POS %s %d", addr, icepap_round(p1*motor_par(num,"step_size"))))
  if(index(ans,"ERROR"))
  {
   _icepap_err
   printf("unable set position for: \"%s\"\n",mne)
   return ".error."
  }

  # give time to the DPM update from concerned DRIVER
  # sleep(.1) 
  tbeg   = time()
  while(((time()-tbeg) < .3)) {
   sscanf(icepap_cmd(num,"position"),"%.15g",fpos)
   if(fabs(fpos-p1) < 1e-3) { break }
  }


  return
 }

 #
 # set acceleration (p1=ms p2=steps/sec^2)
 #
 if (key == "acceleration") {
  ans  = _icepap_query(dev,"#",sprintf("ACCTIME %s %f",addr,p1/1000))
  if(index(ans,"ERROR"))
  {
   _icepap_err
   printf("unable set acceleration for: \"%s\"\n",mne)
   return ".error."
  }
 }


 #
 # set base_rate (p1=rate in Hz)
 #
 if (key == "base_rate") {
  return
 }


 #
 # set slew_rate (p1=rate in Hz)
 #
 if (key == "slew_rate") {
  ans  = _icepap_query(dev,"#",sprintf("VELOCITY %s %d",addr,p1))
  if(index(ans,"ERROR"))
  {
   _icepap_err
   printf("unable set velocity for: \"%s\"\n",mne)
   return ".error."
  }
 }



 #
 # initiate a home or limit search (p1=string with what to search)
 #
 if (key == "search") {
  local lim
  local cmd


  if(ICEPAP[mne]["linked"]=="YES") {
   _icepap_err
   printf("limit search not implemented for linked axis\n")
   return ".error."
  }


  if(p1=="lim+")           { cmd = "SRCH" ; lim="LIM+" }
  else if(p1=="lim-")      { cmd = "SRCH" ; lim="LIM-" }
  else if(p1=="lim+edge")  { cmd = "SRCH" ; lim="LIM+" }
  else if(p1=="lim-edge")  { cmd = "SRCH" ; lim="LIM-" }
  else if(p1=="home+")     { cmd = "HOME" ; lim="+1"   }
  else if(p1=="home-")     { cmd = "HOME" ; lim="-1"   }
  else 
  {
   _icepap_err
   printf("un-supported home or limit search\n")
   return ".error."
  }

  # If already at limit, do nothing
  #
  # NOTE MP 10Nov08:  the default behavior of "LIM+" command is to look for
  # a transition of positive limitswitch (i.e. the DRIVER will automatically
  # change the motor motion sense if the limitswitch is already active)
  # This behavior can be used using "lim+edge" argin.
  #
  if(p1=="lim+") { if(icepap_cmd(num,"get_status") == 0x08) { return } }
  if(p1=="lim-") { if(icepap_cmd(num,"get_status") == 0x04) { return } }

  
  # NOTE MP 10Nov08: the local variables are erased by the recursive call
  # to icepap_cmd("get_status"). Bug fixed in release 5.08.03-3
  mne = motor_mne(num)
  dev = ICEPAP[mne]["dev"]
  addr= ICEPAP[mne]["addr"]
 

  addr = "#" addr
  ans  = _icepap_query(dev,addr,sprintf("%s %s",cmd,lim))
  if(index(ans,"ERROR"))
  {
   _icepap_err
   printf("unable to start limit search\n")
   return ".error."
  }

  # NOTE MP 07Jun10:  do a polling on ?SRCHSTAT ?HOMESTAT to have
  # the found/notfound information from the DSP directly and to be
  # able to do some usage of the SRCHPOS and HOMEPOS
  #
  # from here, spec will do the polling on status until the
  # end of the motion. 
  return
 }

}'


#%IU%(dev,addr,mne)
#%MDESC%
#
def _icepap_print_info(dev,addr,mne) '{
   local n tt[]


   # check if linked axe or linked one
   if(ICEPAP[mne]["linked"] == "YES") {
    n=split(ICEPAP[mne]["linked_addr"],tt)
    for(;n;n--) {
     _icepap_print_info_driver(dev,tt[n-1],mne)
    }
   } else {
    _icepap_print_info_driver(dev,addr,mne)
   }

}'


#%IU%(dev,addr)
#%MDESC%
# TODO: unify with icepap_info macro
#
def _icepap_print_info_driver(dev,addr,mne) '{
   local num
   local tab
   tab="\t|"

   print "\t---------------------------------------------------------"
   num = motor_num(mne)
   print tab"MOTOR  : ",mne
   print tab"SYSTEM : ",motor_par(num,"address")
   print tab"DRIVER : ",sprintf("%02d",addr)
   print tab"WARNING: ",_icepap_query(dev,addr,"?warning")
   print tab"ALARM  : ",_icepap_query(dev,addr,"?alarm")

   print tab"PWRINFO: "
   _icepap_print_tabbed(tab,_icepap_query(dev,addr,"?isg ?pwrinfo"))
   print "\t---------------------------------------------------------"

}'

def _icepap_print_tabbed(tab,str) '{
  local lines[]
  local i,n

  n=split(str,lines,"\n")
  for(i=0,ret="";i<n;i++) {
      print tab,lines[i]
  }
}'

#%UU% [hostname|motor]
#%MDESC%
# Print out information on the specified ICEPAP system or on an
# ICEPAP motor configured in the current SPEC session.
#
def icepap_info '{
   local todo
   local num

   if($# == 0) {
     p "Usage: $0 [hostname | motor]"
     exit
   }

   # TODO MP : add multiple argins
   todo = "$1"
   if((num=motor_num(todo)) != -1) {
      _icepap_print_info_mot(num)
   } else {
      _icepap_print_info_system(todo)
   }
}'


#%IU%(motor)
#%MDESC%
#
def _icepap_print_info_mot(num) '{
   local mne
   local ad sw hn i

   sw  = 15
   mne = motor_mne(num)
   if(!("dev" in ICEPAP[mne])) { 
     p "ERROR: unknown motor"
     return 
   }
   printf("%*s : %s\n",sw,"motor name",mne)

   hn = ICEPAP[mne]["dev"]
   if(i=index(hn,":")) { hn = substr(hn,0,i-1) }
   printf("%*s : %s\n",sw,"system",hn)

   ad = ICEPAP[mne]["addr"]
   printf("%*s : %s\n",sw,"address",ad)

   printf("%*s : %s\n",sw,"power",motor_par(num,"power")?"ON":"OFF")
   printf("%*s : %s\n",sw,"closed loop",motor_par(num,"closed_loop")?"ON":"OFF")
  
}'



#%IU%(hostname)
#%MDESC%
#
def _icepap_print_info_system(dev) '{
   local sw ans silent
   local i nm nums[] ns

   silent = 1


   ans =_icepap_query(dev,0,"?VER", silent)
   if(ans == "") { 
     p "ERROR: unknown or unreachable system"
     return
   }
   sw  = 15
   printf("%*s : %s\n",sw,"system",dev)
   printf("%*s : %s\n",sw,"version",icepap_trim(ans))

   # look for motor from this system configured in the current session
   for(i=0,nm=0,ns="";i<MOTORS;i++) {
      if(!index(motor_par(i,"address"),dev)) { continue }
      nums[nm++] = i
      ns = sprintf("%s%s ",ns,motor_mne(i))
   }
   printf("%*s : %s\n",sw,"motors used",nm?ns:"NONE")
}'


#%UU% [hostname|motor]
#%MDESC%
# Check the firmware version(s) of the specificied ICEPAP system or 
# ICEPAP motor configured in the current SPEC session.
#
def icepap_firmware_check '{
   local todo
   local num

   if($# == 0) {
     p "Usage: $0 [hostname | motor]"
     exit
   }

   todo = "$1"
   if((num=motor_num(todo)) != -1) {
     _icepap_fw_check_mot(num) 
   } else {
     _icepap_fw_check_system(todo) 
   }

}'

#%IU%(motor)
#%MDESC%
#
def _icepap_fw_check_mot(num) '{
   p "NOT IMPLEMENTED YET"
}'




#%IU%(hostname)
#%MDESC%
#
def _icepap_fw_check_system(dev) '{
   local r_saved
   local r_master
   local r_drivers[]
   local n_drivers
   local r_failed_new[]
   local r_failed_old[]
   local s_failed_new
   local s_failed_old
   local n_failed
   local ans
   local addrs
   local i n a 
   local cmp cmp_saved
   local sw



   #
   # Collect information
   #
   printf("collecting data...\r");

   # Get the system revisions
   r_master = _icepap_fw_controller(dev)
   r_saved  = _icepap_fw_saved(dev)

   # Get the list of modules
   ans = _icepap_get_addrs(dev)
   n   = split(ans, addrs)
   for(i=0,n_drivers=0;i<n;i++) {
      a = addrs[i]
      if(a==0) { continue }

      r_drivers[a] = _icepap_fw_driver(dev,a)
      n_drivers++

      if(cmp = _icepap_fw_compare(r_master,r_drivers[a])) {
         if(cmp == 2) { 
             r_failed_new[a] = cmp 
             s_failed_new    = sprintf("%s %s",a,s_failed_new)
             n_failed_new++ 
         }
         if(cmp == 1) { 
             r_failed_old[a] = cmp ; 
             s_failed_old    = sprintf("%s %s",a,s_failed_old)
             n_failed_old++ 
         }
         n_failed++
      }
   }



   #
   # Display results
   #
   sw  = 21
   cmp_saved = _icepap_fw_compare(r_master,r_saved)
   printf("%*s : %s\n",sw,"system version",r_master)
   printf("%*s : %s\n",sw,"check saved",  !cmp_saved?"OK":"FAILED")
   ans = sprintf("check %d drivers",n_drivers)
   printf("%*s : %s\n",sw, ans, !n_failed?"OK":"FAILED")

   if(n_failed) {
      printf("%*s : %s\n",sw,"found mismatch on", \
         sprintf("%d driver%s",n_failed,n_failed==1?"":"s"))
   }
    
   if(n_failed_old) {
      #printf("%*s : ",sw, "too old drivers")
      #for(a in r_failed_old) { printf("%s ", a) }
      printf("%*s : %s",sw, "too old drivers", s_failed_old)
      printf("\n")
   }
   

   if(n_failed_new) {
      printf("%*s : ",sw, "too modern drivers")
      for(a in r_failed_new) { printf("%s ", a) }
      printf("\n")
   }



   #
   # Actions to take
   #
   if(cmp_saved) {
      p ""
      p "WARNING: firmware saved not up to date, giving up"
      p "HINT   : use the command \"icepap_prog\" with flag \"SAVE\" "
      exit
   }

   if((n_failed_new) && (n_failed_new > (n_drivers/2))) {
      p ""
      p "WARNING: there are too many drivers with too modern firmware,giving up"
      p "HINT   : use the command \"icepap_prog\" with flag \"ALL\" "
      exit
   }

   if(n_failed_old) {
      p ""
      p "WARNING: there are drivers with too old firmware"
      if(yesno("Do you agree to upgrade all of them",1)) {
         # NOTE MP 26Feb13: according to ICEPAP User Manual, the PROG command
         # accepts only on module address, therefore, we have to program
         # one after the other
         for(a in r_failed_old) { 
            printf("\n%*s : %s\n",sw, "upgrading driver",a)
            _icepap_prog(dev,"",a,"FORCE")
         }
      }
   }

   if((n_failed_new) && (n_failed_new < (n_drivers/2))) {
      p ""
      p "WARNING: there are drivers with too modern firmware"
      if(yesno("Do you agree to downgrade all of them",0)) {
         # NOTE MP 26Feb13: according to ICEPAP User Manual, the PROG command
         # accepts only on module address, therefore, we have to program
         # one after the other
         for(a in r_failed_new) { 
            printf("\n%*s : %s\n",sw, "downgrading driver",a)
            _icepap_prog(dev,"",a,"FORCE")
         }
      }
   }
}'


#%IU%(rev1, rev2)
#%MDESC%
# Compare deux revisions, given as strings, and returns:
#  0 if rev1 equal rev2
#  1 if rev1 > rev2
#  2 if rev1 < rev2
# -1 if invalid revision numbers
#
def _icepap_fw_compare(rev1, rev2) '{
  local hrev1 lrev1
  local hrev2 lrev2
  local ret

  if(sscanf(rev1,"%d.%d",hrev1,lrev1) != 2) { return(-1) }
  if(sscanf(rev2,"%d.%d",hrev2,lrev2) != 2) { return(-1) }

  if(hrev1>hrev2) { return(1) }
  if(hrev1<hrev2) { return(2) }
  if(lrev1>lrev2) { return(1) }
  if(lrev1<lrev2) { return(2) }

  return(0)
}'


#%IU%(hostname)
#%MDESC%
# Returns, for the specified system, the firmware version of the CONTROLLER
#
def _icepap_fw_controller(dev) '{
  return(_icepap_fw_driver(dev,0))
}'


#%IU%(hostname, addr)
#%MDESC%
# Returns, for the specified system, the firmware version of the DRIVER
# specified by its address.
#
def _icepap_fw_driver(dev, addr) '{
  local rev
  local ans

  # get the system version
  rev="0.0"
  ans=_icepap_query(dev,addr,"?VER")
  if(index(ans,"ERROR")) { return(rev) }

  rev=icepap_trim(ans)
  return(rev)
}'


#%IU%(hostname)
#%MDESC%
# Returns, for the specified system, the firmware version saved in flash
#
def _icepap_fw_saved(dev) '{
  local rev
  local ans
  local i n lines[] elts[]

  # get the system version
  rev="0.0"
  ans=_icepap_query(dev,"","?VER SAVED")
  if(index(ans,"ERROR")) { return(rev) }

  n=split(ans,lines,"\n")
  for(i=0;i<n;i++) {
     if(!index(lines[i],"SYSTEM")) { continue }
     split(lines[i],elts,":")
     rev=icepap_trim(elts[1])
     break
  }

  return(rev)
}'

#%UU% [hostname[:port]]
#%MDESC%
# Reset all the racks of the specified ICEPAP MASTER
#
def icepap_reset'{
 global ICEPAP[]
 local dev
 
 if($# < 1) {
  dev=getval("Icepap MASTER network name",ICEPAP["dev"])
 } else {
  dev="$1"
 }

 if(!index(dev,":")) { dev = dev":5000" }
 ICEPAP["dev"]=dev

 _icepap_wr(dev,"","RESET")
}'



#%IU% axis
#%MDESC%
# CONFIG LOCK:
# Add a lock on the specified axis (ex: 0/2)
#
def icepap_lock '{
 local a
 local u c
 local line

 if($# != 1) {
  print "USAGE: $0 axis"
  print "   Ex: $0 0/2"
  exit
 }
 a="$1"
 _icepap_lock(a,1)
}'


#%IU% axis
#%MDESC%
# CONFIG LOCK:
# Remove a lock on the specified axis (ex: 0/2)
#
def icepap_unlock '{
 local a
 local u c
 local line

 if($# != 1) {
  print "USAGE: $0 axis"
  print "   Ex: $0 0/2"
  exit
 }
 a="$1"
 _icepap_lock(a,0)
}'


#%IU%
#%MDESC%
# CONFIG LOCK:
# Check that lock file exist and create empty if not
#
def _icepap_islockfile() '{

 if(file_info(ICEPAP_LOCKFILE, "-w") == 0){
  printf("ICEPAP WARNING: touching \"%s\"\n",ICEPAP_LOCKFILE)
  if(unix(sprintf("touch %s",ICEPAP_LOCKFILE)) != 0) {
   _icepap_err
   print "error creating file "ICEPAP_LOCKFILE
   return -1
  }
 }
 close(ICEPAP_LOCKFILE)

 if(getline(ICEPAP_LOCKFILE, "open") < 0){
  _icepap_err
  printf("Can\'t open file \"%s\"\n", ICEPAP_LOCKFILE)
  return -1
 } 

 return 0
}'


#%IU%(axis,lock)
#%MDESC%
# CONFIG LOCK:
# Add or remove (lock=1 or 0) a lock on the specified axis (ex: 0/2)
#
def _icepap_lock(arg,lock) '{
 local a
 local u c
 local line
 local nline
 local found

 if(sscanf(arg,"%d/%d",u,c)!=2) {
  _icepap_err
  print "bad axis syntax (Ex: \"0/2\")"
  return -1
 }

 a=sprintf("%d/%d\n",u,c)

 if(_icepap_islockfile() != 0) 
  return -1;


 line =""
 nline=""
 found=0
 while ((line = getline(ICEPAP_LOCKFILE)) != -1) 
 {
  if(lock)  
  {
   if(line == a) {
    print "AXIS already locked"
    close(ICEPAP_LOCKFILE)
    return -1
   }

  } else {

   if(line == a) { 
    printf("ICEPAP : removing lock on axis %s\n",a)
    found=1 
   } else { 
    nline=sprintf("%s%s",nline,line) 
   }
  }

 }

 if(lock) {
  printf("ICEPAP : adding lock on axis %s\n",a)
  fprintf(ICEPAP_LOCKFILE,"%s",a)
  close(ICEPAP_LOCKFILE)
  return 0
 }

 if(found == 0) {
  _icepap_err
  print "axis was not locked"
  close(ICEPAP_LOCKFILE)
  return -1
 }

 unix(sprintf("\\rm -f %s",ICEPAP_LOCKFILE))
 unix(sprintf("touch %s",ICEPAP_LOCKFILE))
 open(ICEPAP_LOCKFILE)
 fprintf(ICEPAP_LOCKFILE,"%s",nline)
 close(ICEPAP_LOCKFILE)
 return 0
}'


#%IU%(axis)
#%MDESC%
# CONFIG LOCK:
# Return 1 if a lock exist for the specified axis (ex: 0/2)
# Return 0 if there is no lock.
# Return -1 in case of error.
#
def _icepap_islock(arg) '{
 local a
 local u c
 local line
 local nline
 local found

 if(sscanf(arg,"%d/%d",u,c)!=2) {
  _icepap_err
  print "bad axis syntax (Ex: \"0/2\")"
  return -1
 }

 a=sprintf("%d/%d\n",u,c)

 if(_icepap_islockfile() != 0) 
  return -1;

 line =""
 found=0
 while ((line = getline(ICEPAP_LOCKFILE)) != -1) 
 {
  if(line == a) { 
   found=1 
   break
  }
 }

 close(ICEPAP_LOCKFILE)
 return(found)

}'



#
# ----------------------- MACRO COUNTER implementation -----------------------
#

#%IU%(hostname:port, address)
#%MDESC%
# MACRO COUNTER: 
# Read the temperature on the specified address
# Returns 0 in case of error
#
def _icepapcnt_gettemp(dev,addr) '{
 local ans
 local t

 ans= _icepap_query(dev,addr,"?MEAS T")
 if(sscanf(ans,"%d",t) != 1) {
  _icepap_err
  printf("%s: addr: %s: unable to get temp\n",mne,addr)
  icepap_chkerr(dev,ans)
  icepap_fiforst(dev)
  return(0)
 }
 return(t)
}'


#%IU%
#%MDESC%
# MACRO COUNTER: 
# Called by spec after reading the config file
#
def icepapcnt_config(num,type,p1,p2,p3) '{
 global ICEPAP_CNT[]
 local mne
 local dev

 if(type=="ctrl") {
  return
 }

 # p1 is the unit
 # p2 is always 0
 # p3 is the channel which is the two digits address
 if(type=="cnt") {
   mne=cnt_mne(num)
   ICEPAP_CNT[mne]["addr"]=p3

   # get the MASTER
   ICEPAP_CNT[mne]["dev"]=""
   dev=counter_par(num,"address")
   if(dev==0) {
    printf("ICEPAPCNT ERROR: missing ADDR field\n")
    return 
   }
   if(index(dev,":") == 0) { dev=sprintf("%s:5000",dev) }
   ICEPAP_CNT[mne]["dev"]=dev
 }
}'

#%IU%
#%MDESC%
# MACRO COUNTER: 
# Called by spec on counter operation.
#
def icepapcnt_cmd(num,key,p1,p2) '{
 global ICEPAP_CNT[]
 local mne
 local addr
 local dev


 if (key == "counts") {
  mne=cnt_mne(num)
  addr=ICEPAP_CNT[mne]["addr"]

  # get the MASTER
  if(!("dev" in ICEPAP_CNT[mne])) { return }
  dev=ICEPAP_CNT[mne]["dev"]
  if(dev=="") { return }

  S[num]=_icepapcnt_gettemp(dev,addr)
 }
}'




#
# ----------------------- MACRO COUNTER implementation -----------------------
#

#%IU%(<counter_number>, <hostname:port>, <address>)
#%MDESC%
# MACRO COUNTER: ENCODER
# Read the encoder position on the specified address
# Returns 0 in case of error
#
def _icepapenc_getpos(counter_num, dev, addr) '{
    local ans
    local t
    local cmd
    local typ

    # "encoder" parameter is set to "inc" by default in config.
    typ = counter_par(counter_num, "encoder")
    cmd = ICEPAP_ENCCMD[typ]
    ans = _icepap_query(dev,addr,sprintf("?ENC %s", cmd))
    if(sscanf(ans, "%lf", t) != 1) {
        _icepap_err
        printf("counter \"%s\": addr: \"%s\": unable to get enc pos\n", \
               cnt_mne(counter_num), addr)
        icepap_chkerr(dev, ans)
        icepap_fiforst(dev)
        return(0)
    }

    # handle specific case of unsigned SSI encoders
    if(typ == "absu") {
        t += ((t<0)?(1<<counter_par(counter_num, "ssi")):0)
    }

    # normal end
    return(t)
}'


#%UU% (<counter_mnemonic>)
#%MDESC%
#    Returns value of encoder associated to macro-counter
# <encoder_mnemonic>.
# This function is similar to the previous one but provided to be
# user-callable by beeing more user-friendly.
def icepapenc_read(counter_mne) '{
    local  _dev  _addr  _res

    if(!("dev" in ICEPAP_ENC[counter_mne])) {
        printf("counter %s: unable to read encoder\n", counter_mne)
        printf("--> check that your icepap macro counter is defined in SPEC config. \n")
        return
    }
    else{
        _dev  = ICEPAP_ENC[counter_mne]["dev"]
        _addr = ICEPAP_ENC[counter_mne]["addr"]

        _res = _icepapenc_getpos(cnt_num(counter_mne), _dev, _addr)
        return _res
    }
}'


#%IU%(counter, hostname:port, address, pos)
#%MDESC%
# MACRO COUNTER: ENCODER
# Set the encoder position on the specified address
# Returns 0 in case of error
#
def _icepapenc_setpos(num,dev,addr,pos) '{
 local ans
 local t
 local cmd 
 local typ
 
 typ = counter_par(num,"encoder")
 if(index(typ,"abs")) {
  _icepap_err
  p "no sense to set the position of an absolute encoder"
  return
 }

 cmd = ICEPAP_ENCCMD[typ]
 ans = _icepap_wrrd(dev,sprintf("#%s",addr),sprintf("ENC %s %d",cmd,pos))
 if(index(ans,"ERROR"))
 {
  _icepap_err
  print ""ans
  return
 }
}'

#%UU% counter position
#%MDESC%
# MACRO COUNTER: ENCODER
# Force the value of an ICEPAP encoder configured as a SPEC counter
#
def icepap_setenc '{
 if($#!=2) {
  p "Usage: $0 counter position"
  exit
 }

 _icepap_setenc("$1",$2)
}'


#%IU%(counter_mne, position)
#%MDESC%
# MACRO COUNTER: ENCODER
# Force the value of an ICEPAP encoder configured as a SPEC counter
#
def _icepap_setenc(mne,pos) '{
 local addr
 local dev
 local num

 addr=ICEPAP_ENC[mne]["addr"]
 dev =ICEPAP_ENC[mne]["dev"]

 if(addr == 0) {
  _icepap_err
  p "\""mne"\" not an ICEPAP encoder counter"
  return
 }
 
 num = cnt_num(mne)
 pos = pos * counter_par(num,"scale")
 _icepapenc_setpos(num,dev,addr,pos)
}'



#%IU%
#%MDESC%
# MACRO COUNTER: ENCODER
# Called by spec after reading the config file
#
def icepapenc_config(num,type,p1,p2,p3) '{
 global ICEPAP_ENC[]
 global ICEPAP_ENCCMD[]
 local mne
 local dev

 if(type=="ctrl") {

  ICEPAP_ENCCMD["inc"]   = "ENCIN"
  ICEPAP_ENCCMD["abs"]   = "ABSENC"
  ICEPAP_ENCCMD["absu"]  = "ABSENC"
  ICEPAP_ENCCMD["inpos"] = "INPOS"
  ICEPAP_ENCCMD["motor"] = "MOTOR"

  dev = icepapenc_ADDR
  ans=_icepap_query(dev,0,"?ID", silent)
  if(ans == "")
    { return ".error." }
  return
 }

 # p1 is the unit
 # p2 is always 0
 # p3 is the channel which is the two digits address
 if(type=="cnt") {
   mne=cnt_mne(num)
   ICEPAP_ENC[mne]["addr"]=p3

   # get the MASTER
   ICEPAP_ENC[mne]["dev"]=""
   dev=counter_par(num,"address")
   if(dev==0) {
    printf("ICEPAPCNT ERROR: missing ADDR field\n")
    return  ".error."
   }
   if(index(dev,":") == 0) { dev=sprintf("%s:5000",dev) }
   ICEPAP_ENC[mne]["dev"]=dev

   # get the type of encoder to read
   typ = counter_par(num,"encoder")
   if((typ == -1) || (typ == 0)) {
    counter_par(num,"encoder","inc","add")
    typ = "inc"
   }
   # minium check
   if((index(typ,"abs")!=1)&&(typ!="inc")&&(typ!="inpos")&&(typ!="motor")) {
    _icepap_err
    printf("invalid \"encoder\" parameter for counter \"%s\"", mne)
    return  ".error."
   }

   # handle the specific case of unsigned SSI encoders
   if((typ != "abs") && index(typ,"absu")) {
    local ssi
    if(sscanf(typ,"absu%d",ssi) != 1) {
      _icepap_err
      printf("invalid \"encoder\" parameter \"absu\" for counter \"%s\"", mne)
      return  ".error."
    }
    counter_par(num,"encoder","absu")
    counter_par(num,"ssi",ssi,"add")
    ICEPAP_ENC[mne]["ssi"]=ssi
   }
   # normal end
   ICEPAP_ENC[mne]["typ"]=typ
 }
}'

#%IU%
#%MDESC%
# MACRO COUNTER: ENCODER
# Called by spec on counter operation.
#
def icepapenc_cmd(num,key,p1,p2) '{
 global ICEPAP_ENC[]
 local mne
 local addr
 local dev


 if (num != "..") {
  mne=cnt_mne(num)
  addr=ICEPAP_ENC[mne]["addr"]

  # get the MASTER
  if(!("dev" in ICEPAP_ENC[mne])) { return }
  dev=ICEPAP_ENC[mne]["dev"]
  if(dev=="") { return }
 }

 if (key == "start_one") {
  icepap_cleanup(dev)
 }

 if (key == "counts") {
  return _icepapenc_getpos(num,dev,addr)
 }

 if (key == "halt_one") {
  icepap_cleanup(dev)
 }
}'






#
# ----------------------- MACRO COUNTER implementation -----------------------
#

#%IU%
#%MDESC%
# MACRO COUNTER: 
# Called by spec after reading the config file
#
def icepapcalc_config(num,typ,p1,p2,p3) '{
 global ICEPAP_CALC[]
 local mne
 local crate
 local type
 local dev

 if(typ=="ctrl") {
  ICEPAP_CALC["min_crate"]=0
  ICEPAP_CALC["max_crate"]=0
  return
 }

 # p1 is the unit
 # p2 is always 0
 # p3 is the channel which is the two digits address
 if(typ=="cnt") {
   mne  =cnt_mne(num)
   crate=counter_par(num,"misc_par_1")
   if((crate<0) || (crate>16)) {
    p "\tERROR: counter "mne" unusable: invalid crate as \"misc_par_1\""
    p "\tHINT:  type \"help local icepap\""
    return
   }
   type =counter_par(num,"misc_par_2")
   if((type!="min") && (type!="max") && (type!="avg")) {
    p "\tERROR: counter "mne" unusable: invalid type as \"misc_par_2\""
    p "\tmust be \"max\" \"min\" or \"avg\""
    p "\tHINT:  type \"help local icepap\""
    return
   }
   ICEPAP_CALC[mne]["crate"]=crate
   ICEPAP_CALC[mne]["type"] =type

   if(crate>ICEPAP_CALC["max_crate"]) { ICEPAP_CALC["max_crate"]=crate }
   if(crate<ICEPAP_CALC["min_crate"]) { ICEPAP_CALC["min_crate"]=crate }

   # get the MASTER
   ICEPAP_CALC["dev"]=""
   dev=counter_par(num,"address")
   if(dev==0) {
    printf("ICEPAPCALC ERROR: missing ADDR field\n")
    return 
   }
   if(index(dev,":") == 0) { dev=sprintf("%s:5000",dev) }
   ICEPAP_CALC["dev"]=dev

 }
}'

#%IU%
#%MDESC%
# MACRO COUNTER: 
# Called by spec on counter operation.
#
def icepapcalc_cmd(num,key,p1,p2) '{
 global ICEPAP_CALC[]
 local mne
 local addr
 local crate
 local type
 local t j c
 local t_min t_max t_tot t_avg n_tot
 local dev

 if (key == "prestart_all") {
  # get the MASTER
  if(!("dev" in ICEPAP_CALC)) { return }
  dev=ICEPAP_CALC["dev"]
  if(dev=="") { return }

  for(crate=ICEPAP_CALC["min_crate"];crate<=ICEPAP_CALC["max_crate"];crate++) {

   t_max=0
   t_min=0
   t_tot=0
   n_tot=0
   for(j=1;j<=8;j++) {
    addr=sprintf("%d%d",crate,j)
    t=_icepapcnt_gettemp(dev,addr)
    if(!t) { continue }
    if(!t_min)  { t_min=t }
    if(t>t_max) { t_max=t }
    if(t<t_min) { t_min=t }
    t_tot += t
    n_tot++
   }
   t_avg=t_tot/n_tot
   ICEPAP_CALC[crate]["min"]=t_min
   ICEPAP_CALC[crate]["max"]=t_max
   ICEPAP_CALC[crate]["avg"]=t_avg
   ICEPAP_CALC[crate]["n"]  =n_tot
  }
 }

 if (key == "counts") {
  mne   =cnt_mne(num)
  type  =ICEPAP_CALC[mne]["type"]
  crate =ICEPAP_CALC[mne]["crate"]
  S[num]=ICEPAP_CALC[crate][type]
 }
}'




#
# ----------------------- MACRO COUNTER implementation -----------------------
#


#%IU%
#%MDESC%
# MACRO COUNTER: MEASURE
# Called by spec after reading the config file
#
def icepapmeas_config(num,type,p1,p2,p3) '{
 global ICEPAP_MEAS[]
 local mne
 local dev
 local cmd

 if(type=="ctrl") {
  return
 }

 # p1 is the unit
 # p2 is always 0
 # p3 is the channel which is the two digits address
 if(type=="cnt") {
   mne=cnt_mne(num)
   ICEPAP_MEAS[mne]["addr"]=p3

   # get the MASTER
   ICEPAP_MEAS[mne]["dev"]=""
   dev=counter_par(num,"address")
   if(dev==0) {
    printf("ICEPAPMEAS ERROR: \"%s\": missing ADDR field\n", mne)
    return ".error."
   }
   if(index(dev,":") == 0) { dev=sprintf("%s:5000",dev) }
   ICEPAP_MEAS[mne]["dev"]=dev

   # get the command to read the DSP internals
   cmd=counter_par(num,"cmd")
   if(cmd==0) {
    printf("ICEPAPMEAS ERROR: \"%s\": missing \"cmd\" parameter\n", mne)
    return ".error."
   }
   ICEPAP_MEAS[mne]["cmd"]=cmd
 }
}'


#%IU%
#%MDESC%
# MACRO COUNTER: MEASURE
# Called by spec on counter operation.
#
def icepapmeas_cmd(num,key,p1,p2) '{
 global ICEPAP_MEAS[]
 local mne
 local addr
 local dev
 local cmd


 if (num != "..") {
  mne =cnt_mne(num)
  addr=ICEPAP_MEAS[mne]["addr"]
  dev =ICEPAP_MEAS[mne]["dev"]
  cmd =ICEPAP_MEAS[mne]["cmd"]
 }

 if (key == "start_one") {
  icepap_cleanup(dev)
 }

 if (key == "counts") {
  local ans
  local t

  ans= _icepap_query(dev,addr,"?"cmd)

  if(index(ans,"ERROR")) {
   printf("ICEPAPMEAS ERROR: \"%s\": unable to get value\n",mne)
   return(0)
  }

  if(sscanf(ans,"%lf",t) != 1) {
   printf("ICEPAPMEAS ERROR: \"%s\": addr: %s: unable to get value\n",mne,addr)
   icepap_chkerr(dev,ans)
   icepap_fiforst(dev)
   return(0)
  }
  return(t)
 }

 if (key == "halt_one") {
  icepap_cleanup(dev)
 }
}'







#
# ----------------------- direct accces macros -------------------------
#



#%UU% hostname cmd [args]
#%MDESC%
# Execute the specified command with its args on all 
# ICEPAP modules (MASTER, SLAVES and DRIVERS) known by the hostname.
# Note that quotes must be used when cmd includes a hash for the
# acknowledge.
#
def icepap_all '{
 local dev
 local cmd


 # Minimum check

 if($# < 2) {
  p "Usage: $0 icepap cmd [args]"
  p "   ex: $0 iceid131 ?ver info"
  p "   ex: $0 iceid131 \"\#power\" off"
  exit
 }

 # Get args
 dev = "$1"
 if(!index(dev,":")) { dev = dev":5000" }
 ICEPAP["dev"]=dev

 # Get args
 cmd = "$2"
 if($# > 2) { cmd = cmd " $3" }
 if($# > 3) { cmd = cmd " $4" }
 
 _icepap_all(dev, cmd)
}' 


#%IU%(hostname,cmd)
#%MDESC%
#
def _icepap_all(dev, cmd) '{
 local ans
 local addrs
 local i n a


 # Not needed
 cmd = icepap__toupper(cmd)
 
 # Just in case
 icepap_fiforst(dev)

 # Get the list of modules
 ans = _icepap_get_addrs(dev)
 n   = split(ans, addrs)

 # Execute the command on each module
 for(i=0;i<n;i++) {
  a = addrs[i]

  if(substr(cmd,1,1)=="?")
   p _icepap_wrrd(dev,a,cmd);
  else if(substr(cmd,1,1)=="#")
   p _icepap_wrrd(dev,"#" a,substr(cmd,2));
  else
   _icepap_wr(dev,a,cmd);
 }

}'


#%IU%(hostname)
#%MDESC%
# Returns the addresses of all ICEPAP modules alive on the
# specficed hostname. The addresses are returned in a single string,
# blank separated.
#
def _icepap_get_addrs(dev) '{
 local cmd_ans
 local addrs
 local rflg
 local r
 local dflg
 local d
 local a


 addrs   = ""
 cmd_ans = _icepap_query(dev,"","?SYSSTAT") 
 if(index(cmd_ans,"ERROR"))
 {
  printf("ICEPAP: sorry, too old firmware to guess racks present\n")
  return(addrs) 
 }

 sscanf(cmd_ans,"%x",rflg)
 for(r=0;r<16;r++)
 {
  if(!(rflg & (1<<r))) { continue }

  cmd_ans = _icepap_query(dev,"",sprintf("?SYSSTAT %d",r)) 
  if(index(cmd_ans,"ERROR")) return(addrs)

  cmd_ans=substr(cmd_ans,index(cmd_ans," ")+1)
  sscanf(cmd_ans,"%x",dflg)

  for(d=0;d<=8;d++)
  {
   if((d!=0) && !(dflg & (1<<(d-1)))) { continue }

   a=(d)+(r*10)
   addrs = sprintf("%s%d ",addrs,a)
  }

 }

  return(addrs) 
}'




#%IU%(bliss_package_name)
#%MDESC%
#
def _is_package_installed(pkg) '{
 local ll
 local cmd

 cmd = "rpm --dbpath ~blissadm/admin/RPM -qa"
 cmd = sprintf("%s | grep \"%s\"",cmd, pkg)
 unix(cmd,ll)
 if(ll=="") {
   p "ERROR: missing package \"",pkg,"\""
   p "HINT : use \"blissinstaller\""
   exit
 }
}'


#%IU%(icepap_hostname)
#%MDESC%
#
def _is_same_subnetwork(dev) '{
 local ll
 local cmd
 local dev_ip[]
 local host_ip[]
 
 cmd = sprintf("nslookup `hostname`")
 cmd = sprintf("%s |grep -i \"address\"|grep -v \"#\"|cut -d\':\' -f2",cmd)
 unix(cmd, ll)
 if(ll=="") {
   p "ERROR: unable to get hostname IP, giving up"
   exit
 }
 split(ll, host_ip, ".")

 cmd = sprintf("nslookup %s", dev)
 cmd = sprintf("%s |grep -i \"address\"|grep -v \"#\"|cut -d\':\' -f2",cmd)
 unix(cmd, ll)
 if(ll=="") {
   p "ERROR: unable to get \""dev"\" IP, giving up"
   exit
 }

 split(ll, dev_ip, ".")
 if(dev_ip[2] != host_ip[2]) {
   p "ERROR: not on same sub-network than \""dev"\", giving up"
   p "HINT : use spec on another machine"
   exit
 }
}'

#%IU%(firmwares_list)
#%MDESC%
#
def _is_valid_version(dev, ll) '{
 local fw_list[]
 local fw_n
 local fw_cur
 local ans

 fw_n = split(ll,fw_list)
 ans=_icepap_query(dev, "", "?VER")
 if(index(ans,"ERROR"))
 {
  p "ERROR: unable to get icepap system version, giving up"
  p "HINT : switch on \""dev"\""
  exit
 }
 fw_cur=icepap_trim(ans)
 for(n in fw_list) {
  if(fw_list[n] == fw_cur)
    return
 }

 p "ERROR: wrong current firmware version \""fw_cur"\" on \""dev"\""
 p "HINT : must be one of these \""ll"\""
 exit
}'


#%IU%(icepap_hostname)
#%MDESC%
#
def _is_alive(dev) '{
  local silent
  local ans
  local cmd
  local n

  cmd = sprintf("ping -c 1 %s > /dev/null",dev)
  for(n=5;n;n--) {
   if(!unix(cmd)) { 
    # Linux is up but give time to the embedded program to start
    sleep(1)

    # Restore SPEC socket connection
    if(!index(dev,":")) { dev = dev":5000" }
    sock_par(dev,"close")
    sock_par(dev,"connect")

    # Check that the embedded program started well
    silent = 1
    ans=_icepap_query(dev,0,"?ID", silent)
    if(ans == "") { 
     p "ERROR: the embedded program is not running on \""dev"\""
     p "HINT : reboot it by cycling its power supply (off/on)"
     return ".error." 
    }

    # Normal end
    return ""
   }
   sleep(1)
  }
  
  p "ERROR: timeout waiting for \""dev"\" to be alive"
  return ".error." 
}'

#%IU%(dev, addr_list)
#%MDESC%
# Bypass external disable signal on the list of drivers given.
# (this was done with DISDIS command on old firmwares <2.0)
# Works only on firmware >=2.0 
#
def _emulate_alldis(dev, addr_list) '{
 local ll
 local i

 split(addr_list, ll)
 for(i in ll) {
  _emulate_onedis(dev, ll[i])
 }
}'


#%IU%(dev, addr)
#%MDESC%
# Bypass external disable signal on the driver given.
# (this was done with DISDIS command on old firmwares <2.0)
# Works only on firmware >=2.0 
#
def _emulate_onedis(dev, addr) '{
 local ans
 local cmd
 local signature

 ans = _icepap_query(dev, addr, "?MODE")
 if(!index(ans,"OPER"))
 {
  p "ERROR: driver \""addr"\" not in OPER mode, giving up"
  exit
 }

 ans = _icepap_query(dev, addr, "?CONFIG")
 signature = ans

 cmd = sprintf("%sCONFIG",spec_par("specwiz")?"BD_":"")
 ans = _icepap_query(dev, "#"addr, cmd)
 if(index(ans,"ERROR"))
 {
  p "ERROR: unable to enter CONFIG on driver \""addr"\", giving up"
  exit
 }

 cmd = sprintf("%sCFG EXTDISABLE NONE",spec_par("specwiz")?"BD_":"")
 ans = _icepap_query(dev, "#"addr, cmd)
 if(index(ans,"ERROR"))
 {
  p "ERROR: unable to change EXTDISABLE on driver \""addr"\", giving up"
  exit
 }

 cmd = sprintf("%sCONFIG \"%s\"",spec_par("specwiz")?"BD_":"", signature)
 ans = _icepap_query(dev, "#"addr, cmd)
 if(index(ans,"ERROR"))
 {
  p "ERROR: unable to change CONFIG on driver \""addr"\", giving up"
  exit
 }

}'


#%UU% hostname
#%MDESC%
# Upgrade the specified icepap system from firmware 1.22 to 2.0
# If called from SPEC wizard mode, the security of the mandatory
# same subnetwork is bypassed.
#
def upgrade_firmware_1_22 '{
 local  dev
 local  fname
 local  addr
 local  opt
 local  dis_list
 local  msg
 local  i


 # Minimum check
 if($# < 1) {
  p "Usage: $0 hostname"
  p "   ex: $0 iceid131"
  exit
 }
 dev = "$1"
 
 

 # Dangerous macro
 p "\nWARNING: this macro will upgrade all the firmwares of \"$1\""
 if(!yesno("Do you want to continue",0)) {
  p "Giving up"
  exit
 }
 p ""


 # Check that packages have been upgarded
 _is_package_installed("icepapcms-src-1.28")
 _is_package_installed("icepap_firmware-src-2.0")


 # Check that the system has firmware version 1.22 or 1.225
 _is_valid_version(dev, "1.22 1.225")


 # Check that we are on the same subnetwork
 if(!spec_par("specwiz")) {
  _is_same_subnetwork(dev)
 }


 # Do an inventory of axis with external signal disabled
 p "Collectng list of axis with external disable signal bypassed"
 dis_list = _icepap_alldis(dev, 1)

 # Keep inventory into a file

 # Upgrade firmwares
 fname = "/users/blissadm/local/userconf/icepap/icepfw_2.0"
 addr  = "ALL"
 opt   = "FORCE"
 p "Upgrading FPGA/DSP firmwares"
 _icepap_prog(dev,fname,addr,opt)


 # Upgrade MCPUx firmwares
 addr  = "MCPU0"
 p "Upgrading embedded Linux firmwares"
 _icepap_prog(dev,fname,addr,opt)
 addr  = "MCPU1"
 _icepap_prog(dev,fname,addr,opt)
 addr  = "MCPU2"
 _icepap_prog(dev,fname,addr,opt)


 # Save new firmware into the icepap system flash
 addr  = "NONE"
 opt   = "SAVE"
 p "Saving new firmware package into embedded flash"
 _icepap_prog(dev,fname,addr,opt)


 # Reboot the icepap system to run the new MCPUx and set RDISPOL
 p "Reboot the system and wait for its wake up"
 _icepap_query(dev,"#","REBOOT")
 sleep(20)
 if(_is_alive(dev) != "") {
   p "HINT : after the reboot run the following macro:"
   p sprintf("_emulate_alldis(\"%s\",\"%s\")",dev, dis_list)
   exit
 }


 # Restore external disable signal
 p "Restoring external disable signal bypasses"
 _emulate_alldis(dev, dis_list)


 # Normal end
 p "Bravo\! System should be upgraded"
 if(i=index(dev,":")) { dev = substr(dev, 0, i-1) }
 msg = sprintf("%s", date())
 msg = sprintf("%s\nsystem      : %s", msg, dev)
 msg = sprintf("%s\nupgraded to : 2.0", msg)
 msg = sprintf("%s\nDISDIS kept : %s", msg, dis_list)
 _icepap_email(sprintf("%s: UPGRADE", dev), msg)
}'





#%UU% hostname
#%MDESC%
# Will try to guess if the DISDIS parameter is really needed on
# each DRIVER of the specified ICEPAP systerm. If not needed, the
# macro will remove it.
# WARNING: this macro is for temporary usage only.
#
def icepap_alldis '{
 local dev
 local silent


 # Minimum check
 if($# < 1) {
  p "Usage: $0 icepap"
  p "   ex: $0 iceid131"
  exit
 }

 # Get args
 dev = "$1"
 ICEPAP["dev"]=dev

 silent = 0
 _icepap_alldis(dev, silent)
}' 


def _icepap_alldis(dev, silent) '{
 global ICEPAP[]
 local  cmd_ans
 local  rflg
 local  r
 local  dflg
 local  d
 local  addr
 local  was_on
 local  addr_list
 local  del_list
 local  msg
 local  i



 # Just in case
 if(!index(dev,":")) { dev = dev":5000" }
 icepap_fiforst(dev)

 # Check if firmware is a valid one
 cmd_ans = _icepap_wrrd(dev,"","?SYSSTAT") 
 if(index(cmd_ans,"ERROR"))
 {
  printf("ICEPAP: sorry, too old firmware to guess racks present\n")
  exit
 }

 addr_list = ""
 del_list  = ""
 cmd_ans = _icepap_query(dev,"","?SYSSTAT") 
 sscanf(cmd_ans,"%x",rflg)
 for(r=0;r<16;r++)
 {
  if(!(rflg & (1<<r))) { continue }

  cmd_ans = _icepap_query(dev,"",sprintf("?SYSSTAT %d",r)) 
  if(index(cmd_ans,"ERROR")) exit;

  cmd_ans=substr(cmd_ans,index(cmd_ans," ")+1)
  sscanf(cmd_ans,"%x",dflg)

  # Skip COMM or MASTER modules
  for(d=1;d<=8;d++)
  {
   if((d!=0) && !(dflg & (1<<(d-1)))) { continue }

   addr=(d)+(r*10)
   cmd_ans = _icepap_query(dev,addr,"?DISDIS") 
   if(index(cmd_ans,"ERROR")) continue;
   if(int(cmd_ans) != 1) { continue }
  
   if(!silent) { printf("Found driver with DISDIS set: %02d\n",addr) }
  
   # Keep a record of the current state in any case
   cmd_ans=_icepap_query(dev,addr,"?POWER")
   was_on =(index(cmd_ans,"ON")?1:0)
   if(!silent) { printf("\tPOWER is  %s\n",(was_on?"ON":"OFF")) }

   # Try to disable DISDIS
   if(was_on)
   {
    cmd_ans=_icepap_query(dev,"#" addr,"POWER OFF")
    if(index(cmd_ans,"ERROR")) 
    {
     printf("ERROR: switching off power\n")
     printf("Giving up\n")
     continue;
    }
   }

   cmd_ans=_icepap_query(dev,"#" addr,"DISDIS 0")
   if(index(cmd_ans,"ERROR")) 
   {
    printf("ERROR: removing DISDIS parameter\n")
    printf("Giving up\n")
    continue;
   }

   # Try to guess if DISDIS is really needed
   cmd_ans=_icepap_query(dev,addr,"?ISG ?PWRINFO")
   if(index(cmd_ans,"axis remote disable signal on")) 
   {
    if(!silent) { printf("\tDISDIS needed\n") }
    addr_list = sprintf("%s%s ", addr_list, addr)
    cmd_ans=_icepap_query(dev,"#" addr,"DISDIS 1")
    if(index(cmd_ans,"ERROR")) 
    {
     printf("ERROR: setting DISDIS parameter\n")
    }
   }
   else
   {
    del_list = sprintf("%s%s ", del_list, addr)
    if(!silent) { printf("\tDISDIS not needed, removed !!!!!!!!!!!!!!\n") }
   }

   # Put back power if needed
   if(was_on)
   {
    if(!silent) { printf("\tPOWER set %s\n",(was_on?"ON":"OFF")) }
    cmd_ans=_icepap_query(dev,"#" addr,"POWER ON")
    if(index(cmd_ans,"ERROR")) 
    {
     printf("ERROR: switching on  power\n")
     printf("Giving up\n")
     continue;
    }
   }
  }

 }

 printf("\tDISDIS kept           : \"%s\"\n",addr_list)
 printf("\tDISDIS deleted        : \"%s\"\n",del_list)

 # Normal end, return the list of drivers than need DISDIS
 if(i=index(dev,":")) { dev = substr(dev, 0, i-1) }
 msg = sprintf("%s", date())
 msg = sprintf("%s\n%s", msg, dev)
 msg = sprintf("%s\nDISDIS kept   : %s", msg, addr_list)
 msg = sprintf("%s\nDISDIS deleted: %s", msg, del_list)
 _icepap_email(sprintf(" %s: DISDIS", dev), msg)
 return addr_list

}'




#%UU% [hostname[:port]]
#%MDESC%
# Console implementation to communicate to icepap MASTER
#
def icepap '{
 global ICEPAP[]
 global ICEPAP_NCOMM
 global ICEPAP_HIST[]
 global ICEPAP_NHIST
 global ICEPAP_CHIST
 local  dev
 local  line c fc
 local  args[]
 

 if($# < 1) {
  dev=getval("Icepap MASTER network name",ICEPAP["dev"])
 } else {
  dev="$1"
 }

 if(!index(dev,":")) { dev = dev":5000" }
 ICEPAP["dev"]=dev


 cdef("cleanup_once",sprintf("_icepap_close(\"%s\")",dev),"ICEPAP")

 if(sock_put(dev, "?_help\n") == -1) { exit }
 sleep(0.1)
 sock_par(dev,"timeout",2)
 p "\n" sock_get(dev)
 
 while(1) {
  line = ""
  printf("\n%d.ICEPAP console> ",ICEPAP_NCOMM);
  while(1) {
    c = input(-1)
    if (c == "\n") {
      printf("\n")
      tty_cntl("cd")
      break
    } else if ((c == "\b" || c == "\177") && (line != "")) {
      line = substr(line, 0, length(line)-1)
      printf("\b \b")
    } else if (asc(c) == 27) {
      sleep(0.05)
      c = input(-1)
      sleep(0.01)
      c = input(-1)
      # left  arrow
      # if(asc(c) == 0x44) 
      # right arrow
      # if(asc(c) == 0x43) 
      if(asc(c) == 0x41) {
       if(ICEPAP_CHIST) { 
        line = ICEPAP_HIST[--ICEPAP_CHIST] 
        printf("\r")
        tty_cntl("cd")
        printf("%d.ICEPAP console> %s",ICEPAP_NCOMM,line);
       }
      } else if(asc(c) == 0x42) {
       if(ICEPAP_CHIST<ICEPAP_NHIST) { 
        line = ICEPAP_HIST[++ICEPAP_CHIST] 
        printf("\r")
        tty_cntl("cd")
        printf("%d.ICEPAP console> %s",ICEPAP_NCOMM,line);
       }
      }
    } else if (c >= " " && c <= "z") {
      line = line c
      printf(c)
    }
    sleep(0.01)
  }

  if(line) {
   ICEPAP_HIST[ICEPAP_NHIST]=line
   ICEPAP_NHIST++
   ICEPAP_CHIST=ICEPAP_NHIST
  }

  split(line,args)
  if(!args[0]) {
   continue
  } else if(whatis(args[0]) & 0x00000002) {
    eval(line)
    continue
  }

  uline = icepap__toupper(line)
  if((uline == "QUIT") || (uline == "CLOSE")) {
   sock_par(dev,"close")
   exit
  }

  fc = substr(line,1,1) 
  if((fc!="#") && (fc!=":")) {
   line = "#" line
  }

  # acknowledge of broadcast commands is forbidden
  if(fc==":") {
    _icepap_wr(dev,"",line)
  } else {
    ans = _icepap_query(dev,"",line,1)
    p icepap_trim(ans)
  }
  ICEPAP_NCOMM++
 }
}'



#%IU%(string, n)
#%MDESC%
# Parse the string to get the ICEPAP device to talk to.
# Try to get as nth argument (starting from 1) in the string, 
# if not prompt the user.
#
def _icepap_getdev(str, i) '{
 local args[]
 local n
 local dev
 local sl

 n=split(str,args)

 if(n < i) {
  dev=getval("Icepap MASTER network name or SL",ICEPAP["dev"])
 } else {
  dev=args[i-1]
 }

 sl = (dev + 0 == dev)?1:0
 if(!sl && !index(dev,":")) { dev = dev":5000" }
 ICEPAP["dev"]=dev

 return(dev)
}'




#%IU%(string, n)
#%MDESC%
# Parse the string to get the ICEPAP tocho file.
# Try to get as nth argument (starting from 1) in the string, 
# if not prompt the user.
#
def _icepap_getfname(str, i) '{
 local args[]
 local n
 local fname
 local fdir
 local uxcom
 local aux
 local m
 local flist[]
 local f


 n=split(str,args)

 if(n < i) {
  fdir=sprintf("%s/%s",BLISSADM,"local/userconf/icepap");
  if(!file_info(fdir)) {
   print "ERROR: missing icepap_firmware, hint: use \"blissinstaller\""
   exit
  }
  
  uxcom = "cd " fdir "; ls -rt"
  unix(uxcom, aux)
  m = split(aux, flist, "\n")
  for(m--;m;m--)
  {
   f=flist[m]
   if(f=="") continue;
   if(file_info(sprintf("%s/%s",fdir,f),"isdir")) continue;
   break;
  }

  fname=sprintf("%s/%s",fdir,flist[m])
  fname=getval("Binary program file",fname)
 } else {
  fname=args[i-1]
 }

 if(file_info(fname,"-r") == 0) {
  print "ERROR: unable to read file \""fname"\""
  exit
 } 

 return(fname)
}'



#%IU%(string, n)
#%MDESC%
# Parse the string to get the addr for programming.
# Try to get as nth argument (starting from 1) in the string, 
# if not prompt the user.
#
def _icepap_getaddr(str, i) '{
 local args[]
 local n
 local addr

 n=split(str,args)

 if(n < i) {
  addr=getval("What to program (\"ALL\"|\"DRIVERS\"|\"CONTROLLERS\"|\"NONE\"|addr) ",\
     "NONE")
 } else {
  addr=args[i-1]
 }
 addr = icepap__toupper(addr)
 if(addr=="NONE") { addr="" }

 return(addr)
}'


#%IU%(string, n)
#%MDESC%
# Parse the string to get the options for programming.
# Try to get as nth argument (starting from 1) in the string, 
# if not prompt the user. 
# Any remaining argument is also return as option.
#
def _icepap_getopts(str, i) '{
 local args[]
 local n
 local opt

 n=split(str,args)

 if(n < i) {
  opt=getval("Options (\"SAVE\"|\"SL\"|\"FORCE\"|\"NONE\") ","NONE")
 } else {
  opt=args[i-1]
  for(;i<n;i++) { opt=opt " " args[i] }
 }
 opt = icepap__toupper(opt)
 if(opt =="NONE") { opt ="" }

 return(opt)
}'


#%IU%(string, n)
#%MDESC%
# Parse the string to get the rack addresses for programming.
# Try to get as nth argument (starting from 1) in the string, 
# if not prompt the user. 
# Any remaining argument is also return as option.
#
def _icepap_getracks(str, i) '{
 local args[]
 local n
 local racks

 n=split(str,args)

 if(n < i) {
  racks=getval("List of racks (ex: 0 3)",0)
 } else {
  racks=args[i-1]
  for(;i<n;i++) { racks=racks " " args[i] }
 }
 racks = icepap__toupper(racks)

 return(racks)
}'




#%UU% [hostname[:port] [file] [rack ... ]]
#%MDESC%
# Program a the ICEPAP specified list of racks using the rack back plane
# serial line.
#
def icepap_rprog '{
 local dev
 local fname
 local racks
 local sl
 local cmd_ans


 # Get args or prompt them to the user
 dev   = _icepap_getdev  ("$*", 1)
 fname = _icepap_getfname("$*", 2)
 racks = _icepap_getracks("$*", 3)

 sl    = (dev + 0 == dev)?1:0


 # Get a connection to ICEPAP
 if(!sl) { _icepap_check(dev) }


 # Send this with acknowledge to ensure that all DRIVERs mode have changed
 cmd = "#MODE PROG"
 printf("\tCommand sent  : \"%s\"\n", cmd)
 if(sl) 
 { 
  ser_put(dev,sprintf("%s\n",cmd)) 
 }  
 else 
 { 
  cmd_ans = _icepap_wrrd(dev,"",cmd) 
  if(!index(cmd_ans,"MODE OK"))
  {
   _icepap_err
   print "unable to switch ICEPAP to programmation mode"
   exit
  }
 }
 
 
 cmd = sprintf("*RPROG %s",racks)
 printf("\tCommand sent  : \"%s\"\n", cmd)
 if(sl) { ser_put(dev,sprintf("%s\n",cmd)) } else { _icepap_wr(dev,"",cmd) }


 # Send the binary data
# _icepap_wrbin(dev,fname)

}'


#%UU% [hostname[:port] [file] [addr] [options]]
#%MDESC%
# Program a the ICEPAP module with the specified binary file
#
def icepap_prog '{
 local dev
 local fname
 local addr
 local opt
 

 # Get args or prompt them to the user
 dev   = _icepap_getdev  ("$*", 1)
 fname = _icepap_getfname("$*", 2)
 addr  = _icepap_getaddr ("$*", 3)
 opt   = _icepap_getopts ("$*", 4)

 _icepap_prog(dev,fname,addr,opt)
}'


#%IU%(dev,fname,addr,opt)
#%MDESC%
# Program a the ICEPAP module with the specified arguments.
# If no firmware file name is given, the programmatino will used
# the firmware saved in the controller flash.
#
def _icepap_prog(dev,fname,addr,opt) '{
 global ICEPAP[]
 local sl
 local cmd
 local cmd_ans
 local dest_mcpu
 local dest_none
 local use_saved
 local dodo

 # Get a connection to ICEPAP
 sl    = (dev + 0 == dev)?1:0
 if(!sl && !index(dev,":")) { dev = dev":5000" }
 if(!sl) { _icepap_check(dev) }

 dest_mcpu=index(addr,"MCPU")
 dest_none=(addr=="")?1:0

 # Minimum check on parameters validity
 if(dest_none && opt == "")
 {
  _icepap_err
  print "nothing to do, giving up"
  exit
 }

 # If no firmware file, then use the saved one
 use_saved = (fname=="")

 # Skip mode handling if no DSP or FPGA is concerned
 if(!dest_none && !dest_mcpu)
 {
  # Send this with acknowledge to ensure that all DRIVERs mode have changed
  cmd = "#MODE PROG"
  printf("\tCommand sent  : \"%s\"\n", cmd)
  if(sl) 
  { 
   ser_put(dev,sprintf("%s\n",cmd)) 
  }  
  else 
  { 
   cmd_ans = _icepap_wrrd(dev,"",cmd) 
   if(!index(cmd_ans,"MODE OK"))
   {
    _icepap_err
    print "unable to switch ICEPAP to programmation mode"
    exit
   }
  }
 }
 
 
 cmd = sprintf("%sPROG %s %s",(use_saved?"":"*"),addr, opt)
 printf("\tCommand sent  : \"%s\"\n", cmd)
 if(sl) { ser_put(dev,sprintf("%s\n",cmd)) } else { 
  _icepap_wr(dev,"",cmd) 
 }


 # Send the binary data
 if(!use_saved) { _icepap_wrbin(dev,fname) }
 

 #Skip mode handling if no DSP or FPGA is concerned
 #if(!dest_none && !dest_mcpu)
 if(!dest_mcpu)
 {
  ans="?????"
  printf("\tState         : %-20s\r",ans)

  # NOTE MP 08Apr11:  should be replaced by an acknowled on *PROG cmd
  sleep(1)

  cmd="?_PROG"
  if(index((_icepap_wrrd(dev,"",cmd)),"ERROR")) { cmd="?PROG" }

  for(;index((ans= _icepap_query(dev,"",cmd)),"ACTIVE");sleep(.5))
   printf("\tState         : %-20s\r",ans);

  printf("\tState         : %-20s\n",ans)
 
  sleep(2)
 }
 else
 {
  ans=" DONE"
  printf("\tState         :%-20s\n",ans)
 }


 if(!dest_none && !dest_mcpu)
 {
  # Give time to the drivers to come back to life
  dodo = 5
  printf("\tWait for      : %dsecs\n", dodo)
  sleep(dodo)

  cmd = "#MODE OPER"
  printf("\tCommand sent  : \"%s\"\n", cmd)
  if(sl)
  {
   ser_put(dev,sprintf("%s\n",cmd))
  }
  else
  {
   cmd_ans = _icepap_wrrd(dev,"",cmd)
   if(!index(cmd_ans,"MODE OK"))
   {
    _icepap_err
    print "unable to switch ICEPAP to operation mode"
    exit
   }
  }

 }

 # Check if a reboot is needed
 cmd="?VER"
 if(index((_icepap_wrrd(dev,"",cmd)),"ERROR Reboot is needed")) 
 { 
  cmd = "REBOOT"
  printf("\tCommand sent  : \"%s\"\n", cmd)
  printf("NOTE: the connection to \"%s\" will be unusable during 30s\n",dev)
  _icepap_wr(dev,"",cmd) 
 }
}'



#%IU%(dev,fname)
#%MDESC%
# Send the contain of the file to the ICEPAP using the ISG
# binary transfer protocol.
#
def _icepap_wrbin(dev,fname) '{
 local sl
 local bin_l
 local bin_chksum


 sl    = (dev + 0 == dev)?1:0

 bin_l = (file_info(fname,"size")/2) & 0xffffffff

 local ushort array icepapfw[bin_l]
 fmt_read(fname,"raw",icepapfw)

 # 2 words startup mark (ex: 0xa5aa555a)
 local ulong array datal[1]
 datal[0]=0xa5aa555a
 if(sl) { ser_put(dev, datal) } else { sock_put(dev, datal) }


 # 2 words for the binary data length
 datal[0]=bin_l
 if(sl) { ser_put(dev, datal) } else { sock_put(dev, datal) }


 # 2 words for the checksum 
 bin_chksum = (array_op("sum",icepapfw)) & 0xffffffff
 printf("\tCheck sum     : 0x%08X\n", bin_chksum)
 datal[0]=bin_chksum
 if(sl) { ser_put(dev, datal) } else { sock_put(dev, datal) }

 # chaud devant !!!
 printf("\tTransferring  : %d words\n", bin_l)
 if(sl) { ser_put(dev, icepapfw) } else { sock_put(dev, icepapfw) }
}'




#%IU% [hostname:port]
#%MDESC%
# Reset the MASTER DSP
#
def icepap_mdspreset '{
 global ICEPAP[]
 local dev

 if($# < 1) {
  dev=getval("Icepap MASTER network name",ICEPAP["dev"])
 } else {
  dev="$1"
 }

 if(!index(dev,":")) { dev = dev":5000" }
 ICEPAP["dev"]=dev

 sock_put(dev,"_dsprst\n")
 print "MASTER DSP reseted"
}'




#%IU% [hostname:port [driver]]
#%MDESC%
# Program one or all DRIVER DSP numbered from 1 to 8
# using the backplane serial line and the DDSP programm taken
# from MDSP flash (no binary transferred)
#
def icepap_ddsppgm_sl '{
 local dev
 local board

 if($# < 1) {
  dev=getval("Icepap MASTER network name","isgtmp5:5000")
 } else {
  dev="$1"
 }

 _icepap_check(dev)

 # Ya un couillon au clavier ?
 board=0
 if($# < 2) { 
  board=getval("Icepap DRIVER to program from 1 to 8 (0 for all)",board)
 } else {
  board=$2 
 }

 if((board<0) || (board>8)) 
 {
  print "ICEPAP: invalid channel/board "board" (valid: 1 to 8 or 0 for all)"
  return
 }

 # ok, on peut bosser
 if (board) { p "\tProgramming board: "board }
 else       { p "\tProgramming all boards" }
 sock_put(dev,sprintf("PROG %d\n",board))

 p "\tBe patient....."
 p "\tHint: check messages on DSP serial line console of MASTER"
}'




#%IU%(hostname:port)
#%MDESC%
# Do not touch, needed by the "raleur"
#
def _icepap_check(dev) '{
 local  ans


 # Allo? Gaston? Tes la?
 sock_par(dev,"close")
 if(!sock_par(dev,"connect"))
 {
  _icepap_err
  print "communication program not running on MASTER"
  print "Hint: reset MASTER or run \"icepap_sock\" on ",dev
  exit
 }
 sock_par(dev,"timeout",ICEPAP_TIMEOUT)
 ans=_icepap_query(dev,"","?_SOCKPING")
 if(ans != "OK") {
  _icepap_err
  print "bad response from MASTER"
  print "Hint: reset MASTER or run \"icepap_sock\" on ",dev
  exit
 }

}'


#%IU%(hostname:port)
#%MDESC%
#
def _icepap_close(dev) '{
 sock_put(dev,"_SOCKCLOSE\n");
 sock_par(dev,"close");
}'



#%IU%
#%MDESC%
#
def _icepap_err '{
 tty_cntl("md")
 printf("ICEPAP ERROR: ")
 tty_cntl("me")
}'


#%IU%
#%MDESC%
#
def _icepap_warn '{
 tty_cntl("md")
 printf("ICEPAP WARNING: ")
 tty_cntl("me")
}'


#%IU%(num, status)
#%MDESC%
# Decode stop reason for the driver status given.
# No action, print only for diagnostic
#
def _icepap_stopcode(num, sta) '{
 local stop_code
 local mne

 # Stop code implemented on bits 14-17 
 stop_code = (sta & ((1<<14)|(1<<15)|(1<<16)|(1<<17))) >> 14
 
 # Normal case is code 0
 if(!stop_code) { return(0) }

 # Ignore limitswitches
 if((stop_code==3) || (stop_code==4)) { return(0) }

 # Treat the stop code only once
 mne=motor_mne(num)
 if(stop_code == ICEPAP[mne]["stop_code"]) { return(1) }
 ICEPAP[mne]["stop_code"]=stop_code

 # Abnormal stop case
 _icepap_warn
 printf("\"%s\": abnormal stop condition: ", mne)
 if(stop_code ==  1) {p "STOP signal received"               ; return(1) }
 if(stop_code ==  2) {p "ABORT signal received"              ; return(1) }
 if(stop_code ==  3) {p "Limit+ activated"                   ; return(1) }
 if(stop_code ==  4) {p "Limit- activated"                   ; return(1) }
 if(stop_code ==  5) {p "close loop settling timeout"        ; return(1) }
 if(stop_code ==  6) {p "axis disabled"                      ; return(1) }
 if(stop_code ==  8) {p "internal failure"                   ; return(1) }
 if(stop_code ==  9) {p "motion failure"                     ; return(1) }
 if(stop_code == 10) {p "power overload"                     ; return(1) }
 if(stop_code == 11) {p "driver overheating"                 ; return(1) }
 if(stop_code == 12) {p "close loop error"                   ; return(1) }
 if(stop_code == 13) {p "control encoder error"              ; return(1) }
 if(stop_code == 15) {p "external abort"                     ; return(1) }

}'

#%IU%(dev,addr,cmd)
#%MDESC%
#
def _icepap_wr(dev,addr,cmd) '{
 local str
 global ICEPAP_HISTORY[]


 if(addr != "" && addr != "#") str=addr":"cmd; else str=cmd;
 if (addr == "#") str = "#" str
 icepap__debug "sending " str
 _icepap_hist_add(dev, str)

 str=str"\n"
 sock_par(dev,"flush")
 sock_put(dev,str);
}'


#%IU%(dev,addr,cmd)
#%MDESC%
#
def _icepap_wrrd(dev,addr,cmd) '{
 local ret
 
 _icepap_wr(dev,addr,cmd)
 ret=sock_get(dev);
 icepap__debug "getting " ret

 _icepap_hist_append(ret)

 return(ret)
}'


#%IU%(dev,addr,cmd,silent)
#%MDESC%
#
def _icepap_query(hn,addr,cmd,silent) '{
  local dev
  local ans[]
  local cmd_ans
  local len_ans 
  local lc
  local n
  local ret
  local i


  
  ret=""
  dev=sprintf("%s%s",hn, index(hn,":")?"":":5000")

  cmd_ans=_icepap_wrrd(dev,addr,cmd)

  for(;;) {
   len_ans=length(cmd_ans)
   lc = substr(cmd_ans,len_ans,1)
   if((lc=="\n") || (lc=="\r")) {
    cmd_ans=substr(cmd_ans,0,len_ans-1)
    continue
   }
   break
  }

  n=split(cmd_ans,ans)
  if(n<1) {
   if(!silent) { 
    _icepap_err
    printf("bad answer from IcePAP\n") 
   }
   return(cmd_ans)
  }
  
  if(index(ans[1],"ERROR")) {
   if(!silent) { 
    _icepap_err
    printf("%s\n",cmd_ans) 
   }
   return(cmd_ans)
  }

  ret=substr(cmd_ans,index(cmd_ans," ")+1)
  return(ret)
}'


#%IU%()
#%MDESC%
# Temporary patch
#
def _icepap_patch(dev, addr) '{
  local slave
  local dest

  print "WARNING: icepap ",dev,"MASTER DSP being reset (",addr,")"

  # Reset the MASTER DSP only, DRIVERs not affected
  _icepap_wr(dev,"","_DSPRST")
 
  # Give a minimum time to DSP to reset (and to DRIVERs to come back)
  # or check that it is alive
  sleep(3)

  # Reaffect a CAN address to the DRIVER
#  slave=int(addr/10)*10
  
  # If the DRIVER is in the MASTER rack, the CAN address is already given
#  if(slave != 0) 
#  { 
#   _icepap_wr(dev,slave,"RESET") 
#   sleep(10)
#  }

}'

#%IU%()
#%MDESC%
# Temporary email after patch
#
def _icepap_patch_email() '{
  dest = "perez@esrf.fr jclement@esrf.fr"
  unix(sprintf("echo \"%s\" |mailx -s \"ERROR on %s session %s\" %s",\
   _icepap_hist_dump(),\
    SPECBL,\
    SPEC,\
    dest))
}'

#%IU%(message)
#%MDESC%
# Send an email with the specificied patch
#
def _icepap_email(subject, str) '{
  dest = "perez@esrf.fr jclement@esrf.fr"
  unix(sprintf("echo \"%s\" |mailx -s \"%s: session: \\\"%s\\\"\" %s",\
    str,\
    subject,\
    SPEC,\
    dest))
}'


#%IU%()
#%MDESC%
# Init the ICEPAP history if needed
#
def _icepap_hist_init() '{
 global ICEPAP_HISTORY[]

 if(!("size" in ICEPAP_HISTORY)) {
  ICEPAP_HISTORY["size"]=1000
  ICEPAP_HISTORY["end"] =0
 }
}'


#%IU%(dev,cmd)
#%MDESC%
# Add to the ICEPAP history an entry
#
def _icepap_hist_add(dev, cmd) '{
 global ICEPAP_HISTORY[]
 local  n
 local  tab

 tab = "   "
 _icepap_hist_init()
 n = ICEPAP_HISTORY["end"] + 1
 if(n >= ICEPAP_HISTORY["size"]) { n = 0}
 
 ICEPAP_HISTORY[n] = sprintf("%s%s%s%s%s",date(),tab,dev,tab,cmd)
 ICEPAP_HISTORY["end"] = n 
}'

#%IU%(str)
#%MDESC%
# Append a string to the last line in the ICEPAP history
#
def _icepap_hist_append(str) '{
 global ICEPAP_HISTORY[]
 local  n

 n = ICEPAP_HISTORY["end"]
 
 ICEPAP_HISTORY[n] = sprintf("%s -> %s",ICEPAP_HISTORY[n], str)
}'


#%IU%()
#%MDESC%
# Return a string with  the history of commands sent to ICEPAP systems
#
def _icepap_hist_dump() '{
 global ICEPAP_HISTORY[]
 local  n
 local  i
 local  s
 local  ret

 ret = ""
 _icepap_hist_init()
 s = ICEPAP_HISTORY["size"]
 for(n=ICEPAP_HISTORY["end"];(n in ICEPAP_HISTORY) && (i<s);i++)
 {
  ret = sprintf("%s%s\n",ret,ICEPAP_HISTORY[n])
  n--
  if(n<0) { n = s - 1 }
 }
 return ret

}'

#%UU%()
#%MDESC%
# Print out the history of commands sent to ICEPAP systems
#
def icepap_history '{
 print _icepap_hist_dump()
}'






#
# ----------------------- test macros -------------------------
#


#%IU%(dev,src_addr,dst_addr)
#%MDESC%
# Will copy the source driver configuration to the destination one
# (bypass of icepapcms)
# ex: ("iceid207",11,25)
#
def icepap_duplicate_conf(dev,src_addr,dst_addr) '{
  local src_conf
  local src_arr[]
  local n i
  local dst_cmd
  local dst_err

  if(!index(dev,":")) { dev = sprintf("%s:5000",dev) }
  src_conf = _icepap_wrrd(dev,src_addr,"?CFG")
  n = split(src_conf,src_arr,"\r\n") - 1
  dst_addr = "#" dst_addr
  for(i=1;i<n;i++) {
     dst_cmd = sprintf("_cfg %s", src_arr[i])
     dst_err = _icepap_wrrd(dev,dst_addr,dst_cmd)
     if(!index(dst_err,"OK")) {
        p "ERROR: sending command: "dst_cmd" error: "dst_err
        return(-1)
     }
  }

  dst_cmd = "_CFG SAVE"
  dst_err = _icepap_wrrd(dev,dst_addr,dst_cmd)
  if(!index(dst_err,"OK")) {
     p "ERROR: sending command: "dst_cmd" error: "dst_err
     return(-1)
  }

  # normal end
  p "ICEPAP configuration of driver "dst_addr" updated"
  return(0)
}'



#%MACROS%
#%IMACROS%
#%AUTHOR% MP BLISS (Original 9/07).
# %BR%$Revision: 2.0 $ / $Date: 2013/10/01 13:18:09 $
#%TOC%