#--------------------------------------------------------------------------
# This is a set of utilities for testing SICServers on instruments
#
# Mark Koennecke, November 2006, March 2007
#-------------------------------------------------------------------------
proc testInventory {inventory} {
    set txt [sicscommand list]
    set objList [split $txt ,]
    set missing 0
    foreach obj $inventory {
	if {[lsearch $objList $obj] < 0} {
	    incr missing
	    puts stdout "MISSING: $obj"
        }
    }
    return $missing
}
#-----------------------------------------------------------------------
proc SICSValue {command} {
	set txt [eval $command]
	set l [split $txt =]
	return [string trim [lindex $l 1]]
}
#-----------------------------------------------------------------------
proc max {one two} {
    if {$one > $two} {
	return $one
    } else {
	return $two
    }
}
#------------------------------------------------------------------------
# Test Motors. All are read: if they are not in the notList we will 
# try driving them by 1 %. Drivecommand can either be drive of dr, 
# depending on instrument
#-----------------------------------------------------------------------
proc testMotors {drivecommand motorList notList} {
    set failed 0
    foreach mot $motorList {
	if {[lsearch $notList $mot] < 0 } {
	    puts stdout "Testing $mot"
	    set rawVal [eval $mot]
	    if {[string first ERROR $rawVal] >= 0} {
		puts stdout "MOTOR: failed to read $mot, error $rawVal"
		incr failed
		continue
	    }
	    set val [string trim [lindex [split $rawVal =] 1]]
#----------- test drive three times tolerance
	    set prec [SICSValue "$mot precision"]
            set status [catch {set move [expr $prec * 3.]} msg]
#------- this fixes virtual motors which will not have a precision
	    if {$status != 0} {
		set move 0.3
            }
	    set newval [expr $val + $move]
	    set status [catch {eval $drivecommand $mot $newval} msg]
	    if {[string first ERROR $msg] >= 0 } {
#------------ if we went into a limit, we try the other direction	       
		if {[string first limit $msg] > 0} {
		    set newval [expr $val - $move]
		    set status [catch {eval $drivecommand $mot $newval} msg]
                }
            }
	    if {[string first ERROR $msg] >= 0} {
		puts stdout "MOTOR: failed to drive $mot with $msg"
		incr failed
                continue
	    }
#---------- drive back 
            set status [catch {eval $drivecommand $mot $val} msg]
	    if {[string first ERROR $msg] >= 0 } {
		puts stdout "MOTOR: failed to drive $mot back with $msg"
		incr failed
	    }
	}
    }
    return $failed
}
#------------------------------------------------------------------------
proc testSaveAndRestore {dir} {
    backup $dir/insttest.tcl
    recover $dir/insttest.tcl
    backup $dir/instdiff.tcl
    set f1 [exe print $dir/insttest.tcl]
    set f2 [exe print $dir/instdiff.tcl]
    set out [open b1.tcl w]
    puts $out $f1
    close $out
    set out [open b2.tcl w]
    puts $out $f2
    close $out
    set status [catch {exec diff b1.tcl b2.tcl} msg]
    if {$status != 0} { 
	puts stdout \
	"BACKUP/RESTORE: Serious problem: differences in restored parameters"
        puts stdout "BACKUP/RESTORE: look at diff between b1.tcl and b2.tcl"
    } else {
	file delete b1.tcl
	file delete b2.tcl
    }
}
#------------------------------------------------------------------------
proc testCommand args {
    set command [join $args]
    set status [catch {eval $command} txt]
    if {$status != 0} {
       puts stdout "COMMAND: Command $command returned with error $txt"
       return 0
    }
    if {[string first ERROR $txt] >= 0} {
	puts stdout "COMMAND: problem while executing command $command"
	puts stdout "COMMAND: output is $txt"
	return 0
    }
    if {[string first WARNING $txt] >= 0} {
	puts stdout "COMMAND: warning while executing command $command"
	puts stdout "COMMAND: output is: $txt"
    }
    return 1
}
#------------------------------------------------------------------------
proc testCommandList {commandList} {
    set count 0
    foreach command $commandList {
        puts stdout "Testing $command"
	if {![testCommand $command]} {
	    incr count
	}
    }
    return $count
}
#------------------------------------------------------------------------
proc testCommandInterrupt {command instrument user password} {
    catch {exec ./interrupt.tcl $instrument $user $password &} msg
    set txt1 [sicscommand $command]
    set t1 [string match -nocase "*stop*" $txt1]
    set txt2 [status] 
    set t2 [string match -nocase "*eager*" $txt2]
    if {$t1 != 1 || $t2 != 1} {
	puts stdout "INTERRUPT: Interrupting $command failed!"
	puts stdout "INTERRUPT: message is $txt1"
	puts stdout "INTERRUPT: status = $txt2"
	puts stdout "Tests after this may fail!"
        error "No interrupt"
    }
}
#------------------------------------------------------------------------
proc testInterruptList {comList instrument user password} {
    set int 0
    foreach com $comList {
	set status \
	    [catch {testCommandInterrupt $com $instrument $user $password} msg]
	if {$status != 0} {
	    incr int
	}
    }
    return $int
}
#-----------------------------------------------------------------------
proc testVarValue {command value {precision NA}} {
    set txt [eval $command]
    if {[string first ERROR $txt] >= 0} {
	error "Failed to read variable $command"
    }
    if {[string first = $txt] > 0} {
	set l [split $txt =]
        set val [string trim [lindex $l 1]]
    } else {
	set val $txt
    }
    if {[string first NA $precision] >= 0} {
	if {[string first $value $val] < 0} {
	    error "Value $value not found in response $txt"
        }
    } else {
	if {abs($value-$val) > $precision} {
	    error "Bad value $val, expected $value"
        }
    }
    return OK
}
#------------------------------------------------------------------------
proc testVarList {varList} {
    puts stdout "Testing variable values"
    set count 0
    foreach var $varList {
	if {[llength $var] > 2} {
	    set status [catch {testVarValue [lindex $var 0] \
				   [lindex $var 1] [lindex $var 2]} msg]
	} else {
	    set status [catch {testVarValue [lindex $var 0] \
				   [lindex $var 1]} msg]
        }
	if {$status != 0}  {
	    set cmd [lindex $var 0]
	    puts stdout "$msg @ $cmd"
	    incr count
	} else {
        }
    }
    return $count
}
#-------------------------------------------------------------------------
proc runStandardTests {parArray} {
    upvar $parArray parameters
    if { [info exists parameters(inventory)] } {
	puts stdout "Testing Inventory.."
	lappend counters [testInventory $parameters(inventory)]
    } else {
	lappend counters 0
    }
    
    if { [info exists parameters(driveCommand)] && \
	     [info exists parameters(motorList)] && \
	     [info exists parameters(notList)] } {
	puts stdout "Testing motors... " 
	lappend counters [testMotors $parameters(driveCommand) \
			      $parameters(motorList) $parameters(notList)]
    } else {
	lappend counters 0
    }

    if { [info exists parameters(commandList)] } {
	puts stdout "Testing a list of commands"
	lappend counters [testCommandList $parameters(commandList)]
    } else {
	lappend counters 0
    }

    if { [info exists parameters(dir)] } {
	puts stdout "Testing backup/recover.."
	catch {testSaveAndRestore $parameters(dir)} msg
    } 

    if { [info exists parameters(interruptList)] && \
	     [info exists parameters(instrument)] && \
	     [info exists parameters(user)] && \
	     [info exists parameters(password)] } {
	puts stdout "Testing interrupting commands... " 
	lappend counters [testInterruptList $parameters(interruptList) \
			      $parameters(instrument) \
			      $parameters(user) $parameters(password)]
    } else {
	lappend counters 0
    }

    if {[info exists parameters(varList)]} {
	lappend counters [testVarList $parameters(varList)]
    } else {
	lappend counters 0
    }
}
#-------------------------------------------------------------------------
proc printStandardSummary {counters} {
    puts stdout [format "%d objects missing" [lindex $counters 0]]
    puts stdout [format "%d motors failed to drive" [lindex $counters 1]]
    puts stdout [format "%d commands failed" [lindex $counters 2]]
    puts stdout [format "%d commands interrupts failed" [lindex $counters 3]]
    puts stdout [format "%d variable values bad" [lindex $counters 4]] 
}
#-----------------------------------------------------------------------
proc SICSValue {command} {
    set txt [eval $command]
    set l [split $txt =]
    return [string trim [lindex $l 1]]
}
#----------------------------------------------------------------------
proc standardPrelude {inst {count co}} {
    global __threshold __batchpath
    set __threshold [SICSValue "counter getthreshold 1"]
    set __batchpath [SICSValue "exe batchpath"]
    if {[string compare $inst local] == 0} {
	catch {sicscommand "exe batchpath /$env(HOME)/tmp"} msg
    } else {
	catch {sicscommand "exe batchpath /home/$inst/tmp"} msg
    }
    catch {sicscommand "scan mode timer"} msg
    catch {sicscommand "counter setmode timer"} msg
    catch {sicscommand "counter setthreshold 1 0"} msg
    catch {sicscommand "exe upload"} msg
    catch {sicscommand "exe append counter setthreshold 1 0"} msg 
    catch {sicscommand "exe append $count timer 120"} msg
    catch {sicscommand "exe forcesave insttestbatch.tcl"} msg
}
#----------------------------------------------------------------------
proc standardRestore {} {
    global __threshold __batchpath
    catch {sicscommand "exe batchpath $__batchpath"} msg
    catch {sicscommand "scan mode monitor"} msg
    catch {sicscommand "counter setmode monitor"} msg
    catch {sicscommand "counter setthreshold 1 $__threshold"} msg
}
