#!/usr/bin/perl

# simple read operation against waterkotte 8126
# (c) 2008 lintenhofer@inode.at

use strict;
use Device::SerialPort;
use DBI;
use Proc::Daemon;
use Proc::PID::File;
use Mail::Sendmail;
use POSIX;
#use Term::ANSIColor qw(:constants);
#use Data::Dumper;

my %MySQLParams = ( 'databasehost' => 'gorm.flake',
                    'database' => 'haus',
                    'databaseuser' => 'root',
                    'databasepwd' => 'xs4R00t' );
    
my %SMTPParams  = ( smtp    => 'gorm.flake',
                    To      => 'philipp@gorm.flake',
                    From    => 'wp@gorm.flake',
                    Subject => 'WP-Ausfallsmeldung !!!',
                    Message => '');

my $logfile  = '/home/philipp/logs/wp_polld.log';
my $xmlfile  = '/home/philipp/logs/wp_data.xml';
my $sms_send = '/home/philipp/bin/relaySMS2Gorm.sh +436646221039';

use constant TYPE_UNDEF     => 0;
use constant TYPE_DATE      => 1;
use constant TYPE_TIME      => 2;
use constant TYPE_DATETIME  => 3;
use constant TYPE_DEC       => 4;
use constant TYPE_FLOAT1    => 5;
use constant TYPE_FLOAT3    => 6;
use constant TYPE_BIN       => 7;
use constant TYPE_BOOL      => 8;
use constant TYPE_SPECI     => 9;

our (%yield,%wp_memory,$currErrorBuffer);
$currErrorBuffer = 0;
require('/home/philipp/bin/wp/WPmemaddr.pl');
require('/home/philipp/bin/wp/WPhelperfunctions.pl');

#####################################################################################################
#####################################################################################################
# main
#

MAIN:
{
       Proc::Daemon::Init();
       if (Proc::PID::File->running(dir => '/tmp')) { exit(0); }

    ################################################################################
     open(STDOUT, ">>$logfile")
        || die "failed to re-open STDOUT to $logfile";
     open(STDERR, ">&STDOUT")
        || die "failed to re-open STDERR to STDOUT";

    ################################################################################
    $SIG{INT} = $SIG{TERM} = sub
                {
                    print "stopped at ".localtime().
                          "\n=================================================\n";
                    $::exit = 1
                };

    ################################################################################
    
    ################################################################################
    ################################################################################
    print "started at ".localtime()." with PID ".$$."\n";
    while (1)
    {
        my @comdump;
        if (my $serial = Device::SerialPort->new("/dev/ttyr00",0,'/tmp/wp_op.lock'))
        {
            # $serial->baudrate(9600);
            # $serial->parity("none");
            # $serial->databits(8);
            # $serial->stopbits(1);
            # $serial->write_setting || undef $serial;
        
            # read 374 (0x176) bytes from address 0x0000
            if (($serial->write("\xff\x10\x02\x01\x15\x00\x00\x01\x76\x10\x03\x79\x2c\xff"))==14)
            {
                sleep 1;
                my ($count,$data) = $serial->read(512);

                if($count > 10)
                {
                    for(my $i=0; $i < length($data); $i++)
                        { @comdump[$i] = ord(substr($data,$i,1)); }
                    interpretData(0x0000,0x0176,@comdump);
                    @comdump = ();
    
                    if (scalar(%yield) > 0)
                    {
                        if (!write2MySQL(%MySQLParams))
                        {
                            if (open(XML,">>$xmlfile"))
                            {
                                print XML createXML();
                                close(XML);
                                print STDERR (localtime().": could not write to database, saved data to $xmlfile\n");
                            }
                            else { print STDERR (localtime().": could not write XML to $xmlfile\n"); }
                        }
                        alertonErrors(%SMTPParams);
                    }
                }
                else { print STDERR (localtime().": received less/equal than 10 bytes\n"); }
            }
            else { print STDERR (localtime().": error writing to rs232\n"); }

            %yield = ();
            $serial->close() || print STDERR (localtime().": close failed\n");
        }
        else { print STDERR (localtime().": open failed\n"); }
        
        if ($::exit) { exit;  }
        else         { sleep 59; }
    }
    ################################################################################
    ################################################################################
}

#####################################################################################################
#####################################################################################################
# functions
#

#####################################################################################################
# interpretData():
# - interpret data bytes of frame
# - pushes results to %yield
#

