#!/usr/bin/perl -w

# MegasquirtAvr 2003
# wbo2 packet log data upload

if (@ARGV < 1) {
    usage();
}

sub usage
{
    print "usage:\n";
    print "\tperl bin/wbo2log.pl [:port]\n";
	print "[:port]\n";
	print "\t:COM1, :/dev/ttyS0\n\n";

    undef $ob if defined $ob;
    exit;
}

use vars qw( $OS_win $ob $port );

#$maxerr = 59;
$bootl_speed = 9600;
$timeout = 200;

BEGIN {
$OS_win = ($^O eq "MSWin32") ? 1 : 0;
if ($OS_win) {
  eval "use Win32::SerialPort";
  die "$@\n" if ($@);
} else {
  eval "use Device::SerialPort";
  die "$@\n" if ($@);
}
} # BEGIN


# Install like this: perl -MCPAN -e 'install Device::SerialPort'
# If you are running Windows 95 or later, you want the Win32::SerialPort
# module instead of this one. It has a compatible user interface.

process_param();

#print STDERR "using serialport:$port\n";
if($OS_win){
	$port = 'COM1' if !defined $port;
	$ob = Win32::SerialPort->new ($port);
} else {
	$port = '/dev/ttyS0' if !defined $port;
	$ob = Device::SerialPort->new ($port);
}
################################################################


serial_init();

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

sub serial_init
{
# found on http://members.aol.com/Bbirthisel/alpha.html
# should work on multiple platforms

die "Can't open serial port $port: $^E\n" unless ($ob);

# save the state of the comm port for later
$old_baud = $ob->baudrate;
$old_parity = $ob->parity;
$old_data = $ob->databits;
$old_stop = $ob->stopbits;
$old_hshake = $ob->handshake;

# try using saved configuration

$ob->baudrate($bootl_speed);
$ob->parity('none');
$ob->databits(8);
$ob->stopbits(1);
$ob->handshake('none');
$ob->binary(T);

# without this communication returned too few bytes sometimes
$ob->read_interval(100) if ($OS_win);

$ob->write_settings || undef $ob;

$tmout = $timeout;
# ok, now port is configured
}

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

$ob->baudrate(9600); # msavr speed
$ob->lookclear();
$ob->read_const_time(500);
comm_send('Man'); 
$b = srl_wait(8);
if ($b =~ /Hello>/) {
#    print "AVR said hello\n";
} else {
    print "No hello from AVR\n";
}
comm_send('mde03');
	%rimm = ();
	%dcmm = ();
	%himm = ();
	%pimm = ();
	%hcmm = ();
	min_max_reset(\%rimm);
	min_max_reset(\%dcmm);
	min_max_reset(\%himm);
	min_max_reset(\%pimm);
	min_max_reset(\%hcmm);

	$b = srl_wait(59 * $numbuf + 58);	# TODO: fix the 4096 byte limit...
	my @a = split /\nw\=/, $b;
	pop @a;
	foreach $samplebuf (@a){
		my $lenb = length($samplebuf);
		next if $lenb == 0;
		if($lenb == 61){
			processbuffer($samplebuf);
		} else {
			print STDERR "invalid buffer, length = $lenb\n";
		}
	}
comm_send($mde_exit);
printf("\n");
sleep(1);
$ob->purge_rx();
comm_send('bye');
$b = srl_wait(5);
if ($b =~ /Bye/) {
#   print "AVR said bye\n";
} else {
    print "No bye from AVR\n";
}

###############################################x

# restore state of comm port
$ob->baudrate($old_baud);
$ob->parity($old_parity);
$ob->databits($old_data);
$ob->stopbits($old_stop);
$ob->handshake($old_hshake);

undef $ob if defined $ob;

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

