#!/usr/bin/wishx
# Copyright (c) 1994, 1995, 1996 New York University
# File rexec_tool
# "@(#) $Id: rexec_tool,v 1.6 1997/05/15 07:47:30 baratloo Exp $"


# Global varialbes...
set x1 40
set cpuy 25
set usersy 50
set jobsy 75
set dely 20
set canvasWidth 350
set nice 0
set fgc IndianRed3 

set progName ""
set displayName ""
set hostName ""
set portNumber 0
set hostNameToAdd ""
set hostNameToSpawn ""
set home $env(HOME)

set rcfile /usr/local/calypso/calypsorc
if [file readable $home/.calypsorc] {
    set  rcfile $home/.calypsorc
}


# -------------------------------------------------------------------------- #
# -------------------------- signal handler routines ----------------------- #

proc sigSIGCHLDhandler sig {
    catch {
		[wait -nohang]
    }

    # This is very stupid but a non-posix systems require it. -ab.
    signal trap SIGCHLD {sigSIGCHLDhandler %S}
}


# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
proc addHost {} {
    global hostNameToAdd
    if {$hostNameToAdd == ""} {
		mkDialog .dialog "-text {Error: missing host name.\n\nYou\
				must select a host by typing its name in the dialog\
				box first.} -justify left -aspect 300" "OK {}"
		tkwait visibility .dialog
		grab .dialog
		tkwait window .dialog
		return
    }
    .ftop.fleft.lhosts insert end $hostNameToAdd
    set hostNameToAdd ""
}



# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
proc withinLimits {x} {
    global canvasWidth
    if {$x == 0} {
		set x 1
    } elseif {$x > [expr $canvasWidth-100]} {
		set x [expr $canvasWidth-100]
    }
    return $x
}



proc drawCPU {w i} {
    global cpuy x1 dely

    set y1 $cpuy
    set y2 [expr $y1+$dely]
    set x2 [expr (3*$i)]
    set x2 [expr $x1+[withinLimits $x2]]
    $w coords poly1 $x1 $y1 $x1 $y2 $x2 $y2 $x2 $y1
    $w coords text1 [expr $x2+5] $y1
    $w itemconfigure text1 -text $i%
    $w coords line1 $x1 $y1 $x1 $y2 $x2 $y2 $x2 $y1
}



proc drawUSERS {w i} {
    global usersy x1 dely

    set y1 $usersy
    set y2 [expr $y1+$dely]
    set x2 [expr (15*$i)]
    set x2 [expr $x1+[withinLimits $x2]]
    $w coords poly2 $x1 $y1 $x1 $y2 $x2 $y2 $x2 $y1
    $w coords text2 [expr $x2+5] $y1
    $w itemconfigure text2 -text $i
    $w coords line2 $x1 $y1 $x1 $y2 $x2 $y2 $x2 $y1
}



proc drawJOBS {w i} {
    global jobsy x1 dely

    set y1 $jobsy
    set y2 [expr $y1+$dely]
    set x2 [expr int(100*$i)]
    set x2 [expr $x1+[withinLimits $x2]]
    $w coords poly3 $x1 $y1 $x1 $y2 $x2 $y2 $x2 $y1
    $w coords text3 [expr $x2+5] $y1
    set units " (avg)"
    $w itemconfigure text3 -text $i$units
    $w coords line3 $x1 $y1 $x1 $y2 $x2 $y2 $x2 $y1
}



# state 0 -- button is off and everything is at the initial position
# state 1 -- button is off
# state 2 -- everything is working
proc alterState state {
    if {$state == 0} {
		drawCPU .fbottom.left.canvas 0
		drawUSERS .fbottom.left.canvas 0
		drawJOBS .fbottom.left.canvas 0
		.fbottom.left.rexec config -state disabled
		.fbottom.right.snice config -state disabled
		set nice 0
		return
    } elseif {$state == 1} {
		.fbottom.left.rexec config -state disabled
		return
    }
    .fbottom.left.rexec config -state normal
    .fbottom.right.snice config -state normal
}



