#!/usr/bin/perl

package satmessage;

# Perl driver for RockRLOCK+ Iridium SBD modem
# Mike Ingle <inglem@pobox.com> 3fac8ca2d2634a06c23932cf049926e2163984de
# https://www.confidantmail.org
# command.txt lets you inject a command without using the satellite:
# echo "sh w" > command.txt
# txcmdfile0 and 1 let you send an outgoing satellite message from an external program
# if nosendfile exists, satellite messages will not be sent, but will be displayed for debugging

$cmdfile = "command.txt";
$txcmdfile0 = "txcommand0.dat";
$txcmdfile1 = "txcommand1.dat";
$nosendfile = "nosend.flag";

sub initial {
	if ( -f $nosendfile) {
		print "No-send flag exists. Satellite messages will not be sent.\n";
		print "To enable sending, rm ${nosendfile} or transmit SEND ON via satellite\n";
	}
	print "satmessage initial\n";
}

sub satsend {
	my ($msg) = @_;
	if ( -f $nosendfile) {
		print "Message not sent (".length($msg)." bytes): ";
		for(my $i=0;$i<length($msg);$i++) { my $c = ord(substr($msg,$i,1)); printf("%02x ",$c); if (($c > 31)&&($c<127)) { print chr($c)." "; } } print "\n";
	} else {
		print "Message queued (".length($msg)." bytes): ";
		for(my $i=0;$i<length($msg);$i++) { my $c = ord(substr($msg,$i,1)); printf("%02x ",$c); if (($c > 31)&&($c<127)) { print chr($c)." "; } } print "\n";
		push(@satcom::sendmsgs,$msg);
		satcom::break_idle();
	}
}

# Put your message actions here
# Right now:
# sh command
#   run a shell command and return the first 50 characters
# sx command
#   run a shell command and return the first 340 characters
# send on
#   enable satellite responses
# send off
#   disable satellite responses
sub process_message {
	my ($msg) = @_;
	if ($msg =~ /^send on$/i) { # enable sending
		unlink($nosendfile);
	} elsif ($msg =~ /^send off$/i) { # disable sending
		open(my $out,'>'.$nosendfile);
		close($out);
	} elsif ($msg =~ /^s[hx] (.+)$/i) { # shell command
		my $shcmd = $1;	
		open(my $in,$shcmd.' 2>&1 |');
		my $output;
		while ($_ = <$in>) { $output .= $_ . ' '; }
		close($in);
		$output =~ s/\r//gs; $output =~ s/\n//gs; $output =~ s/\s+/ /g;
		$output =~ s/^\s*//; $output =~ s/\s*$//;
		if ($msg =~ /^sx/i) {
			$output = substr($output,0,340);
		} else {
			$output = substr($output,0,50);
		}
		if (length($output) > 0) { satsend($output); }
	} elsif ($msg =~ /^force poll/i) { # fake ring for testing
		$satcom::ring_pending = 2;
	}
}

# Put your periodic actions here
sub periodic_poll {
	if ( -f $cmdfile ) {
		open(my $in,$cmdfile);
		my $line = <$in>;
		close($in);
		chomp($line);
		unlink($cmdfile);
		print "Injecting command: ".$line."\n";
		process_message($line);
		satcom::break_idle();
	}
	if ( -f $txcmdfile0 ) {
		my $cmdin;
		open(my $in,$txcmdfile0);
		read($in,$cmdin,512);
		close($in);
		satsend($cmdin);
		unlink($txcmdfile0);
		satcom::break_idle();
	} elsif ( -f $txcmdfile1 ) {
		my $cmdin;
		open(my $in,$txcmdfile1);
		read($in,$cmdin,512);
		close($in);
		satsend($cmdin);
		unlink($txcmdfile1);
		satcom::break_idle();
	}
}

