invalidate cache of tags for unselected readers
[Biblio-RFID.git] / lib / Biblio / RFID / RFID501.pm
1 package Biblio::RFID::RFID501;
2
3 use warnings;
4 use strict;
5
6 use Data::Dump qw(dump);
7
8 =head1 NAME
9
10 Biblio::RFID::RFID501 - RFID Standard for Libraries
11
12 =head1 DESCRIPTION
13
14 This module tries to decode tag format as described in document
15
16   RFID 501: RFID Standards for Libraries
17
18 L<http://solutions.3m.com/wps/portal/3M/en_US/3MLibrarySystems/Home/Resources/CaseStudiesAndWhitePapers/RFID501/>
19
20 Goal is to be compatibile with existing 3M Alphanumeric tag format
21 which, as far as I know, isn't specificed anywhere. My documentation about
22 this format is available at
23
24 L<http://saturn.ffzg.hr/rot13/index.cgi?hitchhikers_guide_to_rfid>
25
26 =head1 Data model
27
28 =head2 3M Alphanumeric tag
29
30  0   04 is 00 tt   i [4 bit] = number of item in set    [1 .. i .. s]
31                    s [4 bit] = total items in set
32                    tt [8 bit] = item type
33
34  1   dd dd dd dd   dd [16 bytes] = barcode data
35  2   dd dd dd dd
36  3   dd dd dd dd
37  4   dd dd dd dd
38
39  5   bb bl ll ll   b [12 bit] = branch [unsigned]
40                    l [20 bit] = library [unsigned]
41
42  6   cc cc cc cc   c [32 bit] = custom signed integer
43
44 =head2 3M Manufacturing Blank
45
46  0   55 55 55 55
47  1   55 55 55 55
48  2   55 55 55 55
49  3   55 55 55 55
50  4   55 55 55 55
51  5   55 55 55 55
52  6   00 00 00 00 
53
54 =head2 Generic blank
55
56  0   00 00 00 00
57  1   00 00 00 00
58  2   00 00 00 00
59
60 =head1 Security
61
62 AFI byte on RFID tag is used for security.
63
64 In my case, we have RFID door which can only read AFI bytes from tag and issue
65 alarm sound or ignore it depending on value of byte.
66
67 =over 8 
68
69 =item 0xD7 214
70
71 secured item (door will beep)
72
73 =item 0xDA 218
74
75 unsecured (door will ignore it)
76
77 =back
78
79
80 =head1 METHODS
81
82 =head2 to_hash
83
84   my $hash = Biblio::RFID::Decode::RFID501->to_hash( $bytes );
85
86   my $hash = Biblio::RFID::Decode::RFID501->to_hash( [ 'blk1', 'blk2', ... , 'blk7' ] );
87
88 =head2 from_hash
89
90   my $blocks = Biblio::RFID::Decode::RFID->from_hash({ content => "1301234567" });
91
92 =head2 blank_3m
93
94 =head2 blank
95
96   my $blocks = Biblio::RFID::Decode::RFID->blank;
97
98 =cut
99
100 my $item_type = {
101         1 => 'Book',
102         6 => 'CD/CD ROM',
103         2 => 'Magazine',
104         13 => 'Book with Audio Tape',
105         9 => 'Book with CD/CD ROM',
106         0 => 'Other',
107
108         5 => 'Video',
109         4 => 'Audio Tape',
110         3 => 'Bound Journal',
111         8 => 'Book with Diskette',
112         7 => 'Diskette',
113 };
114
115 sub to_hash {
116         my ( $self, $data ) = @_;
117
118         return unless $data;
119
120         $data = join('', @$data) if ref $data eq 'ARRAY';
121
122         if ( length($data) < 24 ) {
123                 die "short data from tag ", length($data), " < 24 bytes";
124         }
125
126         warn "## to_hash ",dump($data);
127
128         my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom, $zero ) = unpack('C4Z16Nl>l',$data);
129         my $hash = {
130                 u1 => $u1,      # FIXME 0x04
131                 set => ( $set_item & 0xf0 ) >> 4,
132                 total => ( $set_item & 0x0f ),
133
134                 u2 => $u2,      # FIXME 0x00
135
136                 type => $type,
137                 type_label => $item_type->{$type},
138
139                 content => $content,
140
141                 branch => $br_lib >> 20,
142                 library => $br_lib & 0x000fffff,
143
144                 custom => $custom,
145         };
146
147         warn "expected first byte to be 0x04, not $u1\n"   if $u1 != 4;
148         warn "expected third byte to be 0x00, not $u2\n"   if $u2 != 0;
149         warn "expected last block to be zero, not $zero\n" if $zero != 0;
150
151         return $hash;
152 }
153
154 sub from_hash {
155         my ( $self, $hash ) = @_;
156
157         warn "## from_hash ",dump($hash);
158
159         $hash->{$_} ||= 0 foreach ( qw( set total type branch library ) );
160
161         return pack('C4Z16Nl>l',
162                 0x04,
163                 ( $hash->{set} << 4 ) | ( $hash->{total} & 0x0f ),
164                 0x00,
165                 $hash->{type},
166
167                 $hash->{content},
168
169                 ( $hash->{branch} << 20 ) | ( $hash->{library} & 0x000fffff ),
170
171                 $hash->{custom},
172                 0x00,
173         );
174 }
175
176 sub blank_3m {
177         return ( "\x55" x ( 6 * 4 ) ) . ( "\x00" x 4 );
178 }
179
180 sub blank {
181         return "\x00" x ( 4 * 3 );
182 }
183
184 1;