proc updateUtilization host {
    global hostNameToSpawn nice
    .fstat2.mstat config -text "Updating utilization meters..."
    alterState 1
    set hostNameToSpawn $host
    set timings [exec rstat_tool $host]
    if {[lindex $timings 0] == "error"} {
		alterState 0
		# set hostNameToSpawn ""
		.fstat2.mstat config -text "Waiting for you to select a host."
		.fbottom.left.canvas itemconfigure textHost -text "Error contacting $host."
		.fbottom.left.rexec config -text "Try to start execution on $host"
		alterState 2
		return
    }
    alterState 2
    .fbottom.left.canvas itemconfigure textHost -text "$host at [fmtclock [getclock] "\%a \%I:\%M:\%S"]"
    .fbottom.left.rexec config -text "Start execution on $host"
    drawCPU .fbottom.left.canvas [lindex $timings 1]
    drawUSERS .fbottom.left.canvas [lindex $timings 0]
    drawJOBS .fbottom.left.canvas [lindex $timings 2]
    .fstat2.mstat config -text "Waiting for you to select\
			another host, or to start execution on this one."
}



proc spawnWorker {} {
    global progName hostName portNumber hostNameToSpawn displayName nice
    if {$hostNameToSpawn != ""} {
		if {$nice == 0} {
			set cmd [list exec rsh -n $hostNameToSpawn xterm -T $progName -iconic -geometry 80x8 +ls -display $displayName -e $progName -- -worker $hostName $portNumber >& /dev/null &]
		} else {
			set cmd [list exec rsh -n $hostNameToSpawn xterm -T $progName -iconic -geometry 80x8 +ls -display $displayName -e nice -n $nice $progName -- -worker $hostName $portNumber >& /dev/null &]
		}
		set workerPid [eval $cmd]
		.fstat2.mstat config -text "Started a process on $hostNameToSpawn."
		set nice 0
    }
}



# ------------------------------------------------------------------------- #
# --------------------- Help information ---------------------------------- #
#set helpTitles() ""
#set helpTopics() ""

set helpTitles(about) "Calypso"
set helpTopics(about) "
Calypso is a novel software system that helps you in\
		developing a parallel program, and then in executing\
		it on multiple computers connected by a network.  It\
		is unique in that it keeps all the nuences away from\
		the programmer and does it in its runtime system.\n
This is one of the tools in its integrated toolkit.  This\
		tool allows you to monitor the utilization of remote\
		machines and to use it in your computation."

set helpTitles(version) "the current version number"
set helpTopics(version) {This is Calypso version 1.0}

set helpTitles(steps) "steps to take in order to utilize remote hosts"
set helpTopics(steps) "
Two things you need to do:
1. First select a host by double-clicking its name in the scrolling\
		list.  This also shows you some information on its\
		utilization.  If you need information on how to\
		add a host name to this list, see \"On Adding...\"\
		help option.
2. Then, click on the \"Start execution on...\" button.\n
It is that easy!\n\n
For further information on possible machine names to choose\
		from, and privaledges needed, look at \"On\
		.calypsorc...\", \"On .rhosts...\", and \"On\
		xhost +...\" under the help menu."

set helpTitles(add) "adding a host to the scrolling list"
set helpTopics(rxterm) "
To add a host, simply enter its name in the entry field\
		at the bottom the main window and click on\
		\"Add Host\" button."

set helpTitles(rcfile) ".calypsorc file"
set helpTopics(rcfile) "
This tool requires a list of host names to initially\
		fill the scrolling list.  This list is usually\
		read from a file called .calypsorc that is\
		located in your top directory.  If this file\
		does not exists, we suggest that you create one.\n
Contents of .calypsorc file is simply a list of host\
		names.  The following is an example of one:
wilma.cs.nyu.edu
fred.cs.nyu.edu
barney.cs.nyu.edu
...
Keep in mind that there should be no spaces or\
		blank lines in this file."

