/[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 44 - (hide annotations)
Tue Jun 23 13:10:18 2009 UTC (14 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 17479 byte(s)
/scan now returns JSONP

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26