package Health;
#<copyright>

# ----------------------------------------------------------
# Sun Proprietary/Confidential Code
# Copyright 2001, Sun Microsystems, Inc. All rights reserved.
# ----------------------------------------------------------
#</copyright>

#  $Name: bld21_017 $ 
#  $Id: Health.pm,v 1.61 2005/04/26 16:57:32 sbrooks Exp $
#  alarm          => mapValueChangeEvent
#  alarmEvent     => valueChangeEvent
#  DiscoveryEvent => AssetEvent
#
# SEVERITY ORDER:
#    LogEvent              -0.2    # lowest severity
#    LinkEvent             -0.1
#    Others                 0.0    # include ValueChangeEvents .
#    InsertEvent           +0.1
#    StateEvent            +0.2
#    RemoveComponent       +0.3    # highest severity

# MANAGEMENT LEVELS:
#  C = Component (T3 in rack)
#  D = Device    (T3 stand alone)
#  DS= Rack from SP 
#  D = Rack from the outside

#  Customer View (D + DS)
#  Service Email (D + C)


use Report;
use Carp;
use Transition;
use Thresholds;
use CIM::Instance;
use Grid;
use strict;
use Message;
use Modules;
use State;
use TO;
use Events;
use FCRules;
use Health::Catalog;

sub new {
  my($class, $type) = @_;

  if ($type){
	 my $device_type = uc($type);
	 if (!-f System->get_home() . "/lib/Health/$device_type.pm") {
		return new Health::Catalog($type);
	 }
	 my $module = "Health::" . $device_type;
	 my $rc = Modules->loadOne($module);
	 return $module;
  }

  my($x) = {};
  bless($x, "Health");
  return $x;
}
use vars qw ($TO $NOTOPO %DONE);

sub reset_serial {
  %DONE = ();
}

# SERIALIZE an instrumentation report into a string.

sub serial {
  my($hm, $rep, $orep) = @_;
  return "" if ($rep eq $orep);
  my $k = $rep->{"id.device_key"};
  return "" if ($k && $DONE{$k});
  $DONE{$k} = 1;

  my $out = "#INSTRUMENTATION_REPORT\n";

  foreach my $el (sort keys %$rep) {
     next if ($el =~ /fruDiskPerf/ || $el eq "FC_COUNTERS");
     if (!$orep || !exists $orep->{$el}) {
       $out .= " A\t$el\t$rep->{$el}\n";
     } elsif ($orep->{$el} ne $rep->{$el}) {
       $out .= " U\t$el\t$rep->{$el}\n";
     }
  }
  if ($orep && $rep) {
    foreach my $el (keys %$orep) {
       next if ($el =~ /fruDiskPerf/ || $el eq "FC_COUNTERS");
       if ( !exists $rep->{$el}) {
          $out .= " D\t$el\t$orep->{$el}\n";
       }
    }
  }
  return $out;
}

# RETURN "YES" IF IT'S TIME FOR A NEW AUDIT
sub auditTime {
  my($hm, $key) = @_;
  my $audit;
  my $renv = System->get_renv();

  my $freq  = $renv->{audit_freq} || 7;
  my $audit = System->get_audit() ? "YES" : Timer->isXdays("Audit$key", $freq);
  return $audit;
}


# $hm->discoveryEvent($rep, 'NWS::T3', "YES");

sub discoveryEvent {
  my($hm, $report, $CIM, $audit, $arg) = @_;

  my $rep   = $report->content();
  my $cat   = $rep->{'id.device_type'};
  my $noReport = $arg->{noReport};
  my $etype = $audit eq "YES" ? "$cat.AuditEvent" : "$cat.DiscoveryEvent";
  my $dt    = $audit eq "YES" ? "A" : "D";
  my $mgmtLevel =  $report->value('id.mgmtLevel') || 'D';
  my $id    = $report->id('display');

  my $LB    = Labels->read("CommonDesc");
  my ($desc, $descE) = $LB->expand2( $audit eq "YES" ? "audit":"discovery", $cat, $id);

  Grid->setCode($etype);

  my $key= $report->deviceName();
  my $ip = $rep->{'id.device_ipno'};
  my $serial =  $hm->serial($rep) if (!$noReport);

  my $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => $etype  ],
                  [ Target      => "$cat:$key"    ],
                  [ TargetName  => $id     ],
                  [ SourceIP    => $ip     ],
                  [ MgmtLevel   => $mgmtLevel ],
                  [ EventId     => PDM->getEventSequence()  ],
                  [ Description => $descE   ],
                  [ DescArgs    => $desc  ],
                  [ Data        => $serial ],
                         ]);

  my $sd = Events->sourceDetector({ event => $ev , host => !$arg->{noHost},
                                      rep => $rep});
     
  my $CIM_module = $CIM;
  $CIM_module =~ s/\:\:/\//g;
  require "$CIM_module.pm";
  my $p = $CIM->newSystem($rep, $cat);

  my $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $p->[0] ],
                  [ DiscoveryType => $dt],
                    ]);
  my $monitors = CIM::Instance->new('NWS_AgentMonitors', [
                  [ Agent       => $sd->[0]  ],
                  [ Element     => $p->[0] ],
                    ]);

  my $ed = Message->new( {  id     => $report->id(),
                         instances => [$ev, @$sd, @$p, $monitors, $pertains ],
                         severity  => Message::SEVERITY_NORMAL });

  PDM->saveMessage($ed);
  return $ed;
}


sub statisticEvent {
  my($hm, $report) = @_;

}
  

sub locationChangeEvent {
  my($hm, $report, $rep, $orep, $keyval) = @_;

  my($ev, $key, $sd, $pertains, $ed);
  my $renv = System->get_renv();
  return if ($renv->{solution} ne "N");
  my($cat) = $report->category();
  my($name) = $report->id('display');
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';

  my($old) = "Contract:"   . $orep->get("location.contractNo")  . ", " . 
             "HostId:"     . $orep->get('location.hostId')      . ", " .
             "CustomerNo:" . $orep->get("location.customerNo")  . ", " .
             "Site:" .       $orep->get("location.siteName")    . ", " . 
                             $orep->get("location.siteAddress") . ", " .
                             $orep->get("location.siteCity")    . ", " .
                             $orep->get("location.siteState")   . ", " .
                             $orep->get("location.siteZip")     . ", " .
                             $orep->get("location.siteContact") . "," .
                             $orep->get("location.siteEmail") ;

  my($new) = "Contract:"   . $rep->get("location.contractNo")   . ", " . 
             "HostId:"     . $rep->get('location.hostId')       . ", " .
             "CustomerNo:" . $rep->get("location.customerNo")   . ", " . 
             "Site:" .       $rep->get("location.siteName")    . ", " . 
                             $rep->get("location.siteAddress") . ", " .
                             $rep->get("location.siteCity")    . ", " .
                             $rep->get("location.siteState")   . ", " .
                             $rep->get("location.siteZip")     . ", " .
                             $rep->get("location.siteContact") . "," .
                             $rep->get("location.siteEmail") ;

  # MAKE SURE THE SITE INFO WAS ENTERED IN THE FIRST PLACE.
  my $site  = $rep->get("location.siteName");
  my $osite = $orep->get("location.siteName");
  return if (!$site || !$osite);

  if ($old ne $new) {
    my $LB    = Labels->read("CommonDesc");
    my ($desc, $descE) = $LB->expand2( location => "$cat $name") ;
    my ($data, $dataE) = $LB->expand2( location2 => $old, $new);

    Grid->setCode("$cat.LocationChangeEvent");
    $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => lc($cat) . ".LocationChange"  ],
                  [ EventId     => PDM->getEventSequence  ],
                  [ MgmtLevel   => $mgmtLevel ],
                  [ Target      => lc($cat) . ":" . $keyval ],
                  [ TargetName  => $report->id('display')],
                  [ Description => "$descE:"  ], 
                  [ DescArgs    => $desc      ], 
                  [ Component   => "location" ],
                  [ Data        => $dataE     ],
                         ]);
     my($key) = CIM::Key->new( ['NWS_System',
                   'Name'       => $keyval,
                    CreationClassName => 'NWS_System']);

     $sd = Events->sourceDetector({ event => $ev });

     $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $key ],
                    ]);

     my($loc) = NWS->location($rep);

     my($locp) = CIM::Instance->new('NWS_AgentLocation', [
                  [ Agent       => $sd->[0]  ],
                  [ Element     => $loc ],
                    ]);


     $ed = Message->new( { id        => $report->id,
                            instances => [$ev, @$sd, $pertains, $loc, $locp ],
                          });
     PDM->saveMessage($ed);
  }
     
}

