#!/usr/bin/perl -w


package RelaisServer;

use strict;
eval("use Device::SerialPort");
eval("use Win32::SerialPort");
use IO::Select;
use IO::Socket;


my @r;           # The is array of the registers r[0], r[1], ...
my $relais;      # The Handle for the Relais
my $rules="";    # The rules, that were read in from the rules file, and can be evaluated
my %puffer;      # The puffer for all the handles. It is a Hash, so that it can spread easily, without abusing resources
my %subscribed;  # Manages the subscriptions
my $string="";   


$r[$_]="0" foreach(0..15);

# Autodetect the operating system and the Port

$relais=tie(*FH,'Device::SerialPort',"/dev/usb/ttyUSB0",0) 
     || tie(*FH,'Device::SerialPort',"/dev/usb/ttyUSB1",0) 
     || tie(*FH,'Win32::SerialPort',"COM3",0)
     || tie(*FH,'Win32::SerialPort',"COM4",0)
     || tie(*FH,'Win32::SerialPort',"COM5",0)
     || die "Can't open port: $!\n";

# Set the necessary settings for the RS232 interface:

$relais->baudrate(9600);
# $relais->debug(0);
#print "Handshake: ".$relais->handshake()."\n";
$relais->handshake("none");
$relais->databits(8);
$relais->stopbits(1);

#Test Relais 1 for one second

#print FH "\$R1E-R2A-R3A-R4A-!\n";
#sleep 1;
#print FH "\$R1A-R2A-R3A-R4A-!\n";


# Read in the rules

undef $/;
if(open IN,"<rules.txt")
{
  $rules=<IN>;
  close IN;
}
$/="\n";


# Set up the Network Server:

my $lsn = new IO::Socket::INET(Listen => 10, LocalPort => 0xFA10,Reuse=>1);

print "Listening to port ".0xFA10."\n";


# Set up the multiplexing:

my $sel = new IO::Select( $lsn, \*FH );

while(my @ready = $sel->can_read) 
{
  foreach my $fh (@ready) 
  {
    if($fh eq $lsn) 
    {
      # Create a new socket
      my $new = $lsn->accept;
      $sel->add($new);
      #print "New fileno: ".fileno($new)."\n";
      $puffer{fileno($new)}="";
    }
    else 
    {
      # Process socket
#      print "\n";
#      print ref($fh)."\n";
#      print fileno($fh)."\n";
      my ($content,$length);

      if(ref($fh) eq 'IO::Socket::INET')
      {
        undef $/;
        $length=sysread $fh,$content,1;
        $puffer{fileno($fh)}.=$content;
 
        ReceiveFromClient($fh);
      }
      elsif(ref($fh) eq 'GLOB')
      {
        $length=read $fh,$content,10;
        $puffer{fileno($fh)}.=$content;
 
        ReceiveFromRelais($fh);
      }

      print $content;


      # Maybe we have finished with the socket
      if(!$length and ref($fh) eq 'IO::Socket::INET')
      {
        #print "Cleaning up for ".fileno($fh)."\n";
        $sel->remove($fh);
        $fh->close;
        undef $puffer{fileno($fh)}; 
      }
    }
  }
}

exit;



sub ReceiveFromRelais
{
  if($puffer{fileno($_[0])} =~ s/^.*?\$I1(.)-I2(.)-I3(.)-I4(.)-I5(.)-I6(.)-I7(.)-I8(.)-!//m)
  {
    my @new=($1,$2,$3,$4,$5,$6,$7,$8);
    foreach(0..7)
    {
      $r[$_]= 0 if($new[$_]eq'A');
      $r[$_]= 1 if($new[$_]eq'E');
      $r[$_]=-1 if($new[$_]eq'F');
      #print "$_:$r[$_]\n";
    }
    Calculate();
    Broadcast();
  }
}

sub SendToRelais
{
  $relais->write('$R1'.($r[8]?"E":"A").'-R2'.($r[9]?"E":"A").'-R3'.($r[10]?"E":"A").'-R4'.($r[11]?"E":"A")."-!\n");
}

sub Calculate
{
  #print "Applying: $rules\r\n";
  eval($rules);
}



sub ReceiveFromClient
{
  my $client=$_[0];

#  print "\nReceiveFromClient('".$puffer{fileno($client)}."')\n";

  while ($puffer{fileno($client)} =~ s/^(.*?)[\r\n]+// ) 
  {
    my $in=$1;
    next unless $in;

#    print "\nReceiveFromClient('$in')\n";

    if($in =~ m/^HELP/i)
    {
      print $client "Relaisserver\r\n\r\nKommandos:\r\nSUBSCRIBE reg     abonniert Änderungen eines Registers\r\nSET reg=wert	Ändert den Wert eines Registers\r\nGET reg	Holt den Wert eines Registers\r\nLIST      Listet alle Register mit ihrem aktuellen Wert\r\n";
    }
    elsif($in =~ m/^SUBSCRIBE r?(\d+)$/i)
    {
      push @{$subscribed{$1}},$client;
      #print $client "Subscribed to ".@{$self->{subscribed}}." source(s)\r\n";
    }
    elsif($in =~ m/^SET r?(\d+)=(\d+)$/i)
    {
      $r[$1]=$2;
      Calculate();
      Broadcast();
      print $client "Value set.\r\n";
    }
    elsif($in =~ m/^GET r?(\d+)$/i)
    {
      print $client "Current r$1: ".$r[$1]."\r\n";
    }
    elsif($in =~ m/^LIST$/i)
    {
      foreach (0.. $#r)
      {
        print $client "Current r$_: ".$r[$_]."\r\n";
      }
    }
    elsif($in =~ m/^QUIT$/i)
    {
    }
  }
}

# Routine to send a message to all clients in a mux.
sub Broadcast 
{
  foreach my $num (keys %subscribed) 
  {
    foreach my $fh (@{$subscribed{$num}})
    {
      print $fh "Current r$num: ".$r[$num]."\r\n";
    }
  }
  SendToRelais();
}



