/[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 66 - (hide annotations)
Thu Feb 11 14:14:21 2010 UTC (14 years, 2 months ago) by dpavlin
File MIME type: text/plain
File size: 18145 byte(s)
remove tag_ prefix in /program

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 59 use POSIX qw(strftime);
13 dpavlin 1
14 dpavlin 23 use IO::Socket::INET;
15    
16 dpavlin 50 my $debug = 0;
17    
18 dpavlin 54 my $tags_data;
19     my $tags_security;
20     my $visible_tags;
21    
22 dpavlin 43 my $listen_port = 9000; # pick something not in use
23 dpavlin 59 my $server_url = "http://localhost:$listen_port";
24    
25 dpavlin 43 sub http_server {
26    
27     my $server = IO::Socket::INET->new(
28     Proto => 'tcp',
29     LocalPort => $listen_port,
30     Listen => SOMAXCONN,
31     Reuse => 1
32     );
33    
34     die "can't setup server" unless $server;
35    
36 dpavlin 59 print "Server $0 ready at $server_url\n";
37 dpavlin 43
38     sub static {
39     my ($client,$path) = @_;
40    
41     $path = "www/$path";
42 dpavlin 56 $path .= 'rfid.html' if $path =~ m{/$};
43 dpavlin 43
44     return unless -e $path;
45    
46     my $type = 'text/plain';
47     $type = 'text/html' if $path =~ m{\.htm};
48     $type = 'application/javascript' if $path =~ m{\.js};
49    
50     print $client "HTTP/1.0 200 OK\r\nContent-Type: $type\r\n\r\n";
51     open(my $html, $path);
52     while(<$html>) {
53     print $client $_;
54     }
55     close($html);
56    
57     return $path;
58     }
59    
60     while (my $client = $server->accept()) {
61     $client->autoflush(1);
62     my $request = <$client>;
63    
64 dpavlin 50 warn "WEB << $request\n" if $debug;
65 dpavlin 43
66     if ($request =~ m{^GET (/.*) HTTP/1.[01]}) {
67     my $method = $1;
68 dpavlin 46 my $param;
69     if ( $method =~ s{\?(.+)}{} ) {
70     foreach my $p ( split(/[&;]/, $1) ) {
71     my ($n,$v) = split(/=/, $p, 2);
72     $param->{$n} = $v;
73     }
74 dpavlin 50 warn "WEB << param: ",dump( $param ) if $debug;
75 dpavlin 46 }
76 dpavlin 43 if ( my $path = static( $client,$1 ) ) {
77 dpavlin 50 warn "WEB >> $path" if $debug;
78 dpavlin 43 } elsif ( $method =~ m{/scan} ) {
79     my $tags = scan_for_tags();
80 dpavlin 52 my $json = { time => time() };
81 dpavlin 44 map {
82     my $d = decode_tag($_);
83     $d->{sid} = $_;
84 dpavlin 54 $d->{security} = $tags_security->{$_};
85 dpavlin 44 push @{ $json->{tags} }, $d;
86     } keys %$tags;
87 dpavlin 46 print $client "HTTP/1.0 200 OK\r\nContent-Type: application/x-javascript\r\n\r\n",
88     $param->{callback}, "(", to_json($json), ")\r\n";
89 dpavlin 59 } elsif ( $method =~ m{/program} ) {
90    
91     my $status = 501; # Not implementd
92    
93     foreach my $p ( keys %$param ) {
94 dpavlin 66 next unless $p =~ m/^(E[0-9A-F]{15})$/;
95 dpavlin 59 my $tag = $1;
96 dpavlin 61 my $content = "\x04\x11\x00\x01" . $param->{$p};
97 dpavlin 63 $content = "\x00" if $param->{$p} eq 'blank';
98 dpavlin 59 $status = 302;
99    
100     warn "PROGRAM $tag $content\n";
101     write_tag( $tag, $content );
102     }
103    
104     print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n";
105    
106 dpavlin 43 } else {
107     print $client "HTTP/1.0 404 Unkown method\r\n";
108     }
109     } else {
110     print $client "HTTP/1.0 500 No method\r\n";
111     }
112     close $client;
113     }
114    
115     die "server died";
116     }
117    
118 dpavlin 48
119     my $last_message = {};
120     sub _message {
121     my $type = shift @_;
122     my $text = join(' ',@_);
123     my $last = $last_message->{$type};
124     if ( $text ne $last ) {
125     warn $type eq 'diag' ? '# ' : '', $text, "\n";
126     $last_message->{$type} = $text;
127     }
128     }
129    
130     sub _log { _message('log',@_) };
131     sub diag { _message('diag',@_) };
132    
133 dpavlin 19 my $device = "/dev/ttyUSB0";
134     my $baudrate = "19200";
135     my $databits = "8";
136     my $parity = "none";
137     my $stopbits = "1";
138     my $handshake = "none";
139    
140 dpavlin 29 my $program_path = './program/';
141 dpavlin 34 my $secure_path = './secure/';
142 dpavlin 29
143 dpavlin 43 # http server
144     my $http_server = 1;
145    
146 dpavlin 41 # 3M defaults: 8,4
147     my $max_rfid_block = 16;
148     my $read_blocks = 8;
149    
150 dpavlin 1 my $response = {
151     'd500090400110a0500027250' => 'version?',
152     'd60007fe00000500c97b' => 'no tag in range',
153    
154     'd6000ffe00000501e00401003123aa26941a' => 'tag #1',
155     'd6000ffe00000501e0040100017c0c388e2b' => 'rfid card',
156     'd6000ffe00000501e00401003123aa2875d4' => 'tag red-stripe',
157    
158     'd60017fe00000502e00401003123aa26e0040100017c0c38cadb' => 'tag #1 + card',
159     'd60017fe00000502e00401003123aa26e00401003123aa283124' => 'tag #1 + tag red-stripe',
160     };
161    
162 dpavlin 19 GetOptions(
163 dpavlin 22 'd|debug+' => \$debug,
164 dpavlin 19 'device=s' => \$device,
165     'baudrate=i' => \$baudrate,
166     'databits=i' => \$databits,
167     'parity=s' => \$parity,
168     'stopbits=i' => \$stopbits,
169     'handshake=s' => \$handshake,
170 dpavlin 45 'http-server!' => \$http_server,
171 dpavlin 19 ) or die $!;
172    
173 dpavlin 22 my $verbose = $debug > 0 ? $debug-- : 0;
174    
175 dpavlin 1 =head1 NAME
176    
177     3m-810 - support for 3M 810 RFID reader
178    
179     =head1 SYNOPSIS
180    
181 dpavlin 19 3m-810.pl --device /dev/ttyUSB0
182 dpavlin 1
183     =head1 DESCRIPTION
184    
185     Communicate with 3M 810 RFID reader and document it's protocol
186    
187     =head1 SEE ALSO
188    
189     L<Device::SerialPort(3)>
190    
191     L<perl(1)>
192    
193 dpavlin 15 L<http://stackoverflow.com/questions/149617/how-could-i-guess-a-checksum-algorithm>
194    
195 dpavlin 1 =head1 AUTHOR
196    
197     Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>
198    
199     =head1 COPYRIGHT AND LICENSE
200    
201     This program is free software; you may redistribute it and/or modify
202     it under the same terms ans Perl itself.
203    
204     =cut
205    
206 dpavlin 31 my $item_type = {
207     1 => 'Book',
208     6 => 'CD/CD ROM',
209     2 => 'Magazine',
210     13 => 'Book with Audio Tape',
211     9 => 'Book with CD/CD ROM',
212     0 => 'Other',
213    
214     5 => 'Video',
215     4 => 'Audio Tape',
216     3 => 'Bound Journal',
217     8 => 'Book with Diskette',
218     7 => 'Diskette',
219     };
220    
221     warn "## known item type: ",dump( $item_type ) if $debug;
222    
223 dpavlin 19 my $port=new Device::SerialPort($device) || die "can't open serial port $device: $!\n";
224     warn "using $device $handshake $baudrate $databits $parity $stopbits" if $debug;
225 dpavlin 1 $handshake=$port->handshake($handshake);
226     $baudrate=$port->baudrate($baudrate);
227     $databits=$port->databits($databits);
228     $parity=$port->parity($parity);
229     $stopbits=$port->stopbits($stopbits);
230    
231 dpavlin 48 warn "## using $device $baudrate $databits $parity $stopbits debug: $debug verbose: $verbose\n";
232 dpavlin 1
233     # Just in case: reset our timing and buffers
234     $port->lookclear();
235     $port->read_const_time(100);
236     $port->read_char_time(5);
237    
238     # Turn on parity checking:
239     #$port->stty_inpck(1);
240     #$port->stty_istrip(1);
241    
242 dpavlin 4 # initial hand-shake with device
243    
244 dpavlin 20 cmd( 'D5 00 05 04 00 11 8C66', 'hw version',
245     'D5 00 09 04 00 11 0A 05 00 02 7250', sub {
246 dpavlin 23 my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
247     print "hardware version $hw_ver\n";
248 dpavlin 2 });
249 dpavlin 1
250 dpavlin 20 cmd( 'D6 00 0C 13 04 01 00 02 00 03 00 04 00 AAF2','FIXME: stats?',
251     'D6 00 0C 13 00 02 01 01 03 02 02 03 00 E778', sub { assert() } );
252 dpavlin 1
253 dpavlin 43 sub scan_for_tags {
254 dpavlin 1
255 dpavlin 43 my @tags;
256 dpavlin 20
257 dpavlin 48 cmd( 'D6 00 05 FE 00 05 FA40', "scan for tags",
258 dpavlin 43 'D6 00 0F FE 00 00 05 ', sub { # 01 E00401003123AA26 941A # seen, serial length: 8
259     my $rest = shift || die "no rest?";
260     my $nr = ord( substr( $rest, 0, 1 ) );
261 dpavlin 20
262 dpavlin 43 if ( ! $nr ) {
263 dpavlin 48 _log "no tags in range\n";
264 dpavlin 43 update_visible_tags();
265     $tags_data = {};
266     } else {
267 dpavlin 1
268 dpavlin 43 my $tags = substr( $rest, 1 );
269     my $tl = length( $tags );
270     die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;
271 dpavlin 16
272 dpavlin 43 push @tags, uc(unpack('H16', substr($tags, $_ * 8, 8))) foreach ( 0 .. $nr - 1 );
273     warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags ) if $debug;
274 dpavlin 48 _log "$nr tags in range: ", join(',', @tags ) , "\n";
275 dpavlin 25
276 dpavlin 43 update_visible_tags( @tags );
277     }
278 dpavlin 5 }
279 dpavlin 43 );
280 dpavlin 5
281 dpavlin 48 diag "tags: ",dump( @tags );
282 dpavlin 43 return $tags_data;
283 dpavlin 22
284 dpavlin 43 }
285 dpavlin 22
286 dpavlin 43 # start scanning for tags
287    
288     if ( $http_server ) {
289     http_server;
290     } else {
291 dpavlin 58 while (1) {
292     scan_for_tags;
293     sleep 1;
294     }
295 dpavlin 43 }
296    
297     die "over and out";
298    
299 dpavlin 22 sub update_visible_tags {
300     my @tags = @_;
301    
302     my $last_visible_tags = $visible_tags;
303     $visible_tags = {};
304    
305     foreach my $tag ( @tags ) {
306 dpavlin 51 $visible_tags->{$tag}++;
307 dpavlin 22 if ( ! defined $last_visible_tags->{$tag} ) {
308 dpavlin 25 if ( defined $tags_data->{$tag} ) {
309 dpavlin 64 warn "$tag in range\n";
310 dpavlin 25 } else {
311     read_tag( $tag );
312     }
313 dpavlin 22 } else {
314     warn "## using cached data for $tag" if $debug;
315     }
316     delete $last_visible_tags->{$tag}; # leave just missing tags
317 dpavlin 29
318     if ( -e "$program_path/$tag" ) {
319     write_tag( $tag );
320     }
321 dpavlin 34 if ( -e "$secure_path/$tag" ) {
322     secure_tag( $tag );
323     }
324 dpavlin 22 }
325    
326     foreach my $tag ( keys %$last_visible_tags ) {
327 dpavlin 23 my $data = delete $tags_data->{$tag};
328 dpavlin 64 warn "$tag removed ", dump($data), $/;
329 dpavlin 22 }
330    
331     warn "## update_visible_tags(",dump( @tags ),") = ",dump( $visible_tags )," removed: ",dump( $last_visible_tags ), " data: ",dump( $tags_data ) if $debug;
332     }
333    
334 dpavlin 28 my $tag_data_block;
335 dpavlin 22
336 dpavlin 28 sub read_tag_data {
337     my ($start_block,$rest) = @_;
338     die "no rest?" unless $rest;
339 dpavlin 41
340     my $last_block = 0;
341    
342 dpavlin 28 warn "## DATA [$start_block] ", dump( $rest ) if $debug;
343     my $tag = uc(unpack('H16',substr( $rest, 0, 8 )));
344     my $blocks = ord(substr($rest,8,1));
345     $rest = substr($rest,9); # leave just data blocks
346     foreach my $nr ( 0 .. $blocks - 1 ) {
347     my $block = substr( $rest, $nr * 6, 6 );
348     warn "## block ",as_hex( $block ) if $debug;
349     my $ord = unpack('v',substr( $block, 0, 2 ));
350     my $expected_ord = $nr + $start_block;
351 dpavlin 41 warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
352 dpavlin 28 my $data = substr( $block, 2 );
353     die "data payload should be 4 bytes" if length($data) != 4;
354 dpavlin 40 warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
355 dpavlin 28 $tag_data_block->{$tag}->[ $ord ] = $data;
356 dpavlin 41 $last_block = $ord;
357 dpavlin 28 }
358     $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
359 dpavlin 31
360     my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
361 dpavlin 42 print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
362 dpavlin 41
363 dpavlin 42 return $last_block + 1;
364 dpavlin 28 }
365    
366 dpavlin 59 my $saved_in_log;
367    
368 dpavlin 43 sub decode_tag {
369     my $tag = shift;
370    
371     my $data = $tags_data->{$tag} || die "no data for $tag";
372    
373     my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom ) = unpack('C4Z16Nl>',$data);
374     my $hash = {
375     u1 => $u1,
376     u2 => $u2,
377     set => ( $set_item & 0xf0 ) >> 4,
378     total => ( $set_item & 0x0f ),
379    
380     type => $type,
381     content => $content,
382    
383     branch => $br_lib >> 20,
384     library => $br_lib & 0x000fffff,
385    
386     custom => $custom,
387     };
388    
389 dpavlin 59 if ( ! $saved_in_log->{$tag}++ ) {
390     open(my $log, '>>', 'rfid-log.txt');
391     print $log strftime( "%Y-%m-%d %H:%M:%S", localtime ), ",$tag,$content\n";
392     close($log);
393     }
394    
395 dpavlin 43 return $hash;
396     }
397    
398 dpavlin 16 sub read_tag {
399     my ( $tag ) = @_;
400 dpavlin 1
401 dpavlin 22 confess "no tag?" unless $tag;
402    
403 dpavlin 16 print "read_tag $tag\n";
404 dpavlin 1
405 dpavlin 41 my $start_block = 0;
406 dpavlin 28
407 dpavlin 41 while ( $start_block < $max_rfid_block ) {
408 dpavlin 1
409 dpavlin 41 cmd(
410 dpavlin 65 sprintf( "D6 00 0D 02 $tag %02x %02x BEEF", $start_block, $read_blocks ),
411 dpavlin 41 "read $tag offset: $start_block blocks: $read_blocks",
412     "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";
413     $start_block = read_tag_data( $start_block, @_ );
414     warn "# read tag upto $start_block\n";
415     },
416 dpavlin 65 "D6 00 0F FE 00 00 05 01 $tag BEEF", sub {
417 dpavlin 41 print "FIXME: tag $tag ready? (expected block read instead)\n";
418     },
419     );
420    
421     }
422    
423 dpavlin 33 my $security;
424    
425     cmd(
426 dpavlin 65 "D6 00 0B 0A $tag BEEF", "check security $tag",
427 dpavlin 33 "D6 00 0D 0A 00", sub {
428     my $rest = shift;
429     my $from_tag;
430     ( $from_tag, $security ) = ( substr($rest,0,8), substr($rest,8,1) );
431     die "security from other tag: ",as_hex( $from_tag ) if $from_tag ne str2bytes( $tag );
432     $security = as_hex( $security );
433 dpavlin 54 $tags_security->{$tag} = $security;
434 dpavlin 33 warn "# SECURITY $tag = $security\n";
435     }
436     );
437    
438 dpavlin 43 print "TAG $tag ", dump(decode_tag( $tag ));
439 dpavlin 16 }
440    
441 dpavlin 29 sub write_tag {
442 dpavlin 59 my ($tag,$data) = @_;
443 dpavlin 29
444     my $path = "$program_path/$tag";
445 dpavlin 59 $data = read_file( $path ) if -e $path;
446 dpavlin 29
447 dpavlin 59 die "no data" unless $data;
448    
449 dpavlin 38 my $hex_data;
450 dpavlin 29
451 dpavlin 38 if ( $data =~ s{^hex\s+}{} ) {
452     $hex_data = $data;
453     $hex_data =~ s{\s+}{}g;
454     } else {
455 dpavlin 29
456 dpavlin 38 $data .= "\0" x ( 4 - ( length($data) % 4 ) );
457 dpavlin 30
458 dpavlin 41 my $max_len = $max_rfid_block * 4;
459 dpavlin 30
460 dpavlin 38 if ( length($data) > $max_len ) {
461     $data = substr($data,0,$max_len);
462     warn "strip content to $max_len bytes\n";
463     }
464    
465     $hex_data = unpack('H*', $data);
466     }
467    
468     my $len = length($hex_data) / 2;
469 dpavlin 40 # pad to block size
470     $hex_data .= '00' x ( 4 - $len % 4 );
471     my $blocks = sprintf('%02x', length($hex_data) / 4);
472 dpavlin 38
473     print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
474    
475 dpavlin 29 cmd(
476 dpavlin 65 "d6 00 ff 04 $tag 00 $blocks 00 $hex_data BEEF", "write $tag",
477     "d6 00 0d 04 00 $tag $blocks BEEF", sub { assert() },
478 dpavlin 40 ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
479 dpavlin 29
480     my $to = $path;
481     $to .= '.' . time();
482    
483     rename $path, $to;
484     print ">> $to\n";
485    
486 dpavlin 62 # force re-read of tag
487     delete $tags_data->{$tag};
488     delete $visible_tags->{$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 dpavlin 65 "d6 00 0c 09 $tag $data BEEF", "secure $tag -> $data",
499     "d6 00 0c 09 00 $tag BEEF", sub { assert() },
500 dpavlin 34 );
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 65 warn "checksum error: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n" if $checksum ne "\xBE\xEF";
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 64 die "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