#!/usr/bin/perl

use Getopt::Std;
use strict;
use POSIX; # for isatty

our %opt;

$opt{p}="/dev/ttyUSB0";

getopts("i:o:p:hz", \%opt);
if ($opt{h})
{
    print <<EOF;
This program queries a Fluke 189 with the QD 2 command to fetch its
data logs.  Logs are printed out in a CSV format, and ignore some of the
fields we haven't sorted out yet.

Note: This script assumes that your machine is little-endian, and Perl
does not provide the required primitives to easily make it independent.

Options are:

    -p port	Port to use (currently $opt{p})
    -z		Zero times, presenting everything as time from first reading
    -i file	Read input from existing hex dump, overrides -p
    -o file	When reading from port, also write raw dump to file
EOF
    exit;
}

if ($opt{i} and $opt{o}) {
    die "Using both -i and -o makes no sense.  Try 'cp'.";
}

alarm(3);
$SIG{ALRM} = sub {die "No response!"};

if ($opt{i})
{
    open INPUT, $opt{i} or die "Could not open $opt{i}";
} else 
{
    open SER, "+<$opt{p}" or die "Could not open $opt{p}";
    select(SER);$|=1;select(STDOUT);
    print SER "QD 2\r";

    my $response; read(SER, $response, 2);
    die "No data!" if (substr($response,0,1) eq "5");
    die "Unknown error $response!" if (substr($response,0,1) ne "0");
    read(SER, $response, 3);
    die "Unexpected response $response" unless $response eq "QD,";
}

if ($opt{o}) {
    open OUTPUT, ">$opt{o}" or die "Could not open $opt{o} for writing";
}

my $header;
if ($opt{i}) {
    $header=readhex(18);
} else {
    read(SER, $header, 18);
}

print OUTPUT unpack("H*", $header),"\n" if $opt{o};

printf "%8s, %8s, %7s, %7s, %8s\n",
    "Start", "End", "Min", "Max", "Value";

my $firsttime;

my $row;
while(1)
{
    alarm(1); # reset now that we're getting data

    if ($opt{i}) {
	$row = readhex(32);
    } else {
	read(SER, $row, 32);
    }
    print OUTPUT unpack("H*", $row),"\n" if $opt{o};

    my ($begintime, $dec, undef, $min, $max, $actual, undef,
	    $divisor, $status, $status2, $endtime)=
	unpack ("LCClllllCCL", $row);

    if ($opt{z})
    {
	$firsttime = $begintime if not defined $firsttime;
	$begintime -= $firsttime;
	$endtime -= $firsttime;
    }

    if ($dec >= 0x10) # I don't know what this means
    {
	$dec=0;
    }


    my $minvalue = $min >= 0x70000000 ? 9**999 : $min/10**$dec;
    my $maxvalue = $max >= 0x70000000 ? 9**999 : $max/10**$dec;
    
    my $actualvalue;
    if ($divisor == 0) {
	$actualvalue=9**999;
    } else {
	$actualvalue = $actual >= 0x70000000 ? 9**999 :
	$actual/$divisor/(10**$dec);
    }

    printf "%8.1f, %8.1f, %7.6g, %7.6g, %8.7g\n",
	$begintime/10,
	$endtime/10,
	$minvalue,
	$maxvalue,
	$actualvalue
	unless $status2 != 0x01;
    # Appears to indicate that it is left over from a previous run.
    if ($status2 != 0x01 and fileno(STDOUT) > 0)
    {
	close(STDOUT);
	print STDERR "\nDone, but junk at end.";
    }

    # Normally ends with 0x81, but I have also seen 0xC1.
    last if $status & 0x80;

    print STDERR "." if (not POSIX::isatty(fileno(STDOUT)));
}

########################################################

sub readhex
{
    my $line = <INPUT>;
    $line =~ s/ //g;

    # Ignore anything at front, which might be padding or locations from od
    my $data = substr($line, length($line)-$_[0]*2-1, $_[0]*2);

    return pack ("H*", $data);
}
