/[RFID]/3m-810.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /3m-810.pl

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2 by dpavlin, Sun Sep 28 14:05:43 2008 UTC revision 43 by dpavlin, Tue Jun 23 12:19:30 2009 UTC
# Line 6  use warnings; Line 6  use warnings;
6    
7  use Data::Dump qw/dump/;  use Data::Dump qw/dump/;
8  use Carp qw/confess/;  use Carp qw/confess/;
9    use Getopt::Long;
10    use File::Slurp;
11    
12    use IO::Socket::INET;
13    
14    my $meteor_server = '192.168.1.13:4671';
15    my $meteor_fh;
16    
17    sub meteor {
18            my @a = @_;
19            push @a, scalar localtime() if $a[0] =~ m{^info};
20    
21            if ( ! defined $meteor_fh ) {
22                    if ( $meteor_fh =
23                                    IO::Socket::INET->new(
24                                            PeerAddr => $meteor_server,
25                                            Timeout => 1,
26                                    )
27                    ) {
28                            warn "# meteor connected to $meteor_server";
29                    } else {
30                            warn "can't connect to meteor $meteor_server: $!";
31                            $meteor_fh = 0;
32                    }
33            }
34    
35            if ( $meteor_fh ) {
36                    warn ">> meteor ",dump( @a );
37                    print $meteor_fh "ADDMESSAGE test ",join('|',@a),"\n"
38            }
39    }
40    
41    my $listen_port = 9000;                  # pick something not in use
42    sub http_server {
43    
44            my $server = IO::Socket::INET->new(
45                    Proto     => 'tcp',
46                    LocalPort => $listen_port,
47                    Listen    => SOMAXCONN,
48                    Reuse     => 1
49            );
50                                                                      
51            die "can't setup server" unless $server;
52    
53            print "Server $0 accepting clients at http://localhost:$listen_port/\n";
54    
55            sub static {
56                    my ($client,$path) = @_;
57    
58                    $path = "www/$path";
59    
60                    return unless -e $path;
61    
62                    my $type = 'text/plain';
63                    $type = 'text/html' if $path =~ m{\.htm};
64                    $type = 'application/javascript' if $path =~ m{\.js};
65    
66                    print $client "HTTP/1.0 200 OK\r\nContent-Type: $type\r\n\r\n";
67                    open(my $html, $path);
68                    while(<$html>) {
69                            print $client $_;
70                    }
71                    close($html);
72    
73                    return $path;
74            }
75    
76            while (my $client = $server->accept()) {
77                    $client->autoflush(1);
78                    my $request = <$client>;
79    
80                    warn "<< $request\n";
81    
82                    if ($request =~ m{^GET (/.*) HTTP/1.[01]}) {
83                            my $method = $1;
84                            if ( my $path = static( $client,$1 ) ) {
85                                    warn ">> $path";
86                            } elsif ( $method =~ m{/scan} ) {
87                                    print $client "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n";
88                                    my $tags = scan_for_tags();
89                                    print $client "tags: ",dump($tags);
90                                    my $json;
91                                    map { $json->{$_} = decode_tag($_) } keys %$tags;
92                                    print $client "decoded: ",dump( $json );
93                            } else {
94                                    print $client "HTTP/1.0 404 Unkown method\r\n";
95                            }
96                    } else {
97                            print $client "HTTP/1.0 500 No method\r\n";
98                    }
99                    close $client;
100            }
101    
102            die "server died";
103    }
104    
105    my $debug = 0;
106    
107    my $device    = "/dev/ttyUSB0";
108    my $baudrate  = "19200";
109    my $databits  = "8";
110    my $parity        = "none";
111    my $stopbits  = "1";
112    my $handshake = "none";
113    
114    my $program_path = './program/';
115    my $secure_path = './secure/';
116    
117    # http server
118    my $http_server = 1;
119    
120    # 3M defaults: 8,4
121    my $max_rfid_block = 16;
122    my $read_blocks = 8;
123    
124  my $response = {  my $response = {
125          'd500090400110a0500027250'                              => 'version?',          'd500090400110a0500027250'                              => 'version?',
# Line 19  my $response = { Line 133  my $response = {
133          'd60017fe00000502e00401003123aa26e00401003123aa283124' => 'tag #1 + tag red-stripe',          'd60017fe00000502e00401003123aa26e00401003123aa283124' => 'tag #1 + tag red-stripe',
134  };  };
135    
136    GetOptions(
137            'd|debug+'    => \$debug,
138            'device=s'    => \$device,
139            'baudrate=i'  => \$baudrate,
140            'databits=i'  => \$databits,
141            'parity=s'    => \$parity,
142            'stopbits=i'  => \$stopbits,
143            'handshake=s' => \$handshake,
144            'meteor=s'    => \$meteor_server,
145    ) or die $!;
146    
147    my $verbose = $debug > 0 ? $debug-- : 0;
148    
149  =head1 NAME  =head1 NAME
150    
151  3m-810 - support for 3M 810 RFID reader  3m-810 - support for 3M 810 RFID reader
152    
153  =head1 SYNOPSIS  =head1 SYNOPSIS
154    
155  3m-810.pl [DEVICE [BAUD [DATA [PARITY [STOP [FLOW]]]]]]  3m-810.pl --device /dev/ttyUSB0
156    
157  =head1 DESCRIPTION  =head1 DESCRIPTION
158    
# Line 37  L<Device::SerialPort(3)> Line 164  L<Device::SerialPort(3)>
164    
165  L<perl(1)>  L<perl(1)>
166    
167    L<http://stackoverflow.com/questions/149617/how-could-i-guess-a-checksum-algorithm>
168    
169  =head1 AUTHOR  =head1 AUTHOR
170    
171  Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>  Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>
# Line 48  it under the same terms ans Perl itself. Line 177  it under the same terms ans Perl itself.
177    
178  =cut  =cut
179    
180  # your serial port.  my $tags_data;
181  my ($device,$baudrate,$databits,$parity,$stopbits,$handshake)=@ARGV;  my $visible_tags;
182  $device    ||= "/dev/ttyUSB0";  
183  $baudrate  ||= "19200";  my $item_type = {
184  $databits  ||= "8";          1 => 'Book',
185  $parity    ||= "none";          6 => 'CD/CD ROM',
186  $stopbits  ||= "1";          2 => 'Magazine',
187  $handshake ||= "none";          13 => 'Book with Audio Tape',
188            9 => 'Book with CD/CD ROM',
189            0 => 'Other',
190    
191            5 => 'Video',
192            4 => 'Audio Tape',
193            3 => 'Bound Journal',
194            8 => 'Book with Diskette',
195            7 => 'Diskette',
196    };
197    
198    warn "## known item type: ",dump( $item_type ) if $debug;
199    
200  my $port=new Device::SerialPort($device) || die "new($device): $!\n";  my $port=new Device::SerialPort($device) || die "can't open serial port $device: $!\n";
201    warn "using $device $handshake $baudrate $databits $parity $stopbits" if $debug;
202  $handshake=$port->handshake($handshake);  $handshake=$port->handshake($handshake);
203  $baudrate=$port->baudrate($baudrate);  $baudrate=$port->baudrate($baudrate);
204  $databits=$port->databits($databits);  $databits=$port->databits($databits);
205  $parity=$port->parity($parity);  $parity=$port->parity($parity);
206  $stopbits=$port->stopbits($stopbits);  $stopbits=$port->stopbits($stopbits);
207    
208  print "## using $device $baudrate $databits $parity $stopbits\n";  print "## using $device $baudrate $databits $parity $stopbits debug: $debug verbose: $verbose\n";
209    
210  # Just in case: reset our timing and buffers  # Just in case: reset our timing and buffers
211  $port->lookclear();  $port->lookclear();
# Line 75  $port->read_char_time(5); Line 216  $port->read_char_time(5);
216  #$port->stty_inpck(1);  #$port->stty_inpck(1);
217  #$port->stty_istrip(1);  #$port->stty_istrip(1);
218    
219  cmd( 'D5 00  05  04   00   11                 8C66', 'hw version?',  # initial hand-shake with device
220       'D5 00  09  04   00   11   0A 05 00 02   7250', 'hw 10.5.0.2', sub {  
221          my ( $len, $payload, $checksum ) = @_;  cmd( 'D5 00  05   04 00 11                 8C66', 'hw version',
222          assert( 0, 3 );       'D5 00  09   04 00 11   0A 05 00 02   7250', sub {
223          print "hardware version ", join('.', unpack('CCCC', substr($payload,3,4))), "\n";          my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
224            print "hardware version $hw_ver\n";
225            meteor( 'info', "Found reader hardware $hw_ver" );
226  });  });
227    
228  cmd( 'D6 00  0C  13   04   01 00  02 00  03 00  04 00   AAF2','stats?' );  cmd( 'D6 00  0C   13  04  01 00  02 00  03 00  04 00   AAF2','FIXME: stats?',
229  #     D6 00  0C  13   00   02 01 01 03 02 02 03  00   E778       'D6 00  0C   13  00  02 01 01 03 02 02 03  00     E778', sub { assert() }  );
230    
231    sub scan_for_tags {
232    
233            my @tags;
234    
235            cmd( 'D6 00  05   FE     00  05         FA40', "scan for tags, retry $_",
236                     'D6 00  0F   FE  00 00  05 ', sub { # 01 E00401003123AA26  941A         # seen, serial length: 8
237                            my $rest = shift || die "no rest?";
238                            my $nr = ord( substr( $rest, 0, 1 ) );
239    
240                            if ( ! $nr ) {
241                                    print "no tags in range\n";
242                                    update_visible_tags();
243                                    meteor( 'info-none-in-range' );
244                                    $tags_data = {};
245                            } else {
246    
247  cmd( 'D6 00  05  FE     00  05  FA40', "XXX scan $_",                                  my $tags = substr( $rest, 1 );
      'D6 00  07  FE  00 00  05  00  C97B -- no tag' ) foreach ( 1 .. 10 );  
248    
249  #     D6 00  0F  FE  00 00  05  01  E00401003123AA26  941A       # seen                                  my $tl = length( $tags );
250                                    die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;
251    
252  cmd( 'D6 00  0D  02      E00401003123AA26   00   03     1CC4', 'read offset: 0 blocks: 3' );                                  push @tags, uc(unpack('H16', substr($tags, $_ * 8, 8))) foreach ( 0 .. $nr - 1 );
253                                    warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags ) if $debug;
254                                    print "$nr tags in range: ", join(',', @tags ) , "\n";
255    
256  #     D6 00  1F  02 00   E00401003123AA26   03   00 00   04 11 00 01   01 00   30 30 30 30   02 00   30 30 30 30    E5F4                                  meteor( 'info-in-range', join(' ',@tags));
 warn "D6 00  1F  02 00   E00401003123AA26   03   00 00   04 11 00 01   01 00   31 32 33 34   02 00   35 36 37 38    531F\n";  
257    
258  cmd( 'D6 00  0D  02      E00401003123AA26   03   04     3970', 'read offset: 3 blocks: 4' );                                  update_visible_tags( @tags );
259                            }
260                    }
261            );
262    
263  #     D6 00  25  02 00   E00401003123AA26   04                         03 00   30 30 00 00   04 00   00 00 00 00            warn "## tags: ",dump( @tags );
264  #                                                                      05 00   00 00 00 00   06 00   00 00 00 00    B9BA          return $tags_data;
265  warn "D6 00  25  02 00   E00401003123AA26   04                         03 00   39 30 31 32   04 00   33 34 35 36  
266                                                                         05 00   00 00 00 00   06 00   00 00 00 00    524B\n";  }
267  warn "D6 00  0F  FE  00 00  05 01   E00401003123AA26  941A ##### ready?\n";  
268    # start scanning for tags
269    
270    if ( $http_server ) {
271            http_server;
272    } else {
273            scan_for_tags while 1;
274    }
275    
276    die "over and out";
277    
278    sub update_visible_tags {
279            my @tags = @_;
280    
281            my $last_visible_tags = $visible_tags;
282            $visible_tags = {};
283    
284            foreach my $tag ( @tags ) {
285                    if ( ! defined $last_visible_tags->{$tag} ) {
286                            if ( defined $tags_data->{$tag} ) {
287    #                               meteor( 'in-range', $tag );
288                            } else {
289                                    meteor( 'read', $tag );
290                                    read_tag( $tag );
291                            }
292                            $visible_tags->{$tag}++;
293                    } else {
294                            warn "## using cached data for $tag" if $debug;
295                    }
296                    delete $last_visible_tags->{$tag}; # leave just missing tags
297    
298                    if ( -e "$program_path/$tag" ) {
299                                    meteor( 'write', $tag );
300                                    write_tag( $tag );
301                    }
302                    if ( -e "$secure_path/$tag" ) {
303                                    meteor( 'secure', $tag );
304                                    secure_tag( $tag );
305                    }
306            }
307    
308            foreach my $tag ( keys %$last_visible_tags ) {
309                    my $data = delete $tags_data->{$tag};
310                    print "removed tag $tag with data ",dump( $data ),"\n";
311                    meteor( 'removed', $tag );
312            }
313    
314            warn "## update_visible_tags(",dump( @tags ),") = ",dump( $visible_tags )," removed: ",dump( $last_visible_tags ), " data: ",dump( $tags_data ) if $debug;
315    }
316    
317    my $tag_data_block;
318    
319    sub read_tag_data {
320            my ($start_block,$rest) = @_;
321            die "no rest?" unless $rest;
322    
323            my $last_block = 0;
324    
325            warn "## DATA [$start_block] ", dump( $rest ) if $debug;
326            my $tag = uc(unpack('H16',substr( $rest, 0, 8 )));
327            my $blocks = ord(substr($rest,8,1));
328            $rest = substr($rest,9); # leave just data blocks
329            foreach my $nr ( 0 .. $blocks - 1 ) {
330                    my $block = substr( $rest, $nr * 6, 6 );
331                    warn "## block ",as_hex( $block ) if $debug;
332                    my $ord   = unpack('v',substr( $block, 0, 2 ));
333                    my $expected_ord = $nr + $start_block;
334                    warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
335                    my $data  = substr( $block, 2 );
336                    die "data payload should be 4 bytes" if length($data) != 4;
337                    warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
338                    $tag_data_block->{$tag}->[ $ord ] = $data;
339                    $last_block = $ord;
340            }
341            $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
342    
343            my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
344            print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
345    
346            return $last_block + 1;
347    }
348    
349    sub decode_tag {
350            my $tag = shift;
351    
352            my $data = $tags_data->{$tag} || die "no data for $tag";
353    
354            my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom ) = unpack('C4Z16Nl>',$data);
355            my $hash = {
356                    u1 => $u1,
357                    u2 => $u2,
358                    set => ( $set_item & 0xf0 ) >> 4,
359                    total => ( $set_item & 0x0f ),
360    
361                    type => $type,
362                    content => $content,
363    
364                    branch => $br_lib >> 20,
365                    library => $br_lib & 0x000fffff,
366    
367                    custom => $custom,
368            };
369    
370            return $hash;
371    }
372    
373    sub read_tag {
374            my ( $tag ) = @_;
375    
376            confess "no tag?" unless $tag;
377    
378            print "read_tag $tag\n";
379    
380            my $start_block = 0;
381    
382            while ( $start_block < $max_rfid_block ) {
383    
384                    cmd(
385                             sprintf( "D6 00  0D  02      $tag   %02x   %02x     ffff", $start_block, $read_blocks ),
386                                    "read $tag offset: $start_block blocks: $read_blocks",
387                            "D6 00  1F  02 00", sub { # $tag  03   00 00   04 11 00 01   01 00   31 32 33 34   02 00   35 36 37 38    531F\n";
388                                    $start_block = read_tag_data( $start_block, @_ );
389                                    warn "# read tag upto $start_block\n";
390                            },
391                            "D6 00  0F  FE  00 00  05 01   $tag    941A", sub {
392                                    print "FIXME: tag $tag ready? (expected block read instead)\n";
393                            },
394                    );
395    
396            }
397    
398            my $security;
399    
400            cmd(
401                    "D6 00 0B 0A $tag 1234", "check security $tag",
402                    "D6 00 0D 0A 00", sub {
403                            my $rest = shift;
404                            my $from_tag;
405                            ( $from_tag, $security ) = ( substr($rest,0,8), substr($rest,8,1) );
406                            die "security from other tag: ",as_hex( $from_tag ) if $from_tag ne str2bytes( $tag );
407                            $security = as_hex( $security );
408                            warn "# SECURITY $tag = $security\n";
409                    }
410            );
411    
412            print "TAG $tag ", dump(decode_tag( $tag ));
413    }
414    
415    sub write_tag {
416            my ($tag) = @_;
417    
418            my $path = "$program_path/$tag";
419    
420            my $data = read_file( $path );
421            my $hex_data;
422    
423            if ( $data =~ s{^hex\s+}{} ) {
424                    $hex_data = $data;
425                    $hex_data =~ s{\s+}{}g;
426            } else {
427    
428                    $data .= "\0" x ( 4 - ( length($data) % 4 ) );
429    
430                    my $max_len = $max_rfid_block * 4;
431    
432                    if ( length($data) > $max_len ) {
433                            $data = substr($data,0,$max_len);
434                            warn "strip content to $max_len bytes\n";
435                    }
436    
437                    $hex_data = unpack('H*', $data);
438            }
439    
440            my $len = length($hex_data) / 2;
441            # pad to block size
442            $hex_data .= '00' x ( 4 - $len % 4 );
443            my $blocks = sprintf('%02x', length($hex_data) / 4);
444    
445            print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
446    
447            cmd(
448                    "d6 00  ff  04  $tag  00 $blocks 00  $hex_data  ffff", "write $tag",
449                    "d6 00  0d  04 00  $tag  $blocks  afb1", sub { assert() },
450            ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
451    
452            my $to = $path;
453            $to .= '.' . time();
454    
455            rename $path, $to;
456            print ">> $to\n";
457    
458            delete $tags_data->{$tag};      # force re-read of tag
459    }
460    
461    sub secure_tag {
462            my ($tag) = @_;
463    
464            my $path = "$secure_path/$tag";
465            my $data = substr(read_file( $path ),0,2);
466    
467            cmd(
468                    "d6 00  0c  09  $tag $data 1234", "secure $tag -> $data",
469                    "d6 00  0c  09 00  $tag  1234", sub { assert() },
470            );
471    
472            my $to = $path;
473            $to .= '.' . time();
474    
475            rename $path, $to;
476            print ">> $to\n";
477    }
478    
479    exit;
480    
481  for ( 1 .. 3 ) {  for ( 1 .. 3 ) {
482    
# Line 133  sub writechunk Line 508  sub writechunk
508  {  {
509          my $str=shift;          my $str=shift;
510          my $count = $port->write($str);          my $count = $port->write($str);
511          print ">> ", as_hex( $str ), "\t[$count]\n";          my $len = length($str);
512            die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
513            print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
514  }  }
515    
516  sub as_hex {  sub as_hex {
# Line 141  sub as_hex { Line 518  sub as_hex {
518          foreach my $str ( @_ ) {          foreach my $str ( @_ ) {
519                  my $hex = unpack( 'H*', $str );                  my $hex = unpack( 'H*', $str );
520                  $hex =~ s/(..)/$1 /g if length( $str ) > 2;                  $hex =~ s/(..)/$1 /g if length( $str ) > 2;
521                    $hex =~ s/\s+$//;
522                  push @out, $hex;                  push @out, $hex;
523          }          }
524          return join('  ', @out);          return join(' | ', @out);
525  }  }
526    
527  sub read_bytes {  sub read_bytes {
# Line 151  sub read_bytes { Line 529  sub read_bytes {
529          my $data = '';          my $data = '';
530          while ( length( $data ) < $len ) {          while ( length( $data ) < $len ) {
531                  my ( $c, $b ) = $port->read(1);                  my ( $c, $b ) = $port->read(1);
532                    die "no bytes on port: $!" unless defined $b;
533                  #warn "## got $c bytes: ", as_hex($b), "\n";                  #warn "## got $c bytes: ", as_hex($b), "\n";
534                  $data .= $b;                  $data .= $b;
535          }          }
536          $desc ||= '?';          $desc ||= '?';
537          warn "#< ", as_hex($data), "\t$desc\n";          warn "#< ", as_hex($data), "\t$desc\n" if $debug;
538          return $data;          return $data;
539  }  }
540    
541  my $assert;  our $assert;
542    
543    # my $rest = skip_assert( 3 );
544    sub skip_assert {
545            assert( 0, shift );
546    }
547    
548  sub assert {  sub assert {
549          my ( $from, $to ) = @_;          my ( $from, $to ) = @_;
550    
551          warn "# assert ", dump( $assert );          $from ||= 0;
552            $to = length( $assert->{expect} ) if ! defined $to;
553    
554          my $p = substr( $assert->{payload}, $from, $to );          my $p = substr( $assert->{payload}, $from, $to );
555          my $e = substr( $assert->{expect},  $from, $to );          my $e = substr( $assert->{expect},  $from, $to );
556          warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), "\t[$from-$to]\n" if $e ne $p;          warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
557    
558            # return the rest
559            return substr( $assert->{payload}, $to );
560  }  }
561    
562  sub readchunk {  use Digest::CRC;
563          my ( $parser ) = @_;  
564    sub crcccitt {
565            my $bytes = shift;
566            my $crc = Digest::CRC->new(
567                    # midified CCITT to xor with 0xffff instead of 0x0000
568                    width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
569            ) or die $!;
570            $crc->add( $bytes );
571            pack('n', $crc->digest);
572    }
573    
574    # my $checksum = checksum( $bytes );
575    # my $checksum = checksum( $bytes, $original_checksum );
576    sub checksum {
577            my ( $bytes, $checksum ) = @_;
578    
579            my $len = ord(substr($bytes,2,1));
580            my $len_real = length($bytes) - 1;
581    
582            if ( $len_real != $len ) {
583                    print "length wrong: $len_real != $len\n";
584                    $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
585            }
586    
587          sleep 1;        # FIXME remove          my $xor = crcccitt( substr($bytes,1) ); # skip D6
588            warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
589    
590            if ( defined $checksum && $xor ne $checksum ) {
591                    print "checksum doesn't match: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n";
592                    return $bytes . $xor;
593            }
594            return $bytes . $checksum;
595    }
596    
597    our $dispatch;
598    
599    sub readchunk {
600    #       sleep 1;        # FIXME remove
601    
602          # read header of packet          # read header of packet
603          my $header = read_bytes( 2, 'header' );          my $header = read_bytes( 2, 'header' );
604          my $length = read_bytes( 1, 'length' );          my $length = read_bytes( 1, 'length' );
605          my $len = ord($length);          my $len = ord($length);
606          my $data = read_bytes( $len, 'data' );          my $data = read_bytes( $len, 'data' );
         my ( $cmd ) = unpack('C', $data );  
607    
608          my $payload  = substr( $data, 0, -2 );          my $payload  = substr( $data, 0, -2 );
609          my $payload_len = length($data);          my $payload_len = length($data);
610          warn "## payload too short $payload_len != $len\n" if $payload_len != $len;          warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
611    
612          my $checksum = substr( $data, -2, 2 );          my $checksum = substr( $data, -2, 2 );
613          # FIXME check checksum          checksum( $header . $length . $payload , $checksum );
614    
615          print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), "checksum: ", as_hex( $checksum ),"\n";          print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n" if $verbose;
616    
617          $assert->{len}      = $len;          $assert->{len}      = $len;
618          $assert->{payload}  = $payload;          $assert->{payload}  = $payload;
         $assert->{checksum} = $checksum;  
619    
620          $parser->( $len, $payload, $checksum ) if $parser && ref($parser) eq 'CODE';          my $full = $header . $length . $data; # full
621            # find longest match for incomming data
622            my ($to) = grep {
623                    my $match = substr($payload,0,length($_));
624                    m/^\Q$match\E/
625            } sort { length($a) <=> length($b) } keys %$dispatch;
626            warn "?? payload dispatch to ",dump( $payload, $dispatch, $to ) if $debug;
627    
628            if ( defined $to ) {
629                    my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
630                    warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
631                    $dispatch->{ $to }->( $rest );
632            } else {
633                    print "NO DISPATCH for ",dump( $full ),"\n";
634            }
635    
636          return $data;          return $data;
637  }  }
638    
639  sub str2bytes {  sub str2bytes {
640          my $str = shift || confess "no str?";          my $str = shift || confess "no str?";
641          $str =~ s/\s+(\S\S)(\S\S)+\s*/ $1 $2/;  # fix checksum          my $b = $str;
642          $str =~ s/\s+/\\x/g;          $b =~ s/\s+//g;
643          $str = '"\x' . $str . '"';          $b =~ s/(..)/\\x$1/g;
644          my $bytes = eval $str;          $b = "\"$b\"";
645            my $bytes = eval $b;
646          die $@ if $@;          die $@ if $@;
647            warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
648          return $bytes;          return $bytes;
649  }  }
650    
651  sub cmd {  sub cmd {
652          my ( $cmd, $cmd_desc, $expect, $expect_desc, $coderef ) = @_;          my $cmd = shift || confess "no cmd?";
653            my $cmd_desc = shift || confess "no description?";
654            my @expect = @_;
655    
656          my $bytes = str2bytes( $cmd );          my $bytes = str2bytes( $cmd );
657    
658          warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n";          # fix checksum if needed
659            $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
660    
661            warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
662          $assert->{send} = $cmd;          $assert->{send} = $cmd;
663          writechunk( $bytes );          writechunk( $bytes );
664    
665          if ( $expect ) {          while ( @expect ) {
666                  warn "?? $expect", $expect_desc ? "\t## $expect_desc" : '', "\n";                  my $pattern = str2bytes( shift @expect ) || confess "no pattern?";
667                  $assert->{expect} = substr(str2bytes($expect), 3, -2); # just expected payload                  my $coderef = shift @expect || confess "no coderef?";
668                  readchunk( $coderef );                  confess "not coderef" unless ref $coderef eq 'CODE';
669    
670                    next if defined $dispatch->{ $pattern };
671    
672                    $dispatch->{ substr($pattern,3) } = $coderef;
673                    warn "++ dispatch ", as_hex($pattern) ,dump( $dispatch ) if $debug;
674          }          }
675    
676            readchunk;
677  }  }
678    

Legend:
Removed from v.2  
changed lines
  Added in v.43

  ViewVC Help
Powered by ViewVC 1.1.26