/[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 71 - (hide annotations)
Thu Feb 11 20:57:51 2010 UTC (14 years, 2 months ago) by dpavlin
File MIME type: text/plain
File size: 18927 byte(s)
support secure.js from browser with jQuery.getJSON();

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26