esrf

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

#%TITLE% HEXAPODE_TANGO.MAC
#%NAME% HEXAPODE_TANGO.MAC - Macros to access hexapode device server from spec.
#%DESCRIPTION%
#%PRE%
#   This macro set allows you to macro motors hexapode that will be driven using the conda tango device server.
#
#   To define macro motors you must define:
#
#   an hexapod controller in config:
#
#   MOTORS       DEVICE    ADDR   <>MODE      NUM               <>TYPE
#      YES       hexa      id28/hexapode/01     6         Macro Motors
#
#   motors in config referring to that controller. Channel assignment
#   will decide on the motor role as follow:
#
#   Channel
#     0   -   X
#     1   -   Y
#     2   -   Z
#     3   -   RotZ
#     4   -   RotY
#     5   -   RotX
#
#  Caution : take care not to swap RotZ and RotX...
#
#  for the time being motors defined on arbitrary translations/rotations
#  can only be defined as old-style pseudo motors with old taco server.
#%PRE%
#%BR%
#%EXAMPLE%
#%DL% some other usefull commands
#%DT%  hexapode %DD%
#    Shows complete information about current state and position of the
#    hexapode
#%DT%  hexamenu %DD%
#    It provides a number of commands useful when starting up with
#    a new hexapode. To be used with care.
#%XDL%
#
#%END%


#%IU%(mne, type, unit, module, channel)
#%MDESC%
# Macro motor implementation
#
def hexa_config(mne, type, unit, module, channel) '{
    global HEXALOCKED_POS[]
    local hexadev

    if (type == "ctrl") {
       hexadev = hexa_ADDR
       hexainit
       if (list_item(HEXA_DEV,hexadev) == -1 )
           list_add(HEXA_DEV,hexadev)
       print "Using HEXAPODE Macros Motor on",hexa_ADDR,"(ctrl unit",unit ")"

       # by default, force position update
       HEXALOCKED_POS[hexadev] = 0
    }
}'


def hexainit '{
  global HEXA_DEV HEXA_MOT
  global HEXA_ON
  global HEXA_REF

  global HEXA_PAR[]
  # HEXA_PAR["disabled"]    if 1 : hexapode commands not called.

  if (!(whatis("HEXA_DEV") & 0x1000000) )  {
     list_init HEXA_DEV
     list_init HEXA_MOT
  }

  HEXA_REF["LRS"]=0
  HEXA_REF["IRS"]=1
  HEXA_REF["FRS"]=2
  HEXA_REF["MRS"]=3
}'

