#!/usr/bin/perl

use 5.014;
use warnings;
use utf8;
use List::MoreUtils qw/pairwise/;

use Protocol::Modbus::PowerMod qw(modbus _MB_DEFAULT_PORT_ :FC :FUNC);
use Getopt::Long qw(:config no_ignore_case);

# Exit codes
use constant _OKAY_ => 0;
use constant _WARN_ => 1;
use constant _CRIT_ => 2;
use constant _UNKN_ => 3;

# Nur zur Datenabfrage
use constant FACT => 0;
use constant DATA => 1;

# Globale Messwert- und Ergebnis-Tabelle
use constant NEW   => 0;    # Array für neuermittelte Daten
use constant OLD   => 1;    # Array für letzter Datenwert
use constant DIM   => 2;    # Messtyp
use constant DESC  => 3;    # Beschreibung (Name)
use constant INFO  => 4;    # Info (In Klammer stehende String nach dem Messwert)
use constant SIZE  => 5;    # Sicherungsgröße
use constant WARN  => 6;    # Warning
use constant CRIT  => 7;    # Critical
use constant ONOFF => 8;    # Überwachung aktiv?
use constant ERROR => 9;    # Error-Status

# Verzeichnisse für Config, Alarm und Messwerte
use constant VALUE => 0;    # Messwerte
use constant ALARM => 1;    # Alarmliste
use constant CONFI => 2;    # Konfiguration

# THRESHOLD
use constant _ZERO_ONES_  => 0;
use constant _ZERO_ZERO_  => 1;
use constant _ZERO_RISE_  => 2;
use constant _SIZE_TYPE_  => 3;
use constant _OVERLOAD_W_ => 4;
use constant _OVERLOAD_C_ => 5;

sub print_error($) {
    my ($_message_) = @_;

    print STDERR "UNKNOWN: Eine oder mehrere Optionen fehlerhaft.\n$_message_";
    exit _UNKN_;
}

sub print_help {
    print <<__;

    $0  -H HOST_NAMEs  -VH VIRTUAL_HOST_NAME  [-p PORT] [-dim DIMENSION]
                            [-u  UNITs]            [-n  COUNT]
                            [-d  DATA_ADDRESS]     [-s  SCALE_ADDRESS]
                            [-dt DATA_TYPE]        [-st SCALE_TYPE]
                            [-t  TYPE]             [-info INFORMATION]
                            [-w  WARNINGs]         [-enum ENUMERATION]
                            [-c  CRITICALs]        [-desc DESCRIPTION]
                            [-zo ZERO_ONES]        [-path CONFIG]
                            [-zz ZERO_ZERO]
                            [-zr ZERO_RISE]

__
}

##########################################################
# parsing options
#
GetOptions(

    # Hilfefunktionen
    'h|?' => sub { print_help(); exit 0; },
    'help' => sub {
        exec perldoc => -F => $0 or die "Kann perldoc nicht ausfuehren: $!\n";
    },

    # Verbindungsinformationen
    'H=s' => \my $H_LIST,    # Hostadressen (FQDNs)
    'u=s' => \my $U_LIST,    # Unit/Slave-IDs
    'p=i' => \my $_PORT_,    # TCP-Port für ModbusTCP

    # Daten- und Gruppierungsinformationen
    'n=i'  => \my $_CNT_,       # Anzahl der abzufragenden Datenwerte
    'VH=s' => \my $VIRT_HOST,   # Namen des virtuellen Hosts (Gruppenbezeichner)
    'd=i'  => \my $DATA_ADDR,   # Daten-Startadresse
    'dt=s' => \my $DATA_TYPE,   # Datentyp (für Daten-Startadresse)
    's=s'  => \my $FACT_ADDR,   # Faktor-Startadresse
    'st=s' => \my $FACT_TYPE,   # Datentyp (für Faktor-Startadresse)

    # Schwell- und Grenzwerte
    'zo=s' => \
      my $_ZO_
    , # Ignoriere, wenn vorangegangender Zahlenwert < $_ZR_ und nur ein Stromkreis abfällt
    'zz=s' => \
      my $_ZZ_
    , # Ignoriere, wenn vorangegangender Zahlenwert < $_ZZ_ und beide Einzelstromkreise abfallen
    'zr=s' => \
      my $_ZR_
    , # Ignoriere, wenn vorangegangender Zahlenwert < als Faktor $_RS_ auf dem anderen Feed ansteigt
    't=s' => \my $_TYPE_,    # Sicherungstyp (Ampere-Absicherung)
    'w=s' => \my $_WARN_,    # Warning-Alarm
    'c=s' => \my $_CRIT_,    # Critical-Alarm

    # Text und Darstellungszusätze
    'dim=s'  => \my $_DIM_,                 # Dimension des Datenwertes
    'desc=s' => \my $_DESC_,                # Beschreibung/Name des Datenwertes
    'enum=s' => \( my $_ENUM_ = "1:1" ),    # Nummerierung von 'desc'
    'info=s' => \my $_INFO_,                # Informationen zum Datenwert/Datensatz

    'path=s' => \
      my $_PATH_,    # Stammverzeichnis für Messwerte, Alarme und Konfiguration

    #    'noprefix' => sub { $_PREFIX_  = _NO_;}
);

