package Util::SW0Filename;
##############################################################################
#
# DESCRIPTION: This class is the mission-specific filename generator for Swift.
#
# HISTORY:
# HISTORY: $Log: SW0Filename.pm,v $
# HISTORY: Revision 1.50 2016/07/21 14:20:09 apsop
# HISTORY: Updates for 3.17.05. CALDB patches for XRT 20160609 and clock v114. xrtwtcorr v0.2.2. Fix issue retrieving BAT scaled map.
# HISTORY:
# HISTORY: Revision 1.49 2014/08/15 09:41:45 apsop
# HISTORY: New functions filter_id_for_code and filter_code_for_id.
# HISTORY:
# HISTORY: Revision 1.48 2013/07/16 07:29:26 apsop
# HISTORY: New sub getNonGrismSkyImages.
# HISTORY:
# HISTORY: Revision 1.47 2007/07/25 20:16:09 apsop
# HISTORY: Write REPROC keyword if needed.
# HISTORY:
# HISTORY: Revision 1.46 2007/06/28 20:31:08 apsop
# HISTORY: Changes to shared repository to support files indexed by target number.
# HISTORY:
# HISTORY: Revision 1.45 2007/04/01 19:14:49 apsop
# HISTORY: Add version keyowrds to shared repository files before they are stored.
# HISTORY:
# HISTORY: Revision 1.44 2006/05/02 13:16:02 apsop
# HISTORY: Fix response to no repos file in fetch_from_repository.
# HISTORY:
# HISTORY: Revision 1.43 2006/04/03 16:02:46 apsop
# HISTORY: Fix bug in appending time index to repository file names.
# HISTORY:
# HISTORY: Revision 1.42 2006/02/07 16:39:24 apsop
# HISTORY: Allow for the parsing of gzipped files.
# HISTORY:
# HISTORY: Revision 1.41 2006/01/20 19:44:09 apsop
# HISTORY: Replace "met" flag with more descriptive "not_seqid" flag; Fix up repository file renaming.
# HISTORY:
# HISTORY: Revision 1.40 2006/01/18 16:39:27 apsop
# HISTORY: Add error message if no repository file found.
# HISTORY:
# HISTORY: Revision 1.39 2005/09/26 20:42:14 apsop
# HISTORY: Only sort repository files if there is more than one. Allows use of nonindexed repository files.
# HISTORY:
# HISTORY: Revision 1.38 2005/07/15 16:15:32 apsop
# HISTORY: Bug fix for previous commit.
# HISTORY:
# HISTORY: Revision 1.37 2005/07/15 15:44:11 apsop
# HISTORY: Fix file globbing so that it only matches numeric indexes in the file names.
# HISTORY:
# HISTORY: Revision 1.36 2005/06/01 13:35:47 apsop
# HISTORY: Fix bugs in exporting of repository files.
# HISTORY:
# HISTORY: Revision 1.35 2005/02/08 14:30:52 apsop
# HISTORY: Fix method for detecting grism files.
# HISTORY:
# HISTORY: Revision 1.34 2004/11/30 21:21:29 apsop
# HISTORY: Changes to support test pipeline, which has an st prefix.
# HISTORY:
# HISTORY: Revision 1.33 2004/11/16 14:22:57 apsop
# HISTORY: Added method to return existing/regular files matching specs.
# HISTORY:
# HISTORY: Revision 1.32 2004/10/13 01:40:42 apsop
# HISTORY: Add in filter check to eliminate files with indexes when index==0
# HISTORY:
# HISTORY: Revision 1.31 2004/09/02 00:02:13 apsop
# HISTORY: Bug fix for handling indexed files with non-indexed files.
# HISTORY:
# HISTORY: Revision 1.30 2004/08/27 18:32:00 apsop
# HISTORY: Added methods for converting between UVOT filter codes and names.
# HISTORY:
# HISTORY: Revision 1.29 2004/07/06 20:03:26 apsop
# HISTORY: Keep track of calibration files and delete them at the end.
# HISTORY:
# HISTORY: Revision 1.28 2004/05/27 00:14:46 apsop
# HISTORY: Modified instrument-specific cal file code to accept index=0
# HISTORY:
# HISTORY: Revision 1.27 2004/05/06 19:58:41 dah
# HISTORY: Add version number back into the header comments.
# HISTORY:
# HISTORY: Revision 1.26 2004/04/16 20:20:37 dah
# HISTORY: Begin using embedded history records
# HISTORY:
#
# VERSION: $Revision: 1.50 $
#
#
##############################################################################
use Util::Filename;
use Util::FilenameInfo;
use Util::ShortTermRepository;
@ISA = ("Util::Filename");
use strict;
#########################
# constructor
#########################
sub new {
my $proto=shift;
my $self=$proto->SUPER::new(@_);
$self->{GENERIC_ARGS}=["*","*","*","*","*"];
####################################
# file name extensions
####################################
$self->{INFO} = Util::FilenameInfo->new();
############################################
# BAT shared repository
############################################
if($self->jobpar()->read("use_repository") eq "yes" ) {
my $dir = $self->procpar()->read("repository");
$self->{REPOSITORY} = Util::ShortTermRepository->new($dir, $self);
}
###############################################
# cache of root names for sequence based files
###############################################
$self->{CORRESPONDING_ROOT}={};
$self->{CAL_FILES}=[];
return $self;
}
############################################################################
# Converts a full instrument name into a single character instrument code
############################################################################
sub instrument_code {
my $self = shift;
my $name = shift;
$name = lc($name);
if( $name eq "uvot" ) { return "u"; }
elsif($name eq "xrt" ) { return "x"; }
elsif($name eq "bat" ) { return "b"; }
elsif($name eq "fom") { return "f"; }
elsif($name eq "swift") { return "s"; }
elsif($name eq "proc" ) { return "p"; }
elsif($name eq "all" ) { return "*"; }
else { return $name; }
}
############################################################################
# returns the full name of an instrument in lower case, given its one
# letter code. Returns "swift" for "s" and returns an empty string
# for an unknown instrument code
############################################################################
sub instrument_name {
my $self = shift;
my $inst = shift;
if($inst eq "u") {return "uvot";}
if($inst eq "x") {return "xrt";}
if($inst eq "b") {return "bat";}
if($inst eq "f") {return "fom";}
if($inst eq "s") {return "swift";}
if($inst eq "p") {return "proc";}
if($inst eq "*") {return "all";}
return "";
}
############################################################################
# returns the a list of uinstrument codes from a string which may contain
# globbing wildcards. Things like "*" and "[ux]" are supported.
############################################################################
sub instrument_list {
my $self = shift;
my $template = shift;
if($template eq "*") {
########
# any
########
return ("u", "x", "b", "s", "f", "p");
} elsif( $template =~ /^\[.*\]$/ ) {
###################################
# handle a bracketed list of names
###################################
my @instruments = $template =~ /\[(.)*\]/;
return (@instruments);
} else {
#################
# no wildcards
#################
return ($template);
}
} # end of instrument list method
############################################################################
# returns true if the given type can have its filename constructed genericly
############################################################################
sub isGeneric {
my $self=shift;
my $type=shift;
return $self->{INFO}->is_generic($type);
}
#########################################################################
# generate a filename of a given type
#########################################################################
sub glob_template {
my $self=shift;
my $type =shift || '';
my $inst =shift || 'proc';
my $mode =shift || '';
my $index=shift;
#####################################################
# convert the instrument to a single character code
#####################################################
$inst = $self->instrument_code($inst);
############################################
# initialize an array to hold the templates
############################################
my @list=();
if( $self->isGeneric($type) ) {
#####################################################
# generic file names - this should handle most cases
#####################################################
my @templates;
##########################################
# force index to be at least two digits
##########################################
if( $index && $index =~ /\d+/ && length($index)<2 ) { $index = sprintf('%02d', $index); }
#########################
# default index
#########################
$index = '*' if $self->{'INFO'}->is_repository($type, $inst) eq 'TIME';
$index = '*[0-9][0-9]' if ($index && $index eq '*');
if( ! $index || $index eq '00' ){
$index = '';
}else{
$index = '_' . $index;
}
foreach my $i ($self->instrument_list($inst) ) {
###################################################
# make sure this instrument has files of this type
###################################################
unless($self->{INFO}->is_valid_instrument($type, $i) ) { next; }
#########################
# TDRSS field
#########################
my $tdrss = '';
$tdrss = 'ms' if $self->{INFO}->is_tdrss($type, $i);
my $suffix_noindex = $self->{INFO}->ext($type, $i);
my $gaoff = '';
$gaoff .= 'o[a-f0-9][a-f0-9][a-f0-9][a-f0-9]'
if ($self->{INFO}->is_offset($type, $i) && $mode!~/o[a-f0-9]{4}/);
$gaoff .= 'g[a-f0-9][a-f0-9][a-f0-9][a-f0-9]'
if ($self->{INFO}->is_gain($type, $i) && $mode!~/g[a-f0-9]{4}/);
$suffix_noindex =~ s/(.*)\.(.*)/$1${gaoff}.$2/
if $gaoff;
my $suffix = $suffix_noindex;
$suffix =~ s/(.*)\.(.*)/$1${index}.$2/
if $index;
my $root;
if( $self->{'INFO'}->not_seqid($type, $i) || $self->{'INFO'}->is_repository($type, $i) ||
$self->{'INFO'}->is_tdrss($type, $i) ){
$root = 's[tw][t0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]';
}else{
$root = $self->sequence_specific();
}
push @list, $root . $tdrss . $i . $mode . $suffix_noindex if $index eq '_*[0-9][0-9]';
push @list, $root . $tdrss . $i . $mode . $suffix;
} # end of loop over instruments
} elsif( $type eq "telemetry" ) {
###################################################
# telemetry mode is packet type and index is APID
# note that these have overlapping meaning
# instrument translates into a range of APIDs
# That's handled in the filter method
###################################################
push @list, ("swift_${mode}_".
"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]".
"_[0-9a-f][0-9a-f][0-9a-f][0-9a-f]".
"_${index}*.ccsds");
} else {
########################################################
# Check if this is an instrument cal file. Prepend the
# instrument name to the procpar param name
# This code supports parameter names of the type
# <inst>_<name>_<index>
########################################################
my $procpar = $self->procpar();
foreach ($self->instrument_list($inst)) {
my @cal_list;
my $param = $self->instrument_name($_) . '_' . $type;
$param .= "_$index" if(defined $index && $index ne "" && $index ne '*');
push @cal_list, ($procpar->read($param))
if $procpar->has($param);
if(defined $index && $index eq '*') {
open PARFILE, $procpar->name();
foreach (<PARFILE>){
push @cal_list, ( $procpar->read($1)) if /^(($param)_\d+),/;
}
close PARFILE;
}
push @list, @cal_list;
push @{$self->{CAL_FILES}}, @cal_list;
}
####################################
# inherit mission-independant stuff
####################################
@list=($self->SUPER::glob_template($type,$inst,$index,$mode))
unless @list;
}
return @list;
} # end of glob_template method
#########################################################################
#
#########################################################################
sub filter {
my $self = shift;
my $list = shift;
my $type =shift || '';
my $inst =shift || '';
my $mode =shift || '';
my $index=shift;
$inst = $self->instrument_code($inst);
my @files = @{$list};
if( $self->isGeneric($type) ) {
#######################################################
# In order for $index='*' to work as a glob, must test
# for existence of file with no index.
#######################################################
shift @files if( $index && $index eq '*' && @files && ! -f $files[0] );
@files = (grep !/_\d+\./, @files) if (defined $index && ($index =~ /^0+$/));
return @files unless @files;
#####################################
# eliminate any duplicate file names
#####################################
if( @files> 1 ){
my %files;
foreach (@files) { $files{$_}=1; }
@files = keys %files;
}
##############################################################################
# Check for files that got wrongly associated with this type because they have
# the same suffix as the this type.
#########################################################################
foreach my $i ($self->instrument_list($inst) ) {
my $inst_info = $self->{'INFO'}->{$i};
if( $inst_info->{$type}->{'anti'} ){
##########################################################################
# The type has anti-types. Hopefully this is not true very often, so that
# this part of the code is only executed occasionally
##########################################################################
my @bad;
my $ext = $inst_info->{$type}->{'ext'};
foreach my $atype (@{$inst_info->{$type}->{'anti'}}) {
my $aext = $inst_info->{$atype}->{'ext'};
unless( $aext eq $ext ){
my ($qaext, $gaoff) = ($aext, '');
$gaoff .= '(o[a-f0-9]{4})?' if $self->{'INFO'}->is_offset($atype, $i);
$gaoff .= '(g[a-f0-9]{4})?' if $self->{'INFO'}->is_gain($atype, $i);
$qaext =~ s/(.*)\.(.*)/${1}${gaoff}_?\\d*\\.${2}/;
push @bad, grep(/${qaext}$/, @files);
}else{
######################################################################
# If the extensions for the two types are the same, then there must
# be some other distinguishing factor
######################################################################
my $atdrss = $self->{'INFO'}->is_tdrss($atype, $i);
my $tdrss = $self->{'INFO'}->is_tdrss($type, $i);
if( $atdrss && !$tdrss ){
push @bad, grep(/^s[tw]\d+ms/, @files);
}elsif( !$atdrss && $tdrss ){
push @bad, grep(!/^s[tw]\d+ms/, @files);
}
my $gain = $self->{'INFO'}->is_gain($atype, $i);
my $offset = $self->{'INFO'}->is_offset($atype, $i);
if( $gain && !$offset ){
push @bad, grep( (/g[a-f0-9]{4}[_\.]/ && !/o[a-f0-9]{4}g/), @files );
}elsif( $offset && !$gain ){
push @bad, grep(/o[a-f0-9]{4}[_\.]/, @files);
}elsif( $offset && $gain ){
push @bad, grep(/o[a-f0-9]{4}g[a-f0-9]{4}[_\.]/, @files);
}
}
}
####################################
# Remove 'bad' files from the list
###################################
my %bad;
foreach (@bad){ $bad{$_}=1; }
@files = grep !$bad{$_}, @files;
}
}
###########################################
# Special treatment of repository files
###########################################
if( $self->{'INFO'}->is_repository($type, $inst) && @files > 1) {
my $best_time = 0;
my $best_file;
foreach (@files) {
if( /_([^_.]*)\./ && $1>$best_time && $1<=$index ){
$best_file = $_;
$best_time = $1;
}
}
return ($best_file);
}
return @files;
} elsif($type eq "telemetry") {
#####################################################
# telemetry files. Instrument gets translated into
# a range in APIDs.
#####################################################
if($inst eq "*") { return @files; }
my @instruments = $self->instrument_list($inst);
my @list=();
foreach (@files) {
##########################################
# get the instrument for the current file
##########################################
my $file = $self->remove_path($_);
my ($i, $dum2, $dum3) = $self->parse($file, $type);
########################################################
# check if the instrument is among the ones we asked for
########################################################
if(grep { $_ eq $i } @instruments ) { push @list, ($_); }
}
return @list;
} else {
########################################################
# for anything else we inherit the superclass behavior
# which does no filtering
########################################################
return ($self->SUPER::filter($list, $type,$inst,$index,$mode));
}
} # end of filter method
#########################################################################
#
#########################################################################
sub fetch_from_repository {
my $self = shift;
my $type = shift;
my @args = @_;
my $log = $self->log();
if($self->{REPOSITORY} ) {
######################################
# Truncate time to the nearest second
######################################
$args[2] = sprintf '%f', $args[2];
$args[2] =~ s/\..*$//;
my $file = $self->{REPOSITORY}->fetch($type, @args);
# $log->entry("File name returned from repository is $file .");
unless($file && -f $file){
$log->error(2, "No file returned from repository, type $type, index $args[2]");
return () unless -f $file;
return $file;
}
###################################################
# Remove time index from file name
###################################################
my $new_name = $file;
$new_name =~ s/_[^_.]*\././;
rename $file, $new_name;
return ($new_name) if wantarray();
return $new_name;
} else {
$log->entry("Not using shared repository");
return $self->get($type, @args);
}
}
#########################################################################
#
#########################################################################
sub export_to_repository {
my $self = shift;
my $type = shift;
my $inst = shift;
my @files = @_;
my $log = $self->log();
my $jobpar = $self->jobpar();
my $procpar = $self->procpar();
$inst = $self->instrument_code($inst);
my $repos_type = $self->{'INFO'}->{$inst}->{$type}->{'repository'};
delete $self->{'INFO'}->{$inst}->{$type}->{'repository'};
@files=$self->get($type, $inst, '*', '*') unless(@files);
$self->{'INFO'}->{$inst}->{$type}->{'repository'} = $repos_type;
if( $self->{REPOSITORY} && @files && $files[0] ) {
$log->entry("Exporting the following files to the shared repository: ".
join ' ', @files );
my $soft_version = $jobpar->read('softver');
my $cal_version = $jobpar->read('caldbver');
my $proc_version = $procpar->read('version');
my $seq_num = int($jobpar->read('seqprocnum'));
my $reprocess = $jobpar->read('reprocess');
############################################
# Rename the files to have the proper index
############################################
my @new_files;
foreach my $file (@files){
my $newname = $file;
my $fitsfile = Util::FITSfile->new($file, 0);
$fitsfile->begin_many_keywords();
$fitsfile->keyword('SOFTVER', $soft_version, 'HEASOFT and Swift versions');
$fitsfile->keyword('CALDBVER', $cal_version, 'CALDB index versions used');
$fitsfile->keyword('PROCVER', $proc_version, 'Processing script version' );
$fitsfile->keyword('SEQPNUM', $seq_num, 'Number of times the dataset processed' );
$fitsfile->keyword('REPROC', 'T', 'Is this from a bulk reprocessing run?') if $reprocess eq 'yes';
$fitsfile->end_many_keywords();
my $index;
if($repos_type eq 'OBSID'){
#########################
# Index by target number
#########################
$fitsfile->ext(0);
$index = $fitsfile->keyword('OBS_ID');
$index = substr($index, 2, 8);
$index =~ s/^0+//;
}elsif($repos_type eq 'TIME'){
###############################
# Index by time.
###############################
$fitsfile->ext(1);
$index = $fitsfile->keyword('TSTART');
######################################
# Truncate time to the nearest second
######################################
$index =~ s/\..*$//;
unless($index){
$log->error(1, "File $file does not have proper TSTART. Will not put in respository.");
next;
}
}else{
$log->error(2, "Repository type $repos_type not recognized.");
}
$newname =~ s/^([^\.]+)\.(.*)$/${1}_${index}.${2}/;
rename $file, $newname;
push @new_files, $newname;
}
$self->{REPOSITORY}->export($type, @new_files);
unlink @new_files;
}
}
#########################################################################
# parse the fields in a filename
#########################################################################
sub parse {
my $self=shift;
my $filename=shift;
my $type=shift;
if($self->isGeneric($type) ){
##################################################
# first we have to pull out the instrument code
##################################################
$filename =~ s/^s[tw]t?\d+//;
my $tdrss;
if(substr($filename,0,2) eq 'ms') {
##########################################################
# looks like there is a TDRSS field before the instrument
##########################################################
$filename =~ s/^ms//;
$tdrss = 1;
}
####################################################
# get various parts from the FilenameInfo registry
####################################################
my $inst = substr($filename, 0, 1);
my ($pre_suffix, $post_suffix) = $self->{INFO}->ext($type, $inst) =~ /(\w*)\.(\w+)/;
$pre_suffix .= 'o[a-f0-9]{4}' if $self->{INFO}->is_offset($type, $inst);
$pre_suffix .= 'g[a-f0-9]{4}' if $self->{INFO}->is_gain($type, $inst);
###############################
# now we can parse
###############################
my @parts;
if( $filename =~ /${pre_suffix}_\d+\./ ){
############################
# there is an index field
############################
@parts =
( $filename =~ /^([spbxuf])(.*)${pre_suffix}_(\d+)\.${post_suffix}/ );
}else{
###########################################
# no index field in the filename -
# so the "phantom index" defaults to 00
############################################
@parts = $filename =~ /([spbxuf])(.*)${pre_suffix}\.${post_suffix}/;
$parts[2]=0;
}
return (@parts);
} elsif($type eq "telemetry") {
###################################
# telemetry files
###################################
my @fields = split /_/, $filename;
my $mode =$fields[1];
my $apid = $fields[4];
$apid =~ s/\..*$//;
my $inst = $self->instrument_for_apid($apid);
return ($inst, $mode, $apid);
} else {
################################
# unknown type
###############################
$self->SUPER::parse($filename,$type);
}
} # end of parse method
#########################################################################
# returns the one character instrument name for a given APID
#########################################################################
sub instrument_for_apid {
my $self = shift;
my $apid = shift;
if( $apid <= 100 ) { return "s" }
if($apid >= 289 && $apid <= 571 ) { return "b" }
if($apid >= 576 && $apid <= 1151) { return "u" }
if($apid >=1152 && $apid <= 1439) { return "x" }
return "unknown";
} # end of instrument for apid method
#########################################################################
# Parse the path to see if this is a grism image
#########################################################################
sub is_grism {
my ($self, $path, $type) = @_;
my @parts = $self->parse($path, $type);
my $grism = $parts[1] =~ /^g(u|v)$/;
return $grism;
}
#########################################################################
# Return names of the sky image files, but not including the grism ones.
#########################################################################
sub getNonGrismSkyImages {
my ($self) = @_;
my @skyFiles = $self->get('filterimg', 'uvot', '*', '*');
my @nonGrism;
foreach my $skyFile (@skyFiles) {
next if $self->is_grism($skyFile, 'skyimage');
push(@nonGrism, $skyFile);
}
return @nonGrism;
}
############################################
# Convert between filter code and name or id
# as defined in UvotNames.pm
############################################
sub filter_name_for_code {
my ($self, $code) = @_;
my $codes = $Subs::UvotNames::filterCodes;
my $names = $Subs::UvotNames::filterNames;
for (my $i = 0; $i < @$codes; ++$i) {
if ($codes->[$i] eq $code) {
return $names->[$i];
}
}
return 'UNKNOWN';
}
sub filter_code_for_name {
my ($self, $name) = @_;
my $codes = $Subs::UvotNames::filterCodes;
my $names = $Subs::UvotNames::filterNames;
for (my $i = 0; $i < @$names; ++$i) {
if ($names->[$i] eq $name) {
return $codes->[$i];
}
}
return 'qq';
}
sub filter_id_for_code {
my ($self, $code) = @_;
my $codes = $Subs::UvotNames::filterCodes;
my $ids = $Subs::UvotNames::filterIDs;
for (my $i = 0; $i < @$codes; ++$i) {
if ($codes->[$i] eq $code) {
return $ids->[$i];
}
}
return 'UNKNOWN';
}
sub filter_code_for_id {
my ($self, $id) = @_;
my $codes = $Subs::UvotNames::filterCodes;
my $ids = $Subs::UvotNames::filterIDs;
for (my $i = 0; $i < @$ids; ++$i) {
if ($ids->[$i] eq $id) {
return $codes->[$i];
}
}
return 'qq';
}
######################################################
# Generate file names and only return those that exist
######################################################
sub getExisting
{
my ($self, @args) = @_;
my @gotten = $self->get(@args);
my @exist;
foreach my $name (@gotten) {
if (-f $name) {
push(@exist, $name);
}
}
return @exist;
}
1;