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

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

  ViewVC Help
Powered by ViewVC 1.1.26