sub interpretData
{
    my ($startaddr,$readBytes,@comdump) = @_;
    my @data = fetchFrameData(@comdump);
    
    # check size of data-chunk (frame minus header/trailer)
    if ($readBytes == scalar(@data))
    {
        while (my ($currKey, $currAttr) = each(%wp_memory))
        {
            if ((   $currAttr->{addr}>=$startaddr)
                && ($currAttr->{addr}<($startaddr+$readBytes))
                && ($currAttr->{type} != TYPE_UNDEF))
            {
                my ($currVal,$arrIdx);

                for(my $offset=$currAttr->{addr};$offset<($currAttr->{addr}+$currAttr->{bytes});$offset++)
                {
                    # attention: offset of address is only equal to index of array if $startaddr == 0!
                    $arrIdx = $offset-$startaddr;
                    $currVal .= sprintf("%02x",@data[$arrIdx]);
                }

                if    ($currAttr->{type} == TYPE_DEC)    { $currVal = hex($currVal); }
                elsif ($currAttr->{type} == TYPE_FLOAT1) { $currVal = hex2float1($currVal); }
                elsif ($currAttr->{type} == TYPE_FLOAT3) { $currVal = hex2float3($currVal); }
                elsif ($currAttr->{type} == TYPE_DATE)   { $currVal = sprintf("%02d-%02d-%02d",hex(substr($currVal,4,2)),hex(substr($currVal,2,2)),hex(substr($currVal,0,2))); }
                elsif ($currAttr->{type} == TYPE_TIME)   { $currVal = sprintf("%02d:%02d:%02d",hex(substr($currVal,4,2)),hex(substr($currVal,2,2)),hex(substr($currVal,0,2))); }
                elsif ($currAttr->{type} == TYPE_DATETIME)   { $currVal = sprintf("%02d.%02d.%02d,%02d:%02d:%02d",hex(substr($currVal,6,2)),hex(substr($currVal,8,2)),hex(substr($currVal,10,2)),hex(substr($currVal,4,2)),hex(substr($currVal,2,2)),hex(substr($currVal,0,2))); }
                elsif ($currAttr->{type} == TYPE_BIN)    { $currVal = sprintf("%08b",hex($currVal)); }
                elsif ($currAttr->{type} == TYPE_BOOL)   { $currVal = (hex($currVal)==1)?1:2; }
                elsif ($currAttr->{type} == TYPE_SPECI)
                {
                    if ($currAttr->{menu}=='00.02')   { $currVal = '3.2.3.1'; }
                }

                #print GREEN $currKey." (".$currAttr->{menu}.") -> ".$currAttr->{addr}." (".$currAttr->{bytes}.") => ".$currVal."\n";
                $yield{$currAttr->{menu}} = {'desc' => $currKey, 'val' => $currVal};    
            }
            #else
            #{
                #print RED $currKey." (".$currAttr->{menu}.") -> ".$currAttr->{addr}." (".$currAttr->{bytes}.") => N/A\n";
            #}
        }
    }
    else
    {
        print STDERR (localtime().": dataframe size (".(scalar(@data)).") not equal to read bytes (".$readBytes.")\n");
        foreach (@data) { printf (STDERR "%02x ",$_); }
        print STDERR "\n";
        return 0;
    }
}

#####################################################################################################
# write2MySQL():
# - persist all data to mysqldb
#

sub write2MySQL
{
    my %params = @_;

    my $sql = "INSERT INTO wp_data (";
    $sql .= "tsp_0501_0500,";
    foreach (sort keys %yield) { my $menuentry = $_; $menuentry =~ s/(\d\d)(\.)(\d\d)/m_$1$3/; $sql .= $menuentry.","; }
    chop($sql);
    $sql .= ") VALUES (";
    $sql .= "unix_timestamp('".%yield->{'05.01'}->{'val'}." ".%yield->{'05.00'}->{'val'}."'),"; 
    foreach (sort keys %yield) { $sql .= "\"".%yield->{$_}->{'val'}."\","; }
    chop($sql);
    $sql .= ")";
    
    if (my $dbh = DBI->connect("DBI:mysql:".%params->{'database'}.":".%params->{'databasehost'},%params->{'databaseuser'},%params->{'databasepwd'}))
    {
        my $sth        = $dbh->prepare($sql);
        my $returncode = 1;

        if (!$sth->execute) { print STDERR (localtime().": ".DBI->errstr."\n"); $returncode = 0; }
        $sth->finish;
        $dbh->disconnect;
        return $returncode;
    }
    else { print STDERR (localtime().": error connecting to database\n"); return 0;}
}

#####################################################################################################
# alertonErrors():
# - check Di-Buffer for errors and create mail and sms
# 
#

sub alertonErrors
{
    my $DiBuffer = %yield->{'09.02'}->{'val'};
    $DiBuffer = sprintf('%d', oct("0b$DiBuffer"));
    
    my @errors = ('Störung Kompressor/Öldruck','Störung ND-Pressostat','Störung HD-Pressostat','Störung Pumpe WQ','Systemdruck min','frei','externe Sollwertbeeinfl.','externe Abschaltung');

    if (($DiBuffer & 0xDF)&&($currErrorBuffer ne %yield->{'09.02'}->{'val'}))
    {
        $currErrorBuffer = %yield->{'09.02'}->{'val'};
        my $SMSMsg = '';
        
        for(my $i=0;$i<8;$i++,$DiBuffer>>=1) 
        {
            if(($i!=5)&&($DiBuffer & 0x01))
            {
                $SMTPParams{Message} .= @errors[$i]."\n";
                $SMSMsg .= @errors[$i]." ";
            }
        }

        # send sms
        system("$sms_send 'Ausfall Wärmepumpe: ".$SMSMsg."'");

        if (sendmail(%SMTPParams)) { print STDERR (localtime().': Di-Buffer = '.$currErrorBuffer."\n"); }
        else { print STDERR (localtime().': '.$Mail::Sendmail::error); }
    }
    else { $SMTPParams{Message} = ''; }
}