# $hm->alarm($report, $orep, "BROCADE", "sensor.temperature.1", "status",
#                             $id, $id, $wwn, "sensor.temperature.status");
#      sensor.temperature.1        : used as a key in the state DB
#      sensor.temperature.1.status : used to read the agent-report
#      sensor.temperature.status   : used as a key in the Avail. Map file.

#  id = display id, use by saveState only
#  wwn= cim key
#  shortid = name
#  map_id = used to assign on topology
#  arg->{info} : extra info
#  $arg->{use_map_id}   : use the map_id for the events.
#  $arg->{severity}
#  $arg->{actionable} = Y/N
#  $arg->{state}  : use $arg->{state} for State

#   display_comp  => "comp2"   # change the component name
#                         The 'statusLabel' of 'display_comp' in id
#   comp_info     => "extra"   # extra component info.
#                         The 'statusLabel' of 'display_comp'(comp_info) in id
#   statusLabel   => "label2"  # change the 'State' word in the description

sub mapValueChangeEvent {
  my($class, @args) = @_;
  return $class->alarm(@args);
}


sub alarm {
  my($class, $report, $orep, $cat, $comp, $el, $shortid,
     $display_id, $cimkey, $map_id, $arg) = @_;

  my $rep          = $report->content();

  my $label = "$comp.$el";

  my $s2 = exists $arg->{old_status} ? $arg->{old_status} :
                   ($orep->{$label} || "[Undefined]");
  my $s1 = exists $arg->{new_status} ? $arg->{new_status} :
                   ($rep->{$label}  || "[Undefined]");

  my $extra    = "($arg->{info})" if ($arg->{info});
  my $el_extra;

  if ($arg->{el_info}) {
    $el_extra = "($arg->{el_info})";

  } elsif ($arg->{comp_info}) {
    $el_extra = "($arg->{comp_info})";
  }

  my $desc_display = $arg->{comp_name} || "$comp.$el";
  if ($display_id) {
     $desc_display = "$display_id.$el";
  }

  my $displayTopic = $arg->{comp_name} || "$comp";

  my $state_comp;

  if ($arg->{state}) {
    $state_comp = $arg->{state};
  } else {
    $state_comp   = ($arg->{use_map_id})? $map_id : $comp;
  }

  my($map) = PDM->getDeviceStateMap(lc($cat) . ".availability");

  $map_id = $el if (!$map_id);
  my($ostatus, $status, $map_sev, $map_action) = $map->transition("$map_id.$s2", "$map_id.$s1", $orep, $rep);
  if ($map_sev == -9) {
    Debug->print2("No event generated: $map_id.$s2 => $map_id.$s1");
    return;
  }

  my ($severity, $sign);

  if ($status eq "X") {
    $sign = "X";
    $severity = 1;
  } elsif ($status == 0) {
    $sign = "M";
    $severity = 2;
  } else {
    $sign = "P";
    $severity = 0;
  }
  $severity = $map_sev if ($map_sev >= 0);

  my $al = 0;
  # NEW TEST
  if ($s1 ne $s2 || ($rep == $orep && $status != 1)) {
     my ($st_label, $name_label) = &fix_label_name($desc_display);

     $name_label = $arg->{display_comp} if ($arg->{display_comp});
     $st_label   = $arg->{statusLabel} if ($arg->{statusLabel});

     my $LB    = Labels->read("CommonDesc");
     my ($desc, $descE);
     if ($s1 ne $s2) {
        ($desc, $descE) = $LB->expand2( valueChange => 
                                        "'$st_label'", 
                                        "'$name_label'$el_extra",
                                        $shortid, "'$s1'",
                                        "'$st_label'", "'$s2'",
                                        $extra );
     } else {
        ($desc, $descE) = $LB->expand2( valueChange2 => 
                                        "'$st_label'", 
                                        "'$name_label'$el_extra",
                                        $shortid, "'$s1'",
                                        $extra );
     }
        
     Grid->setSign($sign);
     my $ev = $class->alarmEvent($comp, "", $report, $cimkey, $desc, $severity, 
                  "$sign.$comp.$el" , 
               { stateComp  => $state_comp, 
                 stateAvail => $status, 
                    descE   => $descE,
                   priorValue => $s2,  currentValue => $s1,
                 actionable => $map_action, 
                      nomap => 1,
                      displayTopic => $displayTopic,
               });

     #State->saveState($cat, $cimkey, $state_comp, $display_id, $severity, $desc, $status, $ev);
  }
}

