#!/usr/bin/perl

#
# $Id: sysinfo.pl,v 1.7 2011-10-24 17:49:27 scg Exp $
#
#

use strict;

use GD;
use USBLCD;



use constant CHART_X_STEP	=> 20;



my $CONT = 1;



# get CPU usage info
my $l_user;
my $l_nice;
my $l_sys;
my $l_idle;

sub get_cpu_usage() {
    open FILE, "</proc/stat" || die "Can't open /proc/stat\n";
    while(<FILE>) {
	chomp;
	if(/cpu*\s+(.*)/) {
	    my @field = split /\s/, $1;

	    my $c_user = $l_user;
	    my $c_nice = $l_nice;
	    my $c_sys = $l_sys;
	    my $c_idle = $l_idle;

	    $l_user = $field[0];
	    $l_nice = $field[1];
	    $l_sys = $field[2];
	    $l_idle = $field[3];

	    return undef unless(defined $l_user and defined $l_nice and defined $l_sys and defined $l_idle);
	    return undef unless(defined $c_user and defined $c_nice and defined $c_sys and defined $c_idle);

	    $c_user = $l_user - $c_user;
	    $c_nice = $l_nice - $c_nice;
	    $c_sys = $l_sys - $c_sys;
	    $c_idle = $l_idle - $c_idle;
	    my $total = $c_user + $c_nice + $c_sys + $c_idle;
	    return undef unless $total;

	    return {
		'user' => sprintf("%.0f", $c_user / $total * 100),
		'nice' => sprintf("%.0f", $c_nice / $total * 100),
		'sys' => sprintf("%.0f", $c_sys /  $total * 100),
		'idle' => sprintf("%.0f", $c_idle / $total * 100),
		'usage' => sprintf("%.0f", (1 - $c_idle / $total) * 100),
	    }
	}
    }
    close FILE;
}


# get memory usage info
sub get_memory_usage() {
    my $total = undef;
    my $free = undef;

    open FILE, "</proc/meminfo" || die "Can't open /proc/meminfo\n";
    while(<FILE>) {
	if(/MemTotal:\s*(\d+)/) {
	    $total = $1;
	}
	elsif(/MemFree:\s*(\d+)/) {
	    $free = $1;
	}

	last if defined $total and  defined $free;
    }
    close FILE;
    
    my $usage = sprintf("%.2f", (($total - $free) / $total * 100)) if 
	$total and  defined $free;

    return { 'total' => $total, 'free' => $free, 'usage' => $usage };
}


# draw CPU usage chart
my @cpu_chart_data;
my $chart_xi = 0;
sub cpu_chart($$;$) {
    my $w = shift;
    my $h = shift;
    my $value = shift;

    my $data_length = $w - 2;
    my $max = $h - 2;

    if(defined $value) {
	for(my $i = 1; $i < $data_length; $i++) {
	    $cpu_chart_data[$i - 1] = $cpu_chart_data[$i];
	}
	$cpu_chart_data[$data_length - 1] = $value;
	$chart_xi = ($chart_xi + 1) % &CHART_X_STEP;
    }

    my $im = new GD::Image($w, $h);
    my $white = $im->colorAllocate(255, 255, 255);
    my $black = $im->colorAllocate(0, 0, 0);

    $im->rectangle(0, 0, $w - 1, $h - 1, $black);

    $im->setStyle($black, $white, $white);
    $im->line(0, $h * 1 / 4,  $w, $h * 1 / 4, gdStyled);
    $im->setStyle($black, $white, $white);
    $im->line(0, $h * 2 / 4,  $w, $h * 2 / 4, gdStyled);
    $im->setStyle($black, $white, $white);
    $im->line(0, $h * 3 / 4,  $w, $h * 3 / 4, gdStyled);
    for(my $i = $w - 2 - $chart_xi; $i > 0; $i -= &CHART_X_STEP) {
	$im->line($i, 0 + 1, $i, $h - 2, gdStyled);
    }

    for(my $i = 0; $i < $data_length; $i++) {
	my $d = $cpu_chart_data[$i];

	if(defined $d) {
	    my $d_prev = ($i > 0 and defined $cpu_chart_data[$i - 1]) ?
		$cpu_chart_data[$i - 1] : 0;

	    my $y = $max - int($d * $max / 100) + 1;
	    my $y_prev = $max - int($d_prev * $max / 100) + 1;
	    
	    if($i == 0) {
		$y_prev = $y;
	    }
	    elsif($y_prev < $y and $y_prev < $max) {
		$y_prev++;
	    }
	    elsif($y_prev > $y and $y_prev > 0) {
		$y_prev--;
	    }

	    $im->line($i + 1, $y, $i + 1, $y_prev, $black);
	    
	}
    }

    return $im;
}


# draw CPU and memory usage
sub cpu_mem_info($$;$$) {
    my $w = shift;
    my $h = shift;
    my $value_cpu = shift;
    my $value_mem = shift;

    my $im =  new GD::Image($w, $h);
    my $white = $im->colorAllocate(255, 255, 255);
    my $black = $im->colorAllocate(0, 0, 0);
    $im->string(gdTinyFont, 2, 0,"System Info", $black);
    $im->string(gdTinyFont, 2, 12, "CPU: ".(defined $value_cpu ? $value_cpu."%" : "-"), $black);
    $im->string(gdTinyFont, 2, 20, "Mem: ".(defined $value_mem ? $value_mem."%" : "-"), $black);
    
    return $im;
}


#
# Entry point
#

my $usblcd = new USBLCD;
die "Can't claim the USB LCD device.\n" unless defined $usblcd;

# get OK and ERROR LEDs
my $led_ok;
my $led_error;
foreach my $led_info (@{$usblcd->getleds}) {
    if($led_info->{'type'} == USBLCD_LED_OK) {
	$led_ok = $led_info->{'led'};
    }
    elsif($led_info->{'type'} == USBLCD_LED_ERROR) {
	$led_error = $led_info->{'led'};
    }
}

my $mode_ref = $usblcd->getmode;
$usblcd->setrotate(USBLCD_ROTATE270);

$usblcd->fillrect(0, 0, $mode_ref->{'width'}, $mode_ref->{'height'}, 0);
$usblcd->setpower(USBLCD_POWER_ON);

# set signal handler
$SIG{'INT'} = sub(){ $CONT = undef ;};

# main loop
while($CONT) {
    my $cpuinfo_ref = get_cpu_usage();
    my $meminfo_ref = get_memory_usage();

    # LED
    if(defined $led_ok and defined $led_error) {
	if($cpuinfo_ref->{'usage'} > 75) {
	    $usblcd->setled($led_ok, USBLCD_LED_OFF);
	    $usblcd->setled($led_error, 1);
	}
	else {
	    $usblcd->setled($led_ok, 1);
	    $usblcd->setled($led_error, USBLCD_LED_OFF);
	}
    }

    # CPU chart
    my $im_cpu = cpu_chart(61, 32, defined $cpuinfo_ref ? $cpuinfo_ref->{'usage'} : undef);
    $usblcd->bitbltgd(61, 0, $im_cpu) || last;

    # CPU and memory usage info
    my $im_text = cpu_mem_info(61, 32,
	defined $cpuinfo_ref ? $cpuinfo_ref->{'usage'} : undef,
	defined $meminfo_ref ? $meminfo_ref->{'usage'} : undef);
    $usblcd->bitbltgd(0, 0, $im_text) || last;

    sleep(1);
}