sub processbuffer
{
	my $b = shift;

	@array= split(//, $b);
	my $g;
	for($g=3; $g<=45; $g += 3) {   
		SSeSSe($g);
	} # for

	$g += 3; # 51
	my $Ri = bufword($g);
    print '# Ri='. $Ri. min_max(\%rimm, $Ri). "\n";

	$g += 2;	
	my $DC = bufword($g);
    print '# DC='. $DC. min_max(\%dcmm, $DC). "\n";

	$g += 2;	
	my $heater_integral = signednum(bufword($g));
    print '# heater_integral='. $heater_integral. min_max(\%himm, $heater_integral). "\n";

	$g += 2;	
	my $pump_integral = signednum(bufword($g));
    print '# pump_integral='. $pump_integral. min_max(\%pimm, $pump_integral). "\n";

	$g += 2;	
	my $heatercommand = ord($array[$g]);
    print '# heatercommand='. $heatercommand. min_max(\%hcmm, $heatercommand). "\n";

	printf("\n\n");
}
###########
# get back word value from 2 bytes of buffer, bigendian assumed
sub bufword
{
	my $g = shift;
	return ord($array[$g])*256 + ord($array[$g+1]);
}
#####
	#the time from OC2 edge (read from TCNT2 in 1/..?.. units) is stored 
	#in edgetime[i]; the result of that sampling will be stored from next 
	#irq invocation to sample[i+1]; 

	#we must send to gnuplot the sample[i+1] - sample[i] 
	#(let's call it jump[i])
	#actually the edgetime[i] jump[i] as x,y pairs
  
	# gnuplot> plot 'file' using $1:abs($2) title '...'

############################
# the buffer we get contains (Sample,Sample,edgetime)...
sub SSeSSe
{
# @array is global (yuck)
	my $g = shift;
	my $sample1 = ord($array[$g])*256 + ord($array[$g+1]);
	my $sample2 = ord($array[$g+3])*256 + ord($array[$g+4]);
	my $jump = $sample2 - $sample1;	# use abs() from gnuplot
	my $edgetime = ord($array[$g-1]);	# from the previous SSe
	print "$edgetime"."\t".$jump."\t".$sample1."\n";
}
###############
sub min_max
{
	my $hashref = shift;
	my $current = shift;
	$hashref->{min} = $current if $current < $hashref->{min};
	$hashref->{max} = $current if $current > $hashref->{max};
	return " min=$hashref->{min} max=$hashref->{max}";
}
################
sub min_max_reset
{
	my $hashref = shift;
	$hashref->{min} = 2000777000;
	$hashref->{max} = -2000777000;
}
#################################
sub process_param
{

#print "number of args:$#ARGV\n";
	$numbuf = 16; # default 
	$mde_exit = "mde00";	# turn off the wbo2
my $w;
	# number of args was checked earlier
   	while($w = shift @ARGV) {
	if ($w =~ /^:(\S+)$/){
		$port = $1;
	} elsif ($w =~ /^\_(\d+)$/){
		$numbuf = $1;
	} elsif ($w =~ /^mde[a-fA-F\d]{2}$/){
		$mde_exit = $w;
	}
	} # while
}

#######################
sub comm_send
{
    my $str = shift ;
    $count_out = $ob->write($str);
    warn "write failed\n" unless ($count_out);
    warn "write incomplete\n" if ($count_out != length($str));
    return $count_out;
}

#######################
sub comm_recv
{
    my $b = shift;
    ($count_in, $string_in) = $ob->read($b);
    warn "read unsuccessful\n" unless ($count_in == $b);
    return ($count_in, $string_in);
}

sub flush
{
	my $b = shift;
	$ob->read_const_time($tmout);
	($count_in, $string_in) = $ob->read($b);
	return ($count_in, $string_in);
}


#################################################
# what's the reply???
sub srl_wait
{
	my $realcharcnt = shift;
	if (defined($realcharcnt)) {
	    $charcnt = $realcharcnt;
	} else {
	    $charcnt = 1;
	}
# TODO: if no reply comes within 2 sec, we should start saying 'v' -s or sg.
# finally report the problem (or let the user ctrl-c)
	# $ob->read($in_);
	$ob->read_const_time($tmout);
	my ($cnt, $in_) = comm_recv($charcnt);
	#sysread (SRL, $in_, 1, 0) or die "cannot readback from SRL $!";
	
	if(defined $in_){
		#print "got back $cnt:$in_ :".ord(substr($in_, 0, 1));
		#print ' '.ord(substr($in_, 1, 1)) if(length($in_) > 1);
		#print "\n";
	} else {
		print "nothing\n";
	}
	
	return $in_ if defined($realcharcnt) && ($realcharcnt > 0);
	return undef if $in_ eq "\r";	# success 
	return 1;					# error
}
sub signednum
{
        my $u = shift;
        return $u if $u < 32768;
        return $u - 65535;
}


#