sub fix_label_name {
  my($desc_display) = @_;
  my @L = split(/\./, $desc_display);
  my($st_label, $name_label);
  if ($#L > 0 && ($L[$#L] =~ /status/i ||  $L[$#L] =~ /state/i) ) {
     $st_label = $L[$#L];
     $name_label = join(".", @L[0..$#L-1]);
  } else {
     $st_label = "State"; $name_label = $desc_display;
  }
  return ($st_label, $name_label);
}

# parent FILTER, can be implemented on each device Class

sub FILTER {
  my($hm, $rep, $ev) = @_;
  return if (!$ev);

  if ($rep->{'info.Disclaimer'}) {        # ADD TO DESCRIPTION, SAVE SEVERITY
    my $desc = $ev->value("Description");
    my $col = substr($desc, -1);
    $desc .= " (" . $rep->{'info.Disclaimer'} . ")";
    $desc .= $col if ($col eq ":");
    $ev->setValue("Description", $desc);

  } elsif ($rep->{'info.noActionable'}) { # ADD TO DESCRIPTION, LOWER SEVERITY
    $ev->setValue("Actionable", "FALSE");
    my $sev = $ev->value("Severity");
    $ev->setValue("Severity", 1) if ($sev >= 2);
    my $desc = $ev->value("Description");
    my $col = substr($desc, -1);
    $desc .= " (" . $rep->{'info.noActionable'} . ")";
    $desc .= $col if ($col eq ":");
    $ev->setValue("Description", $desc);
  }
}

sub diagEvent {
  my($hm, $report, $sev, $caption, $command, $target, $start, $end, $desc) = @_;

  my $rep = $report->content();
  my $cat = $report->category();
  my $code = "$cat.DiagnosticTest";
  $code .= "-" if ($sev);
  Grid->setCode("$code.$caption");

  my $action = $sev > 1 ? 1 : 0;

  my $ev = CIM::Instance->new('NWS_DiagnosticTestComplete', [
                  [ EventType   => "$cat.DiagnosticTest"  ],
                  [ EventId     => PDM->getEventSequence  ],
                  [ Severity    => $sev                   ],
                  [ Caption     => $caption               ],
                  [ FailureCode => 2                      ],
                  [ Target      => "$cat:$target"         ],
                  [ Actionable  => $action                ],
                  [ Severity    => $sev                   ],
                  [ StartDate   => $start                 ],
                  [ EndDate     => $end                   ],
                  [ Component   => "alarm"                ],
                  [ Description => $desc                  ],
                   ]);

  $hm->FILTER($rep, $ev);

  my $sd = Events->sourceDetector({ event => $ev , host => 1, rep => $rep});

  my $pertains = CIM::Instance->new('NWS_EventPertainsToSoftwareElement', [
                  [ Event         => $ev      ],
                  [ Element       => $sd->[0] ],
                  [ DiscoveryType => "D"      ],
                    ]);
  my $ed = Message->new( { id        => {deviceName => $target },
                           instances => [$ev, @$sd, $pertains ],
                           state     => ['alarm', 1],
                           severity  => $sev });
  PDM->saveMessage($ed);

  #State->saveState($cat,  $target, "alarm", "Alarm", $sev, $desc, 1, $ev);
  return $ev;
}


sub identificationEvent {
  my($class, $report, $old, $new, $wwn) = @_;

  return undef if (!$report);
  my $old = lc($old);
  my $new = lc($new);
  my $id = $report->id('display');
  my $LB    = Labels->read("CommonDesc");
  my($desc, $descE) = $LB->expand2( identification => $id, "'$old'", "'$new'");

  if ($new && $old && (substr($old,-4) ne substr($new,-4)) ) {
    my $cat = $report->category();
    Grid->setCode("$cat.ValueChangeEvent.identification");
    $class->alarmEvent("system","", $report, $wwn, $desc, 
      1, "identification", {descE => $descE } );
  }
}



# $arg->{actionable} = Y/N
# by default, warnings(1) are not actionable, errors(2) are.

sub valueChangeEvent {
  my($class, @args) = @_;
  return $class->alarmEvent(@args);
}

sub alarmEvent {
  my($hm, $comp, $data, $report, $keyval, $desc, $sev, $caption, $arg) = @_;

  my($ev, $key, $sd, $pertains, $ed);
  my($cat) = $report->category();
  my $captionIsState = $arg->{captionIsState};
  my($id)  = PDM->getEventSequence ;
  my $FRU  = $arg->{FRU};
  my($rep) = $report->content();

  my($descE);
  if ($arg->{descE}) {
    $descE = $arg->{descE};
  } else {
    $descE = $desc;
    $desc  = undef;
  }

  my $mgmtLevel = $arg->{mgmtLevel} || $report->value('id.mgmtLevel') || 'D';
  $caption = "ValueChangeEvent" if (!$caption);

  my %SEV = (I => 0, W => 1, E => 2, D => 3);
  $sev = $SEV{$sev} if (index("IWED", $sev) >= 0);

  my $av = ['X', -1,undef];
  if (!$arg->{nomap}) {
    my $map = PDM->getDeviceStateMap(lc($cat) . ".availability");
    $av  = $map->get2("changeEvent.$caption");
  }
  if ($av->[1] == -9) {
     Debug->print2("No event generated: $comp. $caption ");
     return undef;
  } elsif ($av->[1] >= 0) {
     $sev = $av->[1];
  } else {
     $sev = 1 if (!defined($sev)); # warning by default
  }

  my $actionable = 0;
  if ($av->[2]) {
     $actionable = $av->[2] eq "Y" ? 1:0;
  } elsif ($arg->{actionable}) {
     $actionable = $arg->{actionable} eq "Y" ? 1:0;
  } elsif ($sev >= 2 ) {
     $actionable = 1;
  }
  my $target = lc($cat) . ":" . $keyval;

  $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => lc($cat) . ".ValueChangeEvent"  ],
                  [ EventId     => $id          ],
                  [ Data        => $data        ],
                  [ Caption     => $caption     ],
                  [ Target      => $target      ],
                  [ TargetName  => $report->id('display') ],
                  [ Actionable  => $actionable  ],
                  [ MgmtLevel   => $mgmtLevel   ],
                  [ SourceIP    => $rep->{'id.ipno'}],
                  [ Severity    => $sev         ],
                  [ PriorValue  => $arg->{priorValue}],
                  [ CurrentValue=> $arg->{currentValue}],
                  [ Component   => $comp        ],
                  [ DisplayTopic => $arg->{displayTopic}],
                  [ Description => $descE       ],
                  [ DescArgs    => $desc        ],
                         ]);
   $hm->FILTER($rep, $ev);

   if ($FRU) { # has fru serial, use Physical Package
     $key = CIM::Key->new( ['CIM_PhysicalPackage',   # key of the disk
                  Tag               => $FRU,
                  CreationClassName => 'CIM_PhysicalPackage']);

     $sd = Events->sourceDetector({ event => $ev });

     $pertains = CIM::Instance->new('NWS_EventPertainsToPhysicalElement', [
                  [Event          => $ev   ],
                  [Element        => $key  ],
               ]);

   } else {
     $key = CIM::Key->new( ['NWS_System',
                         'Name'       => $keyval,
                    CreationClassName => 'NWS_System']);

     $sd = Events->sourceDetector({ event => $ev });

     $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $key ],
                    ]);
   }

   $ed = Message->new( { id        => $report->id,
                         state     => [ 
                   exists($arg->{stateComp})  ? $arg->{stateComp} : 
                             ($captionIsState ? $caption : "alarm.$caption"), 
                   exists($arg->{stateAvail}) ? $arg->{stateAvail} : 1 ],
                   instances => [$ev, @$sd, $pertains ],
                        });
   PDM->saveMessage($ed);

   if (!$arg->{noState}) {
     my $c = $captionIsState ? $caption : "alarm.$caption";
     #State->saveState($cat,  $keyval, $c, "Alarm", $sev, $desc, 1, $ev);
   }
   return $ev;
}

# against any switch/3310

sub portStatEvents {
  my($hm , $report, $PORTS, $rep, $orep, $prefix, $args) = @_;
  my $wwn = $report->id('deviceName');
  my $id  = $report->id('display');
  my $cat = $report->category();
  my $info = $args->{info};
  $prefix = "port" if (!$prefix);

  my($x, $port, @P);
  for ($x=0; $x <= $PORTS; $x++) {
     $port = "$prefix.$x";
     if (exists $rep->{"$port.state"} || exists $rep->{"$port.mode"}) {
       my $state = $rep->get("$port.state") || $rep->get("$port.mode");
       next if (!$state);
       $state = lc($state);
       next if ($state =~ /offline/ || $state =~ /unknown/  || $state =~ /not-logged-in/);
     }
     my $rep1 = $rep->subset("$port.error");
     foreach my $val (keys %$rep1) {
         next if ($val eq "C3Discards");
         next if (!exists $orep->{"$port.error.$val"});
         my($old) = $orep->get("$port.error.$val");
         my($new) = $rep->get("$port.error.$val");
         if ($new > $old) {
           my $val0 = $val;
           my $pre0;
           my $ii = rindex($val, ".");
           if ($ii > 0) {
               $val0 = substr($val,$ii+1);
               $pre0 = substr($val,0,$ii) . " ";
           }
           my($level, $cnt, $desc, $mins) = Thresholds->test($cat, $val0, $x, $new-$old, $wwn);
           if ($level eq "W" || $level eq "E") {
	     my $rmins = sprintf("%.2f", $mins);
             $P[$x] .= "Received $cnt '$pre0$desc' in $rmins mins (value=$new)\n";

           }
         }
     }
  }
  for ($x=0; $x <= $PORTS; $x++) {
    if ($P[$x]) {
      my $data = $P[$x];
      my $did = $hm->getDiskId( $rep, $prefix, $x);
      my $info_t = "(" . $rep->{"$prefix.$did.$info"} . ")" if ($info);

      chop($data);
      if ($data =~ /\n/) {
         my $LB = Labels->read("CommonDesc");
         my($desc, $descE) = $LB->expand2( portStat1 => 
                               $cat, $id, $prefix, $x, $info_t); 

         $hm->alarmEvent("$prefix.$x", $data, $report,  $wwn, $desc, 
            Message::SEVERITY_WARNING, "$prefix.$x.statistics", 
                {captionIsState => 1, nomap => 1, descE => $descE });
      } else { 
         my $LB = Labels->read("CommonDesc");
         my($desc, $descE) = $LB->expand2( portStat2 => 
                               $cat, $id, $prefix, $x, $info_t, $data);

         $hm->alarmEvent("$prefix.$x", "", $report,  $wwn, $desc, 
            Message::SEVERITY_WARNING, "$prefix.$x.statistics", 
                {captionIsState => 1, nomap => 1, descE => $descE });
      }
    }
  }

}

sub getDiskId {
    my ($hm, $rep, $prefix, $x) = @_;
    my $port = "$prefix.$x";
    my $rep1 = $rep->subset("$port.error");
    my $disk;
    foreach my $val (keys %$rep1) {
          if ($val =~ /target/) {
             $disk = $rep->get("$port.error.$val");
             return ($disk);
          }
    }
    if (!$disk) {
        return undef;
    }
}