#%IU%(mne, cmd, p1, p2)
#%MDESC%
# Macro motor implementation
#
def hexa_cmd(mne, cmd, p1, p2) '{
   global HEXALOCKED_POS[]
   global HEXAGO_POS[]
   local  hexahw_pos[]
   local  hexapos 
   local  hexadev
   local  i


   #
   #
   #
   if ( mne == ".." ) {

      hexadev = hexa_ADDR

      #
      #
      if ( cmd == "start_all" ) {

        # NOTE: MP15Sep24: SPEC built-in can call the controller "start_all"
        # entry point even if a motion is already running. Therefore
        # ignore "start_all" request in this situation.
        state = tango_get( hexadev,"State")
        if(state != 0) {
           return
        }

        for(i=0;i<6;i++) { hexahw_pos[i] = HEXAGO_POS[hexadev][i] }
 	tango_io( hexadev, "Move", hexahw_pos) 
        return
      }

      #
      #
      if ( cmd == "prestart_all") {
        if(!HEXALOCKED_POS[hexadev]) {
#          if(esrf_io( hexadev,"DevHxpdGetPosition", hexahw_pos) == -1) {
          if(tango_get( hexadev,"Position", hexahw_pos) != 6) {
            printf("Error reading positions. Giving up.\n")
            return
          }
          for(i=0;i<6;i++) { HEXAGO_POS[hexadev][i] = hexahw_pos[i] }
          HEXALOCKED_POS[hexadev] = 1
        }
        return
      }

      # called on <ctrl-c> but after the abort_one on each concerned motor
      if ( cmd == "abort_all" ) {

        # NOTE: MP15Nov17: SPEC built-in can call the controller "abort_all"
        # entry point even if no motion is already running. Therefore
        # ignore "abort_all" request in this situation.
        state = tango_get( hexadev,"State")
        if(state == 0) {
           return
        }

        local hxlegs[]

        # legs motion will stop immediately even if the DS is in a middle 
        # of a "from/to" small motion which means that the DS will let
        # the .tmp file causing a no more usable DS
        printf("\"%s\": motion abort requested\n", hexadev)
        ESRF_ERR = -1
        tango_io( hexadev, "Stop")

        # NOTE: MP15Dec18: the DevHxpdStop switches the server internal
        # state directly to HXPD_FAULT==2 even if the motor is sill moving
        # (decelerating). Therefore we can not poll the state to detect
        # the end of the motion. Use empiric sleep instead (typical 
        # deceleration time for an hexapod leg: 150ms)
        printf("\"%s\": waiting for the end of the motion...\n", hexadev)
        sleep(1)

        # wait for the end of motion abort
        #for(i=0, state=1;(i<100) && ((state == 1) || (state == 2)) ;i++) {
        #  sleep(0.02)
        #  state = tango_get( hexadev,"State")
        #}

        # the DS will remove the .tmp file and return to Ready state
        # if the leg length uncertainty is removed by a set on leg lengths
        printf("\"%s\": synchronizing positions...\n", hexadev)
#        if ((ret=esrf_io(hexadev,"DevHxpdGetLengths",hxlegs)) == -1) {
        if ((ret=tango_get(hexadev,"LegLength",hxlegs)) != 6) {
          printf("Error reading legs lengths. Giving up.\n")
          printf("Hint: use \"Full reset\" from \"hexamenu\"\n")
          return
        }

        # remove uncertainty
#        if ((ret=esrf_io(hexadev,"DevHxpdSetLengths",hxlegs)) == -1) {
        ret = tango_put(hexadev,"LegLength",hxlegs)
#	print "LegLength setting returned ",ret
	if (ret != 0) {
          printf("Error setting legs lengths. Giving up.\n")
          printf("Hint: use \"Full reset\" from \"hexamenu\"\n")
          return
        }

        # force internal synchronization for next motion
        HEXALOCKED_POS[hexadev] = 0

        # normal end of motion abort
        printf("\"%s\": hexapod back to operation\n", hexadev)
        return
      }

      return
   }



   hexadev = motor_par(mne, "address")
   unit    = motor_par(mne, "unit")
   chan    = motor_par(mne, "channel")



   #
   #
   #
   if ( cmd == "position" ) {
#       if(esrf_io(hexadev, "DevHxpdGetPosition", hexahw_pos) == -1) {
        if(tango_get( hexadev,"Position", hexahw_pos) != 6) {
         printf("Error reading positions. Giving up.\n")
          return ".error."
       }
       if ( chan > 2 ) {
          hexapos = rad( hexahw_pos[chan] ) * 1000.0
       } else {
          hexapos = hexahw_pos[chan]
       }
       return sprintf("%.15g",hexapos)
   }


   #
   #
   #
   if ( cmd == "get_status" ) {
       local ret
       local state
       local delta

       state = tango_get( hexadev,"State")

       # no motion is going on
       if(state == 0) {
         # if discrepancy already found, avoid checking again
         if(HEXALOCKED_POS[hexadev] != 0) {
           # if motion is finished, check if delta on positions are acceptable
#           if(esrf_io( hexadev, "DevHxpdGetPosition", hexahw_pos) == -1) {
          if(tango_get( hexadev,"Position", hexahw_pos) != 6) {
            printf("Error reading positions. Giving up.\n")
           } else {
             # unit is mm
             delta = 0.1

             if (!(whatis("HEXAGO_POS")&0x8000000)) {
               for(i=0;i<6;i++) {
                 if(fabs(hexahw_pos[i]-HEXAGO_POS[hexadev][i]) > delta) {
                   printf("WARNING: more than %fmm difference", \
                       delta)
                   printf(" on axis %d of %s, syncing\n",  \
                       i, hexadev)
                   HEXALOCKED_POS[hexadev] = 0
                   break
                 }
               }
             }
           }
         }

         ret = 0

       # at least one leg is still moving
       #} else if ( state == 1 ) {
       } else if ( state == 6 ) {
         ret = 0x2

       # panic, something went wrong
       } else {
       	 print "State is ",state
         ret = 0xc
         print "Major error, force position synchronization on",hexadev
         HEXALOCKED_POS[hexadev] = 0
       }

       return ret
   }

