#----------------------------------------------------------------------------
# fcircle is a collection of SICS-Tcl macro scripts which help with
# the running of a four circle diffractometer. The central idea is
# a data base file which can be manipulated both from SICS and from 
# external programs. The name of this database file is expected to live in
# the SICS variable dbfile. 
#
# Mark Koennecke, November 1999
#---------------------------------------------------------------------------


#----------- fcircleinit
# fcircleinit must be called from the SICS initialization script and will
# then install all necessary variables and the commands defined by this 
# script into SICS. 

proc fcircleinit {} {
   VarMake dbfile Text User
   Publish writepos  User 
   Publish writerefl User
   Publish clearrefl User
   Publish listrefl  User
   Publish updateub  User
   Publish setlambda User
   Publish fcentre   User
}

#------------fsplit
# returns the answer part of a SICS reply of the form something = something
proc fsplit {text} {
    set l [split $text =]
    set t [lindex $l 1]
    return [string trim $t] 
}

#----------- locate
# This is an internal routine and tries to locate a section in the database
# file. The database file is plain ASCII. There are sections in the file
# separated by lines starting with ####. The #### is followed by the title
# name of the section. Then follow lines of section specific data in variable
# syntax. Another complication is that we need to be able to update the data
# in the file. In order to do that the whole file is first read into an array
# before the search is started. This is feasible as we have at max a hundred
# lines.  The array receives a member nl which is the number of lines read.
# The parameters are: 
#    ar the array of lines.
#    fd   a file descriptor as created by open
#    keyword  the keyword to search for
# The function returns the line number where the keyword was found on success,
# else -1 is returned. 
 
#proc dbfile {} {
#  return "dbfile = test.db"
#}