# {actionable => 1}

sub logEvent {
  my($hm, $log, $report, $keyval, $desc, $sev, $caption, $arg) = @_;

  my($descE, $ev, $key, $sd, $pertains, $ed);
  my($cat) = $report->category();
  my($id)  =  PDM->getEventSequence ;
  my($id2) =  $report->id('display');
  $caption = "MessageLog" if (!$caption);
  my $comp = $arg->{component} || $caption;
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';

  my($rep) = $report->content();
  my($name)= $rep->get('id.name') if ($rep);
  my $LB   = Labels->read("CommonDesc");
  
  if (!$desc) {
    ($desc, $descE) = $LB->expand2(log => "$cat.MessageLog", $id2);
  } else {
    $descE = $desc; $desc = "";
  }
  my $action= 0;
  $action = 1 if (($sev == Message::SEVERITY_ERROR) || $arg->{actionable});
  my $etype = lc($cat) . ".LogEvent" ;
  $etype .= ".$arg->{egrid}" if ($arg->{egrid});


  $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => $etype     ],
                  [ EventId     => $id        ],
                  [ Data        => $log       ],
                  [ Caption     => $caption   ],
                  [ Target      => lc($cat) . ":" . $keyval],
                  [ TargetName  => $report->id('display')],
                  [ Actionable  => $action    ],
                  [ MgmtLevel   => $mgmtLevel ],
                  [ SourceIP    => $rep->{'id.ipno'}],
                  [ Component   => $comp      ],
                  [ Severity    => $sev       ],
                  [ Description => "$descE:"  ] ,
                  [ DescArgs    => $desc      ] ,
                         ]);
   $hm->FILTER($rep, $ev);

   $key = CIM::Key->new( ['NWS_System',
                   'Name'       => $keyval,
                    CreationClassName => 'NWS_System']);
   $sd = Events->sourceDetector({ event => $ev });

   $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $key ],
                    ]);

   $sev -= 0.2 ;                 # logEvent < OtherEvents < StateEvent

   my $rid = $report->id();
   my $id  = {%$rid};
   $id->{priority} = $arg->{priority};
   $ed = Message->new( { id         => $id,
                          state     => [ $comp || "alarm.${cat}Log", 1],
                          severity  => $sev, 
                          instances => [$ev, @$sd, $pertains ],
                        });
   PDM->saveMessage($ed);

   #State->saveState($cat,  $keyval, $comp || "alarm.${cat}Log", "Alarm", $sev, $desc, 1, $ev);

   return $ev;
}


##########################
#     FC-EVENTS
##########################

sub fcEvent {
    my($hm, $rep, $orep) = @_;
    return [] if (!$orep || ($rep eq $orep) || !(exists $rep->{data}) || !(exists $orep->{data}));
    my $processed_list = [];

    my $new = $rep->{data};
    my $old = $orep->{data};
    my $FC = {info =>  { hba => $rep->{hba}, enc => $rep->{enc}} };
    my $cnt2 = 0;
    my $processed_list = [];

    foreach my $k (sort keys %$new) {
       my($hba, $wwn, $comp, $type) = split(/\|/, $k);
       next if (!exists $old->{$k}); # don't compare unless you have both old and new key.

       my @n1 = split(/\t/, $new->{$k});
       my @o1 = split(/\t/, $old->{$k});
       my $o_crc = $o1[3];
       my $n_crc = $n1[3];

       if ($n_crc > $o_crc) {
            next if ($type eq "switch" && ($#o1 > 5) && ($o1[6] == $n1[6]) && ($o1[7] == $n1[7]));
            $FC->{error}[$cnt2] = {key => $k, type => "CRC", count => $n_crc - $o_crc}; $cnt2++;
            Debug->print2("CRC went up by " . ($n_crc - $o_crc) . " in $k");
       }
       my $o_itw = $o1[5];
       my $n_itw = $n1[5];
       if ($n_itw > $o_itw) {
            Debug->print2("ITW went up by " . ($n_itw - $o_itw) . " in $k");
            $FC->{error}[$cnt2] = {key => $k, type => "ITW", count => $n_itw - $o_itw}; $cnt2++;
       }
       my $o_sig = $o1[1];
       my $n_sig = $n1[1];
       if ($n_sig > $o_sig) {
           Debug->print2("SIGNAL-LOSS went up by " . ($n_sig - $o_sig) . " in $k");
            $FC->{error}[$cnt2] = {key => $k, type => "SIG", count =>  $n_sig - $o_sig }; $cnt2++;
       }
    }
    if ($cnt2) {
      if ( Util->findMaster() ) {# slave
        $hm->fcRemoteEvent($FC) ;

      } else {                   # master
        my $broke_list = $hm->processFC($FC);
        foreach my $dev (@$broke_list) {
           $dev->{problems} = 1;
           push(@$processed_list, $dev);
        }
      }
    }
    return $processed_list;
}

sub fcRemoteEvent {
   my($hm, $FC) = @_;

   my($id)    =  PDM->getEventSequence ;
   my $renv = System->get_renv();
   my $tt1 = "san.fcEvent";
   require Data::Dumper;
   $Data::Dumper::Indent = 0;
   my $out = Data::Dumper::Dumper($FC);

   my $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => $tt1 ],
                  [ EventId     => $id  ],
                  [ Actionable  => 1],
                  [ Data        => $out ],
                         ]);

   my $ed = Message->new({ id  => {}, instances => [ $ev ], });

   PDM->saveMessage($ed);
}

sub processFC {
  my($hm, $FC)  = @_;

  return [] if ($NOTOPO);

  $TO = TO->readExistingTopo("MERGE-MASTER") || TO->readExistingTopo();
  if (!$TO) {
    $NOTOPO = 1;
    return [];
  }
  my $Config  = System->get_Config();
  my $rules   = FCRules->load_rules();

  my $report = Report->new({ deviceName  => "san",
                             name        => "san",
                             category    => "san"}  , {});

  my $errors = $FC->{error};
  my($x);
  my $broke_list = [];

  for ($x=0; $x <= $#$errors; $x++) {
     my $v = $errors->[$x];
     foreach my $rule (@$rules) {
        my $problems = PDM->get_last_event();
        my($error, $event) = $rule->run($TO, $hm, $report, $v->{count},
                              $v->{key}, $FC->{info}, [], [], $v->{type});
        Debug->print2($event) if ($event);
        if ($error eq "FC") { # event was generated
           my(@k) = split(/\|/, $v->{key});
           my $dev = $Config->deviceByKey($k[1]);
           if ($dev) {
              my $broke = Agent->new_events($problems, $dev, 1);
              push(@$broke_list, $dev);
           }
        }
     }
   }
   return $broke_list;
}
  

# type1:key1 : origin
# type2:key2 : destination (where the CRC increased)

sub linkEvent {
  my($hm, $report, $type1, $key1, $type2, $key2, $desc, $reads, $writes, $hba,
      $fc_type, $dev_name) = @_;

  my($ev, $key, $sd, $pertains, $ed, $ehba);
  my($id)    =  PDM->getEventSequence ;
  my $renv = System->get_renv();
  my $mgmtLevel = $renv->{solution} =~ /^se/? "C" : "D"; 

  my ($t1, $k1, $p1) = split(/\:/, $key1);
  my ($t2, $k2, $p2) = split(/\:/, $key2);
  my $keyval = $k1;

  my($e_hba, $k1, $host_hba, $f1, $f2, $f3, $hostid, $link1, $link2);
  my(@hba_list, $ix, $node1, $loc);

  Grid->setCode("$t2.LinkEvent_$fc_type");
  my $sev = Message::SEVERITY_ERROR;

  $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => "$t2.LinkEvent_$fc_type" ],
                  [ EventId     => $id                ],
                  [ Target      => $key2              ], 
                  [ TargetName  => $dev_name || $key2 ],
                  [ Caption     => "port.$p2.fc_link" ],
                  [ Actionable  => 1                  ],
                  [ SourceIP    => System->get_ipno() ],
                  [ MgmtLevel   => $mgmtLevel         ],
                  [ Description => $desc              ],
                  [ Data        => "target2=$key1"    ],
                  [ Component   => "port.$p2"         ],
                  [ Severity    => $sev               ],
                         ]);

   $sd = Events->sourceDetector({ event => $ev });

   my $system = CIM::Key->new( ['NWS_System',
                   'Name'             => $keyval,
                    CreationClassName => 'NWS_System']);

   $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $system ],
                    ]);

   $ed = Message->new({ id        => $report->id,
                        state     => ["port.$p2.fc_link", 0, "$type2:$k2"],
                        severity  => $sev - 0.1,
                        instances =>
                        [$ev, @$sd, @hba_list, $pertains ],
                      });

   PDM->saveMessage($ed);

   #State->saveState($type2, $k2, "port.$p2.fc_link", "name", Message::SEVERITY_WARNING, $desc, 0, $ev);

}