#class DevState(IntEnum):
#    ON = 0
#    OFF = 1
#    CLOSE = 2
#    OPEN = 3
#    INSERT = 4
#    EXTRACT = 5
#    MOVING = 6
#    STANDBY = 7
#    FAULT = 8
#    INIT = 9
#    RUNNING = 10
#    ALARM = 11
#    DISABLE = 12
#    UNKNOWN = 13


   #
   #
   #
   if ( cmd == "start_one") {
      unit = motor_par(mne, "unit")
      chan = motor_par(mne, "channel")
      if ( chan > 2 ) {
          HEXAGO_POS[hexadev][chan] = deg(p1 / 1000.0)
      } else {
          HEXAGO_POS[hexadev][chan] = p1
      }
   }


   #
   #
   #
   if ( cmd == "set_position") {
      print "Set position not available for hexapode. For now this has to be "
      print "   through device server resources. Sorry"

      return
   }


}'


#%UU%
#%MDESC%
#  Tool for hexapode calibration
def hexamenu '{
    global HEXACMD HEXA_NCMD
    local hxn0 hxdev

    if (!$#) {
       hxn0 = hexaselect()
    } else {
       hxn0 = $1
    }

    hxdev = list_item(HEXA_DEV,hxn0)

    HEXACMD[1] = "Show Hexapode"
    HEXACMD[2] = "STOP"
    HEXACMD[3] = "Soft reset ( reset error flag )"
    HEXACMD[4] = "Full reset ( move ref + move nominal )"
    HEXACMD[5] = "Switch Mode (NORMAL/SIMULATION)"
    HEXACMD[6] = "Calc and Check Positions"
    HEXA_NCMD = 6

    tty_cntl("md")
    printf("\n   < Hexapode Calibration >\n\n")
    tty_cntl("me")

    while (1) {
      newcmd = hexa_showcmds()
      if (newcmd) {
         if (substr(HEXACMD[newcmd],0,4) == "Show") {      # Show hexapode
            hexashow hxn0
         }
         if (substr(HEXACMD[newcmd],0,4) == "STOP") {      # Stop
            hexastop
         }
         if (substr(HEXACMD[newcmd],0,4) == "Soft") {      # Soft reset
            hexareset
         }
         if (substr(HEXACMD[newcmd],0,6) == "Switch") {    # Switch mode
            hexasim hxn0
         }
         if (substr(HEXACMD[newcmd],0,4) == "Full") {      # Full reset
            hexahard hxn0
         }
	 if (substr(HEXACMD[newcmd],0,4) == "Calc") {	# CheckPos
	    hexacalc hxn0 1
	 }
      } else {
         break
      }
    }
}'


#%IU%
#%MDESC%
#  Utility macro for other macros
def hexaselect() '{

    hxno = list_n(HEXA_DEV)

    if (hxno == 1) {
       return(1)
    } else if (hxno > 1 ) {
       printf("Select hexapode from list\n")
       for (hx = 1 ; hx <= hxno ; hx++) {
           printf("  %2d - %18s  ",hx,(hxname=list_item(HEXA_DEV,hx)))
           desc = tango_get(hxname,"Description")
           if (desc != -1 ) {
               tty_cntl("md");printf("%s",desc);tty_cntl("me")
           }
           printf("\n")
       }
       return(getval("\n  Number: ",1))
    } else {
       printf("No hexapode defined. Sorry\n")
       return(-1)
    }
}'

