#!/usr/bin/perl
use strict;            #enforce declarations and quoting
use diagnostics;
use CGI qw(:standard);
use Time::localtime;
use File::Find;

#*******************************************************************
# SETI Parser by steffen merunka
#   parse process node data files and collect process information
#   in a single flat text file,
#   to be used by cgi setimon.pl for display of SETI statistics
#
# Patched by Markus Drechsler, Bavaria, Germany
#   Minor revisions:
#     2001-03-24: v1.0.1 - some bugfixes on generating nodes data
#*******************************************************************

# declare misc.variables
my(
   $base_dir,               #seti base directory
   $target_dir,             #target directory for nodes data file
   $nodes_file_name,        #name of flat text file for nodes data
   @nodes_data,             #list of all SETI processes
   @old_nodes_data,   
   %tmp_node,               #tmp node
   $tmp_node_name,   
   $rtmp_node,              #tmp reference to a node
   $user_file_name,         #user info
   $state_file_name,        #state info
   $TMPFILE,                #filehandle for config files
   @tmplist,
   $tmpstr,
   $total_work_processes,   #total number of workprocesses in target tree
   $total_work_units,       #total number of work units received from SETI
   $total_results,          #total number of results sent back to SETI
   $total_cpu_time,         #total CPU time as reported to SETI
   
   $SETI_DATA_FILE,         #output of collected data into flat text file
   $sDataUpdated            #time of last update
  );

#$base_dir="./seti";
$base_dir="/opt/pub/seti";
$target_dir=".";
$nodes_file_name = "$target_dir/nodesdata.txt";
$user_file_name  = "user_info.sah"; # extension changed with seti v2.0
$state_file_name = "state.sah";     # extension changed with seti v2.0

$total_work_processes=0;
$total_work_units=0;
$total_results=0;
$total_cpu_time=0;

@old_nodes_data = "";

#browse all files in directory tree and build a list of all relevant files
find (\&wanted, "$base_dir");

sub wanted {
  my $name = $File::Find::name;
  #                                machine   cpu  file
  if ($name =~ m|/seti/(.+)/seti/(.+)/($user_file_name)|) {
    #found user info, update global values (totals)
    $total_work_processes++;
    
    open (TMPFILE, $3)
      or die ("cannot open $3: S!");
    #read the whole file at once, and extract user info values
    join("", <TMPFILE>) =~ 
      /name=(.+)\surl.*nwus=(\d+)\snresults=(\d+)\stotal_cpu=(\d+)/s;
    #set total values to bigger value
    if ($2 > $total_work_units) {
      $total_work_units=$2;
    }
    ;
    if ($3 > $total_results) {
      $total_results=$3;
    }
    ;
    if ($4 > $total_cpu_time) {
      $total_cpu_time=$4;
    }
    ;
    close (TMPFILE);
  } 
  elsif ($name =~ m|/seti/(.+)/seti/(.+)/($state_file_name)|) {
    $tmp_node_name = "$1-$2";
    
    #found process state info, extract current values
    open (TMPFILE, $3)
      or die ("cannot open $3: S!");
    #read the whole file at once, and extract process info values
    $tmpstr = join("", <TMPFILE>);
    $tmpstr =~ /fl=(\d+).*
      cpu=(\d+\.\d+).*
    prog=(\d+\.\d+).*
      bs_power=(\d+\.\d+).*
        bs_score=(\d+\.\d+)/sx;
    
    # create anonymous hash for node values 
    # and store a reference in the global nodes array
    push (@nodes_data, { 
            "node" => $tmp_node_name,
            "fl" => $1,
            "cpu" => $2,
            "prog" => $3,
            "peak" => $4
                       }
     );
    close (TMPFILE);
  }                #if
}                #sub wanted

# read old nodes data, (if file exists)
# replace NODE: line prefix by OLD:
# then store old data in front of new data...
$tmpstr = "";
if (open(SETI_DATA_FILE, "<$nodes_file_name")) {
  #read line by line, and extract process info values
  while (<SETI_DATA_FILE>) {
    #keep only node entries, throw away old entries!! 
    if ( /^NODE:/) {
      push (@old_nodes_data, $_);
    }
  }
  
  $tmpstr = join ("", @old_nodes_data);
  #globally replace NODE: tag by OLD: tag
  $tmpstr =~ s/NODE: /OLD: /gs;
  
  close(SETI_DATA_FILE)
    or die ("Cannot close $nodes_file_name: S!");
  
} else {
  print "Cannot open previous copy of $nodes_file_name: $!";
}

#print out results to flat data file
open(SETI_DATA_FILE, ">$nodes_file_name") ||
  die "Cannot open $nodes_file_name: $!";

#store old data in front of new data...
print SETI_DATA_FILE $tmpstr;

#print the new data
foreach $rtmp_node (@nodes_data) {
  #                             node 
  #                             progress 
  #                             cputime 
  #                             flops
  #                             peak
  printf SETI_DATA_FILE ("NODE: %s %s %s %s %s\n",
             $$rtmp_node{"node"},
             $$rtmp_node{"prog"},
             $$rtmp_node{"cpu"},
             $$rtmp_node{"fl"},
             $$rtmp_node{"peak"}
            );
} #foreach $entry

#print sums
#                        current processes
#                        work units (from SETI)
#                        results (back to SETI)
#                        cpu time
printf SETI_DATA_FILE ("TOTALS: %s %s %s %s\n",
                       $total_work_processes,
                       $total_work_units,
                       $total_results,
                       $total_cpu_time
              );

$sDataUpdated = ctime();    #retrieve current time (at server)
print SETI_DATA_FILE "DATAupdate: $sDataUpdated\n";
print "DATAupdate: $sDataUpdated\n";

close(SETI_DATA_FILE)
  or die ("Cannot close $nodes_file_name: S!");