sub thresholdEvent {
  my($class, $report, $orep, $cat, $comp, $el, $shortid, $display_id, $cimkey, $arg) = @_;

  my $rep  = $report->content();
  return if (!exists $rep->{"$comp.$el"} || !exists $orep->{"$comp.$el"});

  my $val  = $rep->{"$comp.$el"};
  my $oval = $orep->{"$comp.$el"};
  my $display = $arg->{display_comp} || $comp;
  my $display_el = substr($el,1) if (substr($el,0,1) eq "_");
  

  my($level, $cnt, $desc, $mins) = Thresholds->test($cat, $el, $comp, $val-$oval, $cimkey);
  if ($level) {
     my $LB = Labels->read("CommonDesc");
     my($desc, $descE) = $LB->expand2( threshold => 
                               $display_el, $display,
                               "$cat $display_id",
                               $cnt, $mins );
     my $sev = $level eq "E" ? 2 : 1;
     $class->alarmEvent($comp, "", $report, $cimkey, $desc,
             $sev, "$comp.$el", { descE => $descE, captionIsState => 1, nomap => 1});

  }
  

}
  
sub tempEvent {
  my($class, $report, $orep, $cat, $comp, $el, $shortid,
     $display_id, $cimkey, $high, $low, $arg) = @_;

  my($rep) = $report->content();
  my $temp = $rep->get("$comp.$el");
  return if ($temp !~ /\d+/ || $temp > 500);
  my $comp_info = "($arg->{comp_info})" if ($arg->{comp_info});

  my($tran, $reps) = Transition->getTransition2({      
                             key => $cimkey,
                            code => "temp_$comp",
                           in_value => ($temp > $high),
                           out_value => ($temp < $low),
                                 });
  my $LB = Labels->read("CommonDesc");
  if ($tran =~ /IN/) { #lost  or lost-repeat
     Grid->setSign("M");
     my($desc, $descE) = $LB->expand2(temperature_over =>
                         "$comp.$el$comp_info",
                         "$cat $display_id",
                          $temp, $high);
     $class->alarmEvent($comp, "", $report, $cimkey, $desc,
             2, "$comp.Temp", {descE => $descE, captionIsState => 1, nomap => 1});
 
  }
  if ($tran eq "OUT") { # can connect
     Grid->setSign("P");
     my($desc, $descE) = $LB->expand2(temperature_under =>
                         "$comp.$el$comp_info",
                         "$cat $display_id",
                          $temp, $low);
     $class->alarmEvent($comp, "", $report, $cimkey, $desc,
             0, "$comp.Temp", {descE => $descE, captionIsState => 1, nomap => 1} );
  }
}

# if threshold_severity == 1 , it mean that on the first repeat (the second comm-lost really), 
# you get an error.

sub set_comm_lost_severity {
  my($hm, $args, $in_a_row_counter, $tran) = @_;

  if (($tran eq "IN") || ($tran eq "IN_AGAIN")) 
  {

     $args->{severity} = Message::SEVERITY_WARNING;
     $args->{actionable} = 0;
     return $tran if (! exists $args->{threshold_severity});
     
     if ($in_a_row_counter == $args->{threshold_severity}) {
          $args->{severity} = Message::SEVERITY_ERROR;
          $args->{actionable} = 1;
          $tran = "IN_AGAIN";
     
     }  elsif ($tran eq "IN_AGAIN" && $in_a_row_counter > $args->{threshold_severity}) {
          $args->{severity} = Message::SEVERITY_ERROR;
          $args->{actionable} = 1;
     }
  }
  return $tran;
}
  
# 2=outoband, 1=inband 3=both
# $args->{info} : extra info for the description

sub connectionEvent {
  my($hm, $keyval, $report, $args) = @_;
  my($key, $ev, $sd, $pertains, $ed, $id, $text, $rc);
  my($cat) = $report->category();
  my($rep) = $report->content();
  my $name = $rep->get('id.name') if ($rep);
  my $LB = Labels->read("CommonDesc");
  return 0 if ($main::TESTMODE);

  if ($args->{method} == 3) {  # BOTH
    $text = $LB->{oob};

    my($tran, $reps, $in_a_row_counter) = Transition->getTransition({      
                                          key => $report->deviceName(),
                                         code => 'status',
                                        value => $report->status(),
                             transition_value => Report::STATUS_CANNOT_CONNECT,
                                       repeat => '24h',
                                 });

    $tran = $hm->set_comm_lost_severity($args, $in_a_row_counter, $tran);

    $rc = $hm->connection("e", $keyval, $tran, $report, $text, $reps, $args);

    if (defined($rep->{'info.IB_status'})) {  # T3 only so far.
       my $host = $rep->{'info.IB_host'}; 

       $text = "$LB->{ib}/" . Util->shortHostname($host);

       my($tran, $reps, $in_a_row_counter) = Transition->getTransition( { 
                                          key => $report->deviceName(),
                                         code => 'statusIB',
                                        value => $rep->{'info.IB_status'},
                             transition_value => Report::STATUS_CANNOT_CONNECT,
                                       repeat => '24h',
                                       });

       $tran = $hm->set_comm_lost_severity($args, $in_a_row_counter, $tran);

       $hm->connection("ib", $keyval, $tran, $report, $text, $reps, $args);
    }
    return $rc;

  } else {
    my $ho = System->hostname();
    my ($ei, $code0);
    if ($args->{method} == 1) {
       $text = "$LB->{ib}/$ho";
       $ei = "ib";
       $code0 = "IB";
    } else {
       $text = "$LB->{oob}/$ho";
       $ei = "e";
    }

    my($tran, $reps,  $in_a_row_counter) = Transition->getTransition({      
                           key => $report->deviceName(),
                          code => "status$code0",
                         value => $report->status(),
              transition_value => Report::STATUS_CANNOT_CONNECT,
                        repeat => '24h',
                     });

    $tran = $hm->set_comm_lost_severity($args, $in_a_row_counter, $tran);

    return $hm->connection($ei, $keyval, $tran, $report, $text, $reps, $args);
  }

}

