/[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 46 - (hide annotations)
Tue Jun 23 13:50:13 2009 UTC (14 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 17679 byte(s)
parse all get variables

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26