# Useful for saving money when sending numeric data with space and slash delimiters
sub bcdencode {
	my ($strin) = @_;
	my $i,$j,$n,$b,$c,$o;
	$n = length($strin);
	$b = 0x00; $o = '';
	for($i=0,$j=0;$i<$n;$i++,$j++) {
		$c = substr($strin,$j,1);
		if (($i % 2) == 1) { $b <<= 4; }
		if ($c eq '0') { $b |= 0x00; }
		elsif ($c eq '1') { $b |= 0x01; }
		elsif ($c eq '2') { $b |= 0x02; }
		elsif ($c eq '3') { $b |= 0x03; }
		elsif ($c eq '4') { $b |= 0x04; }
		elsif ($c eq '5') { $b |= 0x05; }
		elsif ($c eq '6') { $b |= 0x06; }
		elsif ($c eq '7') { $b |= 0x07; }
		elsif ($c eq '8') { $b |= 0x08; }
		elsif ($c eq '9') { $b |= 0x09; }
		elsif ($c eq '.') { $b |= 0x0a; }
		elsif (($c eq ' ')&&(($i+1)<$n)&&(substr($strin,$j+1,1) eq '|')) { $b |= 0x0d; $j++; $n--; }
		elsif ($c eq ' ') { $b |= 0x0b; }
		elsif ($c eq '-') { $b |= 0x0c; }
		elsif ($c eq '/') { $b |= 0x0e; }
		else { $b |= 0x0f; }
		if (($i % 2) == 1) { $o .= chr($b); $b = 0x00; }
	}
	if (($n % 2) == 1) { $b <<= 4; $b |= 0x0f; $o .= chr($b); }
	return $o;
}

sub bcddecode {
	my ($strin) = @_;
	my $h,$l,$o,$i,$n,$c;
	$n = length($strin);
	$o = '';
	for($i=0;$i<$n;$i++) {
		$c = ord(substr($strin,$i,1));
		$h = $c >> 4;
		$l = $c & 0x0f;	
		if($h == 0x00) { $o .= '0'; }
		elsif($h == 0x01) { $o .= '1'; }
		elsif($h == 0x02) { $o .= '2'; }
		elsif($h == 0x03) { $o .= '3'; }
		elsif($h == 0x04) { $o .= '4'; }
		elsif($h == 0x05) { $o .= '5'; }
		elsif($h == 0x06) { $o .= '6'; }
		elsif($h == 0x07) { $o .= '7'; }
		elsif($h == 0x08) { $o .= '8'; }
		elsif($h == 0x09) { $o .= '9'; }
		elsif($h == 0x0a) { $o .= '.'; }
		elsif($h == 0x0b) { $o .= ' '; }
		elsif($h == 0x0c) { $o .= '-'; }
		elsif($h == 0x0d) { $o .= ' |'; }
		elsif($h == 0x0e) { $o .= '/'; }

		if($l == 0x00) { $o .= '0'; }
		elsif($l == 0x01) { $o .= '1'; }
		elsif($l == 0x02) { $o .= '2'; }
		elsif($l == 0x03) { $o .= '3'; }
		elsif($l == 0x04) { $o .= '4'; }
		elsif($l == 0x05) { $o .= '5'; }
		elsif($l == 0x06) { $o .= '6'; }
		elsif($l == 0x07) { $o .= '7'; }
		elsif($l == 0x08) { $o .= '8'; }
		elsif($l == 0x09) { $o .= '9'; }
		elsif($l == 0x0a) { $o .= '.'; }
		elsif($l == 0x0b) { $o .= ' '; }
		elsif($l == 0x0c) { $o .= '-'; }
		elsif($l == 0x0d) { $o .= ' |'; }
		elsif($l == 0x0e) { $o .= '/'; }
	}
	return $o;
}

# Compression for tabular data where similar column values exist
sub abbreviate {
	my ($thisv,$prev) = @_;
	my $nt = length($thisv);
	my $np = length($prev);
	if ($nt != $np) { return $thisv; }
	for(my $i=0;$i<$nt;$i++) {
		if (substr($thisv,$i,1) ne substr($prev,$i,1)) {
			if ($i == 0) {
				return $thisv;
			} else {
				return '|'.substr($thisv,$i);
			}
		}
	}
	return '|';
}

sub debreviate {
	my ($thisv,$prev) = @_;
	if (substr($thisv,0,1) ne '|') { return $thisv; }
	my $nt = length($thisv);
	my $np = length($prev);
	return substr($prev,0,($np - $nt + 1)).substr($thisv,1);
}


package satcom;

$s_init = 0;
$s_register = 1;
$s_idle = 2;
$s_send = 3;
$s_receive = 4;
$s_rcvproc = 5;