sub connection {
  my($hm, $comp, $keyval, $tran, $report, $text, $reps, $args) = @_;
  my($key, $id, $desc, $descE);
  my($ev, $sd, $pertains, $ed);

  my $cat      = $report->category();
  my $rep      = $report->content();

  my $name     = $rep->get('id.name') if ($rep);

  my $connect_errs = $args->{connect_errs} || $rep->get('id.connect_errs');

  my $mgmtLevel  = $report->value('id.mgmtLevel') || 'D';
  my $egrid_info = $comp eq "ib" ? "ib": "oob";

  my $id2      = $report->id('display');

  $key = CIM::Key->new( ['NWS_System',
                   'Name'             => $keyval,
                    CreationClassName => 'NWS_System']);

  return 1 if ($tran eq "LOST");
  my $comp1 = $comp;
  $comp1 = "$comp." . $rep->{'id.caption'} if ($rep && $rep->{'id.caption'});
  my $LB = Labels->read("CommonDesc");

  if ($tran =~ /IN/) { #lost  or lost-repeat
     return 1 if (!$keyval);  # never had good connection
     return 1 if ($reps > 6);
     $id =  PDM->getEventSequence;
     if ($reps > 0) {
       ($desc, $descE) = $LB->expand2(comm_lost_repeat => $text, 
                                      "$cat $id2",
                                       $args->{info}, 
                                      $reps * 24,
                                      $connect_errs);
     } else {
       ($desc, $descE) = $LB->expand2(comm_lost => $text, 
                                      "$cat $id2",
                                       $args->{info}, 
                                      $connect_errs);
     }
     Grid->setCode(lc($cat) . ".CommunicationLostEvent.$egrid_info");
     my $displayTopic = $text;
     my $ix2 = index($displayTopic, "/");
     $displayTopic = substr($displayTopic, 0, $ix2) if ($ix2 > 0);

     $ev = CIM::Instance->new('NWS_CommunicationLostEvent', [
                  [ EventType   => lc($cat) . ".CommunicationLostEvent"  ],
                  [ EventId     => $id              ],
                  [ Caption     => $comp1           ],
                  [ Component   => $comp            ],
                  [ Description => $descE           ],
                  [ DisplayTopic => $displayTopic   ],
                  [ DescArgs    => $desc            ],
                  [ Target      => lc($cat) . ":" . $keyval],
                  [ TargetName  => $report->id('display')],
                  [ Method      => $args->{method}  ],
                  [ MgmtLevel   => $mgmtLevel       ],
                  [ Actionable  => $args->{actionable} ],
                  [ Severity    => $args->{severity}],
                  [ SourceIP    => $rep->{'id.ipno'}],
                  [ Timeout     => 20],
                         ]);
     $hm->FILTER($rep, $ev);
     $sd = Events->sourceDetector({ event => $ev });

     $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $key ],
                    ]);

     #State->saveState($cat,  $keyval, $comp, $name, Message::SEVERITY_DOWN, "Lost Communication ($text) with $id2", 0, $ev);

     $ed = Message->new( { id        => $report->id,
                          state       => [$comp, 0],
                            instances => [$ev, @$sd, $pertains ],
                          });
     PDM->saveMessage($ed);

     return 1;
  }
  if ($tran eq "OUT") { # can connect
     ($desc, $descE) = $LB->expand2(comm_gained => $text, 
                                      "$cat $id2",
                                       $args->{info});

     $id =  PDM->getEventSequence;
     Grid->setCode(lc($cat) . ".CommunicationEstablishedEvent.$egrid_info");

     $ev = CIM::Instance->new('NWS_CommunicationEstablishedEvent', [
                  [ EventType   => lc($cat) . ".CommunicationEstablishedEvent"  ],
                  [ EventId     => $id                     ],
                  [ Description => $descE                  ],
                  [ DescArgs    => $desc                   ],
                  [ Target      => lc($cat) . ":" . $keyval],
                  [ TargetName  => $report->id('display')  ],
                  [ Caption     => $comp1                  ],
                  [ Component   => $comp                   ],
                  [ SourceIP     => $rep->{'id.ipno'}      ],
                  [ Severity    => Message::SEVERITY_GREEN ],
                  [ Method      => 1                       ],
                         ]);
     $sd = Events->sourceDetector({ event => $ev });


     $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $key ],
                    ]);

     #State->saveState($cat,  $keyval, $comp, $name, Message::SEVERITY_GREEN, "Regained Communication ($text)", 1, $ev);

     $ed = Message->new( { id        => $report->id,
                            state    => [$comp, 1],
                            instances => [$ev, @$sd, $pertains ],
                            });

     PDM->saveMessage($ed);

  }
  return 0;  # keep going
}

#
# returns availability-state of the device (old,new, description) :
#    returns (1/0, 1/0, "Old=..., New=...");

sub status {
   croak("Health::status is abstract, must implement in device Health");
}

#
# arg = {
#   logical       => 1,        # use Logical component CIM class
#   descIsValue   => 1,        # skip 'The 'statusLabel'
#                         'display_comp' in id ...
#   info          => "extra info"
#   statusFunc    => "status2" # different name for the status() function
#   statusLabel   => "label2"  # change the 'State' word in the description
#                         The 'statusLabel' of ...
#   display_comp  => "comp2"   # change the component name
#                         The 'statusLabel' of 'display_comp' in id
#   comp_info     => "extra"   # extra component info.
#                         The 'statusLabel' of 'display_comp'(comp_info) in id
#  };   
#

sub stateChangeEvent {
  my($hm, $component, $status_func, $report,  $rep, $orep, $cim1, $nserial, $keyval, $arg) = @_;
  
  my @L = $hm->$status_func($rep, $orep, $component, $arg->{old_comp});

  $arg->{status_results} = \@L;
  return $hm->stateEvent($component, $report,  $rep, $orep, $cim1, $nserial, $keyval, $arg);
}


sub stateEvent {
  my($hm, $component, $report,  $rep, $orep, $cim1, $nserial, $keyval, $arg) = @_;

  my($key, $ev, $sd, $pertains, $ed , $id);
  my $statusLabel = $arg->{statusLabel} || "State";

  my($ovalue, $nvalue, $old, $new, $map_sev, $map_act, $common_ovalue, $common_nvalue);

  if ($arg->{status_results}) {
     ($ovalue, $nvalue, $old, $new, $map_sev, $map_act) = @{$arg->{status_results}};
  } else {
     ($ovalue, $nvalue, $old, $new, $map_sev, $map_act,
       $common_ovalue, $common_nvalue) = 
                $hm->status($rep, $orep, $component, $arg->{old_comp});
  }
  my $display_ovalue = $common_ovalue || $ovalue;
  my $display_nvalue = $common_nvalue || $nvalue;

  if ($map_sev == -9) {
    Debug->print2("No event generated: $component $ovalue => $nvalue");
    return;
  }

  my $name   = $rep->get('id.name');
  my $ix     = index($component, ".");
  my $extra  = "($arg->{info})" if ($arg->{info});
  my $no_action = $arg->{noAction};
  my $id2    = $report->id('display');
  my $cat    = $report->category();
  my $comp_info = $arg->{comp_info};
  my $catcap = uc($cat);
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';
  my $display_comp = $arg->{display_comp} || $component;

  $nserial = $report->deviceName() if (!$nserial);

  my($comp0) = $component;
  if ($ovalue ne $nvalue || ($rep == $orep && !$new) ) { 

         if ($rep == $orep) {
            $old = 1;
            #$ovalue = "unknown";
         }
         $id =  PDM->getEventSequence;
         my $xx0 = (index($display_comp, $nserial) < 0) ? "(id=$nserial) ":"";
         my $xx  = "($comp_info)" if ($comp_info);
         my($desc, $descE);
         my $LB = Labels->read("CommonDesc");
         if ($arg->{descIsValue}) {
            ($desc, $descE) = $LB->expand2( stateChange0 => "$display_comp${xx}",
                                     "$catcap $id2",
                                     "'$display_nvalue'",
                                      $extra);
         } else {
            if ($rep eq $orep) {
              ($desc, $descE) = $LB->expand2( stateChange => 
                                  "'$statusLabel'", 
                                  "$display_comp${xx}",
                                  "$catcap $id2",
                                  "'$display_nvalue'",
                                  "$xx0$extra");
            } else {
              ($desc, $descE) = $LB->expand2( stateChange2 => 
                                  "'$statusLabel'", 
                                  "$display_comp${xx}",
                                  "$catcap $id2",
                                  "'$display_nvalue'",
                                  "'$statusLabel'", "'$display_ovalue'",
                                  "$xx0$extra");
            }
         }

         if (0 && !$nserial) {
             $arg->{noState} = 1;
             my $sev0 = $map_sev >= 0 ? $map_sev : 2;
             $arg->{nomap} = 1;
             my @clist = split(/\./, Grid->getCode(), 3);
             Grid->setCode("$clist[0].ValueChangeEvent.$clist[2]");
             Grid->setSign($new != 1 ? "M": "P");
             $arg->{stateComp}  = $component; 
             $arg->{stateAvail} = $new;
             my $ev0 = $hm->alarmEvent($component, "", $report, $keyval, $desc, 
                                ($new != 1) ? $sev0 : 0, "$cat.$component", $arg);
             return $ev0;
         }
         $old = 0 if ($old eq "X");
         my $yellow;
         my $sign = "X";
         if ($new eq "X") {
            $new = $old;
            $yellow =1;
         }
         my ($severity, $action);
         if (!$new) { #  bad comp
           $severity = Message::SEVERITY_ERROR;
           $action = 1;
           $sign = "M";
         } elsif ($yellow) {
           $severity = Message::SEVERITY_WARNING;
           $action = 0;
         } else {
           $severity = Message::SEVERITY_GREEN;
           $action = 0;
           $sign = "P";
         }
         $severity = $map_sev if ($map_sev >= 0);
         if ($map_act) {
           $action   = $map_act eq "Y"? 1:0;
         }
         Grid->setSign($sign);
         if ($action && $no_action) {
            $severity = 1; $action = 0;
         }

         $ev = CIM::Instance->new('NWS_StateChangeEvent', [
             [ EventType    => lc($cat) . ".StateChangeEvent"   ],
             [ EventId      => $id                      ],
             [ PriorState   => $old                     ],
             [ CurrentState => $new                     ],
             [ MgmtLevel    => $mgmtLevel               ],
             [ PriorValue   => $ovalue                  ],
             [ CurrentValue => $nvalue                  ],
             [ Actionable   => $action                  ],
             [ Caption      => "$sign.$comp0"           ],
             [ Severity    => $severity                 ],
             [ Description => $descE                    ],
             [ DescArgs    => $desc                     ],
             [ Target      => lc($cat) . ":" . $keyval  ],
             [ TargetName  => $report->id('display')    ],
             [ Component   => $component                ],
             [ DisplayTopic=> $display_comp             ],
             [ SourceIP    => $rep->{'id.ipno'}         ],
             [ Data        => $hm->serial($rep, $orep)  ],
                    ]);
         $hm->FILTER($rep, $ev);

         $sd = Events->sourceDetector({ event => $ev });

         $key = CIM::Key->new( [$cim1,
                  Tag               => $nserial,
                  CreationClassName => $cim1]);

         $pertains = CIM::Instance->new(
                $arg->{logical} ? 'NWS_EventPertainsToLogicalDevice': 
                                  'NWS_EventPertainsToPhysicalElement', [
             [ Event       => $ev  ],
             [ Element     => $key ],
               ]);
         my $sev2 = $severity;
         $sev2 += 0.2; # logEvent < OtherEvents < StateEvent
         $ed = Message->new( { id        => $report->id,
                               severity  => $sev2, 
                               state     => [$component, $new],
                               instances => [$ev, @$sd, $pertains ],
                       });

         PDM->saveMessage($ed);
         #State->saveState($cat,  $keyval, $component, $name, $sev2, $desc, $new, $ev);
         return $ev;
   }
   return undef;

}

