move all tag data under $self->{_tags}
[Biblio-RFID.git] / lib / RFID / Biblio / Reader.pm
1 package RFID::Biblio::Reader;
2
3 use warnings;
4 use strict;
5
6 use Data::Dump qw(dump);
7 use Time::HiRes;
8 use lib 'lib';
9 use RFID::Biblio;
10
11 =head1 NAME
12
13 RFID::Biblio::Reader - simple way to write RFID applications in perl
14
15 =head1 DESCRIPTION
16
17 This module will probe all available readers and use calls from
18 L<RFID::Biblio::Reader::API> to invoke correct reader.
19
20 =head1 FUNCTIONS
21
22 =head2 new
23
24   my $rfid = RFID::Biblio::Reader->new( 'optional reader filter' );
25
26 =cut
27
28 sub new {
29         my ( $class, $filter ) = @_;
30         my $self = {};
31         bless $self, $class;
32         $self->{_readers} = [ $self->_available( $filter ) ];
33         return $self;
34 }
35
36 =head2 tags
37
38   my @visible = $rfid->tags(
39                 enter => sub {},
40                 leave => sub {},
41   );
42
43 =cut
44
45 sub tags {
46         my $self = shift;
47         my $triggers = {@_};
48
49         $self->{_tags} ||= {};
50         $self->{_tags}->{$_}->{inventory} = 0 foreach keys %{$self->{_tags}};
51         my $t = time;
52
53         foreach my $rfid ( @{ $self->{_readers} } ) {
54                 warn "# inventory on $rfid";
55                 my @tags = $rfid->inventory;
56
57                 foreach my $tag ( @tags ) {
58
59                         if ( ! exists $self->{_tags}->{$tag} ) {
60                                 if ( my $blocks = $rfid->read_blocks($tag) ) {
61                                         $self->{blocks}->{$tag} = $blocks->{$tag} || die "no $tag in ",dump($blocks);
62                                 } else {
63                                         $self->_invalidate_tag( $tag );
64                                         next;
65                                 }
66                                 if ( my $afi = $rfid->read_afi($tag) ) {
67                                         $self->{_tags}->{$tag}->{afi} = $afi;
68                                 } else {
69                                         $self->_invalidate_tag( $tag );
70                                         next;
71                                 }
72                         }
73
74                         $triggers->{enter}->( $self, $tag ) if ! $self->{inventory}->{$tag} && $triggers->{enter};
75                         $self->{_tags}->{$tag}->{intentory} = $t;
76
77                 }
78
79                 foreach my $tag ( grep { $self->{_tags}->{$_}->{inventory} == 0 } keys %{ $self->{_tags} } ) {
80                         $triggers->{leave}->( $self, $tag ) if $triggers->{leave};
81                         $self->_invalidate_tag( $tag );
82                 }
83
84         }
85
86         warn "## _tags ",dump( $self->{_tags} );
87
88         return grep { $self->{_tags}->{$_}->{inventory} } keys %{ $self->{_tags} };
89 }
90
91 =head2 blocks
92
93   my $blocks_arrayref = $rfid->blocks( $tag );
94
95 =head2 afi
96
97   my $afi = $rfid->afi( $tag );
98
99 =cut
100
101 sub blocks { $_[0]->{_tags}->{$_[1]}->{ 'blocks' } || die "no blocks for $_[1]"; };
102 sub afi    { $_[0]->{_tags}->{$_[1]}->{ 'afi'    } || die "no afi for $_[1]"; };
103
104 =head1 PRIVATE
105
106 =head2 _invalidate_tag
107
108   $rfid->_invalidate_tag( $tag );
109
110 =cut
111
112 sub _invalidate_tag {
113         my ( $self, $tag ) = @_;
114         my $old = delete $self->{_tags}->{$tag};
115         warn "# _invalidate_tag $tag ", dump($old);
116 }
117
118 =head2 _available
119
120 Probe each RFID reader supported and returns succefull ones
121
122   my $rfid_readers = RFID::Biblio::Reader->_available( $regex_filter );
123
124 =cut
125
126 my @readers = ( '3M810', 'CPRM02', 'librfid' );
127
128 sub _available {
129         my ( $self, $filter ) = @_;
130
131         $filter = '' unless defined $filter;
132
133         warn "# filter: $filter";
134
135         my @rfid;
136
137         foreach my $reader ( @readers ) {
138                 next if $filter && $reader !~ /$filter/i;
139                 my $module = "RFID::Biblio::Reader::$reader";
140                 eval "use $module";
141                 die $@ if $@;
142                 if ( my $rfid = $module->new( device => '/dev/ttyUSB0' ) ) {
143                         push @rfid, $rfid;
144                         warn "# added $module\n";
145                 } else {
146                         warn "# ignored $module\n";
147                 }
148         }
149
150         die "no readers found" unless @rfid;
151
152         return @rfid;
153 }
154
155 =head1 AUTOLOAD
156
157 On any other function calls, we just marshall to all readers
158
159 =cut
160
161 # we don't want DESTROY to fallback into AUTOLOAD
162 sub DESTROY {}
163
164 our $AUTOLOAD;
165 sub AUTOLOAD {
166         my $self = shift;
167         my $command = $AUTOLOAD;
168         $command =~ s/.*://;
169
170         my @out;
171
172         foreach my $r ( @{ $self->{_readers} } ) {
173                 push @out, $r->$command(@_);
174         }
175
176         $self->_invalidate_tag( $_[0] ) if $command =~ m/write/;
177
178         return @out;
179 }
180
181 1
182 __END__
183
184 =head1 SEE ALSO
185
186 =head2 RFID reader implementations
187
188 L<RFID::Biblio::Reader::3M810>
189
190 L<RFID::Biblio::Reader::CPRM02>
191
192 L<RFID::Biblio::Reader::librfid>
193