# status file contains: imei number, signal, num pending outgoing, last downlink time, last uplink time
$status_file = "satcom_status.txt";
$register_due_file = "regdue.txt";
$minsignal = 2;
$skip_next_ok = 0;
$count = 0;
$state = $s_init;
$step = 0;
$signal = 0;
$timeout = 0;
$get_any_line = 0;
$get_binary = 0;
$ring_pending = 0;
#$retry_interval = 30;
$retry_interval = 5;
$last_downlink = 0;
$last_uplink = 0;
$register_due = 0;
$poll_due = time();
$receive_retry_due = time();
$send_retry_due = time();
$imei = 'unknown';
@sendmsgs = ();

sub portwrite {
	my ($line) = @_;
	my $lineout = $line;
	$lineout =~ s/\r//g;
	print "> ".$lineout."\n";
	$port->write($line);
}

sub initial {
	($port) = @_;
	open(my $in,$register_due_file);
	$register_due = <$in>;
	close($in);
	$register_due =~ s/ .*//;
	if ($register_due =~ /^[0-9]+/) {
		$register_due += 0;
	} else {
		$register_due = 0;
	}
	portwrite("AT\r");
}

sub process_init {
	my ($line) = @_;
	if (($timeout == 1) && ($step > 0)) { $timeout = 0; $step = 0; }
	if ($line eq 'OK') {
		if ($step == 0) { 
			portwrite('AT&K0'."\r");
			$step ++;
		} elsif ($step == 1) { 
			portwrite('ATE0'."\r");
			$step ++;
		} elsif ($step == 2) { 
			portwrite('AT+CGSN'."\r");
			$step ++;
		} elsif ($step == 3) { 
			portwrite('AT+SBDD0'."\r");
			$step ++;
		} elsif ($step == 4) { 
			portwrite('AT+SBDMTA=1'."\r");
			$step ++;
		} elsif ($step == 5) { 
			portwrite('AT+CIER=1,1,0'."\r");
			$step ++;
		} else {
			$state = $s_register; $step = 0;
			portwrite('AT+SBDREG?'."\r");
		}
	} elsif (($step == 3) && ($line =~ /^[0-9]+$/)) {
		$imei = $line;
		print "Satellite modem IMEI number: ".$imei."\n";
		print "This number must be entered on the server.\n";
	}
}

sub process_register {
	my ($line) = @_;
	if ($step == 0) {
		if (($register_due > time()) && ($line =~ /^\+SBDREG:2/)) { # already registered
			print "Modem is registered with satellite\n";
			$skip_next_ok = 1; sleep(0.5);
			$state = $s_idle; $step = 0;
		} elsif ($line =~ /^\+SBDREG:/) { # failed
			$skip_next_ok = 1; sleep(0.5);
			print "Modem needs to be registered\n";
			if ($signal >= $minsignal) {
				$step = 2;
				portwrite('AT+SBDREG'."\r");
			} else {
				$step = 1;
			}
		} elsif ($timeout == 1) {
			$timeout = 0; $step = 0;
			portwrite('AT+SBDREG?'."\r");
		}
	} elsif (($step == 1)&&($signal >= $minsignal)) {
		$step = 2;
		portwrite('AT+SBDREG'."\r");
	} elsif ($step == 2) {
		if ($line =~ /^\+SBDREG:2/) { # success
			$skip_next_ok = 1; sleep(0.5);
			print "Modem is now registered with satellite\n";
			$register_due = time() + 82800; # 23h
			$state = $s_idle; $step = 0;
			open(my $out,'>'.$register_due_file);
			print $out $register_due . ' ' . datestr($register_due)."\n";
			close($out);
		} elsif ($line =~ /^\+SBDREG:/) { # failed
			$skip_next_ok = 1; sleep(0.5);
			print "Modem still needs to be registered\n";
			$register_due = time() + 300;
			$state = $s_idle; $step = 0;
		} elsif ($timeout == 1) { # don't hang constantly trying to register
			$timeout = 0;
			$register_due = time() + 300;
			$state = $s_idle; $step = 0;
		}
	}
	if ($state == $s_idle) {
		portwrite('AT+SBDSX'."\r");
	}
}

