# Norman Kuring 13-Aug-2010 Original development # Norman Kuring 8-Oct-2010 Break down alarms to the orbit/file # level. # Norman Kuring 9-Mar-2011 Sort orbits by both start and end times # in cases of two identical start times. # Norman Kuring 29-Mar-2011 Allow for the generation of a color- # coded menu even when the user-selected # start and end time do not match those # of any orbit file. # Norman Kuring 19-Apr-2011 Changed the name of the metadata files. # Norman Kuring 21-Apr-2011 Allow comments in parameter index files. # Norman Kuring 5-May-2011 Make this script work for both Aquarius # and Service Platform telemetry. # Norman Kuring 13-May-2011 Allow for reading of service platform # telemetry from both the stand-alone # and L1A-derived files. # Norman Kuring 17-May-2011 When I started representing parameters # with hexadecimal instead of decimal # indices in a bid to shorten URL's, I # inadvertantly messed up array indexing # in other portions of my code. This is # now fixed, and the proper alarm colors # are once more on display. # Norman Kuring 14-Sep-2011 Introduce additional sub-menus via # a separate metadata file. $ENV{PATH} = ''; use strict; use vars qw(%in); use Time::Local; use lib '/web/aquarius/scripts'; use ReadTelemMeta qw( get_max_alarms_range get_max_alarms get_param_specs get_menu_hierarchy readmeta read_units remove_menu_singlets sort_menu_leaves ); use Utility qw( read_dir monthbounds send_header ); my $thisScript = '/cgi/' . (split /\//,$0)[-1]; # Get the CGI parameters that specify what # to display and how to display it. require '/web/aquarius/cgi/cgi-lib.pl'; &ReadParse; # Does the telemetry come from standalone HKT files # or from the L1A files. $in{fr} = $in{fr} eq 'hkt' ? 'hkt' : 'l1a'; my $telemtyp = $in{ty} eq 'sp' ? 'sp' : 'aq'; # Get parameter grouping information if any is available. my %hierarchy = get_menu_hierarchy; # Get parameter information as a hash of arrays. # [ 0 , 1 , 2 , 3, 4, 5, 6, 7 ] # $pindex{PARAM_ID}[GROUP_ID,GROUP_COLUMN,UNITS,RL,YL,YH,RH,DESCRIPTION] my %pindex = get_param_specs($telemtyp); my @params = sort keys %pindex; # Make a hash that make_ul() can use to shorten parameter names. my $i=-1; my %params = map {$i++; ($_,sprintf("%lx",$i))} @params; # Group the parameters by group_id my %groups; foreach my $p (@params){ push @{ $groups{ $pindex{$p}[0] } }, hex($params{$p}); } my @groups = sort keys %groups; my $alarmdir = '/web/aquarius/metadata/alarms'; my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my %month; for(my $i=0; $i<@month; $i++){ $month{$month[$i]} = $i; } my ($selyear) = $in{yr} =~ /^(\d{4})$/; my ($selmon) = $in{mo} =~ /^([A-Z][a-z]{2})$/; my ($selday) = $in{dy} =~ /^(\d{1,2})$/; my (@selorb) = $in{or} =~ /^(\d+)@(\d+)$/; my (%alarms,%monalarms); my @years = map /^Q(\d{4})\.$telemtyp\.bin$/, read_dir($alarmdir,'^Q\d{4}\.' . $telemtyp . '\.bin$'); my $missing = chr(0); my $nominal = chr(1); my $warning = chr(2); my $error = chr(3); my %alarm = ($missing,'-',$nominal,'G',$warning,'Y',$error,'R'); my %aclass= ($missing,'al0',$nominal,'alG',$warning,'alY',$error,'alR'); # Build the year tables. my $ytables = ''; my $monthhdrs = '' . join("\n", @month) . ''; my %dalarms; foreach my $y (@years){ my @args = ($telemtyp,$y); if($selyear and $selmon and $selyear == $y){ push @args,$month{$selmon}; } my $malarms; ($malarms,$dalarms{$y}) = get_max_alarms(@args); my $cols = qq(\n\n\n\n); $ytables .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; $monthhdrs -- foreach my $g (@groups){ $ytables .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; -- for(my $m=0; $m<12; $m++){ my $hl = $y eq $selyear && $month[$m] eq $selmon ? ' highlight' : ''; my $max = $missing; foreach my $p (@{ $groups{$g} }){ $max = $malarms->[$p][$m] if $malarms->[$p][$m] gt $max; } my $q = join '&', "fr=$in{fr}", "ty=$telemtyp", "yr=$y", "mo=$month[$m]"; $ytables .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; -- } $ytables .= "\n"; } $ytables .= "\n
$y
Group
$g $alarm{$max}
\n"; } # Build the month table if a month was selected. my $mtable = ''; if($selyear and $selmon){ my @d = monthbounds($selyear,$month{$selmon}); my $ndays = $d[1] - $d[0] + 1; my $dayhdr = ''; for(my $d=1; $d<=$ndays; $d++){ $dayhdr .= qq($d\n); } $mtable .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; $dayhdr -- foreach my $g (@groups){ $mtable .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; -- for(my $d=1; $d<=$ndays; $d++){ my $hl = $d eq $selday ? ' highlight' : ''; my $max = $missing; foreach my $p (@{ $groups{$g} }){ $max = $dalarms{$selyear}[$p][$d-1] if $dalarms{$selyear}[$p][$d-1] gt $max; } my $q = join '&', "fr=$in{fr}", "ty=$telemtyp", "yr=$selyear", "mo=$selmon", "dy=$d"; $mtable .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; -- } $mtable .= "\n"; } $mtable .= "\n
$selmon $selyear
Group
$g $alarm{$max}
\n
\n"; } # Build the orbit table if a day was selected. my @chosenalarms; my $otable=''; if($selyear and $selmon and $selday){ my $day = timegm(0,0,0,$selday,$month{$selmon},$selyear-1900)/86400; my @stet = orbits_stet($day,$in{fr}); my %max; my $orbhdr = ''; foreach my $t (@stet){ my $shms = sprintf "%02d
%02d
%02d",(gmtime $t->[0])[2,1,0]; my $ehms = sprintf "%02d
%02d
%02d",(gmtime $t->[1])[2,1,0]; $orbhdr .= qq($shms
-
$ehms\n); my @max = get_max_alarms_range($telemtyp,@$t); if(@selorb and $selorb[0] == $t->[0] and $selorb[1] == $t->[1]){ # Save these alarms to build the menu with. @chosenalarms = @max; } foreach my $g (@groups){ foreach my $p (@{ $groups{$g} }){ if($max[$p] gt $max{$g}{"@$t"}){ $max{$g}{"@$t"} = $max[$p]; } } } } $otable .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; $orbhdr -- foreach my $g (@groups){ $otable .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; -- foreach my $t (@stet){ my $key = "@$t"; my $hl = $t->[0] == $selorb[0] && $t->[1] == $selorb[1] ? ' highlight' : ''; my $q = join '&', "fr=$in{fr}", "ty=$telemtyp", "yr=$selyear", "mo=$selmon", "dy=$selday", "or=$t->[0]", "or=$t->[1]"; $otable .= join "\n", map {join ' ',split ' '} split /^/m ,<<" --" . "\n"; -- } $otable .= "\n"; } $otable .= "\n
$selday $selmon $selyear
Group
$g $alarm{$max{$g}{$key}}
\n
\n"; } my $menu = ''; if($selyear and $selmon and $selday and @selorb){ # If the user also selected an orbit... my %units = &read_units; # Group the parameters by group_id and by units. my %menuA; foreach my $p (keys %pindex){ my $units = $units{ $pindex{$p}[2] }[2] || "Dimensionless"; if($hierarchy{$p}){ # Put additional layers into the menu hierarchy # if they have been specified in the metadata. eval 'push @{ $menuA{ $pindex{$p}[0] }' . "{'" . join("'}{'",@{ $hierarchy{$p} }) . "'}" . '{ $units } }, $p'; } else{ # Otherwise just use the top level grouping # subdivided by the parameter units. push @{ $menuA{ $pindex{$p}[0] }{ $units } }, $p; } } # Remove all sub-menus that have just one child. foreach my $k (keys %menuA){ $menuA{$k} = remove_menu_singlets($menuA{$k}); } # Order the parameters in the leaf menus according to the # metadata specification. sort_menu_leaves(\%pindex,\%menuA); # If the user-specified start and end times do not match start and end times # from any telemetry filenames, I still would like to show a color-coded # menu for the selected time range, so I must call get_max_alarms_range # here. @chosenalarms or @selorb and @chosenalarms = get_max_alarms_range($telemtyp,@selorb); $menu = (make_ul(\%menuA,$selorb[0],$selorb[1],\@chosenalarms))[1] . "
\n"; } # Send the HTML to the client. &send_header; print <<"--"; Aquarius Telemetry Alarms (time line) $menu $otable $mtable $ytables -- sub make_ul{ my ($r,$st,$et,$alarms) = @_; my %class = ( chr(0),'hkt_missing', chr(1),'hkt_nominal', chr(2),'hkt_warning', chr(3),'hkt_error', ); my $max = chr(0); my $stime = tstring($st); my $etime = tstring($et); $stime =~ s/\D+/_/g; $etime =~ s/\D+/_/g; my $ul = qq(\n"; ($max,$ul); } sub param_list{ my $r = shift; my @param_list; my $type = ref $r; if($type eq 'HASH'){ foreach my $k (sort keys %$r){ push @param_list,param_list($r->{$k}); } } elsif($type eq 'ARRAY'){ push @param_list,@$r; } else{ die <<" --", "Died"; The param_list() function expects an argument of reference type 'HASH' or 'ARRAY'; got '$type' instead. -- } @param_list; } sub tstring{ my $t = shift; my ($s,$m,$h,$d,$mo,$y) = (gmtime $t)[0..5]; sprintf "%04d-%02d-%02dT%02d:%02d:%02d",$y+1900,$mo+1,$d,$h,$m,$s; } sub orbits_stet{ my ($day,$from) = @_; my %dir = ( l1a => '/web/data/Aquarius/DOWNLINKS/L1', hkt => '/web/data/Aquarius/HKT', ); my %pat = ( l1a => '^Q\d{8}_\d{6}_T\d{8}_\d{6}_\d{8}_\d{6}\.AQ_APDU\.txt$', hkt => '\.SP_T\d{8}_\d{6}_\d{8}_\d{6}\.SP_PCS_MAIN\.txt$', ); my $ds = 86400*$day; # day start second my $de = $ds + 86400; # day end second my %t; for(my $d=$day-1; $d<=$day+1; $d++){ my ($dd,$mm,$y) = (gmtime 86400*$d)[3..5]; $y += 1900; my $mmdd = sprintf "%02d%02d",$mm+1,$dd; opendir D,"$dir{$from}/$y/$mmdd" or next; my @f = grep /$pat{$from}/, readdir D; closedir D; foreach my $f (@f){ my ($sy,$so,$sd,$sh,$sm,$ss,$ey,$eo,$ed,$eh,$em,$es) = $f =~ /_T (\d{4})(\d\d)(\d\d)_(\d\d)(\d\d)(\d\d)_ (\d{4})(\d\d)(\d\d)_(\d\d)(\d\d)(\d\d)\./x; my $st = timegm($ss,$sm,$sh,$sd,$so-1,$sy-1900); next if $st >= $de; my $et = timegm($es,$em,$eh,$ed,$eo-1,$ey-1900); next if $et < $ds; if($et < $st){ warn "End time in filename is less than start time\n", "$dir{$from}/$y/$mmdd/$f\n"; next; } $t{"$st $et"}++; } } sort { $a->[0] <=> $b->[0] or $a->[1] <=> $b->[1] } map [split ' '],keys %t; } sub by_month { my %m = qw( jan 0 feb 1 mar 2 apr 3 may 4 jun 5 jul 6 aug 7 sep 8 oct 9 nov 10 dec 11 ); $m{lc substr $a,0,3} <=> $m{lc substr $b,0,3}; }