File Coverage

blib/lib/RFID/Alien/Reader.pm
Criterion Covered Total %
statement 122 152 80.2
branch 51 80 63.7
condition 11 17 64.7
subroutine 13 17 76.4
pod 6 7 85.7
total 203 273 74.3


line stmt bran cond sub pod time code
1             package RFID::Alien::Reader;
2             $VERSION = '0.003';
3             @ISA=qw(RFID::Reader);
4            
5             # Written by Scott Gifford
6             # Copyright (C) 2004-2006 The Regents of the University of Michigan.
7             # See the file LICENSE included with the distribution for license
8             # information.
9            
10             =head1 NAME
11            
12             RFID::Alien::Reader - Abstract base class for a Alien RFID reader
13            
14             =head1 SYNOPSIS
15            
16             This abstract base class provides most of the methods required for
17             interfacing Perl with an Alien RFID reader. To actually create an
18             object, use L or
19             L. For example:
20            
21             use RFID::Alien::Reader::Serial;
22             use Win32::SerialPort;
23            
24             $com = Win32::SerialPort->new('COM1')
25             or die "Couldn't open COM port 'COM1': $^E\n";
26             my $reader =
27             RFID::Alien::Reader::Serial->new(Port => $com,
28             PersistTime => 0,
29             AcquireMode => 'Inventory',
30             )
31             or die "Couldn't create reader object";
32            
33             $reader->set(AntennaSequence => [0,1],
34             TagListAntennaCombine => 'OFF') == 0
35             or die "Couldn't set reader properties";
36            
37             my @tags = $reader->readtags();
38             foreach my $tag (@tags)
39             {
40             print "I see tag ",$tag->id,"\n";
41             }
42            
43             =head1 DESCRIPTION
44            
45             This abstract base class implements the commands for communicating
46             with an Alien reader. It is written according to the specifications
47             in the I. It was
48             tested with the original tag reader and also the ALR-9780. It
49             inherits from L.
50            
51             To actually create a reader object, use
52             L or
53             L. Those classes
54             inherit from this one.
55            
56             =cut
57            
58 4     4   33843 use Carp;
  4         9  
  4         426  
59 4     4   3636 use POSIX qw(strftime);
  4         30425  
  4         31  
60 4     4   8551 use Time::Local;
  4         8087  
  4         271  
61 4     4   4000 use RFID::Reader;
  4         5469  
  4         204  
62 4     4   4347 use RFID::EPC::Tag;
  4         16644  
  4         9120  