proc locate { data keyword} {
    upvar $data ar
#--------- read file
    set ret [catch {open [fsplit [dbfile]] r} fd]
    if {$ret == 1} {
       set ar(nl) 0 
       return -1
    }
#----- read whole file
    set i 0
    while {[gets $fd line] > 0 } {
	set ar($i) $line
        incr i
    }
    set ar(nl) $i
    close $fd
#----- start search
    for {set i 0} {$i < $ar(nl)} { incr i} {
#------ look for delimiter
        set delim [string range $ar($i) 0 3]
        if {[string compare $delim ####]  == 0} {
#----------- check keyword
	    if {[string first $keyword $ar($i) ] > 0} {
               return $i
            }             
        }
    }
    return -1;
}
#--------------- writerange
# writerange writes a range of lines from an array created by locate 
# to the file specicified by fd. Ranges are automaticcaly adjusted to 
# data limits

proc writerange { data fd start end } {
    upvar $data ar
#----- adjust ranges 
    if {$start < 0} {
       set start 0
    }
    if {$start > $ar(nl) } {
         set start $ar(nl)
    }
    if { $end > $ar(nl) } {
      set end $ar(nl)
    }
#---- write!
    for {set i $start} {$i < $end} {incr i} {
       puts $fd $ar($i)
    }
} 

#----------- writepos 
# writepos writes the current positions of stt, om, ch, ph motors as a A type
# line into the reflection section of the data base file. This is used after
# centering an unindexed reflection.

proc writepos { } {
#----- get positions
       set ret [catch {fsplit [stt]} mystt]
       if {$ret == 1} {
            error $myom
       }
       set ret [catch {fsplit [om]} myom]
       if {$ret == 1} {
            error $myom
       }
       set ret [catch {fsplit [ch]} mych]
       if {$ret == 1} {
            error $myom
       }
       set ret [catch {fsplit [ph]} myph]
       if {$ret == 1} {
            error $myom
       }
#----- find position in file
       set ret [catch {locate data reflections} ind]
#----- write
	set ret [catch {open [fsplit [dbfile]] w} fd]
        if { $ret == 1} {
            error $fd
        }
        if { $ind < 0 } {
           writerange data $fd 0 $data(nl)
           puts $fd "#### reflections"
           puts $fd [format "A %8.2f %8.2f %8.2f %8.2f" \
                    $mystt $myom $mych $myph] 
        } else {
           incr ind
           writerange data $fd 0 $ind
           puts $fd [format "A %8.2f %8.2f %8.2f %8.2f" \
                    $mystt $myom $mych $myph]
           writerange data $fd $ind $data(nl)
        }
        close $fd
        return OK
}  

#------------ writerefl
# writerefl writes a reflection with indexes. This makes an I record in
# the reflections list of the database file. This is meant to be used 
# after centering a reflection after the UB-matrix is known

proc writerefl { } {
#----- get positions
       set ret [catch {fsplit [stt]} mystt]
       if {$ret == 1} {
            error $myom
       }
       set ret [catch {fsplit [om]} myom]
       if {$ret == 1} {
            error $myom
       }
       set ret [catch {fsplit [ch]} mych]
       if {$ret == 1} {
            error $myom
       }
       set ret [catch {fsplit [ph]} myph]
       if {$ret == 1} {
            error $myom
       }
#------ get hkl
       set ret [catch {hkl current} txt]
       if {$ret == 1} {
            error $txt
       }
       set l [split $txt]
       set H [lindex $txt 2]
       set K [lindex $txt 3]
       set L [lindex $txt 4]
#----- find position in file
       set ret [catch {locate data reflections} ind]
#----- write
	set ret [catch {open [fsplit [dbfile]] w} fd]
        if { $ret == 1} {
            error $fd
        }
        if { $ind < 0 } {
           writerange data $fd 0 $data(nl)
           puts $fd "#### reflections"
           puts $fd [format "I %5.2f %5.2f %5.2f %8.2f %8.2f %8.2f %8.2f" \
                    $H $K $L $mystt $myom $mych $myph] 
        } else {
           incr ind
           writerange data $fd 0 $ind
           puts $fd [format "I %5.2f %5.2f %5.2f %8.2f %8.2f %8.2f %8.2f" \
                    $H $K $L $mystt $myom $mych $myph] 
           writerange data $fd $ind $data(nl)
        }
        close $fd
        return OK
}  
 
#----------- clearrefl
# clearrefl clears the list of reflections as stored in the database file

proc clearrefl {} {
#----- find position in file
       set ret [catch {locate data reflections} ind]
#----  nothing to do if no entry 
       if {$ind < 0} {
          return "Nothing to do"
       }
#----- write
	set ret [catch {open [fsplit [dbfile]] w} fd]
        if { $ret == 1} {
            error $fd
        }
        incr ind
        writerange data $fd 0 $ind
        for {set i $ind} {$i < $data(nl)} {incr i} {
            set delim [string range $data($i) 0 3]
            if {[string compare $delim ####] == 0} {
               break
            }           
        }
        set ind [expr $ind + $i - 1]
        writerange data $fd $ind $data(nl)
        close $fd
        return OK
}

#-------- listrefl
# listrefl lists all the entries in the reflection list

proc listrefl {} {
#----- find position in file
       set ret [catch {locate data reflections} ind]
#----  nothing to do if no entry 
       if {$ind < 0} {
          return "Nothing to do"
       }
#------ list
        incr ind
        for {set i $ind} {$i < $data(nl)} {incr i} {
            set delim [string range $data($i) 0 3]
            if {[string compare $delim ####] == 0} {
               break
            } else {
              ClientPut $data($i)
            }          
        }
        return OK
}

#---------- updateub
# updateub reads a UB matrix from the file and replaces the current SICS
# UB matrix with the one from the file. To be used after another program
# created a UB matrix in the first place or after refining the UB matrix
# following a centering operation.
# WARNING: this requires that the Tcl internal scan command has been r
# renamed BEFORE loading the scan command to stscan. Otherwise there is
# as conflict with the scan routine!

proc updateub {} {
#----- find position in file
        set ret [catch {locate data UB} ind]
        if { $ind < 0} {
         error "ERROR: No UB matrix in database"
        }
        incr ind
#------ read three lines of UB
        stscan $data($ind) "%f%f%f" ub11 ub12 ub13
        ClientPut $ub11 $ub12 $ub13
        incr ind 
        stscan $data($ind) "%f%f%f" ub21 ub22 ub23
        incr ind   
        stscan $data($ind) "%f%f%f" ub31 ub32 ub33
        hkl setub $ub11 $ub12 $ub13 $ub21 $ub22 $ub23 $ub31 $ub32 $ub33
        return OK
}

#--------- setlambda 
# setlambda sets the wavelength

proc setlambda {newval} {
       set ret [catch {hkl lambda $newval} msg]
       if {$ret == 1} {
         error $msg
       }
#----- find position in file
       set ret [catch {locate data lambda} ind]
#----- write
	set ret [catch {open [fsplit [dbfile]] w} fd]
        if { $ret == 1} {
            error $fd
        }
        if { $ind < 0 } {
           writerange data $fd 0 $data(nl)
           puts $fd "#### lambda"
           puts $fd [format "   %12.6f" $newval]
        } else {
           incr ind
           writerange data $fd 0 $ind
           puts $fd [format "   %12.6f" $newval]
           incr ind
           writerange data $fd $ind $data(nl)
        }
	 close $fd
	 return OK
 }

#------- fcentre
# fcentre centers a reflection
proc fcentre {} {
#----- rough centering
   opti clear
   opti countmode monitor
   opti preset 1000
   opti threshold 30
   opti addvar om .1 25 .10
   opti addvar stt .20 25 .25
   opti addvar ch 1.0  20 1.
   set ret [catch {opti run} msg]
   if {$ret == 1} {
      error $msg
   }
#----- fine centering
   opti preset 5000
   opti threshold 50
   set ret [catch {opti run} msg]
   if {$ret != 0 } {
     error $msg
   } 
   set txt [om]
   set l [split $txt =]
   set tom [lindex $l 1]
   set txt [stt]
   set l [split $txt =]
   set tstt [lindex $l 1]
   set txt [ch]
   set l [split $txt =]
   set tch [lindex $l 1]
   set txt [ph]
   set l [split $txt =]
   set tph [lindex $l 1]
   ClientPut "Two-Theta Omega     Chi        Phi"
   ClientPut [format "%-10.2f%-10.2f%-10.2f%-10.2f" $tstt $tom $tch $tph]
   storepos
}