#!/usr/bin/wish -f

# Copyright 1999 Paul Mackerras
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version
#  2 of the License, or (at your option) any later version.
#
# $Id: Batmon,v 1.1.1.1 2001/12/07 11:31:43 sleemburg Exp $
#
# $Log: Batmon,v $
# Revision 1.1.1.1  2001/12/07 11:31:43  sleemburg
# Initial CVS import of the unreleased pmud-0.8 to apmud (new project name
# because of a name clash at sourceforge.net).
#
# Revision 1.1  2000/01/06 13:48:19  stephan
# Initial revision
#

global c bgcolor

proc thermometer {tx0 ty0 scale max} {
    global c tscale titem txitem
    set y0 [expr {$ty0 + $scale * $max}]
    $c create line $tx0 $ty0 $tx0 $y0 -width 9 -capstyle round -fill black
    $c create oval [expr $tx0-7] $y0 [expr $tx0+7] [expr $y0+14] \
	    -fill red
    set titem [$c create line $tx0 [expr $y0+7] $tx0 $y0 -width 7 -fill red]
    set tscale $scale
    for {set t 0} {$t <= $max} {incr t 10} {
	set y [expr {$y0 - $scale * $t}]
	.c create text [expr $tx0+10] $y -text $t -anchor w \
		-font -Adobe-Helvetica-Bold-R-Normal--*-100-*-*-*-*-*-*
    }
    set txitem [.c create text [expr $tx0+1] [expr $y0+20] -anchor n]
}

proc smallabel {x y text} {
    global c
    $c create text $x $y -anchor nw -text $text \
	    -font -Adobe-Helvetica-Bold-R-Normal--*-100-*-*-*-*-*-*
}

proc settherm {val} {
    global c tscale titem txitem
    set was [$c coords $titem]
    set x [lindex $was 0]
    set y0 [lindex $was 1]
    set newy [expr {$y0 - 7 - $tscale * $val}]
    $c coords $titem $x $y0 $x $newy
    $c itemconf $txitem -text $val
}

proc bargauge {id x0 y0 len scale off min max tick color} {
    global c bscale$id bitem$id bxitem$id bmax$id bcolor$id boff$id byt$id
    set yb [expr {$y0 + $len}]
    .c create line $x0 [expr $y0-2] $x0 [expr $yb+2] -width 15
    set bitem$id [.c create line $x0 $yb $x0 $y0 -width 11 -fill $color]
    set bscale$id $scale
    set boff$id $off
    for {set v $min} {$v <= $max} {incr v $tick} {
	set y [expr {$yb - $scale * ($v - $off)}]
	.c create text [expr $x0+12] $y -text $v -anchor w \
		-font -Adobe-Helvetica-Bold-R-Normal--*-100-*-*-*-*-*-*
    }
    set bxitem$id [.c create text [expr $x0+2] [expr $yb+8] -anchor n]
    set bmax$id $max
    set bcolor$id $color
    set byt$id $y0
}

proc setgauge {id val {val2 {}}} {
    global c bscale$id bitem$id bxitem$id bmax$id bcolor$id bgcolor boff$id
    global byt$id
    set s [set bscale$id]
    set o [set boff$id]
    set i [set bitem$id]
    set was [$c coords $i]
    set x [lindex $was 0]
    set y0 [lindex $was 1]
    set yt [set byt$id]
    if {$val == ""} {
	global byt$id
	$c itemconf $i -fill $bgcolor
	set y $yt
    } else {
	if {$val < $o} {set val $o}
	$c itemconf $i -fill [set bcolor$id]
	set y [expr {round($y0 - $s * ($val - $o))}]
	if {$y < $yt} {set y $yt}
    }
    $c coords [set bitem$id] $x $y0 $x $y
    if {$val2 == {}} {set val2 $val}
    $c itemconf [set bxitem$id] -text $val2
}

proc indicator {id x y w h color text} {
    global c iitem$id icolor$id
    set iitem$id [$c create rectangle $x $y [expr $x+$w] [expr $y+$h] \
	    -width 2 -fill $color]
    set t [$c create text [expr $x+$w/2] [expr $y+$h/2] -anchor c -text $text]
    set icolor$id $color
}

proc setindic {id val} {
    global c iitem$id icolor$id
    set i [set iitem$id]
    if $val {
	$c itemconf $i -fill [set icolor$id]
    } else {
	$c itemconf $i -fill black
    }
}

