/[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 56 - (hide annotations)
Fri Jun 26 11:46:45 2009 UTC (14 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 18173 byte(s)
default directory index is rfid.html

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26