sub process_idle {
	my ($line) = @_;
	update_status();
	if ($line =~ /^\+SBDSX: (\S+), *(\S+), *(\S+), *(\S+), *(\S+), *(\S+)/) {
		($moflag,$momsn,$mtflag,$mtmsn,$raflag,$mwflag) = ($1,$2,$3,$4,$5,$6);
		$skip_next_ok = 1; sleep(0.5);
		if ($mtflag > 0) {
			print "Got MT data in buffer\n";
			$state = $s_rcvproc; $step = 0; # already in buffer
			portwrite('AT+CIER=0,0,0'."\r");
		} elsif ($raflag > 0) {
			print "Got RA flag\n";
			if ($signal >= $minsignal) {
				serialio::reset_timeout();
				portwrite('AT+SBDIXA'."\r");
				$receive_retry_due = time() + $retry_interval;
				$state = $s_receive; $step = 1;
			} else {
				$state = $s_receive; $step = 0;
			}
		} elsif ($#sendmsgs >= 0) {
			portwrite('AT+SBDD0'."\r");
			$timeout = 0;
			$state = $s_send; $step = 0;
		}
	} elsif ($ring_pending == 2) { # force poll hack
		serialio::reset_timeout();
		portwrite('AT+SBDIX'."\r");
		$ring_pending = 1;
	} elsif ($ring_pending == 1) {
		portwrite('AT+SBDSX'."\r");
		$poll_due = time() + 30;
		$ring_pending = 0;
	} elsif ($#sendmsgs >= 0) {
		portwrite('AT+SBDD0'."\r");
		$timeout = 0;
		$state = $s_send; $step = 0;
	} elsif ($poll_due <= time()) {
		portwrite('AT+SBDSX'."\r");
		$poll_due = time() + 30;
	} elsif ($register_due < time()) {
		$state = $s_register; $step = 0;
		portwrite('AT+SBDREG?'."\r");
	} elsif ($timeout == 1) {
		$timeout = 0; # just waiting for event
	}
}

sub process_receive {
	my ($line) = @_;
	if ($step == 0) {
		if ($signal >= $minsignal) {
			serialio::reset_timeout();
			portwrite('AT+SBDIXA'."\r");
			$receive_retry_due = time() + $retry_interval;
			$step = 1;
		}
	} elsif ($step == 1) { 
		if ($line =~ /^\+SBDIX: (\S+), *(\S+), *(\S+), *(\S+), *(\S+), *(\S+)/) {
			my ($mostatus,$momsn,$mtstatus,$mtmsn,$mtlength,$mtqueued) = ($1,$2,$3,$4,$5,$6);
			$skip_next_ok = 1; sleep(0.5);
			if ($mtstatus == 0) { # no message
				portwrite('AT+SBDSX'."\r");
				$state = $s_idle; $step = 0;
			} elsif ($mtstatus == 1) { # good receive
				$state = $s_rcvproc; $step = 0; # already in buffer
				portwrite('AT+CIER=0,0,0'."\r");
			} elsif ($mostatus > 4) {
				$receive_retry_due = time() + $retry_interval;
			} else { # not sure but might be costing
				$receive_retry_due = time() + 600;
			}
		} elsif ($timeout == 1) {
			$timeout = 0; # just waiting for signal
		} elsif ($receive_retry_due < time()) {
			if ($signal >= $minsignal) {
				serialio::reset_timeout();
				portwrite('AT+SBDIXA'."\r");
				$receive_retry_due = time() + $retry_interval;
			}
		}
	}
}