# The current best guess about the values we read from the PMU on the 3400,
# based on measurements and observations on my 3400 with LiIon battery:
#
# The line we get from pmud is
#	flags vbat hstemp battemp current chargeused switches
#
# Flags is a set of 8 bits, in binary, which we number left-to-right
# from 0 to 7.  (Flag 0 is 0x80 in the byte we get from the PMU.)
# Flag 0 indicates a LiIon battery (conjecture)
# Flag 1 indicates that the `chargeused' field is valid.
#  It gets set when the battery is fully charged and reset when
#  the PMU is charging the battery.
# Flag 2 indicates that the battery is fully charged.
# Flag 5 indicates that the battery is present.
# Flag 6 indicates that the battery is being charged.
# Flag 7 indicates that the AC is connected.
#
# The vbat field gives the battery voltage.  My current best estimate
# is that the actual battery voltage is vbat * 0.0265 + 7.2665 V.
#
# The hstemp field is the temperature of the CPU heatsink in Celsius.
# The battemp field is the temperature of the battery in Celsius.
#
# The current field gives the rate of power consumption in some unknown
# units.  This field is valid only when running from the battery.  It
# seems to freeze when running off the AC mains.
#
# The chargeused field integrates the current field while running
# off the battery.  It resets to 0 when the battery is fully charged.
# A current of x increases the chargeused field by x in 252.6 seconds.
# It ranges from 0 for a fully charged battery to around 6500 when flat.

proc readbat_3400 {f} {
    global c chargei bgcolor curri btempi ctempi tli
    set line [gets $f]
    puts $f ""
    flush $f
    if {[llength $line] < 6} return
    set flags [lindex $line 0]
    for {set i 0} {$i < 8} {incr i} {
	set flag$i [string index $flags $i]
    }
    setindic bpres $flag5
    setindic ac $flag7
    if {$flag6} {
	$c itemconf $chargei -fill red
    } else {
	$c itemconf $chargei -fill $bgcolor
    }
    set current [lindex $line 4]
    set tl {}
    set switches [lindex $line 6]
    if {$flag5} {
	# battery is installed
	set braw [lindex $line 1]
	set v [expr {$braw * 0.0265 + 7.2665}]
	setgauge bat $v [expr {round($v*10) / 10.0}]
	# estimate charge used from voltage
	if {!$flag7} {
	    # running off battery, compensate for drop at high current
	    if {$current > 200} {
		set braw [expr {$braw + ($current - 200) * 0.15}]
	    }
	} elseif {$flag6} {
	    set braw [expr {$braw - 10}]
	}
	set i [expr {(330 - $braw) / 10.0}]
	set j [expr int($i)]
	if {$j <= 0} {
	    set charge 0
	} elseif {$j >= 21} {
	    set charge 6500
	} else {
	    set xx [lrange \
		    { 0 275 850 1680 2325 2765 3160 3500 3830 4115 4360
	    4585 4795 4990 5170 5340 5510 5710 5930 6150 6370
	    6500 } $j [expr $j+1]]
	    set charge [lindex $xx 0]
	    set xx [expr {[lindex $xx 1] - $charge}]
	    set charge [expr {$charge + ($i - $j) * $xx}]
	}
	set charge [expr {round(1000 - $charge / 6.5) / 10.0}]
	if {$flag1} {
	    set pcharge [lindex $line 5]
	    if {$pcharge > 6500} {set pcharge 6500}
	    set pcharge [expr {round(1000 - $pcharge / 6.5) / 10.0}]
	    if {$pcharge < $charge} {set charge $pcharge}
	}
	setgauge bchg $charge
	if {!$flag7 && $current > 0} {
	    set timeleft [expr {int($charge * 274 / $current)}]
	    set tl [format "%d:%.2d" [expr $timeleft/60] [expr $timeleft%60]]
	}
    } else {
	setgauge bat {}
	setgauge bchg {}
    }
    $c itemconf $tli -text $tl
    # settherm [lindex $line 2]
    for {set b 0} {$b < 8} {incr b} {
	setindic b$b [set flag$b]
    }
    $c itemconf $curri -text $current
    $c itemconf $ctempi -text [lindex $line 2]
    if {[string index $flags 5]} {
	$c itemconf $btempi -text [lindex $line 3]
    } else {
	$c itemconf $btempi -text {}
    }
}

# On the lombard, the line we get from pmud is
#   S {flags charge maxcharge ibat vbat} {(same for R batt)}
#
# Flags is a set of 3 bits, in binary, which we number left-to-right
# from 0 to 2.  (Flag 0 is 1 in the byte we get from the PMU.)
# Flag 0 indicates that the AC is connected.
# Flag 1 indicates that the battery is being charged.
# Flag 2 indicates that the battery is present.
#
# The vbat field gives the battery voltage in millivolts maybe.
# The ibat field gives the battery current, positive when charging.