63            
64             # Internal initialization function, called by child objects
65             sub _init
66             {
67 3     3   2005895 my $self = shift;
68 3         32 my(%p) = @_;
69 3         29 my $greeting;
70            
71 3         68 $self->SUPER::_init(%p);
72            
73 3 50 33     64 if (defined($p{Login}) and defined($p{Password}))
74             {
75             # Log in
76 0         0 $self->debug("Logging in\n");
77 0         0 my $s = $self->{_sock};
78 0         0 print $s $p{Login},"\r\n";
79 0         0 $self->_readuntil('Password>');
80 0         0 print $s $p{Password},"\r\n";
81 0         0 my $d = $self->_readuntil('>');
82 0 0       0 if ($d !~ /Alien$/)
83             {
84 0         0 die "Login failed";
85             }
86             }
87            
88             # Ignore unknown settings, since they may be for a child module.
89 3 50       85 if ((my @err = grep { !/Unknown setting/i } $self->set(%p)) != 0)
  5         58  
90             {
91 0         0 croak "Error creating new tag: could not set requested options: @err\n";
92             }
93 3 50       58 scalar($self->_simpleset(TagListFormat => 'text')) == 0
94             or die "Couldn't set TagListFormat to text!\n";
95 3         19 $self;
96             }
97            
98             =head2 Methods
99            
100             =head3 set
101            
102             Set various properties of the reader or the internal state of the
103             object. This method takes a hash-style list of any number of
104             I pairs, and returns a list of errors that occured. In a
105             scalar context, that evaluates to the number of errors that occured,
106             so you can test for errors like this:
107            
108             my @errs = $alien->set(SomeVariable => "New Value") == 0
109             or die "Couldn't set SomeVariable: @errs";
110            
111             See L for the properties that can be set, and
112             see L for more details
113             about this method.
114            
115             =cut
116            
117             sub set
118             {
119 27     27 1 16684 my $self = shift;
120 27         119 my(%p) = @_;
121 27         37 my @errs;
122            
123 27         124 while (my($var,$val)=each(%p))
124             {
125 33 50       385 if (lc $var eq 'timeout')
    100          
    100          
    100          
    100          
126             {
127 0         0 $self->{timeout}=$val;
128             }
129             elsif (lc $var eq 'mask')
130             {
131 8 50       64 if ($val =~ /^([0-9a-f]*)(?:\/(\d*))?(?:\/(\d*))?$/i)
132             {
133 8         31 my($mask,$len,$start) = ($1,$2,$3);
134 8   100     38 $len ||= length($mask)*4;
135 8 100       28 if ( (length($mask) % 2) == 1)
136             {
137 2         4 $mask .= "0";
138             }
139 8   100     39 $start ||= 0;
140 8         99 push(@errs,
141             $self->_simpleset($var,
142             sprintf("%d, %d, %s",
143             $len, $start,
144             join(' ',
145             unpack("a2" x (length($mask)/2),
146             $mask)))));
147             }
148             else
149             {
150 0         0 croak "Invalid mask in ",(caller(0))[3]," mask option\n";
151             }
152             }
153             elsif (lc $var eq 'time')
154             {
155             # Timezone trick from tye on PerlMonks
156             # ( localtime time() + 3600*( 12 - (gmtime)[2] ) )[2] - 12
157 4         8 my $timestr;
158 4 50 66     52 if ($val and $val =~ /\D/)
159             {
160 0         0 $timestr=$val;
161             }
162             else
163             {
164 4   66     20 $val ||= time;
165 4         471 $timestr = strftime("%Y/%m/%d %H:%M:%S",localtime($val));
166             }
167 4         18 push(@errs,$self->_simpleset($var,$timestr));
168             }
169             elsif (lc $var eq 'antennasequence')
170 165         399 {
171 6 50       26 if (ref($val))
172             {
173 6         22 $val = join(", ",@$val);
174             }
175 6         23 push(@errs,$self->_simpleset($var,$val));
176             }
177             elsif (grep { lc $var eq lc $_ }
178             (qw(AcquireMode PersistTime AcqCycles AcqEnterWakeCount
179             AcqCount AcqSleepCount AcqExitWakeCount PersistTime
180             TagListAntennaCombine
181             AcquireSleep AcquireWakeCount
182             )))
183             {
184 8         22 push(@errs,$self->_simpleset($var,$val));
185             }
186             else
187             {
188 7         69 push(@errs,$self->SUPER::set($var,$val));
189             }
190             }
191 27         202 @errs;
192             }
193            
194             # Internal function implementing a very simple set command
195             sub _simpleset
196             {
197 29     29   53 my $self = shift;
198 29         102 my($var,$val)=@_;
199 29         140 my $resp = $self->_command("set $var = $val");
200 29         49 my @ret;
201            
202 29 50       309 if ($resp !~ /^$var /i)
203             {
204 0         0 @ret = ("set $var command failed! Reader said: ".$resp);
205             }
206             else
207             {
208 29         54 @ret = ();
209             }
210 29         162 @ret;
211             }
212            
213             =head3 get
214            
215             Get various properties of the reader or the internal state of the
216             object. This method takes a list of parameters whose value you'd like
217             to get. In a list context, it returns a hash with the parameters you
218             asked for as the keys, and their values as the values. In a scalar
219             context, it returns the value of the last property requested. If an
220             error occurs or a value for the requested property can't be found,
221             it is set to C.
222            
223             For example:
224            
225             my $AcquireMode = $alien->get('AcquireMode');
226             my %props = $alien->get(qw(AcquireMode PersistTime ReaderVersion));
227            
228             See L for the properties that can be retreived
229             with I, and L for
230             more information about this method.
231            
232             =cut
233            
234             sub get
235             {
236 36     36 1 8764 my $self = shift;
237 36         72 my %ret;
238            
239 36         78 foreach my $var (@_)
240             {
241 38 100       306 if (lc $var eq 'mask')
    100          
    100          
    100          
    50          
242             {
243 10         28 my $mask = $self->_simpleget($var);
244 10 100       92 if ($mask =~ /all tags/i)
    50          
245             {
246 2         11 $ret{$var}='';
247             }
248             elsif ($mask =~ /^(\d+),\s*(\d+),\s*(.*)$/)
249             {
250 8         29 my($len,$start,$bits)=($1,$2,$3);
251 8 100       26 if ($len == 0)
252             {
253 2         9 $ret{$var}='';
254             }
255             else
256             {
257 6         106 $bits =~ s/\s//g;
258 6         22 $ret{$var} = "$bits/$len";
259 6 100       27 if ($start)
260             {
261 2         9 $ret{$var} .= "/$start";
262             }
263             }
264             }
265             }
266             elsif (lc $var eq 'time')
267             {
268 4         16 my $timestr = $self->_simpleget($var);
269 4 50 33     56 if (defined($timestr) and
270             $timestr =~ m|(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)|)
271             {
272 4 50       18 if ($1 > 2045)
273             {
274             # Too big for a Unix date!
275 0         0 $ret{$var} = 0xffffffff;
276             }
277             else
278             {
279 4         49 $ret{$var} = timelocal($6,$5,$4,$3,$2-1,$1);
280             }
281             }
282             }
283             elsif (lc $var eq 'antennasequence')
284             {
285 14         63 my $antstr = $self->_simpleget($var);
286 14 50       43 if (defined($antstr))
287             {
288 14         62 $ret{$var} = [map { s/\*$//; $_ } split(/,\s*/,$antstr)];
  20         30  
  20         120  
289             }
290             }
291             elsif (lc $var eq 'readerversion')
292 72         169 {
293 2         18 my $val = $self->_command('get ReaderVersion');
294 2         26 $ret{$var}=$val;
295             }
296            
297             # This parses the reader version. It's currently disabled, but should
298             # probably come back in some form.
299             # elsif (lc $var eq 'readerversion')
300             # {
301             # my $val = $self->_command('get ReaderVersion');
302             # my $r = {};
303             # $r->{string} = $val;
304             # while ( $val =~ /([^:]+):\s*([^\x0d\s,]+),?\s*/sg )
305             # {
306             # if ($1 eq 'Ent. SW Rev')
307             # {
308             # $r->{software}=$2;
309             # }
310             # elsif ($1 eq 'Country Code')
311             # {
312             # $r->{country_code}=$2;
313             # }
314             # elsif ($1 eq 'Reader Type')
315             # {
316             # $r->{reader_type}=$2;
317             # }
318             # elsif ($1 eq 'Firmware Rev')
319             # {
320             # $r->{firmware}=$2;
321             # }
322             # }
323             # $ret{$var}=$r;
324             # }
325             elsif (grep { lc $var eq lc $_ }
326             (qw(AcquireMode PersistTime AcqCycles AcqEnterWakeCount
327             AcqCount AcqSleepCount AcqExitWakeCount PersistTime
328             TagListAntennaCombine
329             )))
330             {
331 8         23 $ret{$var} = $self->_simpleget($var);
332             }
333             else
334             {
335 0         0 %ret=(%ret, $self->SUPER::get($var));
336             }
337             }
338 36 100       480 if (wantarray)
339             {
340 4         48 return %ret;
341             }
342             else
343             {
344             # Return last value
345 32         205 return $ret{$_[$#_]};
346             }
347             }
348            
349             # Internal function implementing a very simple get
350             sub _simpleget
351             {
352 36     36   51 my $self = shift;
353 36         51 my($var)=@_;
354            
355 36         494 my $resp = $self->_command("get $var");
356 36 50       838 if ($resp =~ /^$var\s+.*?=\s*(.*?)[\s\x0a\x0d]*$/is)
357             {
358 36         174 return $1;
359             }
360 0         0 return undef;
361             }
362            
363             =head3 readtags
364            
365             Read all of the tags in the reader's field, honoring the requested
366             L and L settings. This
367             returns a (possibly empty) list of L objects.
368             For example:
369            
370             my @tags = $reader->readtags();
371             foreach my $tag (@tags)
372             {
373             print "I see tag ",$tag->id,"\n";
374             }
375            
376             Parameters are a hash-style list of parameters that should be
377             L for just this read. The parameters are actually set to the
378             requested value at the beginning of the method call, and set back
379             before returning, so if you want to use the same parameters for many
380             calls (say in a loop) you will probably want to set them just once
381             with L.
382            
383             See L for more
384             information about this method.
385            
386             =cut
387            
388             sub readtags
389             {
390 6     6 1 16 my $self = shift;
391 6         15 my(%p)=@_;
392 6         13 my $numreads = '';
393 6 50       24 if ($p{Numreads})
394             {
395 0         0 $numreads = ' '.$p{Numreads};
396 0         0 delete $p{Numreads};
397             }
398 6 50       22 $self->pushoptions(%p)
399             if (keys %p);
400            
401 6         30 my $taglist = $self->_command('get TagList'.$numreads);
402 6         12 my @tags;
403 6         40 foreach my $tagline (split /\x0d\x0a/, $taglist)
404             {
405 10 100       56 next unless $tagline =~ /^Tag:/i;
406 8         19 my %tp = ();
407 8         59 foreach my $prop (split /,\s*/, $tagline)
408             {
409 40 50       182 if ($prop =~ /^(.*?):(.*)/)
410             {
411 40 100       154 if (lc $1 eq 'tag')
    100          
412             {
413 8         105 ($tp{id}=uc $2) =~ s/[^0-9A-f]//g;
414             }
415             elsif (lc $1 eq 'ant')
416             {
417 8         39 $tp{antenna} = $2;
418             }
419             else
420             {
421 24         96 $tp{lc $1}=$2;
422             }
423             }
424             }
425 8         61 my $tag = RFID::EPC::Tag->new(%tp);
426             # hack
427 8         586 $tag->{count} = $tp{count};
428 8         37 push(@tags,$tag);
429             }
430            
431             $self->popoptions()
432 6 50       26 if (keys %p);
433            
434 6         82 return @tags;
435             }
436            
437             =head3 sleeptags
438            
439             Request that all tags addressed by the reader go to sleep, causing
440             them to ignore all requests from the reader until they are
441             L. Which tags are addressed by the reader is
442             affected by the L and L
443             settings.
444            
445             Returns 1 to indicate success; currently it dies on an error, but may
446             return C in the future.
447            
448             This method is not very well tested yet. In particular, although the
449             commands appear to be issued correctly to the reader, the tags don't
450             seem to actually go to sleep.
451            
452             Parameters are a hash-style list of parameters that should be
453             L for just this read. The parameters are actually set to the
454             requested value at the beginning of the method call, and set back
455             before returning, so if you want to use the same parameters for many
456             calls (say in a loop) you will probably want to set them just once
457             with L.
458            
459             =cut
460            
461             sub sleeptags
462             {
463 0     0 1 0 my $self = shift;
464            
465 0 0       0 $self->pushoptions(@_)
466             if (@_);
467            
468 0         0 $self->_command('Sleep');
469            
470 0 0       0 $self->popoptions(@_)
471             if (@_);
472            
473 0         0 1;
474             }
475            
476             =head3 waketags
477            
478             Request that all tags addressed by the reader which are currently
479             L wake up, causing them to once again pay attention
480             to requests from the reader. Which tags are addressed by the reader
481             is affected by the L and L
482             settings.
483            
484             Returns 1 to indicate success; currently it dies on an error, but may
485             return C in the future.
486            
487             This method is not very well tested yet, since L
488             doesn't quite behave as expected.
489            
490             Parameters are a hash-style list of parameters that should be
491             L for just this read. The parameters are actually set to the
492             requested value at the beginning of the method call, and set back
493             before returning, so if you want to use the same parameters for many
494             calls (say in a loop) you will probably want to set them just once
495             with L.
496            
497             =cut
498            
499             sub waketags
500             {
501 0     0 1 0 my $self = shift;
502            
503 0 0       0 $self->pushoptions(@_)
504             if (@_);
505            
506 0         0 $self->_command('Wake');
507            
508 0 0       0 $self->popoptions(@_)
509             if (@_);
510             }
511            
512             =head3 reboot
513            
514             Request that the reader unit reboot.
515            
516             The object may behave unpredictably after a reboot; if you want to
517             continue using the reader you should create a new object. This new
518             object will sync up with the reader and should work OK, once the
519             reboot is completed. This may be fixed in the future.
520            
521             =cut
522            
523             sub reboot
524             {
525 0     0 1 0 my $self = shift;
526 0         0 $self->_command("reboot");
527             }
528            
529             # This was useful for the Matrics reader, but not so much here.
530             # Next version it will probably either be internal, or be exposed
531             # in some more reasonable way.
532             sub finish
533             {
534 0     0 0 0 1;
535             }
536            
537             # Send a command to the reader, and wait for a response. The response
538             # string is returned.
539             sub _command
540             {
541 73     73   163 my $self = shift;
542 73         104 my($cmd)=@_;
543 73         361 $self->debug("sending cmd: '$cmd'\n");
544 73 50       533 $self->_writebytes("\x01".$cmd."\x0d\x0a")
545             or die "Couldn't write: $^E";
546 73         13481 my $r = $self->_getresponse($com);
547 73         2477 $r =~ s/^$cmd\x0a//;
548 73         215 $r;
549             }
550            
551             # Wait for a response from the reader, and return the response string.
552             sub _getresponse
553             {
554 73     73   113 my $self = shift;
555            
556 73         307 my $resp = $self->_readuntil("\0");
557 73         2818 $self->debug(" got resp: '$resp'\n");
558 73         332 return $resp;
559             }
560            
561             =head2 Properties
562            
563             There are various properties that can be controlled by the L
564             and L methods. Some of these settings will cause one or more
565             commands to be sent to the reader, while other will simply return the
566             internal state of the object. The value for a property is often a
567             string, but can also be an arrayref or hashref. These properties try
568             to hide the internals of the Alien reader, and so their syntax doesn't
569             always exactly match that of the actual Alien command.
570            
571             =head3 AcqCycles, AcqEnterWakeCount, AcqCount, AcqSleepCount, AcqExitWakeCount
572            
573             These settings affect the operations of the anti-collision algorithm
574             used by Alien to scan for tags. See the Alien documentation for more
575             information.
576            
577             =head3 AcquireMode
578            
579             Affects the way in which tags are found during a call to
580             L. If the mode is set to the string I,
581             an anti-collision search algorithm is used to find all tags in the
582             reader's view; if the mode is set to the string I, the
583             reader will quickly search for a single tag.
584            
585             See the Alien documentation for more information.
586            
587             =head3 AntennaSequence
588            
589             An arrayref of the antenna numbers that should be queried, and in what
590             order. Antennas are numbered from 0 to 3 (the same as on the front of
591             the reader unit). For example:
592            
593             $alien->set(AntennaSequence => [0,1,2,3]);
594            
595             The default AntennaSequence is C<[0]>; you must override this if you
596             want to read from more than one antenna.
597            
598             =head3 Debug
599            
600             Send debugging information to C. Currently this is only on or
601             off, but in the future various debugging levels may be supported.
602             Debugging information is currently mostly I/O with the reader.
603            
604             =head3 Mask
605            
606             Set or get a bitmask for the tags. After setting the mask, all
607             commands will only apply to tags whose IDs match the given mask.
608            
609             The mask format is a string beginning with the bits of the tag as a
610             hex number, optionally followed by a slash and the size of the mask,
611             optionally followed by the bit offset in the tag ID where the
612             comparison should start. For example, to look for 8 ones at the end
613             of a tag, you could use:
614            
615             $alien->set(Mask => 'ff/8/88');
616            
617             A zero-length mask (which matches all tags) is represented by an empty
618             string.
619            
620             =head3 PersistTime
621            
622             Controls how long the reader will remember a tag after seeing it. If
623             the reader has seen a tag within this time period when you use
624             L, it will be returned even if it is no longer in
625             view of the reader. You can set it to a number of seconds to remember
626             a tag, to C<0> to not remember tags, or to C<-1> to remember tags
627             until the L method is executed. The default is
628             C<-1>.
629            
630             See the Alien documentation for more information.
631            
632             =head3 TagListAntennaCombine
633            
634             If this is set to C, a tag seen by multiple antennas will only
635             return one tag list entry.
636            
637             See the Alien documentation for more information.
638            
639             =head3 Time
640            
641             The current time on the reader unit. All tag responses are
642             timestamped, so setting the time may be useful.
643            
644             The time is represented as Unix epoch time---that is, the number of
645             seconds since midnight on January 1 1970 in GMT. You can either set
646             or get it using this format.
647            
648             If you set the time to an empty string, the reader's time will be set
649             to the current time of the computer running the script.
650            
651             Currently, no attempt is made to deal with the timezone. That may be
652             addressed in the future.
653            
654             =head3 Timeout
655            
656             Request that requests to the reader that do not complete in the given
657             number of seconds cause a C to happen.
658            
659             =head3 ReaderVersion
660            
661             Cannot be set. Returns a string containing information about the
662             reader.
663            
664             =head1 SEE ALSO
665            
666             L, L,
667             L, L, L.
668            
669             =head1 AUTHOR
670            
671             Scott Gifford Egifford@umich.eduE, Esgifford@suspectclass.comE
672            
673             Copyright (C) 2004-2006 The Regents of the University of Michigan.
674            
675             See the file LICENSE included with the distribution for license
676             information.
677            
678             =cut
679            
680            
681             1;