sub process_rcvproc {
	my ($line) = @_;
	if ($step == 0) {
		if ($line =~ /^OK$/) {
			$step = 1;	
			$get_binary = 1;
			portwrite("AT+SBDRB\r");
		} elsif ($timeout == 1) {
			$timeout = 0;
		}
	} elsif ($step == 1) {
		my $n = length($line);
		my $rlen,$rcks,$ccks;
		if ($n >= 4) {
			$rlen = 256*ord(substr($line,0,1)) + ord(substr($line,1,1));
			$rcks = 256*ord(substr($line,$n-2,1)) + ord(substr($line,$n-1,1));
			$ccks = 0;
			for(my $i=2;$i<($n-2);$i++) { $ccks += ord(substr($line,$i,1)); }
			printf("n=%u rlen=%u rcks=%u ccks=%u\n",$n,$rlen,$rcks,$ccks);
		}
		if ((($n - 4) != $rlen) || ($rcks != $ccks)) {
			$step = 0;
			# let OK or timeout resync things here
		} else {
			$line = substr($line,2,$rlen);
			print "Got received data: ";
			for(my $i=0;$i<length($line);$i++) { my $c = ord(substr($line,$i,1)); printf("%02x ",$c); if ($c > 31) { print chr($c)." "; } } print "\n";
			$skip_next_ok = 1; sleep(0.5);
			portwrite('AT+SBDD1'."\r");
			satmessage::process_message($line);
			$step = 2;
			$last_downlink = time();
			update_status();
		}
	} elsif ($step == 2) {
		if ($line =~ /^OK/) {
			portwrite('AT+CIER=1,1,0'."\r");
			$step = 3;
			$timeout = 0;
		}
	} elsif ($step == 3) {
		if ($line =~ /^OK/) {
			portwrite('AT+SBDSX'."\r");
			$step = 4;
			$timeout = 0;
		}
	} elsif ($step == 4) {
		if ($line =~ /^\+SBDSX: (\S+), *(\S+), *(\S+), *(\S+), *(\S+), *(\S+)/) {
			($moflag,$momsn,$mtflag,$mtmsn,$raflag,$mwflag) = ($1,$2,$3,$4,$5,$6);
			$skip_next_ok = 1; sleep(0.5);
			if ($mtflag == 0) { # deleted
				portwrite('AT'."\r");
				$state = $s_idle; $step = 0;
			} else {
				portwrite('AT+SBDD1'."\r");
				$step = 3; # retry delete
			}
		} elsif ($timeout == 1) {
			$timeout = 0;
			portwrite('AT+SBDSX'."\r");
		}
	}
}

sub process_send {
	my ($line) = @_;
	if ($step == 0) {
		if ($#sendmsgs < 0) {
			portwrite('AT+SBDSX'."\r");
			$state = $s_idle; $step = 0;
		} elsif ($timeout == 1) {
			$timeout = 0;
			$step = 0;
			portwrite('AT+SBDD0'."\r");
		} else {
			if ($line =~ /^OK/) {
				my $n = length($sendmsgs[0]);
				portwrite('AT+SBDWB='.$n."\r");
				$step = 1;
			}
		}
	} elsif ($step == 1) {
		if ($line =~ /^READY$/) {
			my $checksum = 0;
			my $sendmsg = $sendmsgs[0];
			my $n = length($sendmsg);
			my $csh,$csl;
			for(my $i=0;$i<$n;$i++) { $checksum += ord(substr($sendmsg,$i,1)); }
			$csl = $checksum % 256;
			$csh = (($checksum % 65536) - $csl) / 256;
			$sendmsg .= chr($csh).chr($csl);
			$port->write($sendmsg); # no print
			for(my $i=0;$i<length($sendmsg);$i++) { my $c = ord(substr($sendmsg,$i,1)); printf("%02x ",$c); if (($c > 31)&&($c<127)) { print chr($c)." "; } } print "\n";
			$step = 2;
			$send_retry_due = 0;
		} elsif ($timeout == 1) {
			$timeout = 0;
			$step = 0;
			portwrite('AT+SBDD0'."\r");
		}
	} elsif ($step == 2) {
		if ($line =~ /^[1-9]$/) { # error code
			$skip_next_ok = 1; sleep(0.5);
			$step = 0; # try again
		} elsif ($line =~ /^OK/) {
			portwrite('AT+SBDSX'."\r");
			$step = 3;
		} elsif ($timeout == 1) {
			$timeout = 0;
			$step = 0;
			portwrite('AT+SBDD0'."\r");
		}
	} elsif ($step == 3) {
		if ($line =~ /^\+SBDSX: (\S+), *(\S+), *(\S+), *(\S+), *(\S+), *(\S+)/) {
			($moflag,$momsn,$mtflag,$mtmsn,$raflag,$mwflag) = ($1,$2,$3,$4,$5,$6);
			$skip_next_ok = 1; sleep(0.5);
			if ($moflag > 0) {
				if ($signal >= $minsignal) {
					serialio::reset_timeout();
					portwrite('AT+SBDIX'."\r");
					$step = 5;
				} else {
					$step = 4;
				}
			} else {
				$step = 0;
				portwrite('AT+SBDD0'."\r");
			}
		} elsif ($timeout == 1) {
			$timeout = 0;
			$step = 0;
			portwrite('AT+SBDD0'."\r");
		}
	} elsif ($step == 4) {
		if ($signal >= $minsignal) {
			if ($send_retry_due < time()) {
				serialio::reset_timeout();
				portwrite('AT+SBDIX'."\r");
				$step = 5;
			}
		} elsif ($timeout == 1) {
			$timeout = 0;
		}
	} elsif ($step == 5) {
		if ($line =~ /^\+SBDIX: (\S+), *(\S+), *(\S+), *(\S+), *(\S+), *(\S+)/) {
			my ($mostatus,$momsn,$mtstatus,$mtmsn,$mtlength,$mtqueued) = ($1,$2,$3,$4,$5,$6);
			$skip_next_ok = 1; sleep(0.5);
			if ($mostatus < 5) { # success
				shift(@sendmsgs); # discard sent message
				portwrite('AT+SBDD0'."\r");
				$step = 6;
				$last_uplink = time();
				update_status();
			} else {
				$send_retry_due = time() + $retry_interval;
				$step = 4;
			}
		} elsif ($timeout == 1) {
			$timeout = 0;
		}
	} elsif ($step == 6) {
		if ($line =~ /^OK/) {
			portwrite('AT+SBDSX'."\r");
			$step = 7;
		} elsif ($timeout == 1) {
			$timeout = 0;
			portwrite('AT+SBDSX'."\r");
			$step = 7;
		}
	} elsif ($step == 7) {
		if ($line =~ /^\+SBDSX: (\S+), *(\S+), *(\S+), *(\S+), *(\S+), *(\S+)/) {
			($moflag,$momsn,$mtflag,$mtmsn,$raflag,$mwflag) = ($1,$2,$3,$4,$5,$6);
			$skip_next_ok = 1; sleep(0.5);
			if ($moflag > 0) {
				portwrite('AT+SBDD0'."\r");
				$step = 6;
			} else {
				portwrite('AT+SBDSX'."\r");
				$state = $s_idle; $step = 0;
			}
		} elsif ($timeout == 1) {
			$timeout = 0;
			portwrite('AT+SBDD0'."\r");
			$step = 6;
		}
	}
}