set helpTitles(rhosts) ".rhosts file"
set helpTopics(rhosts) "
For you to be able to execute a process on a remote machine,\
		you obviously need an account on it.  You also need\
		to tell the other machine that it is OK for a\
		process on this machine to start running a program\
		there.  In the UNIX world, this is usually done by\
		having a .rhosts file in your home directory.  For\
		further information, please look at the manual pages\
		for \"rlogin.\"\n
Contents of .rhosts file is simply a list of host\
		names.  The following is an example of one:
wilma.cs.nyu.edu
fred.cs.nyu.edu
barney.cs.nyu.edu
..."

set helpTitles(xhost) "the command xhost +"
set helpTopics(xhost) "
In order for programs running on other machines to\
		pop-up their display where you are,  you need\
		to tell your machine that it is OK for\
		others to do so.  This is done by issuing the\
		command \"xhost +\" on your machine."



# mkDialog w msgArgs list list ...
#
# Create a dialog box with a message and any number of buttons at
# the bottom.
#
# Arguments:
#    w -	Name to use for new top-level window.
#    msgArgs -	List of arguments to use when creating the message of the
#		dialog box (e.g. text, justifcation, etc.)
#    list -	A two-element list that describes one of the buttons that
#		will appear at the bottom of the dialog.  The first element
#		gives the text to be displayed in the button and the second
#		gives the command to be invoked when the button is invoked.
proc mkDialog {w msgArgs args} {
    catch {destroy $w}
    toplevel $w -class Dialog
    wm title $w "Dialog box"
    wm iconname $w "Dialog"
    set oldFocus [focus]

    # Create two frames in the main window. The top frame will hold the
    # message and the bottom one will hold the buttons.  Arrange them
    # one above the other, with any extra vertical space split between
    # them.

    frame $w.top -relief raised -border 1
    frame $w.bot -relief raised -border 1
    pack $w.top $w.bot -side top -fill both -expand yes

    # Create the message widget and arrange for it to be centered in the
    # top frame.
    
    eval message $w.top.msg -justify center \
			-font -Adobe-times-medium-r-normal--*-180* $msgArgs
    pack $w.top.msg -side top -expand yes -padx 3 -pady 3

    # Create as many buttons as needed and arrange them from left to right
    # in the bottom frame.  Embed the left button in an additional sunken
    # frame to indicate that it is the default button, and arrange for that
    # button to be invoked as the default action for clicks and returns in
    # the dialog.

    if {[llength $args] > 0} {
		set arg [lindex $args 0]
		frame $w.bot.0 -relief sunken -border 1
		pack $w.bot.0 -side left -expand yes -padx 10 -pady 10
		button $w.bot.0.button -text [lindex $arg 0] \
				-command "[lindex $arg 1]; destroy $w; focus $oldFocus"
		pack $w.bot.0.button -expand yes -padx 6 -pady 6
		bind $w <Return> "[lindex $arg 1]; destroy $w; focus $oldFocus"
		focus $w

		set i 1
		foreach arg [lrange $args 1 end] {
			button $w.bot.$i -text [lindex $arg 0] \
					-command "[lindex $arg 1]; destroy $w; focus $oldFocus"
			pack $w.bot.$i -side left -expand yes -padx 10
			set i [expr $i+1]
		}
    }
    bind $w <Any-Enter> [list focus $w]
    focus $w
    wm geometry $w +300+350
}



proc Help {topic {x 0} {y 0}} {
    global helpTopics helpTitles
    if {$topic == ""} return
    if [info exists helpTitles($topic)] {
		set title $helpTitles($topic)
    } else {
		set title $topic
    }
    if [info exists helpTopics($topic)] {
		set msg $helpTopics($topic)
    } else {
		set msg "Sorry, but no help is available for this topic"
    }
    mkDialog .dialog "-text {Information on $title:\n\n$msg} -justify left -aspect 300" "{OK} {}"
}



# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
foreach i [winfo child .] {
    catch {destroy $i}
}



wm title . "Remote Execution Tool"
tk_bindForTraversal .
focus .
#wm resizable . 0 0



