#!/usr/bin/perl
use warnings;
use strict;

use Data::Dump qw(dump);

use lib '.';
use Protocol;

my $debug = $ENV{DEBUG};

warn "## protocol = ",dump( $protocol ) if $debug;

my $pkg_nr = 0;

sub protocol_decode {
	my ( $up_down, $hex ) = @_;

warn "up/down: $up_down hex = ",dump($hex);

my $bin = join('', map { chr(hex($_)) } split(/\s+/,$hex));

warn "bin = ", dump($bin);

if ( $debug ) {
	open(my $dump, '>', "/dev/shm/zc.$pkg_nr.$up_down");
	print $dump $bin;
	close($dump);
	$pkg_nr++;
}

my $cksum = substr($bin, -2, 2);

if ( my $crc = modbus_crc16( substr($bin,0,-2) ) ) {
	if ( $crc ne $cksum ) {
		warn "CRC error, got ",unpack('H*',$crc), " expected ", unpack('H*',$cksum), "\n";
	} else {
		warn "CRC OK ", unpack('H*',$crc), "\n";
	}
}

my ( $header, $ver, $function_code, $len ) = unpack("CCCv", $bin);

my $data = substr($bin,5,-2);

warn "header = $header ver = $ver function_code = $function_code len = $len == ",length($data), " data = ",dump($data), " cksum = ", unpack('H*',$cksum);

warn "header corrupt: $header" if $header != 0x5a;
warn "invalid length $len got ",length($data) if length($data) != $len;

my $function_code_upstream = {
0x07 => 'Upstream Heartbeat Frame',
0x08 => 'Upstream alarm frame',
0x03 => 'Upstream Read Parameter Frame',
0x06 => 'Upstream return write parameter frame',
};

my $function_code_downstream = {
0x03 => 'Downstream read parameter frame',
0x06 => 'Downstream write parameter frame',
};

print "Function code: $function_code ", 
	$up_down eq 'up' ? $function_code_upstream->{ $function_code } : $function_code_downstream->{ $function_code }, "\n";




while ( $data ) {
	my $hex = unpack('H*', $data);
	$hex =~ s/(..)/$1 /g;
	warn "XXX data = $hex\n";

	my $data_id = unpack( 'C', substr($data,0,1) );
	if ( ! exists( $protocol->{$data_id}->{description} ) ) {
		my $len = unpack('C', substr($data,1,1));
		printf "ERROR: no description for data_id %d 0x%2x len %d [%s] SKIPPING!\n", $data_id, $data_id, $len, unpack('H*', substr($data,2,$len));
		$data = substr($data,2 + $len);
		next;
	}
	my $data_id_desc = $protocol->{$data_id}->{description};
	my $pack_fmt = $protocol->{$data_id}->{pack_fmt} || die "can't find pack_fmt for data_id $data_id";

	my $data_range;
	my $data_len;

	if ( $data_id == 0x00 ) {
		# sequence number

		$data_range = substr($data,2,4);
		$data = substr($data,6);

	} elsif ( $up_down eq 'down' && $function_code == 0x03 ) {
		# type A

		$data_range = substr($data,0,1);
		$data = substr($data,1);

	} elsif ( $up_down eq 'up'   && ( $function_code == 0x07 || $function_code == 0x08 || $function_code == 0x03 ) ) {
	       	# type B

		$data_len = unpack('C', substr($data,1,1));
		$data_range = substr($data,2, $data_len);

		$data = substr($data,2 + $data_len);

	} elsif ( $up_down eq 'down' && $function_code == 0x06 ) {
	       	# type B

		$data_len = unpack('C', substr($data,1,1));
		$data_range = substr($data,2, $data_len);

		$data = substr($data,2 + $data_len);

	} elsif ( $up_down eq 'up' && $function_code == 0x06 ) {
		# type C

		# FIXME check first byte?
		my $data_range = substr($data,1,1);

		$data = substr($data,2);

	} else {
		warn "ERROR unknown function_code = $function_code";
	}

	my @v = unpack($pack_fmt, $data_range);

	my $v = join(' ', @v);

	if ( $data_id == 0x0c ) {
		$v = $v / 100;
	} elsif ( $data_id == 0x0d ) {
		$v = $v / 100;
	}

	my $description = $protocol->{$data_id}->{description} || die "can't find description for data_id $data_id";
	print "$up_down | $data_id 0x",unpack('H*', chr($data_id))," $data_len | $description | v=$v | hex:", unpack('H*', $data_range), " fmt:$pack_fmt | range:",$protocol->{$data_id}->{range}, " | remark:", $protocol->{$data_id}->{remark},"\n";
}


} # protocol_decode

my $raw;
my ( $timestamp, $sensor, $imei, $up_down );
my $stat;
while(<>) {
	chomp;

	if ( m{^(\d\d\d\d-\d\d-\d\d\s\d\d:\d\d:\d\d).*Inclinometer/([^/]+)/([^/]+)/(\w+)} ) {
		if ( $raw ) {
			$raw =~ s{^\s+}{}; # strip leading spaces
			print "## $timestamp $sensor $imei $up_down $raw\n";
			protocol_decode( $up_down, $raw );
			$raw = '';
		}
		( $timestamp, $sensor, $imei, $up_down ) = ( $1, $2, $3, $4 );
		$stat->{$sensor}->{$imei}->{$up_down}++;
	} elsif ( m{^\s+} ) {
		s/\s\s\S\S\S+//g; # remove ascii
		s/^\s+/ /;
		$raw .= $_;
		warn "++ $_\n";
	} else {
		warn "IGNORE: $_\n";
	}

}

if ( $raw ) {
	$raw =~ s{^\s+}{}; # strip leading spaces
	print "## $timestamp $sensor $imei $up_down $raw\n";
	protocol_decode( $up_down, $raw );
}

print "stat = ",dump($stat), "\n";