sub break_idle {
	if ($state == $s_idle) {
		process_idle('WAKEUP');
	}
}

sub process_line {
	my ($line) = @_;
	my $reset_timeout = 1;
	$linecount ++;
	if ($get_binary == 1) {
		printf("%s %06u:%u:%u: bin %u\n",datestr(time()),$linecount,$state,$step,length($line));
	} else {
		printf("%s %06u:%u:%u: %s\n",datestr(time()),$linecount,$state,$step,$line);
	}
#		if ($linecount == 40) { push(@sendmsgs,"Testing automatic send"); }
	# global recognition values
	if ($get_binary == 1) { # skip
	} elsif ($line =~ /\+CIEV:0,(.)/) {
		$signal = $1;
		$reset_timeout = 0;
	} elsif ($line =~ /^SBDRING|^\+SBDRING/) {
		$ring_pending = 1;
	} elsif (($line =~ /^OK$/) && ($skip_next_ok == 1)) {
		$skip_next_ok = 0;
		return $reset_timeout;
	}
	# dispatch to state
	if(($get_any_line == 1)||($get_binary == 1)||(!($line =~ /^AT/))) {
		$get_any_line = 0; $get_binary = 0;
		if ($state == $s_init) {
			process_init($line);
		} elsif ($state == $s_idle) {
			process_idle($line);
		} elsif ($state == $s_register) {
			process_register($line);
		} elsif ($state == $s_receive) {
			process_receive($line);
		} elsif ($state == $s_rcvproc) {
			process_rcvproc($line);
		} elsif ($state == $s_send) {
			process_send($line);
		}
	}
	return $reset_timeout;
}

