File Coverage

lib/Biblio/RFID/RFID501.pm
Criterion Covered Total %
statement 24 25 96.0
branch 5 10 50.0
condition 2 2 100.0
subroutine 6 7 85.7
pod 4 4 100.0
total 41 48 85.4


line stmt bran cond sub pod time code
1             package Biblio::RFID::RFID501;
2              
3 2     2   103637 use warnings;
  2         6  
  2         84  
4 2     2   12 use strict;
  2         4  
  2         72  
5              
6 2     2   12 use Data::Dump qw(dump);
  2         3  
  2         1122  
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
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
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 2     2 1 457 my ( $self, $data ) = @_;
117              
118 2 50       8 return unless $data;
119              
120 2 50       16 $data = join('', @$data) if ref $data eq 'ARRAY';
121              
122 2         12 warn "## to_hash ",dump($data);
123              
124 2         657 my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom, $zero ) = unpack('C4Z16Nl>l',$data);
125 2         32 my $hash = {
126             u1 => $u1, # FIXME 0x04
127             set => ( $set_item & 0xf0 ) >> 4,
128             total => ( $set_item & 0x0f ),
129              
130             u2 => $u2, # FIXME 0x00
131              
132             type => $type,
133             type_label => $item_type->{$type},
134              
135             content => $content,
136              
137             branch => $br_lib >> 20,
138             library => $br_lib & 0x000fffff,
139              
140             custom => $custom,
141             };
142              
143 2 50       12 warn "expected first byte to be 0x04, not $u1\n" if $u1 != 4;
144 2 50       7 warn "expected third byte to be 0x00, not $u2\n" if $u2 != 0;
145 2 50       7 warn "expected last block to be zero, not $zero\n" if $zero != 0;
146              
147 2         13 return $hash;
148             }
149              
150             sub from_hash {
151 2     2 1 1723 my ( $self, $hash ) = @_;
152              
153 2         8 warn "## from_hash ",dump($hash);
154              
155 2   100     1284 $hash->{$_} ||= 0 foreach ( qw( set total type branch library ) );
156              
157 2         34 return pack('C4Z16Nl>l',
158             0x04,
159             ( $hash->{set} << 4 ) | ( $hash->{total} & 0x0f ),
160             0x00,
161             $hash->{type},
162              
163             $hash->{content},
164              
165             ( $hash->{branch} << 20 ) | ( $hash->{library} & 0x000fffff ),
166              
167             $hash->{custom},
168             0x00,
169             );
170             }
171              
172             sub blank_3m {
173 0     0 1 0 return ( "\x55" x ( 6 * 4 ) ) . ( "\x00" x 4 );
174             }
175              
176             sub blank {
177 1     1 1 478 return "\x00" x ( 4 * 3 );
178             }
179              
180             1;