#!/usr/bin/perl  -w
#-----------------------------------------------------------------
# By: john@stilen.com
#
# Title:
#     sensors.rrd.pl
#
# Purpouse: 
#     Chart temp stats
#     Collect data from lm_sensors and smartmontools.
#     Store data in mysql database
#     Build chart using rrtool, accessing mysql via libdbi
#
#-----------------------------------------------------------------
use encoding "UTF-8", STDOUT => "utf8";
use utf8;
use RRDs;
use strict;              # for safety
use Data::Dumper ;       # for debugging
use JSON -support_by_pp; # used to parse POL file json
use DBI;                 # MySQL
#-----------------------------------------------------------------
my $debug         =0;                     # debug 1=on, 0=off
my $counter       =1;                     # number of loops
my $sensors       ="/usr/bin/sensors";    # Binary for sensors
my $rrdtool       ="/usr/bin/rrdtool";    # Binary for rrdtool
my $smartctl      ="/usr/sbin/smartctl";  # Binary for smartctl
my $graph_width   ="300";
my $graph_height  ="100";
my $output_dir    ="/var/www/localhost/htdocs/thermal";
my $rrd_file      ="$output_dir/fan_temp.rrd";
my $graph_temp_file_2hr= "$output_dir/temp_2hr.png";
my $graph_cpu_temp_file_2hr= "$output_dir/cpu_temp_2hr.png";
my $graph_rpm_file_2hr=  "$output_dir/fan_2hr.png";
my $graph_temp_file_24hr="$output_dir/temp_24hr.png";
my $graph_rpm_file_12hr= "$output_dir/fan_24hr.png";
my $graph_temp_file_1wk= "$output_dir/temp_1wk.png";
my $graph_rpm_file_1wk=  "$output_dir/fan_1wk.png";
my $graph_temp_file_1mo= "$output_dir/temp_1mo.png";
my $graph_rpm_file_1mo=  "$output_dir/fan_1mo.png";
my $graph_temp_file_1yr= "$output_dir/temp_1yr.png";
my $graph_rpm_file_1yr=  "$output_dir/fan_1yr.png";
my $index_file=          "$output_dir/index.html";
my %stats=(
    cpuPhy => 0,
    cpu0  => 0,
    cpu1  => 0,
    cpu2  => 0,
    cpu3  => 0,
    disk0 => 0,
    disk1 => 0,
    disk2 => 0,
    disk3 => 0,
    board0 => 0,
    board1 => 0,
);
my $degree="\xc2\xb0";
#-----------------------------------------------------------------
#
# Open json file that holds the authentication
# make database conneciton
# Sample of the password file:
#    {
#       "mysql" : {
#	  "password" : "foo",
#	  "user" : "bar"
#       }
#    }
#
my $json_file = "/root/bin/mysql.json";
my %mysql_pass_structure;
my $json_text = do {
    open(my $json_fh, "<:encoding(UTF-8)", $json_file)
      or die("Can't open \$filename\": $!\n");
    local $/;
    <$json_fh>
};
my $json_read = JSON->new;
my $data = $json_read->decode($json_text);
if ( $debug == 1 ){
    print "Data structue after read:\n";
    print Dumper($data);
}
#----------------------------------------------------------------
#
# Create database connection
#
DBI->trace( $debug );
my $host        ='localhost';
my $dbd         ='mysql';
my $db          ='thermal';
my $db_user     =$$data{'mysql'}{'user'};
my $db_password =$$data{'mysql'}{'password'};

my $dbh = DBI->connect("dbi:$dbd:$db:$host","$db_user","$db_password")
    || err_trap("Cannot connect to the database:\t$!\n");