# First a dummy frame to determine the width of the window...
frame .fdummy -width 600
pack .fdummy -side top -fill x



# Menu stuff...
frame .menu -relief raised -borderwidth 2
pack .menu -side top -fill x



# Options menu item...
menubutton .menu.file -text "Options" -menu .menu.file.m -underline 0
menu .menu.file.m
.menu.file.m add command -label "Exit program" -underline 0 -command "exit"
pack .menu.file -side left
tk_menuBar .menu .menu.file

# Help menu items...
menubutton .menu.help -text "Help" -menu .menu.help.m -underline 0
menu .menu.help.m
.menu.help.m add command -label "About Calypso..." -underline 0 -command {Help about}
.menu.help.m add command -label "On Version" -underline 3 -command {Help version}
.menu.help.m add command -label "On Steps to take..." -underline 3 -command {Help steps}
.menu.help.m add command -label "On .calypsorc..." -underline 4 -command {Help rcfile}
.menu.help.m add command -label "On .rhosts..." -underline 4 -command {Help rhosts}
.menu.help.m add command -label "On xhost +..." -underline 3 -command {Help xhost}
pack .menu.help -side right
tk_menuBar .menu .menu.help



# Status line 1 ...
frame .fstat1 -relief ridge -borderwidth 2
label .fstat1.lprog -text "Program name:"
entry .fstat1.eprog -textvariable progName -width 10 -relief flat
label .fstat1.lhost -text "Running on host:"
entry .fstat1.ehost -textvariable hostName -relief flat
label .fstat1.lport -text "Port number:"
entry .fstat1.eport -textvariable portNumber -width 10 -relief flat
pack .fstat1 -side top -fill x
pack .fstat1.lprog .fstat1.eprog -side left
pack .fstat1.lhost .fstat1.ehost -side left
pack .fstat1.lport .fstat1.eport -side left
.fstat1.eprog config -state disabled
.fstat1.ehost config -state disabled
.fstat1.eport config -state disabled



# Frames for the scrool bar and widgets for adding a host
frame .ftop -relief flat -borderwidth 0
frame .ftop.fleft -relief flat -borderwidth 0
frame .ftop.fright -relief flat -borderwidth 0
pack .ftop -side top -fill both
pack .ftop.fleft -side left
pack .ftop.fright -side left -fill both -expand 1



# On the left, the scrool bar...
listbox .ftop.fleft.lhosts -width 25 -height 7 -yscrollcommand ".ftop.fleft.shosts set" \
		-relief sunken -borderwidth 2 -exportselection false
bind .ftop.fleft.lhosts <Double-1> {
    updateUtilization [.ftop.fleft.lhosts get [.ftop.fleft.lhosts curselection]]
}
scrollbar .ftop.fleft.shosts -orient vertical -command ".ftop.fleft.lhosts yview" \
		-relief sunken -borderwidth 2
pack .ftop.fleft.lhosts -side left
pack .ftop.fleft.shosts -side right -fill y -expand 1

if ![file readable $rcfile] {
    mkDialog .dialog "-text \"
    Warning: Can't locate $rcfile.\n\n\This tool looks\
			at a file called .calypsorc\
			in your home directory, to read a list of\
			default host names.\n\nThis file does not exist.\n\n\
			Look at the Help menu for further information.\"\
			-aspect 300 -justify left" {OK {}}
    tkwait visibility .dialog
    grab .dialog
    tkwait window .dialog
} else {
    set f [open $rcfile]
    while {[gets $f line] >= 0} {
		.ftop.fleft.lhosts insert end $line
    }
    close $f
}



# On the right, a frame to add hosts...
label .ftop.fright.lhost -height 2 -text "hostname to add"
entry .ftop.fright.ehost -relief sunken -bd 2 -textvariable hostNameToAdd
button .ftop.fright.badd -text "add" -height 3 -command addHost
pack .ftop.fright.lhost .ftop.fright.ehost -side top -anchor n -fill x -padx 20
pack .ftop.fright.badd -side top -anchor n -fill x -pady 10 -padx 20