sub update_status {
	my $out;
	open($out,'>'.$status_file.'.new');
	print $out $imei."\n";
	print $out $signal,"\n";
	print $out (1+$#sendmsgs)."\n";
	print $out $last_downlink." ".datestr($last_downlink)."\n";
	print $out $last_uplink." ".datestr($last_uplink)."\n";
	close $out;
	unlink($status_file.'.old');
	link($status_file,$status_file.'.old');
	rename($status_file.'.new',$status_file);
}

sub timeout {
	print "timeout\n";
	$timeout = 1;
	portwrite("AT\r");
}	

sub datestr {
	my ($timeval) = @_;
	my (@ts) = localtime($timeval);
	return sprintf("%04i-%02i-%02i %02i:%02i:%02i",$ts[5]+1900,$ts[4]+1,$ts[3],$ts[2],$ts[1],$ts[0]);
}

package serialio;

sub reset_timeout { $timeout=$STALL_DEFAULT; }

# For Linux
use Device::SerialPort;

$serialdev = $ARGV[0];
if ($serialdev eq undef) {
	print "Run with serial device path: perl satcom.pl /dev/ttyUSB0\n";
	exit(1);
}

#my $port,$timeout,$terminate,$chars,$buffer;
#my $STALL_DEFAULT=60; # how many seconds to wait for new input
my $port,$terminate,$chars,$buffer;
$STALL_DEFAULT=600; # how many seconds*10 to wait for new input

for(my $retry = 0;$retry < 20;$retry ++) {
	$port = Device::SerialPort->new($serialdev);
	die "Serial open: $!" if ($port eq undef);

	# For Windows. You only need one or the other.
	# Uncomment these for Windows and comment out above
	#use Win32::SerialPort;
	#my $port = Win32::SerialPort->new("COM3");

	$port->baudrate(19200); # Configure this to match your device
	$port->databits(8);
	$port->parity("none");
	$port->stopbits(1);
	
	#$port->write("\nBegin perl serial listener\n");
	sleep(1);

	$timeout=$STALL_DEFAULT;

	$port->read_char_time(0);     # don't wait for each character
	$port->read_const_time(100); # 0.1 second per unfulfilled "read" call

	# Flush the buffer. If the modem sends +CIER lines while the program is not
	# running, the buffer gets messed up. This open/close process will restore it.
	my $flushcount = 1;
	my $flushsaw;
	$port->write("AT+CIER=0,0,0\r"); # turn off output to init
	sleep(1);
	($flushcount,$flushsaw)=$port->read(262144); # will read _up to_ 262144 chars
	print "Flushing buffer ".$flushcount."\n";
	if ($flushcount >= 1024) {
		$port->close();
		sleep(0.5);
	 } else {
		last;
	}
}

$terminate = 0;
$chars=0;
$buffer="";

satmessage::initial();
satcom::initial($port);
while($terminate == 0) {
	while ($timeout>0) {
		satmessage::periodic_poll();
		my ($count,$saw)=$port->read(255); # will read _up to_ 255 chars
		if ($count > 0) {
			$chars+=$count;
			$buffer.=$saw;
	
			if ($satcom::get_binary == 1) {
				if (length($buffer) >= 2) {
					my $expected = 4 + 256*ord(substr($buffer,0,1)) + ord(substr($buffer,1,1));
					printf("\nlengthbuf=%u expected=%u\n",length($buffer),$expected);
					for(my $i=0;$i<length($buffer);$i++) { my $c = ord(substr($buffer,$i,1)); printf("%02x ",$c); if (($c > 31)&&($c<127)) { print chr($c)." "; } } print "\n";
					if (length($buffer) >= $expected) {
						$line = substr($buffer,0,$expected);
						$buffer = substr($buffer,$expected);
						$timeout=$STALL_DEFAULT;
						satcom::process_line($line);
					}
				}
			} else {
				# Check here to see if what we want is in the $buffer
				# say "last" if we find it
				while ($buffer =~ /^([^\n]*)\n(.*)$/s) { 
					($line,$buffer) = ($1,$2);
					$line =~ s/\r//g;
					if ($line ne '') {
						my $reset_timeout = satcom::process_line($line);
						if ($reset_timeout == 1) {
							$timeout=$STALL_DEFAULT;
						}
					}
				}
			}
		} else {
			$timeout--;
		}
	}

	if ($timeout==0) {
		#print "Waited $STALL_DEFAULT seconds and never saw what I wanted\n";
		satcom::timeout();
		$satcom::get_binary = 0; $satcom::get_any_line = 0;
		$timeout=$STALL_DEFAULT;
	}
}

	
# EOF
