/[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

Annotation of /3m-810.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 45 - (hide annotations)
Tue Jun 23 13:29:10 2009 UTC (14 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 17513 byte(s)
command-line option --http-server

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26