#!/usr/bin/perl

# LED Ticker 1.0 (C)2005 Veit Wahlich <cru@ircnet.de>
# Released under the terms of the GNU Public License, version 2 (GPLv2)

use strict;
use warnings;
use IO::File;

# Default configuration
my $conf={
		version  => '1.0',
		port     => '/dev/ttyS0', # i.e. '/dev/ttyS0'
		speed    => 2,            # range is 0..4
		sign_id  => 0,            # range is 0..255, 0 is default
		page     => 'B'           # either page 'A' or 'B', defaults to 'B'
	};

sub main{
	my $fd;
	my $err=0;
	my $text;
	parseArgs()
		|| do{
			showUsage();
			exit(1);
		};
	if(-c $conf->{port}){
		$text=constructString($ARGV[0],$conf->{speed},$conf->{sign_id},$conf->{page});
		if(defined($text)){
			$fd=new IO::File($conf->{port},'w')
				|| die('Error opening port '.$conf->{port}.': '.$!."\n");
			print($fd $text);
			$fd->close;
		}
		else{
			showUsage();
			exit(1);
		}
	}
	else{
		die($conf->{port}.' is no character device!'."\n");
	}
}

# parseArgs()
# Parse the command line arguments and set config settings
#
sub parseArgs{
	my $arg;
	if(@ARGV == 0){
		return(0);
	}
	while(@ARGV > 1){
		$arg=shift(@ARGV);
		if($arg eq '-d'){
			$conf->{port}=shift(@ARGV) || return(0);
		}
		elsif($arg eq '-s'){
			$arg=shift(@ARGV);
			if(defined($arg) && $arg=~/^[0-4]$/){
				$conf->{speed}=$arg;
			}
			else{
				return(0);
			}
		}
		elsif($arg eq '-i'){
			$arg=shift(@ARGV);
			if(defined($arg) && $arg=~/^[0-9]+$/ && $arg >= 0 && $arg <= 255){
				$conf->{sign_id}=$arg;
			}
			else{
				return(0);
			}
		}
		elsif($arg eq '-p'){
			$arg=shift(@ARGV);
			if(defined($arg) && $arg=~/^(A|B)$/i){
				$conf->{page}=uc($arg);
			}
			else{
				return(0);
			}
		}
		else{
			print(STDERR 'Unknown option: '.$arg."\n");
			return(0);
		}
	}
	return(1);
}

# $string=constructString($text,$speed,$sign_id,$page)
# Builds a control string for the LED ticker
#
# Parameters:
#   $text       : a text string that shall be displayed, max. 420 characters
#                 for page B, 80 chars for page A (reserved for logos)
#   $speed      : scroll speed, integer 0..4
#   $sign_id    : the LED ticker's ID, integer 0..255
#   $page       : page to programm, either 'A' or 'B'
#
# Returns:
#   $string     : the control string ready to be sent to device
#
sub constructString{
	my($text,$speed,$sign_id,$page)=@_;
	my $string;
	return(undef) unless(defined($text) && defined($speed) && defined($sign_id) && defined($page));
	if($page eq 'A'){
		$text=substr($text,0,80);
	}
	elsif($page eq 'B'){
		$text=substr($text,0,420);
	}
	else{
		return(undef);
	}
	$string='<L1><P'.$page.'><FE><M'.chr(69-($speed % 5)).'><WC><FE>'.$text;
	$string=sprintf('<ID%02x>',($sign_id % 256)).$string.sprintf('%02x',checksum($string)).'<E>';
	return($string);
}

# $value=checksum($string)
# Calculates a string's checksum
#
# Parameters:
#   $string     : string to checksum
#
# Returns:
#   $value      : checksum, integer 0..255
#
sub checksum{
	my($string)=@_;
	return(undef) unless(defined($string));
	my $value=0;
	my $i;
	for($i=0; $i < length($string); $i++){
		$value=$value^ord(substr($string,$i,1));
	}
	return($value);
}

# showUsage()
# Print usage info to STDOUT
#
sub showUsage{
	print(<<__END);

LED Ticker $conf->{version} (C)2005 Veit Wahlich <cru\@ircnet.de>

Syntax: $0 [-d <device>] [-s <speed>] [-i <id>] [-p <page>] <text>

Arguments:
  <text>       The text to be shown by the LED ticker

Options [defaults]:
  -d <device>  The serial port the ticker is connected to [$conf->{port}]
  -s <speed>   Ticker scroll speed; 0..4 [$conf->{speed}]
  -i <id>      ID of the ticker to address; 0..255 [$conf->{sign_id}]
  -p <page>    Ticker page to programm; A (reserved) or B [$conf->{page}]

__END
}

main();
1;
