#!/usr/bin/perl -w ################################################################# # from : http://pigeond.net/git/?a=project_list;pf=flightgear # git: git://pigeond.net/flightgear/fgmap.git - hmpstat.bat # AIM: Read a fmgs log file, and extract pilot records ################################################################# use strict; use warnings; use Time::Local; use POSIX qw(strftime); use Getopt::Std; use File::Basename; # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] ) # FIXME my $SERVER_LOCAL = 'mpserver14 (UK)'; my $SERVER_TZ = -8; my @CALLSIGNS_IGNORE = ('mpdummy', 'obscam', '', ''); my $TOP = 20; my %pilots = (); my %crafts_used = (); my %crafts_used_pilots = (); my %crafts_duration = (); my $join_cnt = 0; my @join_hashes = (); my %join_duration = (); my %current = (); my %server_used = (); my %server_callsign = (); my $log_start_time = undef; my $log_end_time = undef; my $flight_duration = 0; my $debug = 0; #my($KEY) = "::"; my $KEY = ":"; #my($SEP) = ","; my $SEP = " "; my ($BN,$BP) = fileparse($0); my %opts; my $arg_start; my $arg_end; getopt('s:e:o:', \%opts); if( $opts{'s'} && $opts{'s'} =~ /(\d\d\d\d)(\d\d)(\d\d)/) { $arg_start = timelocal(0, 0, 0, $3, $2 - 1, ($1 - 1900)); } if ( $opts{'e'} && $opts{'e'} =~ /(\d\d\d\d)(\d\d)(\d\d)/) { $arg_end = timelocal(59, 59, 23, $3, $2 - 1, ($1 - 1900)); } if ($arg_start && $arg_end && ($arg_end < $arg_start)) { print(STDERR $BN.": start time greater than end time\n"); exit(-1); } if ($#ARGV < 0) { print(STDERR "$BN: usage: $BN [-s YYYYMMDD] [-e YYYYMMDD] [-o output file or dir] \n"); print(STDERR "$BN: Error: Need an input of at least one fgms log file...\n"); exit(-1); } my %model_lookup = ( "707" => "707/707-set.xml", "737-300" => "737-300/737-300-set.xml", "777-200" => "777-200/777-200-set.xml", "787" => "787/787-set.xml", "A-10-model" => "A-10/A-10-set.xml", "a300b2" => "A300/A300-set.xml", #"a320_100" => "A320-family/A320-111-set.xml", #"a320_100" => "A320-family/A320-131-set.xml", "a320_100" => "A320-family/A320-set.xml", "a320-fb" => "A320/A320-set.xml", "A380" => "A380/A380-set.xml", "a4-blue" => "a4/a4-set.xml", "a4-blue-uiuc" => "a4/a4-uiuc-set.xml", "a4f-blue" => "a4/a4f-set.xml", "a6m2-anim" => "A6M2/A6M2-set.xml", "a6m2b-green" => "A6M2/A6M2b-set.xml", "aerostar" => "Aerostar-700/aerostar700-set.xml", "AN-225-model" => "AN-225/AN-225-set.xml", "an2-model" => "an2/an2-set.xml", "asw20-stuck-model" => "asw20/asw20-set.xml", #"asw20-stuck-model" => "asw20/asw20-v1-nl-uiuc-set.xml", "b1900d-anim" => "b1900d/b1900d-set.xml", #"b29-model" => "b29/b29-jsbsim-set.xml", #"b29-model" => "b29/b29-magic-set.xml", "b29-model" => "b29/b29-set.xml", #"b29-model" => "b29/b29-yasim-set.xml", "B-52F-model" => "B-52F/B-52F-set.xml", "BAC-TSR2-model" => "BAC-TSR2/BAC-TSR2-set.xml", "baseline" => "f16/f16at-set.xml", "beaufighter" => "beaufighter/beaufighter-set.xml", #"beech99-model" => "beech99/beech99-v1-uiuc-set.xml", #"beech99-model" => "beech99/beech99-yasim-set.xml", "beech99-model" => "beech99/beech99-set.xml", "bell206" => "bell206/bell206-set.xml", "bf109" => "bf109/bf109-set.xml", "bf109g-model" => "bf109/bf109g-set.xml", #"bo105" => "as350/as350-set.xml", "bo105" => "bo105/bo105-set.xml", #"bo105" => "ch47/ch47-set.xml", #"bo105" => "ch47/ch47-yasim-set.xml", "Boeing314Clipper" => "Boeing314/Boeing314A-set.xml", "boeing747-200" => "747-200/747-200-set.xml", #"boeing747-400-jw" => "747/747-100-set.xml", "boeing747-400-jw" => "747/747-set.xml", "Bravo" => "Citation-Bravo/Bravo-set.xml", "c150" => "c150/c150-set.xml", "c172-dpm" => "c172r/c172r-set.xml", #"c172p" => "c172p/c172p-2dpanel-set.xml", "c172p" => "c172p/c172p-set.xml", #"c182-dpm" => "c182/c182-2dpanel-set.xml", "c182-dpm" => "c182/c182-set.xml", "c182rg-dpm" => "c182rg/c182rg-set.xml", #"c310-dpm" => "c310/c310dpm-3d-set.xml", "c310-dpm" => "c310/c310-set.xml", #"c310u3a" => "c310/c310-yasim-set.xml", #"c310u3a" => "c310u3a/c310-ifr-set.xml", "c310u3a" => "c310u3a/c310-set.xml", #"c310u3a" => "c310u3a/c310u3a-jsbsim-set.xml", #"c310u3a" => "c310u3a/c310u3a-set.xml", "CanberraBI8-model" => "CanberraBI8/CanberraBI8-set.xml", "ch53e-model" => "ch53e/ch53e-set.xml", "Citation-II" => "Citation/Citation-II-set.xml", "colditz-model" => "colditz/colditz-set.xml", "ComperSwift-model" => "ComperSwift/ComperSwift-set.xml", "Concorde_ba" => "Concorde/Concorde-set.xml", "dc3-dpm" => "dc3/dc3-set.xml", "dhc2floats" => "dhc2/dhc2F-set.xml", "dhc2wheels" => "dhc2/dhc2W-set.xml", "dhc6" => "dhc6/dhc6-set.xml", "dr1" => "fkdr1/fkdr1-v1-nl-uiuc-set.xml", "E3B" => "E3B/E3B-set.xml", "ec135" => "ec135/ec135-set.xml", "f104" => "f104/f104-set.xml", "f15c" => "f15c/f15c-set.xml", #"f16" => "f16/f16-3d-set.xml", "f16" => "f16/f16-set.xml", "f16klu" => "f16/f16-mlu-set.xml", "f4u-model" => "F4U/f4u-set.xml", "f80" => "F80C/F80C-set.xml", "fgfs-tux-model" => "fgfs-tux/fgfs-tux-set.xml", #"fgfs-tux-model" => "fgfs-tux/fgfs-tux-v1-nl-uiuc-set.xml", "FINNAIRmd11" => "MD11/MD11-FINNAIR-set.xml", "fokker100" => "fokker100/fokker100-set.xml", "fokker50" => "fokker50/fokker50-set.xml", "fokker70" => "fokker100/fokker70-set.xml", "harrier-model" => "harrier/harrier-set.xml", "hgldr-cs-model" => "airwaveXtreme150/airwaveXtreme150-set.xml", #"hgldr-cs-model" => "airwaveXtreme150/airwaveXtreme150-v1-nl-uiuc-set.xml", "hunter-model-2t" => "Hunter/hunter-2tanks-set.xml", "hunter-model" => "Hunter/hunter-set.xml", "hurricane_model" => "Hurricane/hurricaneIIb-set.xml", "j22" => "j22/j22-set.xml", "j3cub" => "j3cub/j3cub-set.xml", "ju52" => "ju52/ju52-set.xml", "KA6-D" => "KA6-D/KA6-D-set.xml", "KC135" => "KC135/KC135-set.xml", "ki-84" => "Ki-84/Ki-84-set.xml", "KLMmd11" => "MD11/MD11-KLM-set.xml", #"l410" => "l410/l410p-3d-jsbsim-set.xml", "l410" => "l410/l410-set.xml", "lightning-model" => "Lightning/lightning-set.xml", "Lockheed1049_twa" => "Lockheed1049/Lockheed1049-set.xml", "MD11" => "MD11/MD11-set.xml", "MiG-15bis-model" => "MiG-15/MiG-15bis-set.xml", "mirage2000" => "mirage2000/mirage2000-set.xml", "opus-model" => "marchetti/marchetti-set.xml", #"opus-model" => "marchetti/marchetti-v1-uiuc-set.xml", "ornithopter-8-4-model" => "ornithopter/ornithopter-set.xml", #"OV10" => "OV10/OV10_CDF-set.xml", #"OV10" => "OV10/OV10_NASA-set.xml", #"OV10" => "OV10/OV10_USAFE-set.xml", "OV10" => "OV10/OV10-set.xml", "p51d-jw" => "p51d/p51d-set.xml", "pa24-250" => "pa24-250/pa24-250-set.xml", "pa28-161" => "pa28-161/pa28-161-set.xml", #"paraglider_model" => "paraglider/paraglider-jsbsim-set.xml", "paraglider_model" => "paraglider/paraglider-set.xml", "pc7" => "pc7/pc7-set.xml", #"Rascal110" => "Rascal/Rascal110-JSBSim-set.xml", #"Rascal110" => "Rascal/Rascal110-YASim-set.xml", "Rascal110" => "Rascal/Rascal110-set.xml", "Saab340-dpm" => "Saab340/Saab340dpm-3d-set.xml", "santa" => "santa/santa-set.xml", "seafire_model" => "Spitfire/seafireIIIc-set.xml", "seahawk-3d-model" => "seahawk/seahawk-set.xml", "sea-vixen-model" => "SeaVixen/sea-vixen-set.xml", #"SenecaII" => "SenecaII/SenecaII-jsbsim-set.xml", #"SenecaII" => "SenecaII/SenecaII-yasim-set.xml", "SenecaII" => "SenecaII/SenecaII-set.xml", #"sgs233" => "bocian/bocian-set.xml", "sgs233" => "sgs233/sgs233-set.xml", "sikorsky76c" => "Sikorsky-76C/s76c-set.xml", "SinglePiston" => "ogel/ogel-set.xml", "sopwithCamel-model" => "sopwithCamel/sopwithCamel-set.xml", #"sopwithCamel-model" => "sopwithCamel/sopwithCamel-v1-nl-uiuc-set.xml", "spitfire_model" => "Spitfire/spitfireIIa-set.xml", "sr20" => "sr20/sr20-set.xml", "SU-37-model" => "SU-37/SU-37-set.xml", "t37" => "T37/T37-set.xml", "T38-model" => "T38/T38-set.xml", "TU-114-model" => "TU-114/TU-114-set.xml", "tu154B" => "tu154/tu154-set.xml", "ufo" => "ufo/ufo-set.xml", "vulcanb2" => "vulcanb2/vulcanb2-set.xml", "WrightFlyer-pb-jw" => "wrightFlyer1903/wrightFlyer1903-set.xml", "X15_flight" => "X15/X15-set.xml", "X15" => "X15/X15-new-set.xml", "yardstik" => "YardStik/YardStik-set.xml", #"YF-23-model" => "NTPS/NTPS-Eng-set.xml", #"YF-23-model" => "NTPS/NTPS-HD1-set.xml", #"YF-23-model" => "NTPS/NTPS-HD2-set.xml", #"YF-23-model" => "NTPS/NTPS-OTW-HUD-set.xml", #"YF-23-model" => "NTPS/NTPS-OTW-NOHUD-set.xml", #"YF-23-model" => "YF-23/NTPS-set.xml", "YF-23-model" => "YF-23/YF-23-set.xml", ); sub array_max { my(@arr) = @_; return (sort { $b <=> $a } @arr)[0]; } sub hash_max { my(%h) = @_; return (sort { $h{$b} <=> $h{$a} } keys(%h))[0]; } sub escape_chars { my($str) = @_; $str =~ s/[\[\]\(\)\^\$\/\.\*]/\\$&/g; return $str; } sub is_ignored { my($callsign) = @_; return 1 if ($callsign =~ /Bad Client/i); return (scalar(grep(/^\Q${callsign}\E$/, @CALLSIGNS_IGNORE))); } sub dprint { if($debug) { print(@_); } } sub min { my($a, $b) = @_; return ($a < $b ? $a : $b); } sub max { my($a, $b) = @_; return ($a > $b ? $a : $b); } sub datetime_to_epoch { my($date, $time) = @_; my($ret); my($DD, $MM, $YY, $hh, $mm, $ss); ($DD, $MM, $YY) = split(/\./, $date); ($hh, $mm, $ss) = split(/\:/, $time); $ret = timelocal($ss, $mm, $hh, $DD, ($MM - 1), ($YY - 1900)); $ret += ($SERVER_TZ * 3600); return $ret; } my %year_stats = (); my %month_stats = (); my %day_stats = (); my %hour_stats = (); my %dow_stats = (); sub datetime_join { my($t) = @_; my($dd, $mm, $yy, $hh, $dow) = (localtime($t))[3, 4, 5, 2, 6]; $year_stats{$yy} += 1; $month_stats{$mm} += 1; $day_stats{$dd} += 1; $hour_stats{$hh} += 1; $dow_stats{$dow} += 1; } my %month_peak = (); my %day_peak = (); my %hour_peak = (); my %dow_peak = (); sub update_current_cnt { my($cnt, $t) = @_; my($dd, $mm, $yy, $hh, $dow) = (localtime($t))[3, 4, 5, 2, 6]; $month_peak{$mm} = max($cnt, $month_peak{$mm} || 0); $day_peak{$dd} = max($cnt, $day_peak{$dd} || 0); $hour_peak{$hh} = max($cnt, $hour_peak{$hh} || 0); $dow_peak{$dow} = max($cnt, $dow_peak{$dow} || 0); } sub model_lookup { my($model) = @_; my($aircraft) = $model_lookup{$model}; if(!$aircraft) { $model =~ s/-model$//g; $model =~ s/-anim$//g; $aircraft = $model."/".$model; } return $aircraft; } sub aircraft_path_split { my($path) = @_; my($dir, $aircraft) = split(/\//, $path, 2); $aircraft =~ s/-set\.xml//g; return ($dir, $aircraft); } sub pilot_join { my($callsign, $t, $mode, $model, $ip, $port) = @_; $join_cnt++; my(%join_hash); my($aircraft) = model_lookup($model); # key: callsign # value: (date,time,local/remote,model,ip,duration) %join_hash = ( 'callsign' => $callsign, 'epoch' => $t, 'mode' => $mode, 'aircraft' => $aircraft, 'model' => $model, 'ip' => $ip, 'port' => $port, 'duration' => 0 ); dprint($callsign." joined\n"); push(@join_hashes, \%join_hash); hash_push(\%pilots, $callsign, \%join_hash); crafts_used_pilots_add(\%crafts_used_pilots, $aircraft, $callsign); $crafts_used{$aircraft} += 1; if($mode eq 'LOCAL') { $server_used{'LOCAL'} += 1; $server_callsign{'LOCAL'}{$callsign} += 1; } elsif($mode eq 'REMOTE') { $server_used{$ip} += 1; $server_callsign{$ip}{$callsign} += 1; } datetime_join($t); my($ref); if($ref = $current{$callsign}) { $ref->{'duration'} = $t - $ref->{'epoch'}; } $current{$callsign} = \%join_hash; } sub pilot_part { my($callsign, $t) = @_; my($ref); if($ref = $current{$callsign}) { my($duration); $duration = $t - $ref->{'epoch'}; $ref->{'duration'} = $duration; $crafts_duration{$ref->{'aircraft'}} += $duration; $join_duration{$callsign} += $duration; $flight_duration += $duration; dprint($callsign." parted\n"); delete($current{$callsign}); } else { # hmm... #print(STDERR "warning: $callsign parting without joining\n"); dprint("warning: $callsign parting without joining\n"); } } sub hash_push { my($href, $key, $value) = @_; my($ref) = $href->{$key}; dprint("key: $key\n"); if($ref) { dprint("old ref find (@$ref)\n"); push(@$ref, $value); } else { $href->{$key} = [ $value ]; dprint("new ref (".$href->{$key}.")\n"); } } sub crafts_used_pilots_add { my($href, $aircraft, $callsign) = @_; if($href->{$aircraft}) { $href->{$aircraft}->{$callsign} += 1; } else { $href->{$aircraft} = { $callsign => 1 }; } } sub secs_to_dhms { my($secs) = @_; my($str) = ""; my($dd, $hh, $mm); $dd = int($secs / (24 * 60 * 60)); $secs -= $dd * 24 * 60 * 60; $hh = int($secs / (60 * 60)); $secs -= $hh * 60 * 60; $mm = int($secs / 60); $secs -= $mm * 60; if($dd > 0) { $str .= "${dd} day"; $str .= "s" if($dd > 1); } if($hh > 0) { $str .= " " if($str ne ''); $str .= "${hh} hour"; $str .= "s" if($hh > 1); } if($mm > 0) { $str .= " " if($str ne ''); $str .= "${mm} min"; $str .= "s" if($mm > 1); } if($secs > 0) { $str .= " " if($str ne ''); $str .= "${secs} sec"; $str .= "s" if($secs > 1); } return $str; } dprint("pilots hash: ".\%pilots."\n"); my($f, $line); my($main_t); my $pilot_count = 0; my $log_files = ''; # process the command line foreach $f (@ARGV) { if(! -f $f) { die("Cannot stat ".$f."\n"); next; } open(IN, "<".$f) or die("Cannot open ".$f."\n"); $log_files .= "$f "; while() { $line = $_; chomp($line); my ($date, $time, $logline, $mode, $callsign, $model, $ip, $port); ($date, $time, $logline) = ($line =~ m/^\s*(\d+\.\d+\.\d\d\d\d)\s+(\d+:\d+:\d+)\s+(.*)$/); if(!defined($date) || !defined($time) || !defined($logline)) { next; } my($t) = datetime_to_epoch($date, $time); if($arg_start && ($t < $arg_start)) { next; } if($arg_end && ($t > $arg_end)) { next; } $main_t = $t; if(!defined($log_start_time)) { $log_start_time = $t; } if($logline =~ /^New (.*?) Client: (.*?) (.*?):([0-9]+) \((.*?)\)$/) { # New pilot logging on $mode = $1; $callsign = $2; $ip = $3; $port = $4; $model = $5; if($model eq '' || $model eq '* unknown *') { next; } if($model =~ /.*\/(.*?)$/) { $model = $1; if($model =~ /(.*?)\..*?$/) { $model = $1; } } if(is_ignored($callsign)) { next; } pilot_join($callsign, $t, $mode, $model, $ip, $port); $pilot_count++; } elsif($logline =~ /TTL exeeded, dropping pilot (.*?) after/) { # Pilot logging off $callsign = $1; if($callsign eq "" || $callsign eq "* Bad Client *") { next; } if(is_ignored($callsign)) { next; } pilot_part($callsign, $t); } elsif($logline =~ /current clients: (\d+) max: \d+/) { update_current_cnt($1, $t); } elsif($logline =~ /^# FlightGear Multiplayer Server .*? started$/) { my($k); foreach $k (keys(%current)) { pilot_part($k, $t); } } } close(IN); } if ($pilot_count == 0) { print(STDERR "$BN: Error: Read logs $log_files, but found NO pilot records...\n"); exit(-1); } # assuming the log is sequential in time $log_end_time = $main_t; my($k); # Who hasn't parted yet? foreach $k (keys(%current)) { my($ref) = $current{$k}; my($duration) = $log_end_time - $ref->{'epoch'}; $ref->{'duration'} = $duration; $crafts_duration{$ref->{'aircraft'}} += $duration; $join_duration{$k} += $duration; $flight_duration += $duration; } #foreach $k (keys(%pilots)) #{ # my($a, $arr); # $arr = $pilots{$k}; # # print("$k:\n"); # # foreach $a (@$arr) # { # print(" ".$a->{'aircraft'}.":".$a->{'duration'}."\n"); # } #} #print("\n\n"); if(!$log_start_time || !$log_end_time) { print(STDERR "$BN: No data\n"); exit(-1); } my($period_start) = strftime("%Y-%m-%d %H:%M:%S", localtime($log_start_time)); my($period_end) = strftime("%Y-%m-%d %H:%M:%S", localtime($log_end_time)); my($pilots_cnt) = scalar(keys(%pilots)); $flight_duration = secs_to_dhms($flight_duration); my($generated) = strftime("%Y-%m-%d %H:%M:%S", localtime(time())); my($XML) = < ${generated} ${period_start} ${period_end} ${join_cnt} ${pilots_cnt} ${flight_duration} XML # server-stats # TODO my(%servernames) = ( '85.214.36.1' => 'mpserver01 (Germany)', '202.65.223.218' => 'mpserver02 (Hong Kong)', '62.75.184.124' => 'mpserver03 (Germany)', '217.151.102.18' => 'mpserver04 (U.K.)', '69.162.115.208' => 'mpserver05 (Texas US)', '83.254.89.70' => 'mpserver06 (Sweden)', '98.100.234.6' => 'mpserver07 (Wisconsin US)', '217.172.186.31' => 'mpserver08 (Germany)', '83.169.22.142' => 'mpserver09 (Germany)', '194.9.62.110' => 'mpserver10 (France)', '193.219.56.151' => 'mpserver11 (Lithuania)', '62.129.133.10' => 'mpserver12 (Netherlands)', ); $XML .= " \n"; sub servername_get { my($ip) = @_; my($servername); if($ip eq 'LOCAL') { $servername = $SERVER_LOCAL; } elsif(!($servername = $servernames{$ip})) { $servername = $ip; } return $servername; } foreach $k (sort { servername_get($a) cmp servername_get($b) } (keys(%server_used))) { my($servername) = servername_get($k); my($ref) = $server_callsign{$k}; my($pilots) = scalar(keys(%$ref)); if($servername =~ /\d+\.\d+\.\d+\.\d+/) { # Skip servers we don't know about next; } $XML .= < XML } $XML .= " \n\n"; my (@truncated); # aircraft-most-flight @truncated = sort { $crafts_used{$b} <=> $crafts_used{$a} } keys(%crafts_used); $#truncated = min($TOP - 1, scalar(@truncated) - 1); $XML .= " \n"; foreach $k (@truncated) { my($path, $aircraft) = aircraft_path_split($k); $XML .= < XML } $XML .= " \n\n"; # aircraft-most-pilot sub crafts_used_pilots_func { my($ha, $hb) = ($crafts_used_pilots{$a}, $crafts_used_pilots{$b}); scalar(keys(%$hb)) <=> scalar(keys(%$ha)); } @truncated = sort crafts_used_pilots_func keys(%crafts_used_pilots); $#truncated = min($TOP - 1, scalar(@truncated) - 1); $XML .= " \n"; foreach $k (@truncated) { my($h) = $crafts_used_pilots{$k}; my($pilots) = scalar(keys(%$h)); my($path, $aircraft) = aircraft_path_split($k); $XML .= < XML } $XML .= " \n\n"; # aircraft-longest-flight-total @truncated = sort { $crafts_duration{$b} <=> $crafts_duration{$a} } keys(%crafts_duration); $#truncated = min($TOP - 1, scalar(@truncated) - 1); $XML .= " \n"; foreach $k (@truncated) { my($duration) = $crafts_duration{$k}; $duration = secs_to_dhms($duration); my($path, $aircraft) = aircraft_path_split($k); $XML .= < XML } $XML .= " \n\n"; # pilots-most-flight sub pilot_most_flight_func { my($pa, $pb) = ($pilots{$a}, $pilots{$b}); scalar(@$pb) <=> scalar(@$pa); } @truncated = sort pilot_most_flight_func keys(%pilots); $#truncated = min($TOP - 1, scalar(@truncated) - 1); $XML .= " \n"; foreach $k (@truncated) { my($ref) = $pilots{$k}; my($flights) = scalar(@$ref); $XML .= < XML } $XML .= " \n\n"; # pilots-longest-flight-single @truncated = sort { $b->{'duration'} <=> $a->{'duration'} } @join_hashes; $#truncated = min($TOP - 1, scalar(@truncated) - 1); $XML .= " \n"; foreach $k (@truncated) { my($duration) = secs_to_dhms($k->{'duration'}); my($jointime) = strftime("%Y-%m-%d %H:%M:%S", localtime($k->{'epoch'})); my($path, $aircraft) = aircraft_path_split($k->{'aircraft'}); $XML .= < XML } $XML .= " \n\n"; # pilots-longest-flight-total @truncated = sort { $join_duration{$b} <=> $join_duration{$a} } keys(%join_duration); $#truncated = min($TOP - 1, scalar(@truncated) - 1); $XML .= " \n"; foreach $k (@truncated) { my($duration) = $join_duration{$k}; $duration = secs_to_dhms($duration); $XML .= < XML } $XML .= " \n\n"; # month-stats if(scalar(keys(%month_stats)) > 1) { $XML .= " \n"; my(@mon_strs) = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); for($k = 0; $k < 12; $k++) { my($flights) = $month_stats{$k} || 0; my($peak) = $month_peak{$k} || 0; $XML .= < XML } $XML .= " \n\n"; } # day-stats $XML .= " \n"; for($k = 1; $k <= 31; $k++) { my($flights) = $day_stats{$k} || 0; my($dd_str) = $k; if($k == 1 || $k == 21 || $k == 31) { $dd_str .= 'st'; } elsif($k == 2 || $k == 22) { $dd_str .= 'nd'; } elsif($k == 3 || $k == 23) { $dd_str .= 'rd'; } else { $dd_str .= 'th'; } my($peak) = $day_peak{$k} || 0; $XML .= < XML } $XML .= " \n\n"; # hour-stats $XML .= " \n"; for($k = 0; $k < 24; $k++) { my($flights) = $hour_stats{$k} || 0; my($hstr) = $k < 10 ? '0'.$k : $k; my($peak) = $hour_peak{$k} || 0; $XML .= < XML } $XML .= " \n\n"; # dow-stats my(@dows) = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); $XML .= " \n"; for($k = 0; $k < 7; $k++) { my($flights) = $dow_stats{$k} || 0; my($peak) = $dow_peak{$k} || 0; $XML .= < XML } $XML .= " \n\n"; $XML .= < XML my($outfile); my($outdir) = ""; if($opts{'o'}) { ($outfile) = ($opts{'o'} =~ /([0-9a-zA-Z_\-\.\/]+)/); if(-d ${outfile}) { $outdir = $outfile."/"; $outfile = ""; } } if(!$outfile || $outfile eq '') { my($outfile_prefix) = 'mpstat-'; my($outfile_suffix) = '.xml'; my($start_date) = strftime("%Y-%m-%d", localtime($log_start_time)); my($end_date) = strftime("%Y-%m-%d", localtime($log_end_time)); $outfile = $outdir. $outfile_prefix.$start_date."-".$end_date.$outfile_suffix; print($outfile."\n"); } my($out); open($out, ">".$outfile); print($out $XML); close($out); exit(0); # eof