Index: trunk/tools/eam/burntool.20250520/Makefile
===================================================================
--- trunk/tools/eam/burntool.20250520/Makefile	(revision 42878)
+++ trunk/tools/eam/burntool.20250520/Makefile	(revision 42878)
@@ -0,0 +1,60 @@
+
+help:
+	@echo "make options: install"
+
+BIN	=	~/src/bin/rawfix
+SRC	= 	src
+
+PANTASKS = \
+rawfix.pt \
+rawfix.advance.pt \
+rawfix.ckchip.pt \
+rawfix.fixchip.pt \
+rawfix.md5sum.pt \
+rawfix.md5chk.pt \
+rawfix.burntool.pt
+
+# interfaces with the rawfix database
+DB_PROG = \
+check.rawfix.sh \
+queue.rawfix.sh \
+update.rawfix.sh
+
+# these are in order used by the tasks
+PERL_PROG = \
+advance.rawfix.pl \
+check_chip_locations.pl \
+get_hosts_fixchip.sh \
+fix_chip_locations.pl \
+get_hosts_md5s.sh \
+get_md5s_instances.pl \
+check_md5s_dateobs.pl \
+psvideostats.sh \
+ipp_apply_burntool_psvideo.pl
+
+# user tools, but not used by the tasks
+TOOLS = \
+update.bnodes.sh \
+check_night_status.pl \
+neb_check_location.pl \
+update_chip_stats.pl \
+finish.fixchip.sh
+
+# not done, or not needed now
+MISC = \
+triple.fix.pl \
+fix_chip_failures.pl \
+create.rawfix.sh
+
+PROGRAMS = $(PANTASKS) $(DB_PROG) $(PERL_PROG) $(TOOLS)
+
+install: $(PROGRAMS)
+
+$(PROGRAMS): % : $(BIN)/%
+
+$(BIN)/% : $(SRC)/%
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	rm -f $(BIN)/$*
+	cp $(SRC)/$* $(BIN)/$*
+
+
Index: trunk/tools/eam/burntool.20250520/src/burntool.pt
===================================================================
--- trunk/tools/eam/burntool.20250520/src/burntool.pt	(revision 42878)
+++ trunk/tools/eam/burntool.20250520/src/burntool.pt	(revision 42878)
@@ -0,0 +1,333 @@
+## pantasks scripts to manage the raw exposure data repair / cleanup
+## this is for the initial chip instance checks
+## use the top-level rawfix.pt to load
+
+if (not($?RAWFIX_BURNTOOL_INIT))
+  book init burntool.book
+  book init burnhost.book
+  $RAWFIX_BURNTOOL_INIT = 1
+end
+
+$MAX_BURNHOST = 2
+
+# select nights in state burntool.new
+# output: burntool.book
+task nights.burntool.load
+  host local
+  periods -poll $TPOLL
+  periods -exec $TEXEC
+  periods -timeout 30
+
+  stdout $LOGDIR/burntool.load.stdout.log  
+  stderr $LOGDIR/burntool.load.stderr.log
+
+  # only run one of these tasks at a time
+  npending 1
+
+  task.exec
+    command check.rawfix.sh state burntool.new 
+  end
+
+  task.exit 0
+    # convert the stdout 'queue' to entries in a book
+    # fields in the table will become words in the page
+    queue2book -raw-columns -key dateobs -uniq -setword pantaskState INIT -setword pantasksQuality OK stdout burntool.book
+    book delpage burntool.book -key pantaskState DONE
+  end
+
+  # exit values other than 0:
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# output: burnhost.book
+task nights.burntool.loadhosts
+  periods      -poll $RPOLL
+  periods      -exec $REXEC
+  periods      -timeout 60
+  host		local
+  npending 1
+  # note: the tasks which return their results via stdout
+  # seem to fail if run remotely.  
+
+  stdout $LOGDIR/burntool.loadhosts.stdout.log  
+  stderr $LOGDIR/burntool.loadhosts.stderr.log
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $REXEC
+
+    book npages burntool.book -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new nights in burntool.book (pantaskState == INIT)
+    book getpage burntool.book 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword burntool.book $pageName pantaskState RUN
+    book getword burntool.book $pageName dateobs -var DATEOBS
+    book getword burntool.book $pageName iter    -var ITER
+    option $pageName
+    # save the page so we can use it below
+
+    substr $DATEOBS 0 4 YEAR
+
+    # write an entry point in the log for this night
+    sprintf tmpline "%s/%s/%s/log.burntool.runhosts.v%d" $mypath $YEAR $DATEOBS $ITER
+    date -var mydate
+    exec echo "\\# start runhosts $mydate" >> $tmpline
+
+    # get_hosts_fixchip.sh selects the most used machine at ITC
+    # the command returns these fields: dateobs stage fixhost chipID
+    # NOTE: stage == ITER
+    command get_hosts_fixchip.sh $mypath $DATEOBS $ITER
+  end
+
+  task.exit 0
+    # convert the stdout 'queue' to entries in a book
+    # fields in the table will become words in the page
+    # queueprint stdout
+    queuesize stdout -var Nhosts
+    queue2book -raw-columns -key dateobs:chipID -uniq -setword pantaskState INIT stdout burnhost.book
+    book setword burntool.book $options:0 NHOST {$Nhosts - 1}
+    book setword burntool.book $options:0 NDONE 0
+    # book list
+    # book listbook burntool.book
+    # use the queuesize (Nhosts) to track how many jobs are done / to be done
+  end
+
+  # exit values other than 0:
+  task.exit    default
+    showcommand failure
+    book setword burntool.book $options:0 pantasksQuality FAIL
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword burntool.book $options:0 pantasksQuality CRASH
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword burntool.book $options:0 pantasksQuality TIMEOUT
+  end
+end
+
+task nights.burntool.run
+  periods      -poll $RPOLL
+  periods      -exec $REXEC
+  periods      -timeout 1800
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $REXEC
+
+    book npages burnhost.book -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new nights in burnhost.book (pantaskState == INIT)
+    book getpage burnhost.book 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword burnhost.book $pageName pantaskState RUN
+    book getword burnhost.book $pageName dateobs -var DATEOBS
+    book getword burnhost.book $pageName iter    -var ITER
+    book getword burnhost.book $pageName fixhost -var BURNHOST
+    book getword burnhost.book $pageName chipID  -var CHIP_ID
+
+    substr $DATEOBS 0 4 YEAR
+    substr $DATEOBS 4 2 MONTH
+    substr $DATEOBS 6 2 DAY
+    sprintf STARTDATE "%s/%s/%s" $YEAR $MONTH $DAY
+    sprintf ENDDATE "%s/%s/%s,23:59:59" $YEAR $MONTH $DAY
+
+    option $pageName $DATEOBS $YEAR
+    # save DATEOBS so we can find the md5sum.book entry
+    # also save YEAR for output log
+
+    stdout $mypath/$YEAR/$DATEOBS/log.burntool.run.$CHIP_ID.v$ITER
+    stderr $mypath/$YEAR/$DATEOBS/log.burntool.run.$CHIP_ID.v$ITER
+
+   
+    host $BURNHOST
+#   host -required $BURNHOST
+#   too many jobs wanted the same machines so the processing was clogged.
+#   allow them to go elsewhere if needed.
+    
+    ## check if this host needs to be launched
+    if (not($?host:$BURNHOST)) 
+      echo "host not defined, creating : $BURNHOST"
+      $host:$BURNHOST = 0
+    end
+    if ($host:$BURNHOST < $MAX_BURNHOST) 
+      control host add $BURNHOST
+      $host:$BURNHOST ++
+    end
+
+    sprintf psvidOutput "%s/%s/%s" $mypath $YEAR $DATEOBS
+
+    # if we are able to run the 'exec', use a short retry time to launch all outstanding jobs
+    periods -exec 0.05
+    command ipp_apply_burntool_psvideo.pl --rerun --dateobs_begin $STARTDATE --dateobs_end $ENDDATE --class_id $CHIP_ID --dbname gpc1 --psvidOutput $psvidOutput
+    ## NOTE the rerun option is set here
+  end
+
+  task.exit 0
+    nights.burntool.exit OK
+  end
+
+  # exit values other than 0:
+  task.exit    default
+    nights.burntool.exit FAIL
+    showcommand failure
+  end
+
+  task.exit    crash
+    nights.burntool.exit CRASH
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    nights.burntool.exit TIMEOUT
+    showcommand timeout
+  end
+end
+
+# output: burnhost.book
+task nights.burntool.clean
+
+  periods      -poll $RPOLL
+  periods      -exec $REXEC
+  periods      -timeout 60
+  npending 1
+
+  stdout $LOGDIR/burntool.clean.stdout.log  
+  stderr $LOGDIR/burntool.clean.stderr.log
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $REXEC
+
+    book npages burntool.book -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for completed nights in burntool.book (pantaskState == STOP)
+    book getpage burntool.book 0 -var pageName -key pantaskState STOP
+    if ("$pageName" == "NULL") break
+
+    # book listpage burntool.book $pageName
+
+    # book setword burntool.book $pageName pantaskState RUN
+    book getword burntool.book $pageName dateobs -var DATEOBS
+    book getword burntool.book $pageName pantasksQuality -var QUALITY
+
+    option $pageName
+    # save the page so we can use it below
+
+    if ("$QUALITY" == "OK")
+      command update.rawfix.sh $DATEOBS burntool.done 
+    else
+      command update.rawfix.sh $DATEOBS burntool.fail 
+    end
+  end
+
+  task.exit 0
+    # now that the entry is set to done in the db, remove the pantasks book entry
+    echo "cleaning this page: $options:0"
+    book setword burntool.book $options:0 pantaskState DONE
+  end
+
+  # exit values other than 0:
+  task.exit    default
+    showcommand failure
+    book setword burntool.book $options:0 pantasksQuality FAIL
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword burntool.book $options:0 pantasksQuality CRASH
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+    book setword burntool.book $options:0 pantasksQuality TIMEOUT
+  end
+end
+
+######################### UTILITIES #################################
+
+macro burntool.loadhosts.state
+  if ($0 != 2)
+    echo "USAGE: burntool.loadhosts.state (state)"
+    break
+  end
+
+  task nights.burntool.loadhosts
+    active $1
+  end
+end
+
+macro burntool.clean.state
+  if ($0 != 2)
+    echo "USAGE: burntool.clean.state (state)"
+    break
+  end
+
+  task nights.burntool.clean
+    active $1
+  end
+end
+
+macro burntool.run.state
+  if ($0 != 2)
+    echo "USAGE: burntool.run.state (state)"
+    break
+  end
+
+  task nights.burntool.run
+    active $1
+  end
+end
+
+macro nights.burntool.exit
+  if ($0 != 2)
+    echo "USAGE: nights.burntool.exit (quality)"
+    break
+  end
+
+  book setword burnhost.book $options:0 pantaskState DONE
+  book getword burntool.book $options:1 NHOST -var Nhosts
+  book getword burntool.book $options:1 NDONE -var Ndone
+  book getword burntool.book $options:1 iter  -var ITER
+  $Ndone ++
+  book setword burntool.book $options:1 NDONE $Ndone
+
+  sprintf tmpline "%s/%s/%s/log.burntool.runhosts.v%d" $mypath $options:2 $options:1 $ITER
+  if ("$1" != "OK")
+    book setword burntool.book $options:1 pantasksQuality $1
+    exec echo "failure $1 $options:0" >> $tmpline
+  else
+    exec echo "success $1 $options:0" >> $tmpline
+  end 
+  if ($Ndone == $Nhosts)
+    book setword burntool.book $options:1 pantaskState STOP
+  end
+  book delpage burnhost.book $options:0
+end
+
Index: trunk/tools/eam/burntool.20250520/src/burntool_night_chip.pl
===================================================================
--- trunk/tools/eam/burntool.20250520/src/burntool_night_chip.pl	(revision 42878)
+++ trunk/tools/eam/burntool.20250520/src/burntool_night_chip.pl	(revision 42878)
@@ -0,0 +1,192 @@
+#!/usr/bin/env perl
+
+# this program generates the list of real (on disk) files for burntool
+# these will be loaded into a book in pantasks and used to generate the
+# individual chip jobs
+
+# USAGE: burntool_night_chip.pl --dbname gpc1 --class_id XY00 --dateobs YYYY/MM/DD
+
+use warnings;
+use strict;
+use Carp;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use Sys::Hostname;
+my $host = hostname();
+my $date = `date`;
+print "\n\n";
+print "Starting script $0 on $host at $date\n\n";
+
+my ( $class_id, $dateobs, $dbname, $rerun, $verbose, $save_temps);
+GetOptions(
+    'class_id=s'        => \$class_id, # chip identifier
+    'dateobs=s'         => \$dateobs, # night of interest
+    'dbname|d=s'        => \$dbname, # Database name
+    'rerun'             => \$rerun,
+    'verbose'           => \$verbose,   # Print to stdout
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --class_id --dateobs --dbname",
+          -exitval => 3,
+          ) unless
+    defined $class_id and
+    defined $dateobs and
+    defined $dbname;
+
+# dateobs is of the form YYYY/MM/DD
+unless ($dateobs =~ m|\d\d\d\d/\d\d/\d\d|) { die "dateobs must be of the form YYYY/MM/DD\n"; }
+my $dateobs_begin = "$dateobs,00:00:00";
+my $dateobs_end   = "$dateobs,23:59:59";
+
+# XXX for now, just GPC1
+my $camera = "GPC1";
+
+my $missing_tools;
+my $regtool      = can_run('regtool')         or (warn "Can't find regtool"         and $missing_tools = 1);
+my $funpack      = can_run('funpack')         or (warn "Can't find funpack"         and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump')    or (warn "Can't find ppConfigDump"    and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+# IPP configuration (including nebulous)
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die("Unable to set up");
+
+# Define list of images to examine.
+my $command = "$regtool -processedimfile";
+$command .= " -class_id $class_id";
+$command .= " -dateobs_begin $dateobs_begin";
+$command .= " -dateobs_end   $dateobs_end";
+$command .= " -ordered_by_date";
+$command .= " -dbname $dbname" if defined $dbname;
+
+my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    run ( command => $command, verbose => $verbose);
+unless ($success) {
+    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+    &my_die("Unable to perform regtool: $error_code", $class_id, $dateobs_begin, $dateobs_end, $PS_EXIT_SYS_ERROR);
+}
+
+# The output of the regtool command above is a collection of rawImfile metadata blocks, one per exposure.
+# We need to process these one at a time to keep the exposures separated.
+my @whole = split /\n/, (join "", @$stdout_buf);
+my @single = ();
+my $files;
+while ( scalar @whole > 0 ) {
+    # copy the lines to a new array
+    my $value = shift @whole;
+    push @single, $value;
+    if ($value =~ /^\s*END\s*$/) {
+	# at the end of a metadata block, pass the array, containing a
+	# single exposure worth of data for this chip, to the parser
+        push @single, "\n";
+
+        my $list = parse_md_list( $mdcParser->parse( join( "\n", @single ) ) );
+        &my_die("Unable to parse output from regtool", $class_id, $dateobs_begin, $dateobs_end, $PS_EXIT_PROG_ERROR) unless
+            defined $list;
+        push @{ $files }, @$list;
+        @single = ();
+    }
+}
+
+my $REALRUN = 1;
+my $burntoolStateGood = 15;
+
+# Process each file in turn, checking to see if it needs processing, and doing it all in a single pass
+my $processNext = 0;
+my $previousTable = '';
+
+foreach my $file (@$files) {
+    my $exp_id     = $file->{exp_id};
+    my $exp_name   = $file->{exp_id};
+    my $state      = $file->{burntool_state};
+    my $data_state = $file->{data_state};
+
+    if ($data_state eq "lossy") {
+	print "## skipping $exp_name, $class_id : data_state $data_state\n";
+	next;
+    }
+
+    my $rawImfile = $file->{uri};
+    my $rawImfileReal = $ipprc->file_resolve($rawImfile, 0);
+    if (not defined $rawImfileReal) {
+        print "DEAD: $rawImfile, no valid instances found\n";
+	next;
+    }
+
+    # instantiate the burntable instance for use by the actual analysis script
+    my $outTable  = $file->{uri};
+    if ($burntoolStateGood == 15) {
+        $outTable =~ s/fits$/burn.v15.tbl/;	    
+    } else {
+        $outTable =~ s/fits$/burn.tbl/;  	    
+    }
+    my $outTableReal = $ipprc->file_resolve($outTable, 1);
+
+    my $process = 1;
+    my $previousTableStyle = 0;
+
+    if ($state == -2) { &my_die("Aborting as $rawImfile has modified pixel data!"); }
+    if ($state == +1 * $burntoolStateGood) {
+        $process = 0;
+        $previousTableStyle = 1;
+    }
+    if ($state == -1 * $burntoolStateGood) {
+        $process = 0;
+        $previousTableStyle = -1;
+    }
+    if ($rerun) { $process = 1; }
+
+    if ($REALRUN == 0) {
+        print "## Pretending to process as instructed!\n";
+        $process = 1;
+    }
+
+    if (($process == 1) || ($processNext == 1)) {
+	my $status;
+        $processNext = 1;
+
+	# we now have $rawImfileReal, $previousTable, $outTableReal
+	print STDERR "$rawImfileReal $previousTable $outTableReal\n";
+
+        $previousTableStyle = -1;
+    }
+
+    ### XXX I think I can make the assumption that we are saving the tables as separate files since we
+    ### want to get to burntool state -15 (v15)
+    $previousTable = "in=$outTableReal";
+}
+
+exit 0;
+
+sub vsystem {
+    my $command = shift;
+    my $realrun = shift;
+
+    print "$command\n";
+
+    my $status = 0;
+    if ($realrun) {
+        $status = system ($command);
+    }
+
+    return $status;
+}
+
+sub my_die {
+    my $message = shift;
+    printf STDERR "$message\n";
+    exit 1;
+}
Index: trunk/tools/eam/burntool.20250520/src/burntool_one_chip.pl
===================================================================
--- trunk/tools/eam/burntool.20250520/src/burntool_one_chip.pl	(revision 42878)
+++ trunk/tools/eam/burntool.20250520/src/burntool_one_chip.pl	(revision 42878)
@@ -0,0 +1,303 @@
+#!/usr/bin/env perl
+
+# this program is used to run 'burntool' on images for a single chip
+# for all exposures for a given time period.
+# USAGE: ipp_apply_burntool.pl --dbname gpc1 --class_id XY00 --dateobs_begin YYYY/MM/DD --dateobs_end YYYY/MM/DD
+
+use warnings;
+use strict;
+use Carp;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+use Sys::Hostname;
+my $host = hostname();
+my $date = `date`;
+print "\n\n";
+print "Starting script $0 on $host at $date\n\n";
+
+my ( $class_id, $dateobs_begin, $dateobs_end, $convert, $dbname, $logfile, $verbose, $save_temps, $rerun, $psvidOutput);
+GetOptions(
+    'class_id=s'        => \$class_id, # chip identifier
+    'dateobs_begin=s'   => \$dateobs_begin, # exposure date/time range start
+    'dateobs_end=s'     => \$dateobs_end, # exposure date/time range stop
+    'dbname|d=s'        => \$dbname, # Database name
+    'psvidOutput|d=s'   => \$psvidOutput, # output base for psvideo data
+    'logfile=s'         => \$logfile,
+    'rerun'             => \$rerun,
+    'verbose'           => \$verbose,   # Print to stdout
+    'save-temps'        => \$save_temps, # Save temporary files?
+
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --class_id --dateobs_begin --dateobs_end --dbname --psvidOutput",
+          -exitval => 3,
+          ) unless
+    defined $class_id and
+    defined $dateobs_begin and
+    defined $dateobs_end and
+    defined $psvidOutput and
+    defined $dbname;
+
+# burntool is only valid for GPC1, do not allow substitutes
+my $camera = "GPC1";
+
+my $missing_tools;
+my $regtool      = can_run('regtool')         or (warn "Can't find regtool"         and $missing_tools = 1);
+my $funpack      = can_run('funpack')         or (warn "Can't find funpack"         and $missing_tools = 1);
+my $burntool     = can_run('burntool')        or (warn "Can't find burntool"        and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump')    or (warn "Can't find ppConfigDump"    and $missing_tools = 1);
+my $nebXattr     = can_run('neb-xattr')       or (warn "Can't find neb-xattr"       and $missing_tools = 1);
+my $nebreplicate = can_run('neb-replicate')   or (warn "Can't find neb-replicate"   and $missing_tools = 1);
+my $psvideostats = can_run('psvideostats.sh') or (warn "Can't find psvideostats.sh" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# XXX check we can access / create directory named $psvidOutput
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+# IPP configuration (including nebulous)
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die("Unable to set up");
+
+$ipprc->redirect_output($logfile) if $logfile;
+
+my $burntoolStateGood = 15;
+if (0) { 
+    # Determine the value of a "good" burntool run.
+    # XXX this program should always generate the latest version
+    # but the processing system must be told to use an earlier version 
+    # until the newer version is validated.  Perhaps split these.
+
+    my $config_cmd = "$ppConfigDump -camera $camera -get-key BURNTOOL.STATE.GOOD";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run ( command => $config_cmd, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform ppConfigDump: $error_code", 0, 0, $class_id, $PS_EXIT_SYS_ERROR);
+    }
+
+    my $recipeData = $mdcParser->parse(join "", @$stdout_buf) or
+	&my_die("Unable to parse metadata config doc", 0, 0, $class_id, $PS_EXIT_SYS_ERROR);
+
+    my $burntoolStateGood = 999;
+    foreach my $cfg (@$recipeData) {
+	if ($cfg->{name} eq 'BURNTOOL.STATE.GOOD') {
+	    $burntoolStateGood = $cfg->{value};
+	}
+    }
+    if ($burntoolStateGood == 999) {
+	&my_die("Failed to determine BURNTOOL.STATE.GOOD", $burntoolStateGood, $class_id, 0, $PS_EXIT_SYS_ERROR);
+    }
+}
+my $outState = -1 * abs($burntoolStateGood);
+
+print "Target burntool version: $burntoolStateGood\n";
+if ($burntoolStateGood != 15) { &my_die ("burntool state is not set to 15\n"); }
+
+# Define list of images to examine.
+my $command = "$regtool -processedimfile";
+$command .= " -class_id $class_id";
+$command .= " -dateobs_begin $dateobs_begin";
+$command .= " -dateobs_end $dateobs_end";
+$command .= " -ordered_by_date";
+$command .= " -dbname $dbname" if defined $dbname;
+
+my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    run ( command => $command, verbose => $verbose);
+unless ($success) {
+    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+    &my_die("Unable to perform regtool: $error_code", $class_id, $dateobs_begin, $dateobs_end, $PS_EXIT_SYS_ERROR);
+}
+
+# EAM : I am not sure why this is implemented in an esoteric way rather than using the MD parsing API
+my @whole = split /\n/, (join "", @$stdout_buf);
+my @single = ();
+my $files;
+while ( scalar @whole > 0 ) {
+    my $value = shift @whole;
+    push @single, $value;
+    if ($value =~ /^\s*END\s*$/) {
+        push @single, "\n";
+
+        my $list = parse_md_list( $mdcParser->parse( join( "\n", @single ) ) );
+        &my_die("Unable to parse output from regtool", $class_id, $dateobs_begin, $dateobs_end, $PS_EXIT_PROG_ERROR) unless
+            defined $list;
+        push @{ $files }, @$list;
+        @single = ();
+    }
+}
+
+#my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+#    &my_die("Unable to parse metadata", $class_id, $dateobs_begin, $dateobs_end, $PS_EXIT_SYS_ERROR);
+#
+#my $files = parse_md_list($metadata) or
+#    &my_die("Unable to parse metadata list", $class_id, $dateobs_begin, $dateobs_end, $PS_EXIT_SYS_ERROR);
+
+my $REALRUN = 1;
+
+# Process each file in turn, checking to see if it needs processing, and doing it all in a single pass
+my $processNext = 0;
+my $previousTable = '';
+
+foreach my $file (@$files) {
+    my $exp_id = $file->{exp_id};
+    my $state = $file->{burntool_state};
+
+    my $rawImfile = $file->{uri};
+    my $rawImfileReal = $ipprc->file_resolve($rawImfile, 0);
+    if (not defined $rawImfileReal) {
+        print "DEAD: $rawImfile, no valid instances found\n";
+	next;
+    }
+
+    my $outTable  = $file->{uri};
+    
+    my $data_state = $file->{data_state};
+    if ($data_state eq "lossy") {
+	print "## skipping $rawImfile : data_state $data_state\n";
+	next;
+    }
+
+    if($burntoolStateGood == 15) {
+        $outTable =~ s/fits$/burn.v15.tbl/;	    
+    } else {
+        $outTable =~ s/fits$/burn.tbl/;  	    
+    }
+    my $outTableReal = $ipprc->file_resolve($outTable, 1);
+
+    my $process = 0;
+    my $previousTableStyle = 0;
+
+    if ($state == 0) {
+        $process = 1;
+    }
+    elsif ($state == -1) {
+        $process = 1;
+    }
+    elsif ($state == -3) {
+        $process = 1;
+    }
+    elsif ($state == -2) {
+        &my_die("Aborting as $rawImfile has modified pixel data!");
+    }
+    elsif ($state == $burntoolStateGood) {
+        $process = 0;
+        $previousTableStyle = 1;
+    }
+    elsif ($state == -1 * $burntoolStateGood) {
+        $process = 0;
+        $previousTableStyle = -1;
+    }
+    else {
+        $process = 1;
+    }
+    if ($rerun) {
+	$process = 1;
+    }
+    if ($REALRUN == 0) {
+        print "##Pretending to process as instructed!\n";
+        $process = 1;
+    }
+    if (($process == 1)||($processNext == 1)) {
+	my $status;
+        $processNext = 1;
+
+        my $tempfile = new File::Temp ( TEMPLATE => "$file->{exp_name}.XXXX",
+                                        DIR => '/tmp/',
+                                        UNLINK => !$save_temps,
+                                        SUFFIX => '.fits');
+        my $tempPixels = $tempfile->filename;
+
+	# extract local copy of thie chip imfile
+        $status = vsystem ("$funpack -S $rawImfileReal > $tempPixels", $REALRUN);
+        if ($status) {
+	    print "FAIL: funpack fails for $rawImfile\n";
+            &my_die("failed on funpack",$exp_id,$class_id);
+        }
+
+        # Run burntool
+        if ($previousTable ne '') {
+            $status = vsystem ("$burntool $tempPixels $previousTable out=$outTableReal tableonly=t persist=t", $REALRUN);
+        } else {
+            $status = vsystem ("$burntool $tempPixels out=$outTableReal tableonly=t persist=t", $REALRUN);
+        }
+        if ($status) {
+	    print STDERR "BURN: burntool failed for $exp_id $class_id\n";
+            &my_die("failed on burntool",$exp_id,$class_id);
+        }
+        &my_die("Unable to find output file: $outTableReal", $exp_id, $class_id) unless $ipprc->file_exists($outTableReal);
+
+        $status = vsystem ("$nebXattr --write $outTable user.copies:2",$REALRUN);
+	$status = vsystem ("$nebreplicate --set_copies 2 $outTable",$REALRUN);
+        if ($status) {
+            &my_die("failed to replicate imfile");
+        }
+
+	# update database with new burntool state
+        $status = vsystem ("$regtool -dbname $dbname -updateprocessedimfile -exp_id $exp_id -class_id $class_id -burntool_state $outState", $REALRUN);
+        if ($status) {
+            &my_die("failed to update imfile");
+        }
+
+	# generate psvideostats
+	my $psvidOutfile = "$psvidOutput/psvideostats.$class_id.dat";
+        $status = vsystem ("$psvideostats $tempPixels $psvidOutfile $file->{exp_name} $class_id", $REALRUN);
+        if ($status) {
+	    # die or warn?
+            &my_die("failed to update imfile");
+        }
+
+        $previousTableStyle = -1;
+
+        print "\n";
+    }
+
+    if ($previousTableStyle == 1) {
+        $previousTable = "infits=$rawImfileReal";
+    }
+    elsif ($previousTableStyle == -1) {
+        $previousTable = "in=$outTableReal";
+    }
+    else {
+        die "previousTableStyle undefined!\n";
+    }
+
+}
+
+exit 0;
+
+sub vsystem {
+    my $command = shift;
+    my $realrun = shift;
+
+    print "$command\n";
+
+    my $status = 0;
+    if ($realrun) {
+        $status = system ($command);
+    }
+
+    return $status;
+}
+
+sub my_die {
+    my $message = shift;
+    printf STDERR "$message\n";
+    if ($#_ != -1) {
+        my $exp_id = shift;
+        my $class_id = shift;
+        vsystem("$regtool -dbname $dbname -updateprocessedimfile -exp_id $exp_id -class_id $class_id -burntool_state -3",1);
+    }
+    print STDERR "updated database with burntool state -3\n";
+    exit 1;
+}
