lib/Quancom/Test/Server.pm
changeset 12 ad264ee5d5ba
child 13 d6f681329542
equal deleted inserted replaced
11:352d5517f1f2 12:ad264ee5d5ba
       
     1 package Quancom::Test::Server;
       
     2 
       
     3 # This package is for internal use only - for testing the
       
     4 # Quancom.pm module and should work like the real USB-OPTO device of
       
     5 # Quancom.
       
     6 
       
     7 use strict;
       
     8 use warnings;
       
     9 use Carp;
       
    10 use IO::Socket::UNIX;
       
    11 use IO::Select;
       
    12 
       
    13 my $STX = "\x02";
       
    14 
       
    15 sub new {
       
    16     my $class = ref $_ ? ref shift : shift;
       
    17 
       
    18     my $self = bless {} => $class;
       
    19 
       
    20     # if there's a filename passed, then we assume it as
       
    21     # the UNIX socket for communication, otherwise we communicate
       
    22     # via STDIN/STDOUT
       
    23     if (@_) {
       
    24         $self->{fifo}   = shift;
       
    25         $self->{socket} = new IO::Socket::UNIX(
       
    26             Listen => 1,
       
    27             Local  => $self->{fifo}
       
    28         ) or croak "Can't create IO::Socket::UNIX: $!\n";
       
    29         warn "listening on: $self->{fifo}\n";
       
    30     }
       
    31     else {
       
    32         warn "listening on: stdio\n";
       
    33     }
       
    34 
       
    35     # we can't use 64bit as Vector (vec()), since not all platforms support it
       
    36     @{ $self->{outputs} } = map { pack "c", $_ } (0, 0, 0, 0, 0, 0, 0, 0);
       
    37     $self->show;
       
    38 
       
    39     return $self;
       
    40 }
       
    41 
       
    42 sub show {
       
    43     my $self = shift;
       
    44     printf STDERR "%0v8b\n", join "", @{ $self->{outputs} };
       
    45 }
       
    46 
       
    47 sub DESTROY {
       
    48     my $self = shift;
       
    49     unlink $self->{fifo} if $self->{fifo};
       
    50 }
       
    51 
       
    52 sub run {
       
    53     my $self = shift;
       
    54 
       
    55     if ($self->{socket}) {
       
    56 
       
    57         # It's a quick and dirty runner!
       
    58         # This runner lives with the assumption, that the client always
       
    59         # sends a line terminated by "\r" in one chunk. No other processing
       
    60         # takes place between the first character and the final "\r",
       
    61         # especially no accepting of new connections or reading of other
       
    62         # connection data or sending data!
       
    63         # BUT: This socket based server can talk to more than one
       
    64         # client.
       
    65 
       
    66         my $s = new IO::Select $self->{socket};
       
    67         while (my @ready = $s->can_read) {
       
    68             foreach my $c (@ready) {
       
    69 
       
    70                 # create a new connection or process incoming
       
    71                 # data
       
    72 
       
    73                 if ($c == $self->{socket}) {
       
    74 		    my $n = $self->{socket}->accept;
       
    75 		    $n->autoflush(1);
       
    76                     $s->add($n);
       
    77                     next;
       
    78                 }
       
    79 
       
    80                 local $/ = "\r";    # quancom sends CR as line terminator
       
    81                 my $l = <$c>;
       
    82                 $s->remove($c), next if not defined $l;
       
    83                 chomp $l;
       
    84 		$l = $self->_process($l);
       
    85 		warn "sending $l\n";
       
    86                 $self->{socket}->print($l . "\r");
       
    87 		warn "done\n";
       
    88             }
       
    89         }
       
    90         return;
       
    91     }
       
    92 
       
    93     # STDIO communication
       
    94     while (<>) {
       
    95         chomp;
       
    96         print $self->_process($_), "\n";
       
    97     }
       
    98 }
       
    99 
       
   100 sub _process {
       
   101     my $self = shift;
       
   102     my $line = shift;
       
   103     my $retval;
       
   104 
       
   105     # some fixups
       
   106     $line =~ s/^$STX//;    # cut STX, if any
       
   107     $line =~ s/(..)$//;    # cut checksum
       
   108 
       
   109     warn "got: <STX>$line($1)\n";
       
   110 
       
   111     return pack("ac", "E", 1)    # checksum error
       
   112       if $1 ne ".." and hex($1) != unpack("%8C*", "$STX$line");
       
   113 
       
   114     my ($jid, $cmd, $width, $addr, $data) = (
       
   115         $line =~ /
       
   116 	([\da-f]{2})	# jid
       
   117 	((?-i)[RWSC])	# cmd
       
   118 	((?-i)[BWLX])	# width
       
   119 	([\da-f]{4})	# addr
       
   120 	(.*?)		# data
       
   121 	$/xi
       
   122     );
       
   123 
       
   124     # some transformations for more easy use
       
   125     $addr = hex($addr);
       
   126     $width =
       
   127         $width eq "B" ? 1
       
   128       : $width eq "W" ? 2
       
   129       : $width eq "L" ? 3
       
   130       : $width eq "X" ? 4
       
   131       :                 0;
       
   132 
       
   133     if ($cmd eq "W") {
       
   134         my @data = $data =~ /(..)/g;
       
   135         return pack("ac", "E", 3)    # width error
       
   136           if @data != $width;
       
   137 
       
   138         my $offset = $addr - 0x100;
       
   139 
       
   140 	warn "@data\n";
       
   141 
       
   142         $self->show;
       
   143         @{ $self->{outputs} }[ $offset .. $offset + $width - 1 ] =
       
   144           map { pack "C", hex($_) } @data;
       
   145         $self->show;
       
   146 
       
   147 	$retval = "O$jid";
       
   148     }
       
   149     else {
       
   150 	warn "command \"$cmd\" not supported\n";
       
   151 	$retval = pack("ac", "E", 2);
       
   152     }
       
   153 
       
   154     return $retval . sprintf("%02x", unpack("%8C*", $retval));
       
   155 }
       
   156 
       
   157 1;