# Zuordnung der Angaben und setzen der Standardwerte
my $hostlist = $H_LIST || "# ( Option -h  ): Unbekannte Hosts.\n";
my $unitlist = $U_LIST || "1,2";
my $port     = $_PORT_ || _MB_DEFAULT_PORT_;
my $count    = $_CNT_  || 42;
my $path     = $_PATH_ || "/etc/nagios3";
my $desc     = $_DESC_ || "";
my $info     = $_INFO_ || "";
my $dim      = $_DIM_  || "A";

my @hosts;
push( @hosts, $VIRT_HOST, split( /,/, $hostlist ) );

my @units = split( /,/, $unitlist );

my @addr;
my @type;

$addr[DATA] = $DATA_ADDR
  || 1336;    # Standard-Register für Stromwerte in den BCPMs
$type[DATA] = $DATA_TYPE
  || "int";    # Standard-Datentyp für Stromwerte in den BCPMs
$addr[FACT] = $FACT_ADDR
  || 1000;    # Standard-Register zur Normalisierung von Stromwerte in den BCPMs
$type[FACT] = $FACT_TYPE
  || "int";    # Standard-Datentyp zur Normalisierung von Stromwerte in den BCPM

my @THRESHOLD;
$THRESHOLD[_ZERO_ONES_] = $_ZO_
  || 0.25
  ;    # Vorangehender Wert in Ampere, wenn ein Einzelstromkreis auf 0 abfällt
$THRESHOLD[_ZERO_ZERO_] = $_ZZ_
  || 0.25
  ;  # Vorangehender Wert in Ampere, wenn beide Einzelstromkreise auf 0 abfallen
$THRESHOLD[_ZERO_RISE_] = $_ZR_
  || 0.3
  ; # Prozentangabe für einen Stromkreis, der auf einem anderen Feed ansteigen muss, wenn der komplementäre auf 0 abfällt.
$THRESHOLD[_SIZE_TYPE_] = $_TYPE_ || 16; # Standard-Stromkreisabsicherung = 16 A
$THRESHOLD[_OVERLOAD_W_] = $_WARN_
  || 0.95;    # Prozentangabe aller Feeds für Warning Overload
$THRESHOLD[_OVERLOAD_C_] = $_CRIT_
  || 0.994;      # Prozentangabe alles Feeds für Critical Overload

my $message;