#-----------------------------------------------------------------
#
# Create data directoy if it doesn't exist
#
if ( ! -e $output_dir ){ 
  mkdir( $output_dir, 755 ) || die "Can't create data directory ($output_dir)\t$!\n";
}
#-----------------------------------------------------------------
#
# Collect a date
#
my $EventDate;
open (DATE, "date|") || die "Could not run date:\t$?\n";
chomp($EventDate=<DATE>);
close (DATE);
if ($debug==1 ){  print "DATE: $EventDate\n";  }
#-----------------------------------------------------------------
# 
# flush the buffer (not sure I need this now)
#
$| = 1;
#-----------------------------------------------------------------
#
# Number of iterations
#
while ( $counter ){
    #-------------------------------------
    #
    # Collect Data
    #
    &get_i2c( \%stats ) || die "can't check sensors:\t $!\n";
    &get_smart( \%stats ) || die "can't check smart:\t $!\n";
    #-------------------------------------
    #
    # Debug output
    #
    if ( $debug == 1 ){  
        print "Data after collection:\n";
        for my $key ( sort ( keys ( %stats ) ) ){
	  print "\t$key:\t$stats{$key}\n";
	}
    }
    #-------------------------------------
    #
    # Populate the database
    #
    &record_data(\%stats);
    #-------------------------------------
    #
    # Graph from mysql
    #
    &graph_data( $dbh );	
    #-------------------------------------
    #
    # Build index.html file
    #
    &make_index;
    #-------------------------------------
    #
    # Number of times we loop 
    # count down to zero from $counter
    #
    $counter--;  

} # END main while loop
#-----------------------------------------------------------------
#
# Takes ref to data struct 
#
sub record_data{
    my $stats_ref = shift @_;

    if ( $debug == 1 ){ 
        print "Data Before Insert:\n"; 
        for my $key ( sort ( keys(%$stats_ref) ) ){
            print "\t$key:\t$$stats_ref{$key}\n";
        }
    }

    my $add_data=q{
        INSERT into temperature 
	set cpuPhy=?,
	    cpu0=?,
	    cpu1=?,
	    cpu2=?,
	    cpu3=?,
	    disk0=?, 
	    disk1=?,
	    disk2=?,
	    disk3=?,
	    board0=?,
	    board1=?
    };

    if ( $debug == 1 ){ 
	print qq{ 

            INSERT into temperature 
	    set cpuPhy=$stats_ref->{cpuPhy},
		cpu0=$stats_ref->{cpu0},
		cpu1=$stats_ref->{cpu1},
		cpu2=$stats_ref->{cpu2},
		cpu3=$stats_ref->{cpu3},
		disk0=$stats_ref->{disk0}, 
		disk1=$stats_ref->{disk1},
		disk2=$stats_ref->{disk2},
		disk3=$stats_ref->{disk3},
		board0=$stats_ref->{board0},
		board1=$stats_ref->{board1}

	};
    }
    
    my $sth = $dbh->prepare( $add_data );
    
    $sth->execute(
        $stats_ref->{cpuPhy},
        $stats_ref->{cpu0},
        $stats_ref->{cpu1},
        $stats_ref->{cpu2},
        $stats_ref->{cpu3},
        $stats_ref->{disk0},
        $stats_ref->{disk1},
        $stats_ref->{disk2},
        $stats_ref->{disk3},
        $stats_ref->{board0},
	$stats_ref->{board1},
    ) || die "Mysql Statement Error:\t".$sth->errstr."\n";

}
#-----------------------------------------------------------------
#
# Takes 
# 
#
sub graph_data {
    my $dbh = shift @_;
 
    # Create hash based for the time intervals we are interested in
    my %graph_perioud=(
        "2hr"  => -1*60*60*2,
	"24hr" => -1*60*60*24,
	"1wk"   =>-1*60*60*24*7,
	"1mo"  => -1*60*60*24*7*4,
	"1yr"   =>-1*60*60*24*365,
    );

    # Turn on sql debugging
    my $rrddebugsql = "";
    if ( $debug == 1 ){
	$ENV{RRDDEBUGSQL}="1";
    }

    for my $key (keys %graph_perioud){
	print "$key\t$graph_perioud{$key}\n";

	my $image_file_name_cpu="$output_dir/cpu_temp_${key}.png";
	my $type="MIN"; # AVERAGE, MAX, LAST
	my $mark="LINE"; # LINE1, AREA
        my $alpha = "cc"; # Opaqueness: 00 = 100% tranparent, ff = 100% opaque 
	my $dsnames = "min";  # min, avg, max, count and sigma
	my $cons = "AVERAGE"; # MIN, MAX, AVERAGE
	
	if ( $debug == 1 ){  print "Draw $key CPU Temp: $image_file_name_cpu\n"; }
	# BACK:   background
        # FONT:   forground
        # CANVAS: graph background
        # SHADEA: left & top boarder
        # SHAREB: right & bottom border
	RRDs::graph (
            "$image_file_name_cpu",
   	    "--imgformat=PNG",
   	    "--start=$graph_perioud{$key}",
   	    "-c", "BACK#343435$alpha",  
            "-c", "FONT#ffffff$alpha",  
            "-c", "CANVAS#605f6022$alpha",
   	    "--title=CPU Temp for $key\n$EventDate",
   	    "--height=$graph_height", 
	    "--width=$graph_width", 
	    "--border=0",
	    "--slope-mode",   # curve rather than staircase
	    "--watermark=stilen.com thermal data",
   	    "--vertical-label=Temp (${degree}C)",
   	    "--step", "1",
	    "DEF:cpuPhym=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/cpuPhy/:$dsnames:$cons", 
	    "DEF:cpu0m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/cpu0/:$dsnames:$cons", 	    
	    "DEF:cpu1m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/cpu1/:$dsnames:$cons", 	    	    ,
	    "DEF:cpu2m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/cpu2/:$dsnames:$cons", 	    	    	    
	    "DEF:cpu3m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/cpu3/:$dsnames:$cons", 	    	    	    
   	    "$mark:cpu0m#f601b4$alpha:cpu0\\:",
   	      "GPRINT:cpu0m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",  
   	    "$mark:cpu1m#b403f6$alpha:cpu1\\:",
   	      "GPRINT:cpu1m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",  
   	    "$mark:cpu2m#3103f6$alpha:cpu2\\:", 
   	      "GPRINT:cpu2m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",  
   	    "$mark:cpu3m#03f3f6$alpha:cpu3\\:",
   	      "GPRINT:cpu3m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",  
            "$mark:cpuPhym#f43e42$alpha:cpuP\\:",
   	      "GPRINT:cpuPhym:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",  
        );
        my $image_file_name_disk="$output_dir/temp_${key}.png";

        if ( $debug == 1 ){  print "Draw 2 Hour Temp: $image_file_name_disk\n"; }
        RRDs::graph (
            "$image_file_name_disk",
   	    "--imgformat=PNG",
   	    "--start=$graph_perioud{$key}",
   	    "-c", "BACK#343435$alpha", 
            "-c", "FONT#ffffff$alpha", 
            "-c", "CANVAS#605f6022",
   	    "--title=DISK Temp for $key\n$EventDate",
   	    "--height=$graph_height", 
	    "--width=$graph_width", 
	    "--border=0",
	    "--slope-mode",   # curve rather than staircase
	    "--watermark=stilen.com thermal data",
   	    "--vertical-label=Temp (${degree}C)",
   	    "--step", "10",
            "DEF:disk0m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/disk0/:$dsnames:$cons:step=600",
            "DEF:disk1m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/disk1/:$dsnames:$cons:step=600",
            "DEF:board0m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/board0/:$dsnames:$cons:step=600",
            "DEF:board1m=sql//mysql/host=127.0.0.1/dbname=thermal/username=thermal/password=thermal//temperature/*date/board1/:$dsnames:$cons:step=600",
   	    "$mark:disk0m#03f63c88:disk0\\: ", 
   	      "GPRINT:disk0m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",  
   	    "$mark:disk1m#b4f603$alpha:disk1\\: ", 
   	      "GPRINT:disk1m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",  
   	    "$mark:board0m#f6b403$alpha:board0\\: ",
   	      "GPRINT:board0m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",    
   	    "$mark:board1m#f80707$alpha:board1\\: ",
   	      "GPRINT:board1m:$cons:\%-6.2lf${degree}C",
   	      "COMMENT:\\n",    
        );
    }
}
#-----------------------------------------------------------------
sub get_i2c {
    my $stats_ref = shift @_;
    my $temp1;
    my $temp2;
    my $temp3;
    my $cpuPhy;
    my $cpu0;
    my $cpu1;
    my $cpu2;
    my $cpu3;
    my $board0;
    my $board1;
    my $Start;
    my $input;
    
    if ( $debug == 1 ){  print "Parsing lm_sensors output\n"; }
    my $sensors="/usr/bin/sensors";
    open ( SENSORS, "$sensors |") || die "Cannot run sensors\n";
    {
        local $/;
        $input = <SENSORS>;
    }
    #-------------------------------------------------
    # Motherboard board sensors:
    #
    while( $input =~ m{

	acpitz-virtual-0\n
	Adapter:\sVirtual\sdevice\n
	temp1:\s+\+(\d+\.\d+).*C\s+\(crit.*\n
	temp2:\s+\+(\d+\.\d+).*C\s+\(crit.*\n
	}smxig # allow comments, case insensetive, global

    ){
	if ( $debug == 1 ){
	    print "---------------------\n";
	    print "Input: acpitz-virtual-0\nboard0:$1\nboard1:$2\n";
	    print "---------------------\n";
	}
	$board0 = $1;
	$board1 = $2;

    }	   
    #-------------------------------------------------
    while( $input =~ m{

	coretemp-isa-0000\n
	Adapter:\sISA\sadapter\n
	Physical\sid\s0:\s+\+(\d+\.\d+).*C.*\n
	Core\s0:\s+\+(\d+\.\d+).*C.*\n
	Core\s1:\s+\+(\d+\.\d+).*C.*\n
	Core\s2:\s+\+(\d+\.\d+).*C.*\n
	Core\s3:\s+\+(\d+\.\d+).*C.*\n
	}smxig  # allow comments, case insensetive, global

    ){
	if ( $debug == 1 ){
	    print "---------------------\n";
	    print "Input: coretemp-isa-0000\nPhys0:$1\nCore0:$2\nCore1:$3\nCore2:$4\nCore3:$5\n";
	    print "---------------------\n";
	}
	$cpuPhy = $1;
	$cpu0 = $2;
	$cpu1 = $3;
	$cpu2 = $4;
	$cpu3 = $5;

    }

    # Should never get here.
    close ( SENSORS );
    
    if ( 1 ){
	$stats_ref->{cpuPhy}  = "$cpuPhy";
	$stats_ref->{cpu0}  = "$cpu0";
	$stats_ref->{cpu1}  = "$cpu1";
	$stats_ref->{cpu2}  = "$cpu0";
	$stats_ref->{cpu3}  = "$cpu1";
	$stats_ref->{board0}  = "$board0";
	$stats_ref->{board1}  = "$board1";
	
        return ( 1 ) ;
    } else {
        exit 1;
    } 
}
#-----------------------------------------------------------------
sub get_smart {
    my $stats_ref = shift @_;
    my $input; 
    #-------------------------------------------------
    # Disk0 Temp
    # smartctl  -d sat+megaraid,4 /dev/sda -A |grep Temp
    #    190 Airflow_Temperature_Cel 0x0032   060   048   000    Old_age   Always       -       40
    #
    my $smart_cmd_disk0 = "$smartctl -d sat+megaraid,4 /dev/sda -A |grep Temp";
    open ( SMART, "$smart_cmd_disk0 |") || die "Cannot run smart on disk0\n";
    {
        local $/;
        $input = <SMART>;
    }
    close(SMART);
    while( $input =~ m{
	.*\s+(\d+)\n
	}smxig # allow comments, case insensetive, global

    ){
	if ( $debug == 1 ){
	    print "---------------------\n";
	    print "Disk0: $1\n";
	    print "---------------------\n";
	}
	$stats_ref->{disk0}  = $1;
    }	   
    #-------------------------------------------------
    # Disk1 Temp
    # smartctl  -d sat+megaraid,5 /dev/sda -A |grep Temp
    #    190 Airflow_Temperature_Cel 0x0032   063   054   000    Old_age   Always       -       37
    my $smart_cmd_disk1 = "$smartctl -d sat+megaraid,5 /dev/sda -A |grep Temp";
    open ( SMART, "$smart_cmd_disk1 |") || die "Cannot run smart on disk0\n";
    {
        local $/;
        $input = <SMART>;
    }
    close(SMART);
    while( $input =~ m{
	.*\s+(\d+)\n
	}smxig # allow comments, case insensetive, global

    ){
	if ( $debug == 1 ){
	    print "---------------------\n";
	    print "Disk1: $1\n";
	    print "---------------------\n";
	}
	$stats_ref->{disk1}  = $1;
    }
    return ( 1 );
    #-------------------------------------------------
    # Disk2 Temp
    # smartctl  -d sat+megaraid,6 /dev/sda -A |grep Temp
    #   194 Temperature_Celsius     0x0022   114   083   000    Old_age   Always       -       33
#    my $smart_cmd_disk2 = "$smartctl -d sat+megaraid,6 /dev/sda -A |grep Temp";
#    open ( SMART, "$smart_cmd_disk1 |") || die "Cannot run smart on disk0\n";
#    {
#        local $/;
#        $input = <SMART>;
#    }
#    close(SMART);
#    while( $input =~ m{
#	.*\s+(\d+)\n
#	}smxig # allow comments, case insensetive, global
#
#    ){
#	if ( $debug == 1 ){
#	    print "---------------------\n";
#	    print "Disk2: $1\n";
#	    print "---------------------\n";
#	}
#	$stats_ref->{disk2}  = $1;
#    }
#    return ( 1 );
}
#-----------------------------------------------------------------
sub make_index {
    my $html_file= q{
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd">
<HTML >
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta http-equiv="refresh" content="120;URL=./" >
    <link rel="icon" href="/favicon.ico" type="image/x-icon" >
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" >
    <link rel="stylesheet" type="text/css" href="./style.css">
  </head >
  <body >
    <table border="0" >
      <tr>
	<td colspan=2><a href="/scripts/perl/sensors.rrd.pl">Download this script</a></td>
      </tr><tr>
	<td colspan=2><font size="2" color="yellow">Page Reloads every 2min.</font></td>
      </tr><tr>
	<td valign="top"><img src="temp_2hr.png" alt="2 hour hd temp" >  </td>
	<td valign="top"><img src="cpu_temp_2hr.png"  alt="2 hour cpu temp"> </td>
      </tr><tr>
	<td valign="top"><img src="temp_24hr.png" alt="12 hour temp"></td>
	<td valign="top"><img src="cpu_temp_24hr.png" alt="12 hour fan"></td>
      </tr><tr>
	<td valign="top"><img src="temp_1wk.png" alt="1 week temp"></td>
	<td valign="top"><img src="cpu_temp_1wk.png" alt="1 week fan"></td>
      </tr><tr>
	<td valign="top"><img src="temp_1mo.png" alt="1 month temp"></td>
	<td valign="top"><img src="cpu_temp_1mo.png" alt="1 week fan"></td>
      </tr><tr>
	<td valign="top"><img src="temp_1yr.png" alt="1 year temp"></td>
	<td valign="top"><img src="cpu_temp_1yr.png" alt="1 week fan"></td>
      </tr>
    </table >
  </body >
</HTML >
    };
    
    open ( HTML, ">$index_file") || die "Could not open file:\t$?\n";
    print HTML "$html_file\n";
    close (HTML);

    return ( 1 );
}