#%IU%
#%MDESC%
def hexa_showcmds() '{

    tty_cntl("md")
    printf("\n   Commands:\n\n")
    tty_cntl("me")

    for (i = 1;i<=HEXA_NCMD;i++) {
        tty_cntl("md");printf("   %d - ",i);tty_cntl("me")
        printf("%s\n",HEXACMD[i])
    }
    tty_cntl("md");printf("\n   0 - ");tty_cntl("me")
    printf("Quit\n")

    ret = getval("\n\n     Command? ",0)
    return(ret)
}'

#%UU% [ hexapode-number ]
#%MDESC%
#  Macro to visualize state/position/lengths of hexapode
def hexashow '{

    local hxdev hxn hxname

    if (!$#) {
       hxn=hexaselect()
    } else {
       hxn = $1
    }

    if (hxn < 1 || hxn > list_n(HEXA_DEV) ) {
       printf("Wrong hexapode selected\n")
       exit
    }

    _hexadesc(hxn)
    _hexalength(hxn)
    _hexapos(hxn)

    printf("  X          = %10.5f (mm)    |  Leg1: %8.4f - %s\n", \
                HEXAPOS[0],HEXALENGTH[0],HEXA_ACTSTATE[0])
    printf("  Y          = %10.5f (mm)    |  Leg2: %8.4f - %s\n",  \
                HEXAPOS[1],HEXALENGTH[1],HEXA_ACTSTATE[1])
    printf("  Z          = %10.5f (mm)    |  Leg3: %8.4f - %s\n",  \
                HEXAPOS[2],HEXALENGTH[2],HEXA_ACTSTATE[2])
    printf("  Phi  /RotZ = %10.5f (mrad)  |  Leg4: %8.4f - %s\n", \
                rad(HEXAPOS[3])*1000,HEXALENGTH[3],HEXA_ACTSTATE[3])
    printf("  Theta/RotY = %10.5f (mrad)  |  Leg5: %8.4f - %s\n", \
                rad(HEXAPOS[4])*1000,HEXALENGTH[4],HEXA_ACTSTATE[4])
    printf("  Psi  /RotX = %10.5f (mrad)  |  Leg6: %8.4f - %s\n", \
                rad(HEXAPOS[5])*1000,HEXALENGTH[5],HEXA_ACTSTATE[5])
}'

def _hexadesc(hxn) '{
   local err lerror lerrstr descr hexastat hexadev opmode

   hexadev = list_item(HEXA_DEV,hxn)

   err=0
   if ((descr=tango_get(hexadev,"Description")) == -1) {
       err=1
   }

   if ((opmode=tango_get(hexadev,"Mode")) == -1) {
       err=1
   }

   if ((hexastat=tango_get(hexadev,"Status")) == -1) {
       err=1
   }

#   TODO    check LastError return when status is fault, as well as what Status returns in such case
#   lerror=""
#   if (hexastat == "FAULT") {
#       lerr=lerror&~((0x3)<<26)&~((0x1)<<18)
#       if (lerror==-1) err=1
#       lerrstr=esrf_db("ERROR/3/1",lerr)
#   }

   if (err) {
      printf("Error accessing device %s\n", hexadev)
      return
   }

   printf("\n        Hexapode: ")
      tty_cntl("md");printf("    <%s>\n\n",hexadev);tty_cntl("me")
   printf("        Description:  %s\n",descr)
   printf("        State:        ")
      tty_cntl("md");printf("%s - %s\n",hexastat,lerrstr);tty_cntl("me")
   printf("        Mode:         ")
      tty_cntl("md");printf("%s\n\n",opmode?"Simulation":"Normal");tty_cntl("me")
}'

#%IU%
#%MDESC%
#
def _hexalength(hxn) '{

   global HEXALENGTH HEXA_ACTSTATE
   local argout lstate ststr

   hexadev = list_item(HEXA_DEV,hxn)

#   if (esrf_io(hexadev, "DevHxpdActuatorStatus", lstate) != 6) {
   if (tango_get(hexadev, "ActuatorStatus", lstate) != 6) {
      printf("Error accessing device %s\n", hexadev)
      exit
   }

