package Finalize;
use strict;
use English;
use Carp;
use vars
qw(@ISA $VERSION $name $author $date $version @instList $numberOfStreams);
@ISA = qw(Module);
use ModuleResources;
use BlackBoard;
# Declare identity, version, author, date, etc.
$name = __PACKAGE__;
$VERSION = '2.19';
$version = $VERSION;
$author = 'Richard West,Dean Hinshaw,Duncan John Fyfe,Ian Stewart,Duncan Law-Green,Ed Chapin';
$date = '2013-06-11';
#
# ChangeLog
# =========
#
# Version 2.19 - 2013-06-11 (EC)
# ------------
#
# + Merge changes from SOC version.
# + This version includes for the first time the changes made to this module
# in Slew pipeline (2.06->2.18).
#
# version 2.18 - 2010-08-17 DLG
# ------------
#
# + SLEW ONLY: Try forcing DPSS skip via blackboard flags
#
# version 2.17 - 2010-08-10 DLG
# ------------
#
# + SLEW ONLY: Check for ignored('SlewProcess')
#
# version 2.16 - 2010-06-17 DLG
# ------------
#
# + SLEW ONLY: Additional addattibute call to add standard headers to PINDEX file
#
# version 2.15 - 2010-06-15 DLG
# ------------
#
# + SLEW ONLY: Added block to attempt to hack OBSERVER keyword in product output
#
# version 2.14 - 2010-06-02 DLG
# ------------
#
# + SLEW ONLY: Now uses 'ppsslewsumm' instead of 'ppssumm'
#
# version 2.13 - 2010-05-25 DLG
# ------------
#
# + SLEW ONLY: Modified OBJECT keyword to 'Slew' in StampKeywords section
#
# version 2.12 - 2010-05-17 DLG
# ------------
#
# + SLEW ONLY: Absorb StampKeywords into slew Finalize pro tem
#
# version 2.11 - 2010-05-13 DLG
# ------------
#
# + SLEW ONLY: Modify flow control to follow StampKeywords module call
#
# version 2.10 - 2010-02-10 DLG
# ------------
#
# + Use latest version of eslewchain (1.4) which generates agreed PCMS-compliant filenames
#
# version 2.09 - 2010-01-25 DLG
# ------------
#
# + Convert slew band numbers to PCMS standard
#
# version 2.08 - 2010-01-13 DLG
# ------------
#
# + Iterate over slew bands, rename step images
#
# version 2.07 - 2009-12-16 DLG
# ------------
#
# + Started slew products cleanup and generation of PCMS-compliant names
#
# version 2.06 - 2009-06-23 DLG
# ------------
#
# + Added start dependence on SlewProcess
#
# version 2.05 - 2006-12-05 DJF
# ------------
#
# + Need to call newFile to create the XCORRE product file name and db entry if it has not been
# returned by ACDS. If we don't use new file there will be no DB entry and it won't be packed
# into the .XMM file.
#
# version 2.04 - 2006-03-23 DJF
# ------------
#
# + Replace dependence on StampEndKeywords. That module has been removed
#
# version 2.03 - 2006-03-23 DJF
# ------------
#
# + Compression now tests for previously compressed files remivng the need for a specialised prodPack.
#
# version 2.02 - 2005-12-15 DJF
# ------------
#
# Separated product compression and product packing in module resources.
# Finalize amended to take adavantage of ths wrt ppssumm HTM pages
#
# version 2.01 - 2005-12-02 DJF
# ------------
#
# + Changed dependence from StampKeywords to StampEndKeywords
#
# version 2.00 - 2005-11-21 IMS
# ------------
#
# + Add explicit perl module headers. Previously these were supplied
# at compile time. This will make debugging and extending the modules
# through additional perl libraries easier.
#
# + Code layout made more uniform with perltidy
# + Standerdized date format (ccyy-mm-dd)
# + Standerdized author list to use comma separator
# + Make use of perl $VERSION magic. Now $Version = version = ''
#
# Version 1.22 - 2004-02-21 (DJF)
# ------------
#
# + SAS 6.0 ppssumm needs a filename of the catpage
# parameter even if the file does not exist.
#
#
# Version 1.21 - 2003-07-17 (DJF)
# ------------
#
# + Adapted ppssumm command line to deal with absent
# catalogue index file.
#
# Version 1.20 - 2003-06-20 (DJF)
# ------------
#
# + Changed to take account of ppssumm 3.0+ which now
# generates all summary pages in one go.
#
# Version 1.19 - 2002-02-26 (DH)
# ------------
#
# + added additional output to logs to indicate progress
#
# Version 1.18 - 2002-02-26 (DH)
# ------------
#
# + Bug fixes for 1.17.
#
# Version 1.17 - 2002-02-20 (DJF)
# ------------
#
# + Change newFile to findFile for index files to be created.
# These files are now created for obssumm and populated here.
#
# Version 1.16 - 2001-11-04 (DH)
# ------------
#
# + Remove dependence on SendDPSS in evaluateRules.
#
# Version 1.15 - 2001-11-03 (DH)
# ------------
#
# + Remove sequenceComplete call from the end of the module.
#
# Version 1.14 - 2001-03-21 (DH)
# ------------
#
# + Replace getFile call for SSC logo to findFile call.
#
# Version 1.13 - 2001-03-16 (DH)
# ------------
#
# + Print out version number in performAction() for
# tracking purposes.
#
# Version 1.12 - 2001-03-13 (DH)
# ------------
#
# + Add prodPack(), which creates the XFTS file for sending
# to the SOC, to the end of the processing.
#
# Version 1.11 - 2001-03-08 (DH)
# ------------
#
# + Move obs summary file production to VerifyODF module.
#
# Version 1.10 - 2001-02-19 (DH)
# ------------
#
# + Put in the new obssumm parameter useinfile.
#
# Version 1.09 - 2001-01-17 (DH)
# ------------
#
# + Change name of used logo from 'SSC LOGO 1'
# to 'SSC LOGO 2' to avoid conflicts with the ACDS.
#
# Version 1.08 - 2000-12-19 (DH)
# ------------
#
# + Fix bug with path to logo files.
#
#
# Version 1.07 - 2000-12-14 (DH)
# ------------
#
# + First production version.
#
# Declare list of instruments this module is interested in
@instList=qw(all);
# Number of streams
sub numberOfStreams {
return 1;
}
# Rules method
sub evaluateRules {
if ( $ENV{PCMS_ISSLEW} )
{
start()
if allComplete( module => 'SlewProcess' )
}
else
{
start()
if allComplete( module => 'ReceiveACDS' )
and allComplete( module => 'RGSProducts' )
and allComplete( module => 'RGSMakeFluxed' )
and allComplete( module => 'OMSourceCombine' )
and allComplete( module => 'OMMosaic' )
and allComplete( module => 'OMFastAnalyse' )
and allComplete( module => 'EPICSourceProducts' )
}
}
# Action method
sub performAction {
info("Module version number: $version");
my ( @name, @type, @value, @comment, @withcomment );
if ( $ENV{PCMS_ISSLEW} )
{
# Set up parameters for slew product tidy ----------------
# Modified for eslewchain-1.4, PCMS-"compliant" filenames
my $rootdir = $ModuleResources::Seqdb->{'directories'}{'root'};
info("Sequence root directory: $rootdir");
# move files to intermediate subdir
# Does the new eslewchain still create these?
# moveFile(source => 'histo.fits',
# destination => 'intermediate/histo.fits');
# moveFile(source => 'image.fits',
# destination => 'intermediate/image.fits');
# moveFile(source => 'spectrum.fits',
# destination => 'intermediate/spectrum.fits');
# moveFile(source => "temp_sp_*",
# destination => 'intermediate/');
moveFile(source => "*Badpixels.ds",
destination => 'intermediate/') unless ignored( module => 'SlewProcess', stream => 1 );
# Identify output products and assign PCMS-compatible filenames
# Attitude file:P*OBX000ATTTSR0000.FITS
my $obsid = $ENV{'SAS_RAND_SEED'};
my ($attfile) = glob("$rootdir/P*OBX000ATTTSR0000.ds");
my $attdest = newFile(class => 'product'
,instrument => 'ob'
,content => 'SLEW ATTITUDE FILE'
,format => 'FITS'
);
moveFile(source => "$attfile"
,destination => "$attdest") unless ignored( module => 'SlewProcess', stream => 1 );
my $sourcename = sprintf("P%010s",$obsid);
doCommand(
'addattribute', set => $attdest
, attributename => 'OBJECT'
, attributetype => 'string'
, stringvalue => "$sourcename"
# , attributecomment => '"Object name"'
) unless ignored(module => 'SlewProcess', stream => 1 );
# Single raw events file: P*PNS003SLEVLI0000.FITS
my ($rawfile) = glob("$rootdir/*ImagingEvts.ds");
# my ($rawfile) = glob("$rootdir/P*PNS003PIEVLI0SLW.ds");
my $rawdest = newFile(class=>'product'
,instrument => 'epn'
,exp_id => 'S003'
,content => 'SLEW SINGLE RAW EVENT LIST'
,format => 'FITS'
);
moveFile(source => "$rawfile"
,destination => "$rawdest") unless ignored( module => 'SlewProcess', stream => 1 );
my $sourcename = sprintf("P%010s",$obsid);
doCommand(
'addattribute', set => $rawdest
, attributename => 'OBJECT'
, attributetype => 'string'
, stringvalue => "$sourcename"
# , attributecomment => '"Object name"'
) unless ignored( module => 'SlewProcess', stream => 1 );
# filtered event files per step: P*PNS003PIEVLI0*.FITS
my $filtfile;
my $filtdest;
my $filtstep = 0;
my @filtlist = glob("$rootdir/P*PNS003PIEVLI0*.ds");
foreach $filtfile (@filtlist) {
$filtdest = newFile(class => 'product'
,instrument => 'epn'
,exp_id => 'S003'
,content => 'SLEW STEP FILTERED EVENT LIST'
,format => 'FITS'
,src_num => "$filtstep" # this needs to be in decimal, not hex
);
moveFile(source => "$filtfile"
,destination => "$filtdest");
my $sourcename = sprintf("P%010s_%03x",$obsid,$filtstep);
doCommand(
'addattribute', set => $filtdest
, attributename => 'OBJECT'
, attributetype => 'string'
, stringvalue => "$sourcename"
# , attributecomment => '"Object name"'
);
$filtstep++;
}
# Slew step band exposure map: P*PNS003EXPMAPbiii.FITS
# Loop over slew bands
my $slewband;
my $stepfile;
my $stepdest;
foreach $slewband ( '6', '7', '8' ) {
$filtstep = 0;
my @steplist = glob("$rootdir/P*PNS003EXPMAP${slewband}*.ds");
foreach $stepfile (@steplist) {
my $stepdest = newFile(class => 'product'
, instrument => 'epn'
, exp_id => 'S003'
, content => 'SLEW STEP EXPOSURE MAP'
, format => 'FITS'
, src_num => "$filtstep"
, band => "$slewband"
);
moveFile(source => "$stepfile"
,destination=> "$stepdest");
my $sourcename = sprintf("P%010s_%03x",$obsid,$filtstep);
doCommand(
'addattribute', set => $stepdest
, attributename => 'OBJECT'
, attributetype => 'string'
, stringvalue => "$sourcename"
# , attributecomment => '"Object name"'
);
$filtstep++;
}
}
# Slew step band images: P*PNS003IMAGE_biii.FITS
# Loop over slew bands
foreach $slewband ( "6", "7", "8" ) {
$filtstep = 0;
my @steplist = glob("$rootdir/P*PNS003IMAGE_${slewband}*.ds");
foreach $stepfile (@steplist) {
my $stepdest = newFile(class => 'product'
, instrument => 'epn'
, exp_id => 'S003'
, content => 'SLEW STEP IMAGE'
, format => 'FITS'
, src_num => "$filtstep"
, band => "$slewband"
);
moveFile(source => "$stepfile"
,destination=> "$stepdest");
# Set OBJECT header to source step name (Nora's request)
my $sourcename = sprintf("P%010s_%03x",$obsid,$filtstep);
doCommand(
'addattribute', set => $stepdest
, attributename => 'OBJECT'
, attributetype => 'string'
, stringvalue => "$sourcename"
# , attributecomment => '"Object name"'
);
$filtstep++;
}
}
# Unfiltered step images: P*PNS003UNFDAT8*FITS
$filtstep = 0;
my $unffile;
my $unfdest;
my @unflist = glob("$rootdir/P*PNS003UNFDAT8*.ds");
foreach $unffile (@unflist) {
$unfdest = newFile(class => 'product'
,instrument => 'epn'
,exp_id => 'S003'
,content => 'UNFILTERED SLEW STEP IMAGE'
,format => 'FITS'
,src_num => "$filtstep" # this needs to be in decimal, not hex
,band => '8'
);
moveFile(source => "$unffile"
,destination => "$unfdest");
# Set OBJECT header to source step name (Nora's request)
my $sourcename = sprintf("P%010s_%03x",$obsid,$filtstep);
doCommand(
'addattribute', set => $unfdest
, attributename => 'OBJECT'
, attributetype => 'string'
, stringvalue => "$sourcename"
# , attributecomment => '"Object name"'
);
$filtstep++;
}
# Tidy: Drop all ds files from root directory
my @unlinklist = glob("$rootdir/*.ds");
unlink @unlinklist unless ignored( module => 'SlewProcess', stream => 1 );
# --------------------------------------------------------
# +++ StampKeywords +++
# Import portion of StampKeywords module to run here for slew
# Add basic headers to all FITS product files
info("Starting StampKeywords subroutine...");
# Fetch list of product files
my @list = findFile(
class => 'product'
, 'format' => 'FITS'
);
# We really really really want to avoid running addattribute on ACDS products.
# This can happen if we restart a post ACDS sequence at a pre ACDs stage and apply --acds_magic=1
@list = grep { $_ !~ /CAX000/} @list;
return success() unless @list;
# Fetch observation properties
my $obs = getObservationProperties();
my $odsver = getOdsVer();
my $odfver = getOdfVer();
# Use any old file to get global properties (like seq id)
my $info = fileInfo( class => 'product', name => $list[0] );
# Set values of keywords
# Reset OBJECT keyword to 'Slew' for slew sequences
# push @name, 'OBJECT';
# push @type, 'string';
# push @value, 'Slew';
# push @comment, 'Object name';
# push @withcomment, 'y';
push @name, 'SEQ_ID';
push @type, 'string';
push @value, $info->{sequence};
push @comment, 'Pipeline sequence';
push @withcomment, 'y';
push @name, 'REVOLUT';
push @type, 'string';
push @value, $obs->{orbit};
push @comment, 'Satellite Revolution Number';
push @withcomment, 'y';
push @name, 'PROCDATE';
push @type, 'string';
push @value, $obs->{procdate};
push @comment, 'Processing date';
push @withcomment, 'y';
push @name, 'PROCREV';
push @type, 'string';
push @value, $obs->{procrevision};
push @comment, 'Processing revision';
push @withcomment, 'y';
push @name, 'PPSVERS';
push @type, 'string';
push @value, $obs->{ppsversion};
push @comment, 'PPS configuration';
push @withcomment, 'y';
push @name, 'SASVERS';
push @type, 'string';
push @value, $obs->{sasversion};
push @comment, 'SAS version';
push @withcomment, 'y';
push @name, 'ODSVER';
push @type, 'string';
push @value, "$odsver";
push @comment, 'ODS version';
push @withcomment, 'y';
push @name, 'ORIGIN';
push @type, 'string';
push @value, 'Leicester/SSC';
push @comment, 'Origin of FITS file';
push @withcomment, 'y';
# Added block to hack OBSERVER keyword name in slews - DLG
push @name, 'OBSERVER';
push @type, 'string';
push @value, 'XMM-Newton';
push @comment, 'Observer';
push @withcomment, 'y';
push @name, 'ODFVER';
push @type, 'string';
push @value, sprintf('%03d',$odfver);
push @comment, 'ODF_VERSION';
push @withcomment, 'y';
# CONTENT keyword must be last in the list, as we set its value in
# the loop
push @name, 'CONTENT';
push @type, 'string';
push @comment, 'Contents of file';
push @withcomment, 'y';
# Loop over each file
foreach my $file (@list)
{
next unless fileExists( file => $file );
my $l_info = fileInfo( class => 'product', name => $file );
# Set the other keywords
doCommand(
'addattribute', set => $file
, attributename => [@name]
, attributetype => [@type]
, stringvalue => [ @value, uc( $l_info->{content} ) ]
, attributecomment => [@comment]
)
or return exception();
}
info("StampKeywords subroutine complete!");
# --------------------------------------------------------
}
# Fetch name of product index file
my $prodIndex=newFile(class => 'product'
,instrument => 'ob'
,content => 'PPS product index'
);
# Compress all product .FIT to .FTZ
&compressProducts()
or return exception()
;
info("Product index file: $prodIndex");
# Create index file
makePPSIndex(file => $prodIndex)
or return exception()
;
if ( $ENV{PCMS_ISSLEW} )
{
# Set other keywords for product index file
my $l_info = fileInfo( class => 'product', name => $prodIndex );
doCommand(
'addattribute', set => $prodIndex
, attributename => [@name]
, attributetype => [@type]
, stringvalue => [ @value, uc( $l_info->{content} ) ]
, attributecomment => [@comment]
)
or return exception();
}
&compress(file => $prodIndex);
# The product index might be compressed.
# Re-find the filename
$prodIndex=findFile(class => 'product'
,instrument => 'ob'
,content => 'PPS product index'
);
info("Found product index file: $prodIndex");
# Get logo
my $logoFrom = &logoFile();
my $logoTo=newFile(class => 'product'
,instrument => 'ob'
,content => 'SSC LOGO 2'
,format => 'PNG'
);
copyFile(source => $logoFrom
,destination => $logoTo
);
# Remove directories from file name
my $obssummLogo = $logoTo;
$obssummLogo =~ s/^.*\///;
copyFile(source => $logoTo
,destination => $obssummLogo
);
# Summary file names
my $obsSummary=newFile(class => 'product'
,instrument => 'ob'
,content => 'PPS OBSERVATION SUMMARY'
,format => 'HTML'
);
my $ppsSummary=newFile(class => 'product'
,instrument => 'ob'
,content => 'PPS RUN SUMMARY'
,format => 'HTML'
);
my $epicSummary=newFile(class => 'product'
,instrument => 'epic'
,content => 'EPIC observation summary'
,format => 'HTML'
);
my $rgsSummary=newFile(class => 'product'
,instrument => 'rgs'
,content => 'RGS observation summary'
,format => 'HTML'
);
my $omSummary=newFile(class => 'product'
,instrument => 'om'
,content => 'OM observation summary'
,format => 'HTML'
);
# ppssumm command line parameters
my @ppssumm = (
pindex => $prodIndex
,obspage => $obsSummary
,ppspage => $ppsSummary
,epxpage => $epicSummary
,omxpage => $omSummary
,rgxpage => $rgsSummary
,logofile => $obssummLogo
);
# The catalogue page may not exist
my @catSummary=findFile(class => 'product'
,instrument => 'cat'
,content => 'MAIN CROSS CORRELATION PAGE'
,format => 'HTML'
);
my $catSummary;
if ( @catSummary )
{
$catSummary = $catSummary[0];
}
else
{
# Create a DB entry for this file. It MUST be allocated to the SendACDS module
# otherwise restarting Finalize will delete this file.
# This is a work around made necessary by an early cat2 bug.
# This does mean this file cannot be recreated by running the Finalize module.
$catSummary = newFile( class => 'product'
, instrument => 'cat'
, content => 'MAIN CROSS CORRELATION PAGE'
, format => 'HTML'
, module => 'SendACDS'
);
}
push ( @ppssumm , ( catpage => $catSummary) );
# Fetch name of PPS log file. Ditto the comment above.
my $logFile=newFile(class => 'product'
,instrument => 'ob'
,content => 'PPS script log'
,format => 'ASCII'
);
# Create summary files
info("Creating Summary Files");
if ( $ENV{PCMS_ISSLEW} )
{
doCommand('ppsslewsumm'
,@ppssumm
) or return exception();
# Hack fix for product index file path in PPS summary file
Exec::system("sed -i 's/PINDEX0000.FIT/PINDEX0000.FTZ/g' $ppsSummary");
}
else
{
doCommand('ppssumm'
,@ppssumm
) or return exception();
}
# Fetch the name of index html file (INDEX.HTM)
my $htmlIndex=newFile(class => 'product'
,instrument => 'ob'
,content => 'PPS OBSERVATION SUMMARY'
,name => 'INDEX.HTM'
,format => 'HTML'
);
# Its just a copy of the obssumm page
copyFile(source => $obsSummary, destination => $htmlIndex);
# Create log file
createLogScript(file => $logFile)
or return exception()
;
ModuleResources::Createprodset();
# Pack the products up into an XFTS package
prodPack()
or return exception()
;
# prodPacknoCompression()
# or return exception()
# ;
if ( $ENV{PCMS_ISSLEW} )
{
# ------------------------------------------------
# set blackboard flags forcing DPSS skip
my %senddpss = ( module => 'SendDPSS', stream => 1, instrument => 'all');
BlackBoard::raiseFlag( %senddpss, value => 'complete');
my %dpssdts = ( module => 'DPSSDTS', stream => 1, instrument => 'all');
BlackBoard::raiseFlag( %dpssdts, value => 'complete');
my %receivedpss = ( module => 'ReceiveDPSS', stream => 1, instrument => 'all');
BlackBoard::raiseFlag( %receivedpss, value => 'complete');
# ------------------------------------------------
}
#
return success();
}
1;