#!/usr/bin/perl -I Measurement/blib/lib

# basic waveform data acquisition program for LeCroy WaveRunner 640
# oscilloscope.  Uses the 'Connection::Trace' facility
# to record the conversation with the scope, so that
# the full information can be reconstructed in offline
# analysis.
#
# usage:
#   DAQ_WR640 [options] outputfile
# options:
#       -a --address=ip         ip address or hostname of scope
#
#       -S --Setup=n           load scope setup #n before starting acquistion
#       -n --nevents=#         number of events to store [def: 0=>infinity]
#       -I --ID='str'          ID string, stored in trace file
#       -c --channels='chans'  channels to read [def: all visible]
#       -s --self=T            force trigger every T seconds
#       -f --force             force overwrite of output file
#       -d --debug             turn on debugging
#       -q --quiet             suppress info messages
#       -h -? --help           show usage
#
# Default usage records the visible
# waveforms whenever the scope has a trigger (however the scope is set
# to trigger) and continues until stopped by a "kill" signal or
# Control-C.
#
# See the Lab::Connection::Trace module for how data and
# run comments are recorded to the data file.
#
# See the Lab::Data::Analysis::WaveRun module for code that
# can read and analyze the resulting output data file
#
use Lab::Generic::CLOptions;    # reclaim --debug switch
use Lab::Instrument::WR640;
use Lab::Connection::Trace;
use Carp;
use Getopt::Long qw(:config bundling auto_version no_ignore_case);
use Time::HiRes qw(sleep gettimeofday);
use Data::Dumper;
use strict;

our $DEBUG   = $Lab::Generic::CLOptions::DEBUG;
our $VERSION = '3.544';
our $VERBOSE = 1;
our $TSTART;

#
# handle forced stop/interrupt
#
our $SHUTDOWN         = 0;
our $SHUTDOWN_TIMEOUT = 30;

sub stopreq {
    $SHUTDOWN = 1;
    alarm($SHUTDOWN_TIMEOUT);
}

sub muststop {
    die("timeout after shutdown requested");
}

$SIG{TERM} = \&stopreq;
$SIG{INT}  = \&stopreq;
$SIG{ALRM} = \&muststop;

main();