frame .fbottom -relief ridge -borderwidth 2
frame .fbottom.left -relief flat -borderwidth 0
frame .fbottom.right  -relief flat -borderwidth 0
pack .fbottom -side top -fill both -expand 1
pack .fbottom.left .fbottom.right -side left -fill both -expand 1



canvas .fbottom.left.canvas -width $canvasWidth -height 100 \
		-bd 0 -highlightthickness 0 -borderwidth 2
.fbottom.left.canvas create polygon $x1 $cpuy $x1 $cpuy $x1 $cpuy $x1 $cpuy -fill PaleTurquoise4 -tags poly1
.fbottom.left.canvas create polygon $x1 $usersy $x1 $usersy $x1 $usersy $x1 $usersy -fill CadetBlue4 -tags poly2
.fbottom.left.canvas create polygon $x1 $jobsy $x1 $jobsy $x1 $jobsy $x1 $jobsy -fill turquoise4 -tags poly3
.fbottom.left.canvas create text 40 [expr $cpuy+5] -text "" -fill $fgc -anchor nw -tag text1
.fbottom.left.canvas create text 40 [expr $usersy+5] -text "" -fill $fgc -anchor nw -tag text2
.fbottom.left.canvas create text 40 [expr $jobsy+5] -text "" -fill $fgc -anchor nw -tag text3
.fbottom.left.canvas create line $x1 $cpuy $x1 $cpuy $x1 $cpuy $x1 $cpuy -fill black -tags line1
.fbottom.left.canvas create line $x1 $usersy $x1 $usersy $x1 $usersy $x1 $usersy -fill black -tags line2
.fbottom.left.canvas create line $x1 $jobsy $x1 $jobsy $x1 $jobsy $x1 $jobsy -fill black -tags line3
.fbottom.left.canvas create text 5 [expr $cpuy+5] -text "cpu" -fill $fgc -anchor nw
.fbottom.left.canvas create text 5 [expr $usersy+5] -text "users" -fill $fgc -anchor nw
.fbottom.left.canvas create text 5 [expr $jobsy+5] -text "jobs" -fill $fgc -anchor nw
.fbottom.left.canvas create text 100 5 -text "" -fill $fgc -anchor nw -tag textHost
drawCPU .fbottom.left.canvas 0
drawUSERS .fbottom.left.canvas 0
drawJOBS .fbottom.left.canvas 0
pack .fbottom.left.canvas -side top -fill both -expand yes -padx 5



button .fbottom.left.rexec -foreground $fgc -width 40 -height 2 -command spawnWorker
pack .fbottom.left.rexec


label .fbottom.right.lnice -text "nice gauge" -foreground $fgc
scale .fbottom.right.snice -foreground $fgc -orient vertical -length 100 -from 0 -to 20 -variable nice
pack .fbottom.right.snice .fbottom.right.lnice -side top


# Status line 2 ...
frame .fstat2 -relief sunken -borderwidth 1
message .fstat2.mtitle -text "Status:" -justify left -width 30c
message .fstat2.mstat -text "Waiting for you to select a host." -justify left -bd 2 -width 30c
pack .fstat2 -side bottom -fill x
pack .fstat2.mtitle -side left
pack .fstat2.mstat -side left



# Read the runtime arguments and set globals...
if {$argc != 4} {
    mkDialog .dialog "-text \"Error in usage.\n\nUsage:\n\
			$argv0 <prog> <display> <host> <port>, where\n\n\
			<prog> is the name of the Calypso program\n\
			<display> is the host running the X server\n\
			<host> is the host running the program\n\
			<port> is the port number.\n\n\
			Look at the Help menu for further information.\"\
			-aspect 300 -justify left" {OK {}}
    tkwait visibility .dialog
    grab .dialog
    tkwait window .dialog
    exit
}



set progName [lindex $argv 0]
set displayName [lindex $argv 1]
set hostName [lindex $argv 2]
set portNumber [lindex $argv 3]

#tk_setPalette bisque1

signal trap SIGCHLD {sigSIGCHLDhandler %S}