# USED FOR Sun SOLUTIONS ONLY
# old/new are severities, ovalue and nvalue are descriptions
# new=1 (warning) new=2 (error) etc..

sub sevStateEvent {
  my($hm, $component, $report,  $rep, $orep, $cim1, $nserial, $keyval, $arg) = @_;
  my($key, $ev, $sd, $pertains, $ed , $id);

  my($ovalue, $nvalue, $old, $new, $oavail, $navail,$ndesc) = $hm->status($rep, $orep, $component);

  my $name   = $rep->get('id.name');
  my $data   = "";
  my $ix     = index($component, ".");
  my $id2    = $report->id('display');
  my $error  = 0;
  my $cat    = $report->category();
  my $catcap = uc($cat);
  my $display_comp = $arg->{display_comp} || $component;
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';
  my $sign   = $navail eq "X" ? "X" : ($navail == 1 ? "P": "M");
  my($comp0) = $component;

  if ($ovalue ne $nvalue || ($rep == $orep && $new) ) { # availability change or new report  and unavailable
         $id =  PDM->getEventSequence;
         my $nvalue2 = $ndesc;
         $nvalue2 = substr($nvalue2,19) if ($nvalue2 =~ /^\d+\-\d\d/);
         my $org  = State->eventHash($rep->{"$component.status-details"});
         my $org_name = $org->{TargetName};
         my($g_info, $g_cause, $g_action, $g_code) = 
                      Grid->getInfoString($org->{EventType}, $org->{Caption});
         my $type = $report->value($comp0.'.dev-type');
         my $et = lc($cat) . ".StateChangeEvent.$type";
         my $ac = $org->{Actionable};
         my $ca = "$sign.$comp0";
         my $co = $component;
         my $se = $new;
         my $ta = lc($cat) . ":" . $keyval;
         my $tan= $report->id('display');
         my $si = $rep->{'id.ipno'};
         #Grid->setCode("$et.$type");
         Grid->setSign($se == 0 ? "P": "M");
         my $nstate= ($se==0) ? "positive" : "negative";
         my $desc = "$arg->{label} $catcap-$id2($rep->{'id.device_ip'}): Component $org_name has encountered a $nstate state change."; 

         $ev = CIM::Instance->new('NWS_StateChangeEvent', [
             [ EventType    => $et     ],
             [ EventId      => $id     ],
             [ PriorState   => $oavail ],
             [ CurrentState => $navail ],
             [ MgmtLevel    => $mgmtLevel ],
             [ PriorValue   => $ovalue ],
             [ CurrentValue => $nvalue ],
             [ Actionable   => $ac     ],
             [ Caption      => $ca     ],
             [ DisplayTopic=> $display_comp ],
             [ Severity     => $se     ],
             [ Description  => $desc   ],
             [ Target       => $ta     ], 
             [ TargetName  =>  $tan    ],
             [ Data         => $data   ],
             [ Component    => $co     ],
             [ SourceIP     => $si     ],
             [ OriginalEvent => $rep->{"$component.status-details"}. 
                                  "|grid_code=$g_code" ],
                    ]);
         $hm->FILTER($rep, $ev);

         $sd = Events->sourceDetector({ event => $ev });

         if ($arg->{logical}) {              # logical (like NWS_Slot,
           $key = CIM::Key->new( [$cim1,    
                  CreationClassName => $cim1, 
                  SystemName        => $rep->{'id.wwn'} . "." . $component,
                  DeviceID          => $nserial 
                  ]);
         } else {
           $key = CIM::Key->new( [$cim1,    # physical (like NWS_PhysicalPackage)
                  Tag               => $nserial,
                  CreationClassName => $cim1
                  ]);
         }

         $pertains = CIM::Instance->new(
                $arg->{logical} ? 'NWS_EventPertainsToLogicalDevice': 
                                  'NWS_EventPertainsToPhysicalElement', [
             [ Event       => $ev  ],
             [ Element     => $key ],
               ]);

         $ed = Message->new( { id => $report->id,
                        severity  => $se + 0.2,
                        state     => [$component, $navail, undef, 1], 
                        instances => [$ev, @$sd, $pertains ], });

         PDM->saveMessage($ed);
         my $renv = System->get_renv();
         return $ev;

   }
   return undef;

}