$message .= " $hostlist"
  if ( $hostlist =~ /^\#/ );

# Initiierung von Enumerationen
my ( $start, $step );
if ( defined $_ENUM_ ) {
    ( $start, $step ) = split( ":", $_ENUM_ );
    $message .= "# ( Option -enum ): Ungueltiges Argument!\n"
      if ( $start !~ /[\d]+/ or $step !~ /^[rl]?[1-9]+/ );
}

if ( defined $message and $message ne "" ) {
    print_help();
    print_error("$message");
}

####======================================
#### Datenabfrage (und Normalisierung)
####======================================

# Datenwerte der Einzeltromkreise
my @VAL;

foreach my $_hid_ ( keys @hosts ) {
    next if $_hid_ eq 0;

    foreach my $unit (@units) {
        my $connect;
        $connect->{'proto'} = "tcp";
        $connect->{'host'}  = $hosts[$_hid_];
        $connect->{'slave'} = $unit;
        $connect->{'port'}  = $port;

        my $access;
        $access->{'dataaddr'} = $addr[FACT];
        $access->{'datatype'} = $type[FACT];
        $access->{'count'}    = $count;

        my @FACT = modbus(

            $connect,
            _MB_RHR_,
            $access

        ) or exit _UNKN_;

        $access->{'dataaddr'} = $addr[DATA];
        $access->{'datatype'} = $type[DATA];
        $access->{'count'}    = $count;

        my @DATA = modbus(

            $connect,
            _MB_RHR_,
            $access

        ) or exit _UNKN_;

        push( @{ $VAL[$_hid_][NEW] }, pairwise { $a * (10)**$b } @DATA, @FACT );

    }
}

####======================================
####  Datenverarbeitung/Zuordnung
####======================================

# Summenwerte der Verteiler und Phasen
my @SUM;

### Summen-Sub-Array mit 0 Initialisieren
# Summenwerte der Einzelstromkreise (Pärchen)
@{ $VAL[0][NEW] } = (0) x ( scalar( @{ $VAL[1][NEW] } ) );

# Summenwerte der Verteiler (Feeds und Phasen)
foreach ( 0 .. 2 ) {
    @{ $SUM[$_] } = (0) x (4);
}

my @file;

foreach my $_hid_ ( keys @hosts )    # geht die Hosts durch
{
    $file[$_hid_][VALUE] = "$path/value/$hosts[$_hid_]";
    $file[$_hid_][ALARM] = "$path/alarm/$hosts[$_hid_]";
    $file[$_hid_][CONFI] = "$path/config/$hosts[$_hid_]";

    next if ( $_hid_ eq 0 );

    # addiert die Pärchen der Einzelabgängen
    # F1/A + F1/B  |  F2/A + F2/B  |  F3/A + F3/B  | ...
    @{ $VAL[0][NEW] } = pairwise { $a + $b } @{ $VAL[0][NEW] },
      @{ $VAL[$_hid_][NEW] };

    foreach ( 0 .. $count - 1 ) {

        # addiert alle Einzelabgänge eines Feeds
        # F1/A + F2/A + F3/A + Fn/A | F1/B + F2/B + F3/B + Fn/B | ...
        ${ $SUM[$_hid_] }[0] += ${ $VAL[$_hid_][NEW] }[$_];

# addiert alle Einzelabgänge einer Feeds in einzelne Phasen
# F1/A + F4/A + F7/A + ... | F2/A + F5/A + F8/A + ... | F3/A + F6/A + F9/A + ...
# F1/B + F4/B + F7/B + ... | F2/B + F5/B + F8/B + ... | F3/B + F6/B + F9/B + ...
        ${ $SUM[$_hid_] }[ ( $_ % 3 ) + 1 ] += ${ $VAL[$_hid_][NEW] }[$_];
    }
}
foreach ( 0 .. $count - 1 ) {

    # addiert A- und B-Feed komplett
    # L1/A + L2/A + L3/A  +  L1/B + L2/B + L3/B
    ${ $SUM[0] }[0] += ${ $VAL[0][NEW] }[$_];

    # addiert A- und B-Feed in einzelne Phasen
    # L1/A + L1/B  |  L2/A + L2/B  |  L3/A + L3/B
    ${ $SUM[0] }[ ( $_ % 3 ) + 1 ] += ${ $VAL[0][NEW] }[$_];
}

foreach my $_hid_ ( keys @VAL ) {
    foreach my $_id_ ( keys @{ $VAL[$_hid_][NEW] } ) {
        my $tmp = $step;
        if ( $step !~ /[rl]/ ) {
            $tmp = $start + ( $_id_ * $tmp );
        }
        elsif ( $step =~ /[r]/ ) {
            $tmp =~ s/[r]//;
            $tmp = $start + ( $_id_ / $tmp );
        }
        elsif ( $step =~ /[l]/ ) {
            $tmp =~ s/[l]//;
            $tmp = $start + ( $_id_ % $tmp );
        }
        ${ $VAL[$_hid_][DESC] }[$_id_] = "$desc$tmp";

        ${ $VAL[$_hid_][INFO] }[$_id_] = $info;
        ${ $VAL[$_hid_][DIM] }[$_id_]  = $dim;

    }
}

####======================================
#### Dateiverarbeitung
####======================================

foreach my $_hid_ ( keys @file ) {
    if ( -e ${ $file[$_hid_] }[CONFI] ) {
        open( my $IN, '<', "${$file[$_hid_]}[CONFI]" )
          or die
          "   Konnte '${$file[$_hid_]}[CONFI]' nicht zum Lesen öffnen.\n   Zugriffsrechte der Datei überprüfen!\n";

        my $line_id = 0;

        while (<$IN>) {
            my $line = $_;
            chomp $line;

            ### Überwachung aktiv?
            if ( $line =~ /^#/ ) {
                push( @{ $VAL[$_hid_][ONOFF] }, "off" );
                $line =~ s/^#[ ]*//g;
            }
            else {
                push( @{ $VAL[$_hid_][ONOFF] }, "on" );
            }

            # Erst einmal die Standard-Werte übertragen
            ${ $VAL[0][DIM] }[$line_id] = $dim;
            ${ $VAL[$_hid_][DIM] }[$line_id] = $dim;
            ${ $VAL[$_hid_][SIZE] }[$line_id] = $THRESHOLD[_SIZE_TYPE_];
            ${ $VAL[$_hid_][WARN] }[$line_id] =
              $THRESHOLD[_SIZE_TYPE_] * $THRESHOLD[_OVERLOAD_W_];
            ${ $VAL[$_hid_][CRIT] }[$line_id] =
              $THRESHOLD[_SIZE_TYPE_] * $THRESHOLD[_OVERLOAD_C_];

            ### Optionen und Argumente parsen
            my (@optargs) = split( / +/, $line );
            foreach my $optarg (@optargs) {
                my ( $opt, $arg ) = split( /=/, $optarg );

                if ( defined $opt and defined $arg ) {
                    $arg =~ s/_/ /g;
                    ${ $VAL[$_hid_][DIM] }[$line_id] = $arg
                      if ( $opt eq "dim" );    # Messtyp
                    ${ $VAL[$_hid_][DESC] }[$line_id] = $arg
                      if ( $opt eq "desc" );    # Beschreibung
                    ${ $VAL[$_hid_][INFO] }[$line_id] = $arg
                      if ( $opt eq "info" );    # Informationen

                    if ( $opt eq "t" )          # Sicherungs-Typ
                    {

                    # Wenn sich der Sicherungstyp ändert, dann auch W&C ändern
                        ${ $VAL[$_hid_][SIZE] }[$line_id] = $arg;
                        ${ $VAL[$_hid_][WARN] }[$line_id] =
                          $arg * $THRESHOLD[_OVERLOAD_W_];
                        ${ $VAL[$_hid_][CRIT] }[$line_id] =
                          $arg * $THRESHOLD[_OVERLOAD_C_];
                    }

# Wenn über die Option zusätzlich W&C angegeben wurde, dann den Wert erneut überschreiben
                    ${ $VAL[$_hid_][WARN] }[$line_id] = $arg
                      if ( $opt eq "w" );    # Warning-Alarm wenn neuer Wert > X
                    ${ $VAL[$_hid_][CRIT] }[$line_id] = $arg
                      if ( $opt eq "c" );   # Critical-Alarm wenn neuer Wert > X
                }
            }
            $line_id++;
        }
    }
}

my @falling_down;

foreach my $_hid_ ( keys @file ) {
    if ( $_hid_ and -e ${ $file[$_hid_] }[ALARM] ) {
        open( my $IN, '<', "${$file[$_hid_]}[ALARM]" )
          or die
          "   Konnte '${$file[$_hid_]}[ALARM]' nicht zum Lesen öffnen.\n   Zugriffsrechte der Datei überprüfen!\n";

        while (<$IN>) {
            my $line = $_;
            chomp $line;

            push( @{ $falling_down[$_hid_] }, $line );
        }
    }
}

foreach my $_hid_ ( keys @file ) {

    if ( -e ${ $file[$_hid_] }[VALUE] ) {
        open( my $IN, '<', "${$file[$_hid_]}[VALUE]" )
          or die
          "   Konnte '${$file[$_hid_]}[VALUE]' nicht zum Lesen öffnen.\n   Zugriffsrechte der Datei überprüfen!\n";

        while (<$IN>) {
            my $line = $_;
            chomp $line;

            push( @{ $VAL[$_hid_][OLD] }, $line );

        }
        close($IN);
    }

    open( my $OUT, '>', "${$file[$_hid_]}[VALUE]" )
      or die
      "   Konnte '${$file[$_hid_]}[VALUE]' nicht anlegen.\n    Zugriffsrechte des Verzeichnisses überprüfen!\n";

    foreach my $_val_ ( @{ $VAL[$_hid_][NEW] } ) {
        print $OUT "$_val_\n";
    }
    close($OUT);
}

####======================================
#### Datenauswertung
####======================================

## ToDo:
## Dieses Checkscript wurde so geschrieben, dass man mehrere Hosts
## und somit mehrere Messstellen als Cluster abrufen kann.
## Die folgende Auswertung jedoch beziehen sich nur auf 2
## korrespondierenden Hosts. Bei bedarf einer Auswertung für
## mehr als 2 Hosts im Cluster ist hier die Auswertung zu
## überarbeiten, nicht jedoch das ganze Script.

my $exit = _OKAY_;

foreach my $_id_ ( keys @{ $VAL[0][OLD] } ) {
    my $tmp;
    foreach my $_hid_ ( keys @VAL ) {
        ${ $VAL[$_hid_][ERROR] }[$_id_] = 0;
        $tmp->{ ${ $VAL[$_hid_][INFO] }[$_id_] } = $_hid_ - 1 if ($_hid_);
    }

    my @sort;
    my $cnt = 0;
    foreach ( sort keys $tmp ) {
        $sort[ $cnt++ ] = $_;
    }
    ${ $VAL[0][INFO] }[$_id_] = join( ' / ', @sort );

    # Überprüft, ob eine Sicherung gefallen ist
    if (    ${ $VAL[0][OLD] }[$_id_] > $THRESHOLD[_ZERO_ZERO_]
        and ${ $VAL[1][NEW] }[$_id_] eq 0
        and ${ $VAL[2][NEW] }[$_id_] eq 0 )
    {

#        print
#"if (${$VAL[0][OLD]}[$_id_] > $THRESHOLD[_ZERO_ZERO_] and ${$VAL[1][NEW]}[$_id_] eq 0 and ${$VAL[2][NEW]}[$_id_])";
        if (    ${ $VAL[1][ONOFF] }[$_id_] eq "on"
            and ${ $VAL[1][NEW] }[$_id_] eq 0
            and ${ $VAL[1][OLD] }[$_id_] gt $THRESHOLD[_ZERO_ONES_] )
        {
            ${ $VAL[1][ERROR] }[$_id_] = 1;
            push( @{ $falling_down[1] }, ${ $VAL[1][DESC] }[$_id_] );
        }
        if (    ${ $VAL[2][ONOFF] }[$_id_] eq "on"
            and ${ $VAL[2][NEW] }[$_id_] eq 0
            and ${ $VAL[2][OLD] }[$_id_] gt $THRESHOLD[_ZERO_ONES_] )
        {
            ${ $VAL[2][ERROR] }[$_id_] = 1;
            push( @{ $falling_down[2] }, ${ $VAL[2][DESC] }[$_id_] );
        }
    }
    elsif (
        ${ $VAL[0][OLD] }[$_id_] > $THRESHOLD[_ZERO_ONES_]
        and (
            ( ${ $VAL[1][NEW] }[$_id_] eq 0 and ${ $VAL[1][OLD] }[$_id_] gt 0 )
            and (
                ${ $VAL[2][NEW] }[$_id_] > (
                    ${ $VAL[2][OLD] }[$_id_] +
                      $THRESHOLD[_ZERO_RISE_] * ${ $VAL[1][OLD] }[$_id_]
                )
            )
        )
      )
    {

#        print
#"elsif (${$VAL[0][OLD]}[$_id_] > $THRESHOLD[_ZERO_ONES_] and ( ( ${$VAL[1][NEW]}[$_id_] eq 0 and ${$VAL[1][OLD]}[$_id_] gt 0) and ( ${$VAL[2][NEW]}[$_id_] > (${$VAL[2][OLD]}[$_id_] + $THRESHOLD[_ZERO_RISE_] * ${$VAL[1][OLD]}[$_id_] )) ) )";
        if ( ${ $VAL[1][ONOFF] }[$_id_] eq "on" ) {
            ${ $VAL[1][ERROR] }[$_id_] = 1;
            push( @{ $falling_down[1] }, ${ $VAL[1][DESC] }[$_id_] );
        }
    }
    elsif (
        ${ $VAL[0][OLD] }[$_id_] > $THRESHOLD[_ZERO_ONES_]
        and (
            ( ${ $VAL[2][NEW] }[$_id_] eq 0 and ${ $VAL[2][OLD] }[$_id_] gt 0 )
            and (
                ${ $VAL[1][NEW] }[$_id_] > (
                    ${ $VAL[1][OLD] }[$_id_] +
                      $THRESHOLD[_ZERO_RISE_] * ${ $VAL[2][OLD] }[$_id_]
                )
            )
        )
      )
    {

#        print
#"elsif (${$VAL[0][OLD]}[$_id_] > $THRESHOLD[_ZERO_ONES_] and ( ( ${$VAL[2][NEW]}[$_id_] eq 0 and ${$VAL[2][OLD]}[$_id_] gt 0) and ( ${$VAL[1][NEW]}[$_id_] > (${$VAL[1][OLD]}[$_id_] + $THRESHOLD[_ZERO_RISE_] * ${$VAL[2][OLD]}[$_id_] )) ) )";
        if ( ${ $VAL[2][ONOFF] }[$_id_] eq "on" ) {
            ${ $VAL[2][ERROR] }[$_id_] = 1;
            push( @{ $falling_down[2] }, ${ $VAL[2][DESC] }[$_id_] );
        }
    }

    # Überprüft, ob die redundanz eines Sicherungspärchens gefährdet ist.
    if ( ${ $VAL[0][NEW] }[$_id_] > ${ $VAL[1][CRIT] }[$_id_] ) {
        if (    ${ $VAL[1][ONOFF] }[$_id_] eq "on"
            and ${ $VAL[2][ONOFF] }[$_id_] eq "on" )
        {
            ${ $VAL[0][ERROR] }[$_id_] = 2;
            ${ $VAL[0][NEW] }[$_id_]   = "*** ${$VAL[0][NEW]}[$_id_] ***";
            $exit = _CRIT_;
        }
    }

    elsif ( ${ $VAL[0][NEW] }[$_id_] > ${ $VAL[1][WARN] }[$_id_] ) {
        if (    ${ $VAL[1][ONOFF] }[$_id_] eq "on"
            and ${ $VAL[2][ONOFF] }[$_id_] eq "on" )
        {
            ${ $VAL[0][ERROR] }[$_id_] = 1;
            ${ $VAL[0][NEW] }[$_id_]   = "* ${$VAL[0][NEW]}[$_id_] *";
            $exit = _WARN_ if ( $exit eq _OKAY_ );
        }
    }
}

####======================================
#### Primäre Alarmausgabe
####======================================

if ( $exit eq _OKAY_ ) {
    print "Alle Messwerte innerhalb der Grenzwerte:\n";
}
elsif ( $exit ne _OKAY_ and scalar(@falling_down) > 0 ) {
    $exit = _CRIT_;
    print "Potentieller Sicherungsfall:\n";
    foreach my $_hid_ ( keys @falling_down ) {
        if ( defined( scalar @{ $falling_down[$_hid_] } )
            and ( scalar @{ $falling_down[$_hid_] } ) gt 0 )
        {
            print "$hosts[$_hid_] - "
              . join( ',', @{ $falling_down[$_hid_] } ) . "\n";
            if ( -e ${ $file[$_hid_] }[ALARM] ) {
                open( my $OUT, '>', "${$file[$_hid_]}[ALARM]" )
                  or die
                  "   Konnte '${$file[$_hid_]}[ALARM]' nicht zum Schreiben öffnen.\n   Zugriffsrechte der Datei überprüfen!\n";

                print $OUT join( "\n", @{ $falling_down[$_hid_] } );
            }
        }
    }
}
elsif ( $exit eq _WARN_ and scalar(@falling_down) == 0 ) {
    print "OVERLOAD: Leistungsaufnahme an der gesicherten Lastgrenze!\n";
}
elsif ( $exit eq _CRIT_ and scalar(@falling_down) == 0 ) {
    print "OVERLOAD: Leistungsaufnahme an der physikalischen Lastgrenze!\n";
}

####======================================
#### Sekundäre (Alarm)informationen
####======================================

my @feed = ('TOT');
foreach my $_hid_ ( keys @VAL ) {
    next if ( $_hid_ eq 0 );
    push( @feed, "  " . sprintf( "%c", $_hid_ + 64 ) );
}

print "\n";

my @output = ( 'L', 'L1', 'L2', 'L3' );
foreach my $_feed_ ( keys @SUM ) {
    print "Summe $feed[$_feed_] (L/L1/L2/L3): ";
    print join( ' / ', @{ $SUM[$_feed_] } );
    print " $dim\n";
}

print "\n";

foreach my $_lineid_ ( keys @{ $VAL[0][NEW] } ) {
    print "${$VAL[1][DESC]}[$_lineid_]: ";
    my @tmp;
    foreach my $_hid_ ( keys @VAL ) {
        push( @tmp, ${ $VAL[$_hid_][NEW] }[$_lineid_] );
    }
    print join( ' / ', @tmp )
      . " ${$VAL[0][DIM]}[$_lineid_]"
      . " ${$VAL[0][INFO]}[$_lineid_]\n";
}

exit $exit;

__END__

=encoding utf8

=head1 NAME

    check_bcpm - Auswertung von redundanten Stromkreise

=head1 BESCHREIBUNG

check_bcpm ermittelt Messwerte aus Messgeräte via ModbusTCP deren Messung der Stromversorgung von Serversystemen mit redundanter Stromversorgung gilt und setzt sie in Relation zueinander.

=head1 SYNOPSE

   check_bcpm -H HOST_NAMEs  -VH VIRTUAL_HOST_NAME  [-p PORT] [-dim DIMENSION*]
                             [-u  UNITs]            [-n  COUNT]
                             [-d  DATA_ADDRESS]     [-s  SCALE_ADDRESS]
                             [-dt DATA_TYPE]        [-st SCALE_TYPE]
                             [-t  TYPE]             [-info INFORMATION*]
                             [-w  WARNING]          [-enum ENUMERATION*]
                             [-c  CRITICAL]         [-desc DESCRIPTION*]
                             [-zo ZERO_ONES]        [-path CONFIG]
                             [-zz ZERO_ZERO]
                             [-zr ZERO_RISE]

=head1 OPTIONEN

=over 4

=item B<-H HOST_NAMEs>

Hier werden die Hostnamen angegeben, welche in Relation gesetzt werden sollen.
(Beispiel: -H "modbus-gateway1.example.com,modbus-gateway2.example.com")

=item B<-VH VIRTUAL_HOST>

Hier wird ein logischer Hostnamen angegeben, der als virtuelle Bezeichnung für die in der Option B<-H> angegebene Hosts (Verbund/Cluster) dienen soll.
(Beispiel: -VH "MODBUS-GATEWAY")

=item B<-u UNITs>

Angabe der Slave-Adressen, welche auf allen in B<-H> angegebenen Hosts abgerufen werden sollen.

=item B<-n COUNT>

Die Anzahl der abzurufenden Daten je Unit und je Host.

=item B<-d DATA_ADDRESS>

Hier wird die Datenadresse angegeben, welche auf allen in der Option B<-h> angegebene Hosts abgerufen werden sollen.
(Beispiel: -d 1234)

=item B<-dt DATA_TYPE>

Hier wird der Adresstyp für die in der Option B<-d> angegebe Datenandresse festgelegt. Unterstützt werden:
  * int/uint
  * int32/uint32
  * float
  * quad
(Beispiel: -dt int)

=item B<-s SCALE_ADDRESS>

Hier wird die Adresse angegeben, welche zur Skalierung der Daten aus den Datenadressen verwendet werden sollen.
(Beispiel: -s 5678)

=item B<-st SCALE_TYPE>

Hier wird der Adresstyp für die in der Option B<-d> angegebe Datenandresse festgelegt. Unterstützt werden:
  * int/uint
  * int32/uint32
  * float
  * quad
  * FIX
(Beispiel: -st float)

=item B<-t TYPE>

Mit der Option B<-t> kann die Größe einer Sicherung übergeben werden. Hierbei bilden sich die Warning- und Critical-Grenzwerte wie folgt:

  * Warning  = B<TYPE> * B<WARNING>
  * Critical = B<TYPE> * B<CRITICAL>

=item B<-w WARNING>

Faktor für die Berechnung der Warning-Grenze.

=item B<-c CRITICAL>

Faktor für die Berechnung des Critical-Grenze.

=item B<-dim DIMENSION>

Gibt die Dimension des Messwerts an. Genauer, den Messtyp, um was für eine Art Messung es sich handelt. Strom (A), Spannung (V), Temperatur (dC), etc.

=item B<-info INFORMATION>

Gibt hinter der Dimension eine Zeichenkenne aus.

=item B<-desc DESCRIPTION>

Gibt vor dem Messwert eine Beschreibung aus.

=item B<-desc ENUMERATION>

Gibt an der Beschreibung eine Enumeration aus.

=item B<-zo ZERO_ONES>

Mindestsummenwert aller in Bezug stehenden Sicherungen, bei der ein Alarm erzeugt werden soll, wenn ein Messwert auf "0" abfällt.

=item B<-zz ZERO_ZERO>

Mindestsummenwert aller in Bezug stehenden Sicherungen, bei der ein Alarm erzeugt werden soll, wenn beide Messwerte auf "0" abfallen.

=item B<-zr ZERO_RISE>

Prozentsatz eines Messwertes, der anschließend auf "0" abgefallen ist, dessen Wert auf der komplementären Messung ansteigen muss, damit ein Alarm erzeugt werden soll.

=item B<-path CONFIG>

Pfad zu den Systemdateien. Am Beispiel $CONFIG = /etc/nagios3/bcpm

  Pfad zur Konfigurationsdatei :  /etc/nagios3/bcpm/config
  Pfad zur Messwertdatei       :  /etc/nagios3/bcpm/value
  Pfad zur Alarmdatei          :  /etc/nagios3/bcpm/alarm

Zu jedem Pfad gibt es jeweils für jeden angegebenen Host und dem virtuellen Host eine Datei. Beispiel:

  Konfigurationsdateien:
    /etc/nagios3/bcpm/config/MODBUS-GATEWAY
    /etc/nagios3/bcpm/config/modbus-gateway1.example.com
    /etc/nagios3/bcpm/config/modbus-gateway2.example.com

  Messwertdateien
    /etc/nagios3/bcpm/value/MODBUS-GATEWAY
    /etc/nagios3/bcpm/value/modbus-gateway1.example.com
    /etc/nagios3/bcpm/value/modbus-gateway2.example.com

   Alarmdateien
    /etc/nagios3/bcpm/alarm/MODBUS-GATEWAY
    /etc/nagios3/bcpm/alarm/modbus-gateway1.example.com
    /etc/nagios3/bcpm/alarm/modbus-gateway2.example.com

  
=item B<-h|?>

Zeigt eine Kurzinfo an.

=item B<-help>

Um (nur) diese Hilfe anzeigen zu lassen.

=back

Stefan Steiner (Stoni)
für die noris network AG
