#
# Monitor.tcl 
#
# $Log: Monitor.tcl,v $
# Revision 1.2  2006/10/26 09:39:17  vfom
# Added code for netstate server query
#
# Revision 1.1.1.1  2006/09/21 10:57:02  vfom
# Loading TkNetmon to CVS
#
#
namespace eval Monitor {
variable	fd	0			\
		fd2	0			\
		conversation_state	""	\
		conversation_exitFlag 	0	\
		conversation_state2	""	\
		conversation_exitFlag2 	0	\
		last_update		""	\
		lock2			0	\
		monitorProperties_exitFlag 0	
}
#
#
#
proc ReloadStatus { flag } {
    global activeID hostName hostIP hostRTT hostCNT hostLRQ hostLRP \
	hostLCH globalCursor dataReloadPeriod
    global node_ip node_name  node_key
    global monitor_mode netStateServer netStateServerPort

    if { [ winfo exists .monitor_state ] } {
	if { $::monitor_mode == "disable" } {
	    .monitor_state configure -bg grey
	} else {
	    set rlf [ .monitor_state cget -relief ]
	    if { $rlf == "raised" } {
		.monitor_state configure -relief sunken
	    } else { 
		.monitor_state configure -relief raised
	    }
	    .monitor_state configure -bg  green
	}
    }

    if { [string equal $::monitor_mode "disable"] } {
	if { $::Monitor::fd != 0 }  {
    	    puts  $::Monitor::fd "QUIT\r\n"
	    flush $::Monitor::fd
	    close $::Monitor::fd
    	    set ::Monitor::fd 0
	}
	after cancel [ after info ]
	return
    }

    set ::Monitor::last_update [clock format [clock seconds ] ]
    if { [ catch { puts $::Monitor::fd "\n"} ] } {
	#
	# if error, we need reopen the socket
	#
	if { $::Monitor::fd  != 0 } {
	    close $::Monitor::fd 
	}
	if { [ catch { socket $::netStateServer $::netStateServerPort } ::Monitor::fd ] } {
	    set ::monitor_mode "disable"
	    if { [ winfo exists .monitor_state ] } { .monitor_state configure -bg  grey }
	    tk_dialog .sockErr "Monitor Error" \
		"Can't  connect NETSTATE at $::netStateServer:$::netStateServerPort -\n\"$::Monitor::fd\"" \
		error 0 [msgcat::mc "Approve"]
    	    set ::Monitor::fd 0
	    return	    
	}
	fconfigure $::Monitor::fd -blocking 0
    } else {
	flush $::Monitor::fd
    }
    set ::Monitor::conversation_exitFlag 0
    set ::Monitor::conversation_state "Start"


    fileevent $::Monitor::fd readable { catch {
	global node_ip node_name node_key
	if [ catch { fconfigure $::Monitor::fd -peername } rez2 ] {
	    set ::Monitor::conversation_state "Error"		
	    close $::Monitor::fd
	    set ::Monitor::fd 0
	    set ::Monitor::conversation_exitFlag 1		 
	    # puts stderr "Error talking NETSTATE at $::netStateServer:$::netStateServerPort -\n\"$rez2\".\n"
	    update idletasks
	    return
	}	    	    
	switch $::Monitor::conversation_state {
	    Start	{
		gets $::Monitor::fd str
		if { [string match $str "" ] } { 
		    close $::Monitor::fd
		    set ::Monitor::fd 0
		    set ::Monitor::conversation_exitFlag 1		 
		}
		if { [string match $str "!" ] } { 
		    puts  $::Monitor::fd "OBJECT STATE"
		    flush $::Monitor::fd
		    set ::Monitor::conversation_state "getStateData"
		    #
		}
	    }
	    getStateData {
		gets $::Monitor::fd str		    
		if { [string match $str "" ] } { 
		    close $::Monitor::fd
		    set ::Monitor::fd 0
		    set ::Monitor::conversation_exitFlag 1		 
		}
		if { [string match $str "!" ] } { 
		    puts  $::Monitor::fd "OBJECT PING"
		    flush $::Monitor::fd
		    set ::Monitor::conversation_state "getRttData"
		}
		if { [string index $str 0] != "!" } {
		    set tmp_list [split $str '!']
		    set l_node   [lindex $tmp_list 0]
		    set tmp_val  [lindex $tmp_list 1]
		    set l_node_state [join [lindex [split $tmp_val '='] 1]]
    		    if { [ info exists ::node_key($l_node) ] } {
			set key $::node_key($l_node)
			set l_node_state [ CalcNodeState $key $l_node_state ]	
			if { $l_node_state!=$::node_state($key) } {
			    set ::node_state($key)    $l_node_state	
			    set ::node_flashcnt($key) $::node_FlashCnt
			    .canvas addtag JUSTCHANGE withtag $key.icon
			}
		    }
		}
	    }
	    getRttData {
		gets $::Monitor::fd str		    
		if { [string match $str "" ] } { 
		    close $::Monitor::fd
		    set ::Monitor::fd 0
		    set ::Monitor::conversation_exitFlag 1		 
		}
		if { [string match $str "!"] } {
		    if { $::activeID !="" &&  [ .canvas type $::activeID  ] == "window"} {
			set hostKey [.canvas itemcget $::activeID -window ]
			puts  $::Monitor::fd "OBJECT $::node_name($hostKey)!"
			flush $::Monitor::fd
			set ::Monitor::conversation_state "getActiveNodeData"			    			
		    } else {
			close $::Monitor::fd
			set ::Monitor::fd 0
			set ::Monitor::conversation_exitFlag 1
		    }		
		}
		if { [string index $str 0] != "!" } {
		    set tmp_list [split $str '!']
		    set l_node   [lindex $tmp_list 0]
		    set tmp_val  [lindex $tmp_list 1]
		    set l_node_var   [join [lindex [split $tmp_val '='] 0]]
		    set l_node_rtt [join [lindex [split $tmp_val '='] 1]]
		    if { $l_node_var=="PING" } {			
			if { [string equal $l_node_rtt "Operation timed out" ] }	{ set l_node_rtt "?tmo" }
			if { [string equal $l_node_rtt "TTL exceeded in transit" ] }	{ set l_node_rtt "?exc" }
			if { [string equal $l_node_rtt "Bad IP header" ] }		{ set l_node_rtt "?prob" }
			if { [string equal $l_node_rtt "Host unreachable" ] }		{ set l_node_rtt "?reach" }
			if { [string equal $l_node_rtt "Administratively prohibited" ] } { set l_node_rtt "?flt" }
    			if { [ info exists ::node_key($l_node) ] } {
    			    set dnlID [.canvas find withtag $::node_key($l_node).dn]
    			    .canvas itemconfigure $dnlID -text "rtt=$l_node_rtt"
			}
		    }
		}
	    }
	    getActiveNodeData {
		gets $::Monitor::fd str
		if { [string match $str "" ] } { 
		    close $::Monitor::fd
		    set ::Monitor::fd 0
		    set ::Monitor::conversation_exitFlag 1		 
		}
		if { [string match $str "!"]  } {
		    close $::Monitor::fd
		    set ::Monitor::fd 0
		    set ::Monitor::conversation_exitFlag 1
		}
		if { [string index $str 0] != "!" } {
		    set tmp_list  [ split $str "!" ]
		    set l_node    [lindex $tmp_list 0]
		    set tmp_val   [lindex $tmp_list 1]
		    set tmp_vlist [split $tmp_val '=']
		    set l_variable [join [ lindex $tmp_vlist 0 ] ]
		    set l_value    [join [ lrange $tmp_vlist 1 end ] ]			
		    switch $l_variable {
			NAME   	{ set ::hostName [join $l_value] }
			ADDRESS 	{ set ::hostIP   [join $l_value] }
			PING 	{ set ::hostRTT  [join $l_value] }
			HOPCOUNT 	{ set ::hostCNT  [join $l_value] }
			QUERYTIME 	{ set ::hostLRQ [ clock format [ join $l_value ] -format "%T %d.%m.%Y %Z"] }
			REPLYTIME 	{ set ::hostLRP [ clock format [ join $l_value ] -format "%T %d.%m.%Y %Z"] }
			LASTCHANGE 	{ set ::hostLCH [ clock format [ join $l_value ] -format "%T %d.%m.%Y %Z"] }			    			    
		    }
		}
	    }	
	    default {}
	}	
    } rez }
    tkwait variable ::Monitor::conversation_exitFlag 
    if { $::Monitor::fd != 0 } {
	fileevent $::Monitor::fd  readable {}
	close $::Monitor::fd
	set ::Monitor::fd 0
    } 
    if [string equal $flag "cycle"] {
	if [string equal  $::Monitor::conversation_state "Error" ] {
	    uplevel #0 "after 100 { ReloadStatus cycle }"	
	} else {
	    uplevel #0 "after $dataReloadPeriod { ReloadStatus cycle }"
	}
    }
}
#
proc FlashJustChange { } {
    foreach lb [.canvas find withtag JUSTCHANGE ] {
	set key [.canvas itemcget $lb -window ]
	if { $::node_flashcnt($key) > 0 } {	
	    if { $::node_flashcnt($key) == $::node_FlashCnt } {
		# first flash
		eval "$::stateSignal($::node_state($key)) $key"
	    }
	    set oldfill [ $key cget -background ]
	    if { $oldfill == $::globalBG } { 
		$key configure -background $::stateColor($::node_state($key))
	    } else {
		$key configure -background $::globalBG
	    }
	    incr ::node_flashcnt($key) -1 
	} else {
	    .canvas dtag $lb JUSTCHANGE
	    $key configure -background $::stateColor($::node_state($key))
	    set ::node_flashcnt($key) 0	    
	}		
    }
    uplevel #0 "after 500 { FlashJustChange }"
}
proc CalcNodeState { key state } {
    return $state
}
#
proc ActiveNodeReloadStatus {  } {
    if { [string equal $::monitor_mode "disable"] } {
	return
    }
    if { $::Monitor::lock2 >0 } {
	return
    }
    set ::Monitor::lock2 1
    if { [ catch { socket $::netStateServer $::netStateServerPort } ::Monitor::fd2 ] } {
	tk_dialog .sockErr "Monitor Error" \
	    "Can't  connect NETSTATE at $::netStateServer:$::netStateServerPort -\n\"$::Monitor::fd2\"" \
	    error 0 [msgcat::mc "Approve"]
	    return	    
    }
    fconfigure $::Monitor::fd2 -blocking 0 -encoding koi8-r

    set ::Monitor::conversation_exitFlag2 0
    set ::Monitor::conversation_state2  "Start"
    fileevent $::Monitor::fd2 readable {  catch {
	if [ catch { fconfigure $::Monitor::fd2 -peername } rez2  ] {
	    close $::Monitor::fd2
	    set $::Monitor::fd2 0
	    set ::Monitor::conversation_exitFlag2 1
	    set ::Monitor::conversation_state2 "Error"				
	    puts "ActiveNodeReloadStatus: Error talking NETSTATE at $::netStateServer:$::netStateServerPort -\n\"$rez2\".\n"
	    update idletasks
	    return
	}	    	    
	switch $::Monitor::conversation_state2 { 
	    Start	{
	        gets $::Monitor::fd2 str		    
	        if { [string match $str "!"] } {
	    	    if { $::activeID !="" &&  [ .canvas type $::activeID  ] == "window"} {
		        set hostKey [.canvas itemcget $::activeID -window ]
			puts  $::Monitor::fd2 "OBJECT $::node_name($hostKey)!"
			flush $::Monitor::fd2
			set ::Monitor::conversation_state2 "getActiveNodeData"			    			
		    } else {
			close $::Monitor::fd2
			set ::Monitor::fd2 0
			set ::Monitor::conversation_exitFlag2 1
		    }			
		}
	    }
	    getActiveNodeData {
		gets $::Monitor::fd2 str
		if { [string match $str "" ] } { 
		    close $::Monitor::fd
		    set ::Monitor::fd 0
		    set ::Monitor::conversation_exitFlag 1		 
		}
		if { [string match $str "!"] } {
		    close $::Monitor::fd2
		    set ::Monitor::fd2 0
		    set ::Monitor::conversation_exitFlag2 1
		}
		if { [string index $str 0] != "!" } {
		    set tmp_list  [ split $str "!" ]
		    set l_node    [lindex $tmp_list 0]
		    set tmp_val   [lindex $tmp_list 1]
		    set tmp_vlist [split $tmp_val '=']
		    set l_variable [join [ lindex $tmp_vlist 0 ] ]
		    set l_value    [join [ lrange $tmp_vlist 1 end ] ]			
		    switch $l_variable {
			NAME   	{ set ::hostName [join $l_value] }
			ADDRESS 	{ set ::hostIP   [join $l_value] }
			PING 	{ set ::hostRTT  [join $l_value] }
			HOPCOUNT 	{ set ::hostCNT  [join $l_value] }
			QUERYTIME 	{ set ::hostLRQ [ clock format [ join $l_value ] -format "%T %d.%m.%Y %Z"] }
			REPLYTIME 	{ set ::hostLRP [ clock format [ join $l_value ] -format "%T %d.%m.%Y %Z"] }
			LASTCHANGE 	{ set ::hostLCH [ clock format [ join $l_value ] -format "%T %d.%m.%Y %Z"] }			    			    
		    }
		}
	    }	
	    default {}
	}
    } rez } 
    tkwait variable ::Monitor::conversation_exitFlag2
    if { $::Monitor::fd2 != 0 } {
	fileevent $::Monitor::fd2  readable {}
	close $::Monitor::fd2
	set ::Monitor::fd2 0
    }         
    if [string match  $::Monitor::conversation_state2 "Error" ] {
        uplevel #0 "after 100 ActiveNodeReloadStatus"	
    }
    set ::Monitor::lock2 0
}
#
proc Restart_netmond {} {
    global daemonRestartCmd activeFile activeFileChanged rootDir bgStatus restOut
    if { $::activeFileChanged !="no" } {
	if {[ tk_dialog .savedialog [msgcat::mc "Stop!"] \
		  [msgcat::mc "Current active file was changed. Save it?"] info 0 [msgcat::mc "Save"] [msgcat::mc "Cancel"] ] == 0 } {
	    if { $::activeFile !="noname.conf"} {
		SaveFile $::activeFile
	    } else {
		SaveFile [ tk_getSaveFile -title [msgcat::mc "Save As"]]
	    }
	}
    }
    if { $::activeFileChanged !="no" } { 
	# file was not saved - create tmp file
	set save_chg_flag $::activeFileChanged
	set save_active_file  $::activeFile
	set fname "[clock seconds].netmon.conf"
	set config_file [file join "/tmp" $fname ]
	SaveFile $config_file
	set ::activeFileChanged $save_chg_flag	
	set ::activeFile $save_active_file
    } else {
	if {  [ regexp -- {^\/} $::activeFile ] } {
	    set config_file $::activeFile  
	} else {
	    # not full pathname 
	    set config_file [file join [ pwd ] $::activeFile ] 
	}
    }
    set restOut ""
    set bgStatus ""

    catch { eval blt::bgexec bgStatus -output restOut $::daemonRestartCmd $config_file } 
    if { $::activeFileChanged !="no" } { 
	del_fromFileHistory $config_file
	file delete -force $config_file	
    }
}
#
#
#
proc Start_Update {} {
    set ::monitor_mode "enable"    
    after 10 "ReloadStatus cycle"
    after 500 FlashJustChange
}
#
proc Stop_Update {} {
    set ::monitor_mode "disable"    
    after 10 "ReloadStatus single"
}
#
#
#
proc monitorProperties_GUIconstructor { } {
    set w .monitorProperties
    set f $w.entries
    set b $w.buttons

    toplevel $w -class Dialog
    wm transient $w [winfo toplevel [winfo parent $w]]
    wm withdraw $w

    set curr_row 0

    frame $f -bd 2 -relief raised
    pack $f -side top -fill both -expand on -ipadx 10 -ipady 10
#
    label $f.lbrootdir -text [msgcat::mc "Root Directory: "] 
    grid $f.lbrootdir -in $f \
	-row $curr_row -column 0 -columnspan 1 -rowspan 1 -sticky e
    entry $f.erootdir -width 64 
    grid $f.erootdir -in $f \
	-row $curr_row -column 1 -columnspan 1 -rowspan 1 -sticky w
    button $f.brwrootdir -text [msgcat::mc "Browse"]  \
	-command {browseDir .monitorProperties.entries.erootdir [msgcat::mc {Find Root Directory}]} 
    grid $f.brwrootdir -in $f \
	-row $curr_row -column 2 -columnspan 1 -rowspan 1 -sticky w
    incr curr_row
#
    label $f.lbsrv -text [msgcat::mc "NetState server:"] 
    grid $f.lbsrv -in $f \
	-row $curr_row -column 0 -columnspan 1 -rowspan 1 -sticky e
    entry $f.esrv -width 64 
    grid $f.esrv -in $f \
	-row $curr_row -column 1 -columnspan 1 -rowspan 1 -sticky w
    incr curr_row
#
    label $f.lbport -text [msgcat::mc "NetState server Port:"] 
    grid $f.lbport -in $f \
	-row $curr_row -column 0 -columnspan 1 -rowspan 1 -sticky e
    entry $f.eport -width 8
    grid $f.eport -in $f \
	-row $curr_row -column 1 -columnspan 1 -rowspan 1 -sticky w
    incr curr_row
#
    label $f.lbdcmd -text [msgcat::mc "Restart Netmond Cmd:"] 
    grid $f.lbdcmd -in $f \
	-row $curr_row -column 0 -columnspan 1 -rowspan 1 -sticky e
    entry $f.edcmd -width 64
    grid $f.edcmd -in $f \
	-row $curr_row -column 1 -columnspan 1 -rowspan 1 -sticky w
    incr curr_row
#
    label $f.lbdprm -text [msgcat::mc "NetState access rules:"] 
    grid $f.lbdprm -in $f \
	-row $curr_row -column 0 -columnspan 1 -rowspan 1 -sticky ne
    text $f.edprm -height 12 -width 64
    grid $f.edprm -in $f \
	-row $curr_row -column 1 -columnspan 1 -rowspan 1 -sticky w
    incr curr_row
#
    frame $b -bd 2
    pack $b -side bottom -padx 5 -pady 5 
    button $b.apply   -text [msgcat::mc "Apply"]  -command "set ::Monitor::monitorProperties_exitFlag 1"
    button $b.cancel  -text [msgcat::mc "Cancel"] -command "set ::Monitor::monitorProperties_exitFlag 2"
    pack $b.apply $b.cancel -side left -padx 5 -pady 5

    update idletasks
    set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
	    - [winfo vrootx [winfo parent $w]]}]
    set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
	    - [winfo vrooty [winfo parent $w]]}]
    wm geom $w +$x+$y
    bind $w <Destroy> "set ::Monitor::monitorProperties_exitFlag 2"
}
#
proc monitorProperties {} {
    set w .monitorProperties
    set f $w.entries
    set b $w.buttons
#
    if { ![winfo exists $w] } {
	monitorProperties_GUIconstructor
    }
#
# Load actual data
#
    $f.erootdir insert 0 $::rootDir
    $f.esrv insert 0  $::netStateServer
    $f.eport insert 0 $::netStateServerPort
    $f.edcmd insert 0 $::daemonRestartCmd
    $f.edprm insert 1.0 [ join $::netStateServerAccess "\n" ]
#
    set ::Monitor::monitorProperties_exitFlag 0
    update idletasks    
    if { [string equal [wm state $w] withdrawn ] } {
        wm deiconify $w
    }
    raise $w    

    set oldFocus [focus]
    set oldGrab [grab current $w]
    if {[string compare $oldGrab ""]} {
	set grabStatus [grab status $oldGrab]
    }
    grab $w
    focus $b.apply

    tkwait variable ::Monitor::monitorProperties_exitFlag
    
    if {[winfo exists $w] } {
	catch {focus $oldFocus}
	grab release $w
	wm withdraw $w
	if { $::Monitor::monitorProperties_exitFlag==1 } {
	    set ::rootDir 			[$f.erootdir	get]
	    set ::netStateServer 		[$f.esrv	get]
	    set ::netStateServerPort 		[$f.eport	get]
	    set ::daemonRestartCmd 		[$f.edcmd	get]	
	    set ::netStateServerAccess [split [$f.edprm get 1.0 "end-1 chars" ] "\n"]	
	    set ::activeFileChanged "yes"
	}
	$f.erootdir delete 0 end
	$f.esrv delete 0 end
	$f.eport delete 0 end
	$f.edcmd delete 0 end
	$f.edprm delete 1.0 end
    }
    if {[string compare $oldGrab ""]} {
	if {![string compare $grabStatus "global"]} {
	    grab -global $oldGrab
	} else {
	    grab $oldGrab
	}
    }
    return 
}
#
proc ShowMonitorStatus {} {
	tk_dialog .monitorStatustDlg "Monitor Status"\
	    "Monitoring: $::monitor_mode\nNetstate server: $::netStateServer:$::netStateServerPort\nLast update: $::Monitor::last_update" \
	info 0 [msgcat::mc "Approve"]
}
#
#  add dialogs for pregeneration
#
lappend ::GUI_pregenerate_list [list {monitorProperties_GUIconstructor} {Loading MonitorProperties dialog...}]
#