sub main {

    my $nev = 0;
    my $id;
    my $chans;
    my (@acqch);

    my $selfdelay;

    my $force = 0;
    my $outfile;

    my $address;
    my $help;
    my $quiet = 0;
    my $setup;

    Getopt::Long::GetOptions(
        "nevents|n=s"    => \$nev,
        "ID|Id|id|i|I=s" => \$id,
        "channels|c=s"   => \$chans,
        "self|s=s"       => \$selfdelay,
        "quiet|q"        => \$quiet,
        "Setup|S=s"      => \$setup,

        "force|f" => \$force,

        "address|a=s" => \$address,
        "debug|d+"    => \$DEBUG,
        "h|?|help"    => \$help,
    );

    if ( defined($help) ) {
        usage();
        exit(0);
    }

    $VERBOSE = !$quiet;

    $outfile = shift(@ARGV);
    if ( defined($outfile) ) {
        if ( -e $outfile && !$force ) {
            croak("output file exists! use --force to overwrite");
        }
    }
    else {
        croak("missing output file parameter");
    }
    print "Sending output to $outfile\n" if $VERBOSE;

    if ( $nev <= 0 && $VERBOSE ) {
        print "Infinite running, use Control-C or 'kill $$' to stop\n";
    }

    if ( !defined($address) ) {
        croak("scope address needed");
    }

    OpenTraceFile($outfile);

    my $args = {};
    $args->{connection_type} = 'VICP::Trace';
    $args->{remote_addr}     = $address if defined $address;
    $args->{debug}           = $DEBUG;

    my $s = new Lab::Instrument::WR640($args);
    croak("error opening WR640") unless defined $s;

    print_errors( $s, "initial" ) if $DEBUG;
    $s->connection->Comment("ID:$id") if defined($id);
    $s->connection->Comment("FORCED_TRIGGER delay=$selfdelay")
        if defined($selfdelay);

    $s->recall($setup) if defined($setup);

    print "Setting up for acquisition..." if $VERBOSE;
    print_errors( $s, "in DAQ setup" ) if $DEBUG;

    $s->get_setup();    # records setup, plus fills cache
    print_errors( $s, "in DAQ setup" ) if $DEBUG;

    my (@want) = (qw(C1 C2 C3 C4));
    if ( defined($chans) ) {
        my (@l) = split( /\s*,\s*/, $chans );    # list requested chans
        my (%hch);                               # hash of chans
        foreach (@l) {
            if (/^(ch|c)?([1-4])$/i) {
                $hch{"C$2"} = 1;
            }
            else {
                carp("invalid channel '$_' requested");
            }
        }
        @want = ( sort( keys(%hch) ) );
    }

    $s->connection->MuteTrace(1) unless $DEBUG;
    foreach my $ch (@want) {
        push( @acqch, $ch ) if $s->get_visible($ch);
        print_errors( $s, "in DAQ setup" ) if $DEBUG;
    }
    $s->connection->MuteTrace(0) unless $DEBUG;

    print_errors( $s, "after DAQ setup" ) if $DEBUG;

    $s->write('TRMD SINGLE');
    sleep(10);    # wait for self-cal to complete
    print "...ready to go!\n" if $VERBOSE;
    StartRun();

    my $event = 0;
    while ( ( $nev <= 0 || $event != $nev ) && !$SHUTDOWN ) {
        print "\tRead... " if $VERBOSE;
        if ( defined($selfdelay) ) {
            sleep($selfdelay);
            $s->write('FRTR');
        }
        else {
            $s->write('ARM');
        }

        MuteTrace(1) unless $DEBUG;
        my $n = 10;    # don't need all the BUSY? checks.
        while ( !$SHUTDOWN ) {

            #$s->write('WAIT 1');
            sleep(1);
            my $inr = $s->query('INR?');
            $inr =~ s/^INR\s*(\d+)/$1/i;
            $inr += 0;
            last if ( $inr & 0x01 ) != 0;
            last if $n-- < 0;
        }
        MuteTrace(0) unless $DEBUG;

        my $tev = gettimeofday();
        $event++;
        NextEvent();
        print "Event ", $event, " \@ t=$tev ", scalar( localtime($tev) ),
            " .. reading"
            if $VERBOSE;

        foreach my $ch (@acqch) {
            $s->get_waveform($ch);
        }

        print_errors( $s, "after event" ) if $DEBUG;
        print "\n" if $VERBOSE;
    }

    print_errors( $s, "after last event" ) if $DEBUG;

    #cleanup

    $s->connection->MuteTrace(1) unless $DEBUG;
    print_errors( $s, "after cleanup" ) if $DEBUG;
    $s->connection->MuteTrace(0) unless $DEBUG;
    StopRun();

    my $tend = gettimeofday();
    print "STOP $event events @ t=$tend ", scalar( localtime($tend) ), "\n"
        if $VERBOSE;

}

sub usage {
    print "$0 [options] outputfile\n";
    print "  OPTIONS:\n";
    print "  -n --nevents=#          number of events to take,0=infinity\n";
    print "  -I --ID='string'        store ID with run header\n";
    print "  -c --channels='chans'   channels to read (def: all visible)\n";
    print "  -s --self=T             self-trigger, every T seconds\n";
    print "  -S --Setup=N            load scope setup N (1..10)\n";
    print "  -f --force              force overwrite of output file\n";
    print "  -a --address=N          hostname/ipaddress of scope\n";
    print "  -d --debug              turn on debug\n";
    print "  -q --quiet              quiet mode, supress messages\n";
    print "  -h -? --help            this text\n";
}

sub print_errors {
    my $s    = shift;
    my $info = shift;

    my $dirty = 0;
    my (@msgs) = $s->get_error();
    foreach my $msg (@msgs) {
        next if ( $msg =~ /^\s*$/ );
        print "$info:\n" if defined($info) && !$dirty;

        my $n = length($msg);
        $n = 20 if $n > 20;
        $msg = substr( $msg, 0, $n );

        print "\t$msg\n";
        Comment("$msg");
        $dirty = 1;
    }
}
