/[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 54 - (hide annotations)
Wed Jun 24 13:39:43 2009 UTC (14 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 18131 byte(s)
color tags according to security byte

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26