#!/usr/bin/perl

use strict;
use warnings;

my $debug = 1;

sub hexdump ($) {
	return join ' ', map { sprintf("%02x", ord($_)); } split //, $_[0];
}

my %decoder = (
1 => sub { 
	# V DC measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = $_[11];
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);
	
	my $range;
	if( ($_[17] & 0x03) == 0x01 ) {
		$range = "Auto";
	} else {
		$range = "manual";
	}
	print "$value V DC, range $range\n";
	},
2 => sub {
	# V AC measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = $_[11];
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	print STDERR "WARNING: negative AC voltage is impossible\n" if $negative;
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	my $dc = ($_[15] == 3 ? "+DC" : "");
	
	my $range;
	if( ($_[17] & 0x03) == 0x01 ) {
		$range = "Auto";
	} else {
		$range = "manual";
	}
	print "$value V AC$dc Trms, range $range\n";
	},
3 => sub {
	# mV measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = 3; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	print "$value mV\n";
	},
4 => sub {
	# Ohm measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7];  # only 4 digits on resistance
	my $value = "";
	
	my $decimal_point = $_[11];
	my $unit = (" ", "k", "M")[ int(($decimal_point+1) / 3) ];
	$decimal_point = ($decimal_point + 1) % 3 + 1;

	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	print STDERR "WARNING: negative resistance is impossible\n" if $negative;
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);
	
	my $range;
	if( ($_[17] & 0x03) == 0x01 ) {
		$range = "Auto";
	} else {
		$range = "manual";
	}
	print "$value ${unit}Ohm, range $range\n";
	},
5 => sub {
	# Capacity measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";

	my $decimal_point = $_[11];
	my $unit = ("n", "u", "m")[ int(($decimal_point) / 3) ];
	$decimal_point = ($decimal_point) % 3 + 1;
	
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);
	
	my $range;
	if( ($_[17] & 0x03) == 0x01 ) {
		$range = "Auto";
	} else {
		$range = "manual";
	}
	print "$value ${unit}F, range $range\n";
	},
6 => sub {
	# degrees Celcius measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = 4; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	print "$value ºC\n";
	},	
7 => sub {
	# uA measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = $_[11] + 3;
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	my $acdc;
	if( $_[15] == 0 ) { $acdc = "DC"; }
	elsif( $_[15] == 3 ) {
		print STDERR "WARNING: negative AC current is impossible\n" if $negative;
		$acdc = "AC+DC Trms";
	} elsif( $_[15] == 1 ) {
		print STDERR "WARNING: negative AC current is impossible\n" if $negative;
		$acdc = "AC Trms";
	}
	
	my $range;
	if( ($_[17] & 0x03) == 0x01 ) {
		$range = "Auto";
	} else {
		$range = "manual";
	}
	print "$value uA $acdc, range $range\n";
	},
8 => sub {
	# mA measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = $_[11] + 2;
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	my $acdc;
	if( $_[15] == 0 ) { $acdc = "DC"; }
	elsif( $_[15] == 3 ) {
		print STDERR "WARNING: negative AC current is impossible\n" if $negative;
		$acdc = "AC+DC Trms";
	} elsif( $_[15] == 1 ) {
		print STDERR "WARNING: negative AC current is impossible\n" if $negative;
		$acdc = "AC Trms";
	}
	
	my $range;
	if( ($_[17] & 0x03) == 0x01 ) {
		$range = "Auto";
	} else {
		$range = "manual";
	}
	print "$value mA $acdc, range $range\n";
	},
9 => sub {
	# A measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = 2; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	my $acdc;
	if( $_[15] == 0 ) { $acdc = "DC"; }
	elsif( $_[15] == 3 ) {
		print STDERR "WARNING: negative AC current is impossible\n" if $negative;
		$acdc = "AC+DC Trms";
	} elsif( $_[15] == 1 ) {
		print STDERR "WARNING: negative AC current is impossible\n" if $negative;
		$acdc = "AC Trms";
	}
	
	my $range;
	if( ($_[17] & 0x03) == 0x01 ) {
		$range = "Auto";
	} else {
		$range = "manual";
	}
	print "$value A $acdc, range $range\n";
	},
