#!/usr/bin/perl
#---------------------------------------------------------------------------
#  Title:
#      Cross-Platform Demo - "uses" right module on either Win32 or linux
#  Usage:
#      perl elec_meter.pl PORT
#  if PORT eq 'TEST' uses Test::Device:SerialPort emulator
#---------------------------------------------------------------------------

# must be LF-only line ends to run on all platforms

use lib './lib','../lib';	# can run before final install

use strict;
use warnings;
use Data::Dumper;

our $OS_win;
our $ob;
our $port;

BEGIN {
	die "\nUsage: perl elec_meter.pl PORT\n" unless (@ARGV);
	$port = shift @ARGV;
	my $eval_module;
	my $module_version;

        $OS_win = ($^O eq "MSWin32" || $^O eq "cygwin") ? 1 : 0;

        print "Perl version: $]\n";
        print "OS   version: $^O\n";

            # This must be in a BEGIN in order for the 'use' to be conditional
        if ($port eq 'TEST') {
        	print "Loading Emulator module\n";
        	$eval_module = "Test::Device::SerialPort";
		$module_version = 0.04;
        }
        elsif ($OS_win) {
        	print "Loading Windows module\n";
        	$eval_module = "Win32::SerialPort";
		$module_version = 0.21;
        }
        else {
            print "Loading Unix module\n";
            $eval_module = "Device::SerialPort";
	    $module_version = 1.04;
        }
        my $eval_str = "use $eval_module qw( :STAT $module_version ); \$ob = $eval_module->new('$port');";
	warn $eval_str . "\n";
	eval $eval_str;
	die "$@\n" if ($@);
	die "Can't open serial port $port: $^E\n" unless ($ob);
} # End BEGIN

$ob->user_msg(1); # misc. warnings
## $ob->error_msg(1); # hardware and data errors

$ob->baudrate(1200) || die "fail setting baud";
$ob->parity("even") || die "fail setting parity";
$ob->parity_enable(1);
$ob->databits(7) || die "fail setting databits";
$ob->stopbits(1) || die "fail setting stopbits";
$ob->handshake("none") || die "fail setting handshake";

$ob->write_settings || die "no settings";

$ob->are_match("\cM\cC");	# end string = CR ETX

if ($port eq 'TEST') {
	# emulate meter output
	$ob->set_test_mode_active(1);
	my $data = "PAPP 00400 %\cM\cJ";	# start partway thru pattern
	$data .= "MOTDETAT 000000 B\cM\cC\cB\cJ";
	$data .= "ADCO 012345678901 E\cM\cJ";
	$data .= "OPTARIF BASE 0\cM\cJ";
	$data .= "ISOUSC 30 9\cM\cJ";
	$data .= "BASE 024576277 3\cM\cJ";
	$data .= "PTEC TH.. \$\cM\cJ";
	$data .= "IINST 002 Y\cM\cJ";
	$data .= "IMAX 026 G\cM\cJ";
	$data .= $data;
	$data .= $data;		# total of 4 sets of pattern
	$ob->lookclear($data);		# preset buffers
} else {
	# really read from the meter
	$ob->lookclear;		# empty buffers
}
my $gotit = "";
my $match1 = "";
until ("" ne $gotit) {
	if ($OS_win) {
		# *ix handles errors differently
		my @stat = $ob->status;
		if ($stat[ST_ERROR]) {
			$ob->reset_error;
		}
	}
	$gotit = $ob->streamline;	# poll until data ready
	last if ($gotit);
	$match1 = $ob->matchclear;	# match is first thing received
	last if ($match1);
	sleep 1;
}

# so to get here, we have seen an ETX aned synced up
# but we don't know how much of a transmission was caught
# so we discard it and start new_but_sync'd

$gotit = "";
until ("" ne $gotit) {
	# I doubt if we need the status check here, but it does no harm
	# reset_error only needed on Windows
	if ($OS_win) {
		my @stat = $ob->status;
		if ($stat[ST_ERROR]) {
			$ob->reset_error;
		}
	}
	$gotit = $ob->streamline;	# poll until next ETX
}

# so let's see what we actually got

$gotit =~ s/\cB\cJ//g;	# remove STX LF from start

my @readings = split ("\cM\cJ", $gotit);
push @readings, 'BAD_ONE 00610 (';	# added for test, WCB
## warn Dumper \@readings;

my %results;

foreach my $r (@readings) {
	my $sum = 0;
	my $csum = chop $r; # remove and save checksum
	chop $r; # remove space before checksum
	my @char = unpack ('C*', $r);
	foreach my $c (@char) {
		$sum += $c;
	}
	# print "$r\n@char\n";
	my $sum2 = $sum & 0x3f;
	$sum2 += 0x20;
	my $csum2 = chr($sum2);
	# printf "sum=%x, sum2=%x, %s..%s\n", $sum, $sum2, $csum2, $csum;
	my ($item, $value) = split (' ', $r);
	if ($csum eq $csum2) {
		$results{$item} = $value;
	} else {
		$results{$item} = 'invalid';
	}
}
warn Dumper \%results;

$ob->close || die "\nclose problem with $port\n";
undef $ob;