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;
|