a => sub { 
	# Resistance-beep measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7];  # 4 digits only
	my $value = "";
	my $decimal_point = 3; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	print STDERR "WARNING: negative resistance is impossible\n" if $negative;
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	print "$value Ohm - continuity measurment\n";
	},
b => sub {
	# Diode junction voltage measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9];
	my $value = "";
	my $decimal_point = 1; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	print STDERR "WARNING: negative junction voltage is impossible\n" if $negative;
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	print "$value V - junction\n";
	},
c => sub {
	# Frequency measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9];
	my $value = "";
	
	if( $_[16] eq 'b' ) {
		my $decimal_point = 3;
	
		$value .= join('', @digit[0..$decimal_point-1]);
		$value .= ".";
		$value .= join('', @digit[$decimal_point..$#digit]);

		print " $value % duty cycle\n";
	} else {
		my $decimal_point = $_[11];
		my $unit = (" ", "k", "M")[ int(($decimal_point+1) / 3) ];
		$decimal_point = ($decimal_point + 1) % 3 + 1;
	
		my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
		print STDERR "WARNING: negative frequency is impossible\n" if $negative;
		$value = ($negative ? "-" : " ");
		$value .= join('', @digit[0..$decimal_point-1]);
		$value .= ".";
		$value .= join('', @digit[$decimal_point..$#digit]);
	
		my $range;
		if( ($_[17] & 0x03) == 0x01 ) {
			$range = "Auto";
		} else {
			$range = "manual";
		}
	
		print "$value ${unit}Hz, range $range\n";
	}
	
	},
d => sub {
	# degrees Farenheit measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = 4; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	print "$value ºF\n";
	},	
e => sub {
	# Power measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = 4; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	print "$value W\n";
	},
f => sub {
	# % of 4-20mA measurement
	my @digit = map { tr/ac/ L/; $_; } @_[1,3,5,7,9]; 
	my $value = "";
	my $decimal_point = 3; # fixed point
	my $negative = (($_[17] & 0x04) == 0x04 ? 1 : 0);
	print STDERR "WARNING: negative percentage is impossible\n" if $negative;
	$value = ($negative ? "-" : " ");
	$value .= join('', @digit[0..$decimal_point-1]);
	$value .= ".";
	$value .= join('', @digit[$decimal_point..$#digit]);

	print "$value mA%\n";
	},
);


my $state = "WAIT_FOR_SYNC";
while(1) {
	read STDIN, my $data, 1;
	die("EOF\n") if $data eq "";

	if( $state eq "WAIT_FOR_SYNC" ) {
		if( $data eq "\x0d" ) {
			$state = "SYNC1";
		} else {
			print STDERR "OOS: ", unpack("H2", $data), "\n" if $debug;
		}
	} elsif( $state eq "SYNC1" ) {
		if( $data eq "\x8a" ) {
			$state = "DATA";
		} else {
			$state = "WAIT_FOR_SYNC";
			print STDERR "OOS: ", unpack("H2", $data), "\n" if $debug;
		}
	} elsif( $state eq "DATA" ) {
		$state = "WAIT_FOR_SYNC";
		read STDIN, my $data2, 8;

		my @now = localtime(time);
		printf("%04u-%02u-%02u-%02u-%02u-%02u : ", # print timestamp
			$now[5]+1900, $now[4]+1, $now[3], $now[2], $now[1], $now[0]);
		my @nibble = split //, unpack("H*", $data . $data2);
		print join('', @nibble) . " : ";
		my $decoder = $decoder{ $nibble[13] };
		if( defined $decoder ) {
			$decoder->(@nibble);
		} else {
			print "\n";
		}
	} else {
		die "Should never get here; state = $state";
	}
}