proc readbat_g3 {f} {
    global c chargei bgcolor curri volti tli acpower
    set line [gets $f]
    puts $f ""
    flush $f
    if {[llength $line] < 3 || [lindex $line 0] != "S"} return
    set lbat [lindex $line 1]
    set rbat [lindex $line 2]
    set lflags [lindex $lbat 0]
    set rflags [lindex $rbat 0]
    for {set i 0} {$i < 3} {incr i} {
	set lflag$i [string index $lflags $i]
	set rflag$i [string index $rflags $i]
	set flag$i [expr {[set lflag$i] | [set rflag$i]}]
    }
    setindic bpres $flag2
    setindic ac $flag0
    if {$flag1} {
	$c itemconf $chargei -fill red
    } else {
	$c itemconf $chargei -fill $bgcolor
    }
    set current {}
    set voltage {}
    set charge 0
    if {$lflag2} {
	set charge [lindex $lbat 1]
	set current [lindex $lbat 3]
	set voltage [format "%.2f" [expr {[lindex $lbat 4]/1000.0}]]
	set q [expr {round([lindex $lbat 1]*1000.0 / [lindex $lbat 2]) / 10.0}]
	setgauge bchgl $q
    } else {
	setgauge bchgl {}
    }
    if {$rflag2} {
	set charge [expr {$charge + [lindex $rbat 1]}]
	set curr [lindex $rbat 3]
	if {$current == "" || abs($curr) > abs($current)} {
	    set current $curr
	    set voltage [format "%.2f" [expr {[lindex $rbat 4]/1000.0}]]
	}
	set q [expr {round([lindex $rbat 1]*1000.0 / [lindex $rbat 2]) / 10.0}]
	setgauge bchgr $q
    } else {
	setgauge bchgr {}
    }
    
    set tl {}
    if {!$flag0 && $current != "" && $current < 0} {
	set mins [expr {int($charge * 59.2 / -$current)}]
	set tl [format "%d:%.2d" [expr $mins/60] [expr $mins%60]]
    }
    $c itemconf $tli -text $tl
    $c itemconf $curri -text $current
    $c itemconf $volti -text $voltage

    if {$flag0 != $acpower} {
	global dpms_ac dpms_bat
	set acpower $flag0
	set t [expr {$acpower? $dpms_ac: $dpms_bat}]
	if {$t != ""} {
	    catch {exec /usr/X11R6/bin/xset dpms 0 0 $t}
	}
    }
}

proc dosleep {} {
    global batf
    puts $batf "sleep"
    flush $batf
}

set batf [socket localhost 879]
set line [gets $batf]
if {$line == ""} exit

set c [canvas .c -width 90 -height 240]
pack .c
global bgcolor
set bgcolor [.c cget -background]

global acpower dpms_ac dpms_bat
set acpower {}
set dpms_ac 900
set dpms_bat 180

indicator ac 8 8 20 15 green AC
indicator bpres 54 8 30 15 blue Batt
global chargei
set chargei [$c create line 29 15 53 15 -width 9 -fill $bgcolor -arrow last]

set pmuvers [lindex $line 2]
if {$pmuvers >= 10} {
    smallabel 6 33 "Left"
    smallabel 52 33 "Right"
    bargauge bchgl 18 55 100 1 0 0 100 20 green
    bargauge bchgr 61 55 100 1 0 0 100 20 green

    global curri volti tli
    smallabel 6 184 "Time left:"
    set tli [$c create text 85 184 -anchor ne]
    smallabel 6 200 "Current:"
    set curri [$c create text 85 200 -anchor ne]
    smallabel 6 216 "Voltage:"
    set volti [$c create text 85 216 -anchor ne]

    fileevent $batf read "readbat_g3 $batf"

} elseif {$pmuvers == 9} {
    # 3400/2400/3500
    .c conf -height 270
    smallabel 6 33 "Charge"
    smallabel 52 33 "Voltage"
    bargauge bchg 18 55 100 1 0 0 100 20 green
    bargauge bat 63 55 100 15.4 10.5 11 17 1 blue

    for {set b 0} {$b < 8} {incr b} {
	indicator b$b [expr 7+$b*10] 255 10 10 \
		[lindex {yellow white cyan orange orange blue red green} $b] ""
    }

    global curri btempi ctempi tli
    smallabel 6 184 "Time left:"
    set tli [$c create text 85 184 -anchor ne]
    smallabel 6 200 "Current:"
    set curri [$c create text 85 200 -anchor ne]
    smallabel 6 216 "CPU temp:"
    set ctempi [$c create text 85 216 -anchor ne]
    smallabel 6 232 "Bat temp:"
    set btempi [$c create text 85 232 -anchor ne]

    fileevent $batf read "readbat_3400 $batf"

} else {
    puts "Unknown PMU version $pmuvers"
    exit
}

set lower .lower
frame $lower -bd 2 -relief sunk
button $lower.snooze -text "Sleep" -command dosleep
pack $lower.snooze
pack $lower -fill x