sub removeCompEvent {
  my($hm, $component, $pf, $report,  $orep, $cim1, $oserial, $keyval, $arg) = @_;
  
  my($key, $ev, $sd, $pertains, $ed, $contain);
  my($cat) = $report->category();
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';
  my $extra_desc = $arg->{desc};
  my $logical = $arg->{logicalDevice};

  my $comp0  = $component;
  my @comps  = split(/\./, $component);

  my $id     =  PDM->getEventSequence;
  my $rep    = $report->content();
  my $name   = $rep->get('id.name') if ($rep);
  my $comp1  = $arg->{display_comp} || $component;
  my $topic  = $comp1;

  my $id2    = $report->id('display');
  my $comp_info = "($arg->{comp_info})" if ($arg->{comp_info});
  my $LB = Labels->read("CommonDesc");
  my($desc, $descE) = $LB->expand2(removeComp => "$comp1$comp_info",
                             "$cat $id2", $oserial, $extra_desc);

  my $sev;
  my $map = PDM->getDeviceStateMap(lc($cat) . ".availability");
  my $av  = $map->get2("ComponentRemoveEvent.$comps[0]");

  if ($av->[1] == -9) {
     Debug->print2("No event generated: ComponentRemove.$component");
     return undef;
  } elsif ($av->[1] >= 0) {
     $sev = $av->[1];
  } else {
     # Default to warning if the map transition is not specified.
     $sev = 1;
  }
  my $act = 1 if ($sev >= 2);

  $ev = CIM::Instance->new('NWS_Event', [
             [ EventType    => lc($cat) . ".ComponentRemoveEvent"  ],
             [ Caption      => "remove.$comp0"],
             [ Description  => $descE     ],
             [ DescArgs     => $desc      ],
             [ MgmtLevel    => $mgmtLevel ],
             [ PriorValue   => $oserial   ],
             [ Actionable   => $act       ],
             [ Severity     => $sev       ],
             [ SourceIP     => $rep->{'id.ipno'}],
             [ Target       => lc($cat) . ":" . $keyval],
	     [ Component    => $component],
             [ DisplayTopic => $topic    ],
             [ TargetName  => $report->id('display')],
             [ EventId     => $id ],
              ]);
   $hm->FILTER($rep, $ev);

   $sd = Events->sourceDetector({ event => $ev });

   my @pert;
   if ($logical) {
      $key = CIM::Key->new( [$cim1,   # key of the disk
                  SystemName     => $oserial,
                  CreationClassName => $cim1,
                  DeviceID       => $oserial]);

      $pertains = CIM::Instance->new('NWS_EventPertainsToLogicalDevice', [
             [ Event       => $ev  ],
             [ Element     => $key ],
               ]);
     push(@pert, $pertains);

   } else {
     $key = CIM::Key->new( [$cim1,   # key of the disk
                  Tag               => $oserial,
                  CreationClassName => $cim1]);

     $contain = CIM::Instance->new( 'CIM_Container', [
                  [GroupComponent => $pf],            # frame is a package
                  [PartComponent  => $key],           # package is an element
                  [LocationWithinContainer => $comp1 ],
                  [DiscoveryType  => "D"   ],   # delete
                   ]);

     $pertains = CIM::Instance->new('NWS_EventPertainsToPhysicalElement', [
                  [Event          => $ev   ],
                  [Element        => $key  ],
               ]);
     push(@pert, $contain, $pertains);
   }

   $sev += 0.3;
   #State->saveState($cat,  $keyval, $component, $name, $sev, $desc, 0, $ev);

   $ed = Message->new( { id        => $report->id,
                       instances => [$ev, @$sd, @pert ],
                       state     => [$component, 0 ],
                       severity  => $sev});

   PDM->saveMessage($ed);
   return $ed;
}

sub insertCompEvent {
  my($hm, $component, $pf, $report, $orep, $part, $nserial, $keyval, $arg) = @_;
  my($key, $ev, $sd, $pertains, $ed, $contain, $pertains2);

  my($cat) = $report->category();
  my $extra_desc = $arg->{desc};
  my $logical = $arg->{logicalDevice};
  my $comp_info = "($arg->{comp_info})" if ($arg->{comp_info});

  my $ix        = index($component, ".");
  my $comp0     = $component;
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';

  my $rep  = $report->content();
  my $name = $rep->get('id.name') if ($rep);

  my $id  =  PDM->getEventSequence;
  my $id2 = $report->id('display');

  my $comp1 = $arg->{display_comp} || $component;
  my $topic = $comp1;

  my $fru = $part->[0];
  my $fru_status;
  my $SEV = Message::SEVERITY_NORMAL;
  my $LB = Labels->read("CommonDesc");
  my($desc, $descE);

  if ($fru) {
     my $statusInfo = $fru->value("StatusInfo");
     my $status     = $fru->value("Status");
     my $volStatus  = $fru->value("VolStatus");

     my $displayStatus = $statusInfo || $status || $volStatus;
     $fru_status ="current status is '$displayStatus'";
     ($desc, $descE) = $LB->expand2(insertComp2 => "$comp1$comp_info",
                               "$cat $id2",
                               "'$displayStatus'",
                               $nserial, $extra_desc);
  } else {
     ($desc, $descE) = $LB->expand2(insertComp => "$comp1$comp_info",
                               "$cat $id2",
                               $nserial, $extra_desc);
  }

  $ev = CIM::Instance->new('NWS_Event', [  
             [ EventType    => lc($cat) . ".ComponentInsertEvent"  ],
             [ Caption      => "add.$comp0" ],
             [ DisplayTopic => $topic       ],
             [ Description  => $descE       ],
             [ DescArgs     => $desc        ],
             [ MgmtLevel    => $mgmtLevel   ],
             [ CurrentValue => $nserial     ],
             [ Target       => lc($cat) . ":" . $keyval],
             [ TargetName   => $report->id('display')],
             [ Severity     => $SEV        ],
             [ Component    => $component  ],
             [ SourceIP     => $rep->{'id.ipno'}],
             [ EventId      => $id         ]]);

  $sd = Events->sourceDetector({ event => $ev });
  my @pert;
  if ($logical) {
     $pertains = CIM::Instance->new('NWS_EventPertainsToLogicalDevice', [
             [ Event       => $ev  ],
             [ Element     => $part->[0]],
               ]);
     push(@pert, $pertains);
  } else {
    $pertains = CIM::Instance->new('NWS_EventPertainsToPhysicalElement', [
             [ Event       => $ev  ],
             [ Element     => $part->[1]],
               ]);

    $contain = CIM::Instance->new('CIM_Container', [
              [GroupComponent           => $pf],         #PhysicalFrame
              [PartComponent            => $part->[1]],
              [LocationWithinContainer  => $comp1],
              [DiscoveryType            => "A"       ],
               ]);

     push(@pert, $pertains, $contain);
  }

  #State->saveState($cat,  $keyval, $component, $name, Message::SEVERITY_NORMAL, $desc, 1, $ev);

  $SEV+= 0.1;

  $ed = Message->new( { id        => $report->id,
                        state    => [$component, 1],
                       instances => [$ev, @$sd, @$part, @pert],
                       severity  => $SEV } );

  PDM->saveMessage($ed);
}

#
# INSERT/DELETE/UPDATE Map
# ($insert, $delete, $old) = Health->ido_map($rep, $orep, "fru", "_name");
# create 3 hashes with the inserted, deleted and changed frus 
# using the _name attribute to match the frus correcly from new to old reports.
#
# INSERT and DELETE are { _name => index ...   } for entries added or deleted.
# UPDATE is a hash of { new_index => old_index } for entries present in both reports but
#                                                that may have moved.
#
sub idu_map {
  my($class, $rep, $orep, $section, $tag) = @_;
  my (%INSERT, %DELETE, %UPDATE);
  my(%MAP, %OMAP);

  foreach my $e (keys %$rep) {
    my @L = split(/\./, $e);
    if (index(",$section,", ",$L[0],") >= 0 && $L[2] eq $tag) {
      $MAP{$rep->{$e}} = "$L[0].$L[1]";  # $MAP{serial} = disk.1
    }
  }
  foreach my $e (keys %$orep) {
    my @L = split(/\./, $e);
    if (index(",$section,", ",$L[0],") >= 0 && $L[2] eq $tag) {
      $OMAP{$orep->{$e}} = "$L[0].$L[1]";
    }
  }
  foreach my $el (keys %MAP) {
    if (!exists $OMAP{$el}) {
       $INSERT{$el} = $MAP{$el};  # INSERT{serial} = disk.1
    } else {
       $UPDATE{$MAP{$el}} = $OMAP{$el};
    }
  }
  foreach my $el (keys %OMAP) {
    if (!exists $MAP{$el}) {
       $DELETE{$el} = $OMAP{$el};
    }
  }

  return(\%INSERT, \%DELETE, \%UPDATE);
}


1;

