/[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 58 - (hide annotations)
Sat Jul 4 08:33:56 2009 UTC (14 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 18193 byte(s)
sleep a second between two scans when running without 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 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 dpavlin 58 while (1) {
304     scan_for_tags;
305     sleep 1;
306     }
307 dpavlin 43 }
308    
309     die "over and out";
310    
311 dpavlin 22 sub update_visible_tags {
312     my @tags = @_;
313    
314     my $last_visible_tags = $visible_tags;
315     $visible_tags = {};
316    
317     foreach my $tag ( @tags ) {
318 dpavlin 51 $visible_tags->{$tag}++;
319 dpavlin 22 if ( ! defined $last_visible_tags->{$tag} ) {
320 dpavlin 25 if ( defined $tags_data->{$tag} ) {
321     # meteor( 'in-range', $tag );
322     } else {
323     meteor( 'read', $tag );
324     read_tag( $tag );
325     }
326 dpavlin 22 } else {
327     warn "## using cached data for $tag" if $debug;
328     }
329     delete $last_visible_tags->{$tag}; # leave just missing tags
330 dpavlin 29
331     if ( -e "$program_path/$tag" ) {
332     meteor( 'write', $tag );
333     write_tag( $tag );
334     }
335 dpavlin 34 if ( -e "$secure_path/$tag" ) {
336     meteor( 'secure', $tag );
337     secure_tag( $tag );
338     }
339 dpavlin 22 }
340    
341     foreach my $tag ( keys %$last_visible_tags ) {
342 dpavlin 23 my $data = delete $tags_data->{$tag};
343     print "removed tag $tag with data ",dump( $data ),"\n";
344 dpavlin 25 meteor( 'removed', $tag );
345 dpavlin 22 }
346    
347     warn "## update_visible_tags(",dump( @tags ),") = ",dump( $visible_tags )," removed: ",dump( $last_visible_tags ), " data: ",dump( $tags_data ) if $debug;
348     }
349    
350 dpavlin 28 my $tag_data_block;
351 dpavlin 22
352 dpavlin 28 sub read_tag_data {
353     my ($start_block,$rest) = @_;
354     die "no rest?" unless $rest;
355 dpavlin 41
356     my $last_block = 0;
357    
358 dpavlin 28 warn "## DATA [$start_block] ", dump( $rest ) if $debug;
359     my $tag = uc(unpack('H16',substr( $rest, 0, 8 )));
360     my $blocks = ord(substr($rest,8,1));
361     $rest = substr($rest,9); # leave just data blocks
362     foreach my $nr ( 0 .. $blocks - 1 ) {
363     my $block = substr( $rest, $nr * 6, 6 );
364     warn "## block ",as_hex( $block ) if $debug;
365     my $ord = unpack('v',substr( $block, 0, 2 ));
366     my $expected_ord = $nr + $start_block;
367 dpavlin 41 warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
368 dpavlin 28 my $data = substr( $block, 2 );
369     die "data payload should be 4 bytes" if length($data) != 4;
370 dpavlin 40 warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
371 dpavlin 28 $tag_data_block->{$tag}->[ $ord ] = $data;
372 dpavlin 41 $last_block = $ord;
373 dpavlin 28 }
374     $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
375 dpavlin 31
376     my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
377 dpavlin 42 print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
378 dpavlin 41
379 dpavlin 42 return $last_block + 1;
380 dpavlin 28 }
381    
382 dpavlin 43 sub decode_tag {
383     my $tag = shift;
384    
385     my $data = $tags_data->{$tag} || die "no data for $tag";
386    
387     my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom ) = unpack('C4Z16Nl>',$data);
388     my $hash = {
389     u1 => $u1,
390     u2 => $u2,
391     set => ( $set_item & 0xf0 ) >> 4,
392     total => ( $set_item & 0x0f ),
393    
394     type => $type,
395     content => $content,
396    
397     branch => $br_lib >> 20,
398     library => $br_lib & 0x000fffff,
399    
400     custom => $custom,
401     };
402    
403     return $hash;
404     }
405    
406 dpavlin 16 sub read_tag {
407     my ( $tag ) = @_;
408 dpavlin 1
409 dpavlin 22 confess "no tag?" unless $tag;
410    
411 dpavlin 16 print "read_tag $tag\n";
412 dpavlin 1
413 dpavlin 41 my $start_block = 0;
414 dpavlin 28
415 dpavlin 41 while ( $start_block < $max_rfid_block ) {
416 dpavlin 1
417 dpavlin 41 cmd(
418     sprintf( "D6 00 0D 02 $tag %02x %02x ffff", $start_block, $read_blocks ),
419     "read $tag offset: $start_block blocks: $read_blocks",
420     "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";
421     $start_block = read_tag_data( $start_block, @_ );
422     warn "# read tag upto $start_block\n";
423     },
424     "D6 00 0F FE 00 00 05 01 $tag 941A", sub {
425     print "FIXME: tag $tag ready? (expected block read instead)\n";
426     },
427     );
428    
429     }
430    
431 dpavlin 33 my $security;
432    
433     cmd(
434     "D6 00 0B 0A $tag 1234", "check security $tag",
435     "D6 00 0D 0A 00", sub {
436     my $rest = shift;
437     my $from_tag;
438     ( $from_tag, $security ) = ( substr($rest,0,8), substr($rest,8,1) );
439     die "security from other tag: ",as_hex( $from_tag ) if $from_tag ne str2bytes( $tag );
440     $security = as_hex( $security );
441 dpavlin 54 $tags_security->{$tag} = $security;
442 dpavlin 33 warn "# SECURITY $tag = $security\n";
443     }
444     );
445    
446 dpavlin 43 print "TAG $tag ", dump(decode_tag( $tag ));
447 dpavlin 16 }
448    
449 dpavlin 29 sub write_tag {
450     my ($tag) = @_;
451    
452     my $path = "$program_path/$tag";
453    
454     my $data = read_file( $path );
455 dpavlin 38 my $hex_data;
456 dpavlin 29
457 dpavlin 38 if ( $data =~ s{^hex\s+}{} ) {
458     $hex_data = $data;
459     $hex_data =~ s{\s+}{}g;
460     } else {
461 dpavlin 29
462 dpavlin 38 $data .= "\0" x ( 4 - ( length($data) % 4 ) );
463 dpavlin 30
464 dpavlin 41 my $max_len = $max_rfid_block * 4;
465 dpavlin 30
466 dpavlin 38 if ( length($data) > $max_len ) {
467     $data = substr($data,0,$max_len);
468     warn "strip content to $max_len bytes\n";
469     }
470    
471     $hex_data = unpack('H*', $data);
472     }
473    
474     my $len = length($hex_data) / 2;
475 dpavlin 40 # pad to block size
476     $hex_data .= '00' x ( 4 - $len % 4 );
477     my $blocks = sprintf('%02x', length($hex_data) / 4);
478 dpavlin 38
479     print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
480    
481 dpavlin 29 cmd(
482 dpavlin 38 "d6 00 ff 04 $tag 00 $blocks 00 $hex_data ffff", "write $tag",
483     "d6 00 0d 04 00 $tag $blocks afb1", sub { assert() },
484 dpavlin 40 ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
485 dpavlin 29
486     my $to = $path;
487     $to .= '.' . time();
488    
489     rename $path, $to;
490     print ">> $to\n";
491    
492 dpavlin 30 delete $tags_data->{$tag}; # force re-read of tag
493 dpavlin 29 }
494    
495 dpavlin 34 sub secure_tag {
496     my ($tag) = @_;
497    
498     my $path = "$secure_path/$tag";
499     my $data = substr(read_file( $path ),0,2);
500    
501     cmd(
502     "d6 00 0c 09 $tag $data 1234", "secure $tag -> $data",
503     "d6 00 0c 09 00 $tag 1234", sub { assert() },
504     );
505    
506     my $to = $path;
507     $to .= '.' . time();
508    
509     rename $path, $to;
510     print ">> $to\n";
511     }
512    
513 dpavlin 19 exit;
514    
515 dpavlin 1 for ( 1 .. 3 ) {
516    
517     # ++-->type 00-0a
518     # 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
519     # 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
520     # 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
521    
522     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 $_" );
523     warn "D6 00 0D 04 00 E00401003123AA26 07 CFF1 -- ack 7 block?\n";
524    
525     }
526     warn " D6 00 0F FE 00 00 05 01 E00401003123AA26 941A\n";
527    
528     cmd( 'D6 00 05 FE 00 05 FA 40', "port-write scan $_" ) foreach ( 1 .. 2 );
529    
530     cmd('D6 00 0C 09 E00401003123AA26 D7 3AF0', 'checkin?',
531     'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
532     cmd('D6 00 0C 09 E00401003123AA26 DA EB5D', 'checkout?',
533     'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
534    
535     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',
536     'D6 00 0D 04 00 E00401003123AA26 06 DFD0 -- ack 6 blocks' ) foreach ( 1 .. 3 );
537    
538     undef $port;
539     print "Port closed\n";
540    
541     sub writechunk
542     {
543     my $str=shift;
544     my $count = $port->write($str);
545 dpavlin 38 my $len = length($str);
546     die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
547 dpavlin 19 print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
548 dpavlin 1 }
549    
550     sub as_hex {
551     my @out;
552     foreach my $str ( @_ ) {
553     my $hex = unpack( 'H*', $str );
554 dpavlin 2 $hex =~ s/(..)/$1 /g if length( $str ) > 2;
555 dpavlin 8 $hex =~ s/\s+$//;
556 dpavlin 1 push @out, $hex;
557     }
558 dpavlin 8 return join(' | ', @out);
559 dpavlin 1 }
560    
561     sub read_bytes {
562     my ( $len, $desc ) = @_;
563     my $data = '';
564     while ( length( $data ) < $len ) {
565     my ( $c, $b ) = $port->read(1);
566 dpavlin 28 die "no bytes on port: $!" unless defined $b;
567 dpavlin 1 #warn "## got $c bytes: ", as_hex($b), "\n";
568     $data .= $b;
569     }
570     $desc ||= '?';
571 dpavlin 4 warn "#< ", as_hex($data), "\t$desc\n" if $debug;
572 dpavlin 1 return $data;
573     }
574    
575 dpavlin 5 our $assert;
576 dpavlin 2
577 dpavlin 5 # my $rest = skip_assert( 3 );
578     sub skip_assert {
579     assert( 0, shift );
580     }
581    
582 dpavlin 2 sub assert {
583     my ( $from, $to ) = @_;
584    
585 dpavlin 5 $from ||= 0;
586 dpavlin 4 $to = length( $assert->{expect} ) if ! defined $to;
587    
588 dpavlin 2 my $p = substr( $assert->{payload}, $from, $to );
589     my $e = substr( $assert->{expect}, $from, $to );
590 dpavlin 3 warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
591 dpavlin 5
592     # return the rest
593     return substr( $assert->{payload}, $to );
594 dpavlin 2 }
595    
596 dpavlin 15 use Digest::CRC;
597    
598     sub crcccitt {
599     my $bytes = shift;
600     my $crc = Digest::CRC->new(
601     # midified CCITT to xor with 0xffff instead of 0x0000
602     width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
603     ) or die $!;
604     $crc->add( $bytes );
605     pack('n', $crc->digest);
606     }
607    
608 dpavlin 8 # my $checksum = checksum( $bytes );
609     # my $checksum = checksum( $bytes, $original_checksum );
610     sub checksum {
611     my ( $bytes, $checksum ) = @_;
612    
613 dpavlin 16 my $len = ord(substr($bytes,2,1));
614 dpavlin 17 my $len_real = length($bytes) - 1;
615 dpavlin 16
616 dpavlin 17 if ( $len_real != $len ) {
617     print "length wrong: $len_real != $len\n";
618 dpavlin 38 $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
619 dpavlin 17 }
620    
621 dpavlin 38 my $xor = crcccitt( substr($bytes,1) ); # skip D6
622     warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
623    
624 dpavlin 8 if ( defined $checksum && $xor ne $checksum ) {
625 dpavlin 10 print "checksum doesn't match: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n";
626 dpavlin 16 return $bytes . $xor;
627 dpavlin 8 }
628 dpavlin 16 return $bytes . $checksum;
629 dpavlin 8 }
630    
631 dpavlin 20 our $dispatch;
632    
633 dpavlin 1 sub readchunk {
634 dpavlin 43 # sleep 1; # FIXME remove
635 dpavlin 2
636 dpavlin 1 # read header of packet
637     my $header = read_bytes( 2, 'header' );
638 dpavlin 2 my $length = read_bytes( 1, 'length' );
639     my $len = ord($length);
640 dpavlin 1 my $data = read_bytes( $len, 'data' );
641    
642 dpavlin 2 my $payload = substr( $data, 0, -2 );
643     my $payload_len = length($data);
644     warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
645 dpavlin 8
646 dpavlin 2 my $checksum = substr( $data, -2, 2 );
647 dpavlin 20 checksum( $header . $length . $payload , $checksum );
648 dpavlin 1
649 dpavlin 22 print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n" if $verbose;
650 dpavlin 2
651     $assert->{len} = $len;
652     $assert->{payload} = $payload;
653    
654 dpavlin 20 my $full = $header . $length . $data; # full
655     # find longest match for incomming data
656     my ($to) = grep {
657     my $match = substr($payload,0,length($_));
658     m/^\Q$match\E/
659     } sort { length($a) <=> length($b) } keys %$dispatch;
660     warn "?? payload dispatch to ",dump( $payload, $dispatch, $to ) if $debug;
661 dpavlin 2
662 dpavlin 42 if ( defined $to ) {
663     my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
664 dpavlin 20 warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
665     $dispatch->{ $to }->( $rest );
666     } else {
667 dpavlin 53 print "NO DISPATCH for ",as_hex( $full ),"\n";
668 dpavlin 20 }
669    
670 dpavlin 2 return $data;
671 dpavlin 1 }
672    
673 dpavlin 2 sub str2bytes {
674     my $str = shift || confess "no str?";
675 dpavlin 5 my $b = $str;
676 dpavlin 17 $b =~ s/\s+//g;
677     $b =~ s/(..)/\\x$1/g;
678     $b = "\"$b\"";
679 dpavlin 5 my $bytes = eval $b;
680 dpavlin 2 die $@ if $@;
681 dpavlin 5 warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
682 dpavlin 2 return $bytes;
683     }
684    
685     sub cmd {
686 dpavlin 20 my $cmd = shift || confess "no cmd?";
687     my $cmd_desc = shift || confess "no description?";
688     my @expect = @_;
689    
690 dpavlin 2 my $bytes = str2bytes( $cmd );
691    
692 dpavlin 16 # fix checksum if needed
693     $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
694    
695 dpavlin 22 warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
696 dpavlin 2 $assert->{send} = $cmd;
697     writechunk( $bytes );
698    
699 dpavlin 20 while ( @expect ) {
700     my $pattern = str2bytes( shift @expect ) || confess "no pattern?";
701     my $coderef = shift @expect || confess "no coderef?";
702     confess "not coderef" unless ref $coderef eq 'CODE';
703    
704     next if defined $dispatch->{ $pattern };
705    
706     $dispatch->{ substr($pattern,3) } = $coderef;
707     warn "++ dispatch ", as_hex($pattern) ,dump( $dispatch ) if $debug;
708 dpavlin 2 }
709 dpavlin 20
710     readchunk;
711 dpavlin 2 }
712    

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26