#   if (esrf_io(hexadev, "DevHxpdGetLengths", HEXALENGTH) != 6) {
   if (tango_get(hexadev, "LegLength", HEXALENGTH) != 6) {
      printf("Error accessing device %s\n", hexadev)
   } else {

      for (leg=0;leg<6;leg++) {
         HEXA_ACTSTATE[leg]=""
         if (lstate[leg] & 0x01 )
             HEXA_ACTSTATE[leg] = sprintf("%s %s",HEXA_ACTSTATE[leg],"READY")
         if (lstate[leg] & 0x02 )
             HEXA_ACTSTATE[leg] = sprintf("%s %s",HEXA_ACTSTATE[leg],"MOVING")
         if (lstate[leg] & 0x04 )
             HEXA_ACTSTATE[leg] = sprintf("%s %s",HEXA_ACTSTATE[leg],"UPLIM")
         if (lstate[leg] & 0x08 )
             HEXA_ACTSTATE[leg] = sprintf("%s %s",HEXA_ACTSTATE[leg],"DOWNLIM")
         if (lstate[leg] & 0x10 )
             HEXA_ACTSTATE[leg] = sprintf("%s %s",HEXA_ACTSTATE[leg],"HOME")
         if (lstate[leg] & 0x20 )
             HEXA_ACTSTATE[leg] = sprintf("%s %s",HEXA_ACTSTATE[leg],"SEARCH")
         if (lstate[leg] & 0x80 )
             HEXA_ACTSTATE[leg] = sprintf("%s %s",HEXA_ACTSTATE[leg],"FAULT")
      }
   }
}'

#%IU%
#%MDESC%
#
def _hexapos(hxn) '{
    global HEXAPOS
    local hexadev

    if (HEXA_DEBUG) {
	print "_hexapos: reload position"
    }
    hexadev = list_item(HEXA_DEV,hxn)

    if (tango_get(hexadev, "Position", HEXAPOS) != 6) {
      printf("Error accessing device %s\n", hexadev)
    }
    list_setpar(HEXA_DEV,hxn,"x",    HEXAPOS[0])
    list_setpar(HEXA_DEV,hxn,"y",    HEXAPOS[1])
    list_setpar(HEXA_DEV,hxn,"z",    HEXAPOS[2])
    list_setpar(HEXA_DEV,hxn,"phi",  HEXAPOS[3])
    list_setpar(HEXA_DEV,hxn,"theta",HEXAPOS[4])
    list_setpar(HEXA_DEV,hxn,"psi",  HEXAPOS[5])
}'

#%UU%
#%MDESC%
#
def hexastop '{
    local hexa dev state
    for (hexa=1;hexa<=list_n(HEXA_DEV);hexa++) {
       dev = list_item(HEXA_DEV,hexa)
#       state = tango_get(dev,"State")
#       if (state == 1) { 
          tango_io(dev,"Stop")
#       }
    }
}'

#%UU% [ hexapode-number ]
#%MDESC%
# Does a soft reset on one hexapode
def hexareset '{
   if ($#) {
      hxn=$1
   } else {
      hxn=hexaselect()
   }
   if ( hxn <1 || hxn>list_n(HEXA_DEV)) {
      print "Wrong hexapode selected"
      exit
   }
   hexadev = list_item(HEXA_DEV,hxn)
#   esrf_io(hexadev,"DevHxpdReset",1)
   tango_io(hexadev,"Reset",1)
}'


#%UU% [ hexapode-number ]
#%MDESC%
#  Set operation mode to SIMULATION
def hexasim '{
   local mode newmode hxn

   if ($#) {
      hxn=$1
   } else {
      hxn=hexaselect()
   }

   hexadev = list_item(HEXA_DEV,hxn)

   if ( hxn <1 || hxn>list_n(HEXA_DEV)) {
      print "Wrong hexapode selected"
      exit
   }

#   mode    = esrf_io(hexadev,"DevHxpdGetMode")
   mode    = tango_get(hexadev,"Mode")

   printf("\nHexapode %s was in ",hexadev)
   tty_cntl("md");printf("%s",mode?"SIMULATION":"NORMAL");tty_cntl("me")

   newmode = mode?0:1
#   esrf_io(hexadev,"DevHxpdSetMode",newmode)
   tango_put(hexadev,"Mode",newmode)

   printf(" mode. Now is ")
   mode    = tango_get(hexadev,"Mode")
   tty_cntl("md");printf("%s.\n",mode?"SIMULATION":"NORMAL");tty_cntl("me")
}'


#%UU% [hexapode-number]
#%MDESC%
# Does a full reset on one hexapode
def hexahard '{
   if ($#) {
      hxn=$1
   } else {
      hxn=hexaselect()
   }

   if ( hxn <1 || hxn>list_n(HEXA_DEV)) {
      print "Wrong hexapode selected"
      exit
   }

   hexadev = list_item(HEXA_DEV,hxn)

   tty_cntl("md");printf("\n     WARNING! WARNING!\n");tty_cntl("me")
   printf("     A hard reset on the hexapode will move \n")
   printf("       the hexapode to its limit switches.\n")
   printf("         !!  This may be dangerous !!.\n")
   tty_cntl("md");printf("\n     WARNING! WARNING!\n");tty_cntl("me")
   _yn = yesno("Do you want to continue",0)

   if (_yn) {
       tango_io(hexadev,"Reset",0)
   }
}'




#%UU%
#%MDESC%
#  Enters a loop to prompt for positions to check
#  No motion is done with this macro
def hexacalc '{
    local hxn hxdev hxno hxname
    local hxin hxang argout
    local absmin absmax
    local lstmin lstmax
    local ret err

    if (!$#) {
       hxn=hexaselect()
    } else {
       hxn = $1
    }

    if (hxn < 1 || hxn > list_n(HEXA_DEV) ) {
       printf("Wrong hexapode selected\n")
       exit
    }

    hxdev = list_item(HEXA_DEV,hxn)

    if ((ret=tango_get(hxdev,"Position",hxin)) != 6) {
           err=1
    }
    hxang[0] = rad(hxin[3]) * 1000;
    hxang[1] = rad(hxin[4]) * 1000;
    hxang[2] = rad(hxin[5]) * 1000;

    while(1)  {

       err    = 0
       lstmax = lstmin = -1

       printf("\nEnter position to calc:\n")
       hxin[0]  = getval("   X:    ",hxin[0])
       hxin[1]  = getval("   Y:    ",hxin[1])
       hxin[2]  = getval("   Z:    ",hxin[2])
       hxang[2] = getval("   RotX: ",hxang[2])
       hxang[1] = getval("   RotY: ",hxang[1])
       hxang[0] = getval("   RotZ: ",hxang[0])
       hxin[3] = deg(hxang[0]/1000)
       hxin[4] = deg(hxang[1]/1000)
       hxin[5] = deg(hxang[2]/1000)

       ESRF_ERR=-1
#       if ((ret=esrf_io(hxdev,"DevHxpdCheckPosition",hxin,argout)) == -1) {
	if ((ret=tango_io(hxdev,"CheckPosition",hxin, argout))!=6) {
            err=1
            tty_cntl("md")
            print "\n       !! Position out of limits !!\n\n"
            tty_cntl("me")
       }

       if  ( err != 1 ) {
          tty_cntl("md")
          printf("Leg1         Leg2         Leg3        ")
          printf(" Leg4         Leg5         Leg6\n")
          tty_cntl("me")

          for (leg=0;leg<6;leg++) {
             printf("%6.4f     ",argout[leg])
             if ( argout[leg] < absmin || absmin < 2) absmin = argout[leg]
             if ( argout[leg] > absmax ) absmax = argout[leg]
             if ( argout[leg] < lstmin || lstmin < 2) lstmin = argout[leg]
             if ( argout[leg] > lstmax ) lstmax = argout[leg]
          }
       }
       printf("\n\n")

       tty_cntl("md"); printf("Last minimum : "); tty_cntl("me")
       printf("%6.4f  ",lstmin)
       tty_cntl("md"); printf("Last maximum : "); tty_cntl("me")
       printf("%6.4f  \n",lstmax)
       tty_cntl("md"); printf("Abs. minimum : "); tty_cntl("me")
       printf("%6.4f  ",absmin)
       tty_cntl("md"); printf("Abs. maximum : "); tty_cntl("me")
       printf("%6.4f  \n",absmax)
       if ($2) break
    }
}'