line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package RFID::Matrics::Reader;
|
2
|
|
|
|
|
|
|
our $VERSION = '0.002';
|
3
|
|
|
|
|
|
|
@ISA = qw(RFID::Reader Exporter);
|
4
|
4
|
|
|
4
|
|
26
|
use strict;
|
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
299
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# Written by Scott Gifford
|
7
|
|
|
|
|
|
|
# Copyright (C) 2004 The Regents of the University of Michigan.
|
8
|
|
|
|
|
|
|
# See the file LICENSE included with the distribution for license
|
9
|
|
|
|
|
|
|
# information.
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
RFID::Matrics::Reader - Abstract base class for a Matrics RFID reader
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
This abstract base class provides most of the methods required for
|
18
|
|
|
|
|
|
|
interfacing Perl with a Matrics RFID reader. To actually create an
|
19
|
|
|
|
|
|
|
object, use
|
20
|
|
|
|
|
|
|
L or
|
21
|
|
|
|
|
|
|
L. It is based
|
22
|
|
|
|
|
|
|
on L.
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use RFID::Matrics::Reader::Serial;
|
25
|
|
|
|
|
|
|
my $reader =
|
26
|
|
|
|
|
|
|
RFID::Matrics::Reader::Serial->new(Port => $com_port_object,
|
27
|
|
|
|
|
|
|
Node => 4,
|
28
|
|
|
|
|
|
|
Antenna => 1)
|
29
|
|
|
|
|
|
|
or die "Couldn't create reader object\n";
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my @err = $reader->set(PowerLevel => 0xff,
|
32
|
|
|
|
|
|
|
Environment => 4) == 0
|
33
|
|
|
|
|
|
|
or die "Couldn't set params: @err\n";
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my @tags = $reader->readtags;
|
36
|
|
|
|
|
|
|
foreach my $tag (@tags)
|
37
|
|
|
|
|
|
|
{
|
38
|
|
|
|
|
|
|
my $tag_info = $tag->get('Antenna','ID','Type');
|
39
|
|
|
|
|
|
|
print "I see tag $tag_info{Type}.$tag_info{ID} ".
|
40
|
|
|
|
|
|
|
"at antenna $tag_info{Antenna}.\n";
|
41
|
|
|
|
|
|
|
}
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
This abstract base class implements the commands for communicating
|
46
|
|
|
|
|
|
|
with a Matrics reader. It is written according to the specifications
|
47
|
|
|
|
|
|
|
in Matrics' I
|
48
|
|
|
|
|
|
|
Specification>, using version 2.8 from October 19th 2003. It was
|
49
|
|
|
|
|
|
|
tested with an RDR-001 model reader.
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
To actually create a reader object, use
|
52
|
|
|
|
|
|
|
L or
|
53
|
|
|
|
|
|
|
L. Those
|
54
|
|
|
|
|
|
|
classes inherit from this one.
|
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
This class inherits some methods and settings from
|
57
|
|
|
|
|
|
|
L.
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=cut
|
60
|
|
|
|
|
|
|
|
61
|
4
|
|
|
4
|
|
5781
|
use RFID::Reader qw(hexdump);
|
|
4
|
|
|
|
|
6977
|
|
|
4
|
|
|
|
|
372
|
|
62
|
4
|
|
|
4
|
|
38
|
use Exporter;
|
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
160
|
|
63
|
4
|
|
|
4
|
|
25
|
use Carp qw(cluck croak carp);
|
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
590
|
|
64
|
4
|
|
|
4
|
|
27
|
use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS);
|
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
474
|
|
65
|
|
|
|
|
|
|
# You shouldn't use the antenna constants anymore, just use numbers 1-4.
|
66
|
|
|
|
|
|
|
@EXPORT_OK = qw(MATRICS_ANT_1 MATRICS_ANT_2 MATRICS_ANT_3 MATRICS_ANT_4 hexdump);
|
67
|
|
|
|
|
|
|
%EXPORT_TAGS = (ant => [ qw(MATRICS_ANT_1 MATRICS_ANT_2 MATRICS_ANT_3 MATRICS_ANT_4)]);
|
68
|
|
|
|
|
|
|
|
69
|
4
|
|
|
4
|
|
2458
|
use RFID::Matrics::CRC qw(crc);
|
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
466
|
|
70
|
4
|
|
|
4
|
|
4916
|
use RFID::EPC::Tag;
|
|
4
|
|
|
|
|
27021
|
|
|
4
|
|
|
|
|
245
|
|
71
|
4
|
|
|
4
|
|
3105
|
use RFID::Matrics::Tag;
|
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
203
|
|
72
|
|
|
|
|
|
|
|
73
|
4
|
|
|
4
|
|
26
|
use constant MATRICS_ANT_1 => 0xa0;
|
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
196
|
|
74
|
4
|
|
|
4
|
|
21
|
use constant MATRICS_ANT_2 => 0xb0;
|
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
159
|
|
75
|
4
|
|
|
4
|
|
20
|
use constant MATRICS_ANT_3 => 0xc0;
|
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
379
|
|
76
|
4
|
|
|
4
|
|
21
|
use constant MATRICS_ANT_4 => 0xd0;
|
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
36458
|
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
our %antname = (
|
79
|
|
|
|
|
|
|
&MATRICS_ANT_1 => 1,
|
80
|
|
|
|
|
|
|
&MATRICS_ANT_2 => 2,
|
81
|
|
|
|
|
|
|
&MATRICS_ANT_3 => 3,
|
82
|
|
|
|
|
|
|
&MATRICS_ANT_4 => 4,
|
83
|
|
|
|
|
|
|
);
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
our %paramblock_setting = (power_level => 1,
|
86
|
|
|
|
|
|
|
environment => 1,
|
87
|
|
|
|
|
|
|
);
|
88
|
|
|
|
|
|
|
our %paramblock_rename = (PowerLevel => 'power_level',
|
89
|
|
|
|
|
|
|
Environment => 'environment',
|
90
|
|
|
|
|
|
|
Mask => 'Mask',
|
91
|
|
|
|
|
|
|
);
|
92
|
|
|
|
|
|
|
our %readerstatus_rename = (ReaderVersion => 'version',
|
93
|
|
|
|
|
|
|
ReaderSerialNum => 'serialnum',
|
94
|
|
|
|
|
|
|
);
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
our %antid = (1 => MATRICS_ANT_1,
|
97
|
|
|
|
|
|
|
2 => MATRICS_ANT_2,
|
98
|
|
|
|
|
|
|
3 => MATRICS_ANT_3,
|
99
|
|
|
|
|
|
|
4 => MATRICS_ANT_4);
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Initializer used by derived objects
|
102
|
|
|
|
|
|
|
sub _init
|
103
|
|
|
|
|
|
|
{
|
104
|
3
|
|
|
3
|
|
2008341
|
my $self = shift;
|
105
|
3
|
|
|
|
|
33
|
my(%p)=@_;
|
106
|
|
|
|
|
|
|
|
107
|
3
|
|
66
|
|
|
47
|
$self->{default_node} = $p{Node}||$p{node};
|
108
|
3
|
50
|
33
|
|
|
73
|
$self->{timeout} = $p{timeout}||$p{Timeout}
|
109
|
|
|
|
|
|
|
unless ($self->{timeout});
|
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# This can't be set again, so let's delete it.
|
112
|
3
|
|
|
|
|
10
|
delete $p{node};
|
113
|
3
|
|
|
|
|
88
|
delete $p{Node};
|
114
|
|
|
|
|
|
|
|
115
|
3
|
|
|
|
|
73
|
my @errs = $self->set(%p);
|
116
|
3
|
50
|
|
|
|
11
|
if (grep { $_ !~ /Unknown setting/ } @errs)
|
|
5
|
|
|
|
|
45
|
|
117
|
|
|
|
|
|
|
{
|
118
|
0
|
|
|
|
|
0
|
die "Error setting properties: @errs\n";
|
119
|
|
|
|
|
|
|
}
|
120
|
|
|
|
|
|
|
|
121
|
3
|
|
66
|
|
|
25
|
$self->{default_antenna} = $p{Antenna}||$p{antenna};
|
122
|
3
|
50
|
|
|
|
18
|
if (!defined($self->{default_antenna}))
|
123
|
|
|
|
|
|
|
{
|
124
|
0
|
0
|
|
|
|
0
|
if (defined($self->{_antenna_sequence}))
|
125
|
|
|
|
|
|
|
{
|
126
|
0
|
|
|
|
|
0
|
$self->{default_antenna} = $self->{_antenna_sequence}[0];
|
127
|
|
|
|
|
|
|
}
|
128
|
|
|
|
|
|
|
else
|
129
|
|
|
|
|
|
|
{
|
130
|
0
|
|
|
|
|
0
|
$self->{default_antenna} = 1;
|
131
|
|
|
|
|
|
|
}
|
132
|
|
|
|
|
|
|
}
|
133
|
3
|
50
|
|
|
|
17
|
$self->{_antenna_sequence} = [$self->{default_antenna}]
|
134
|
|
|
|
|
|
|
if (!$self->{_antenna_sequence});
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
3
|
50
|
33
|
|
|
76
|
$self->stop_constant_read(node => $self->{default_node})
|
138
|
|
|
|
|
|
|
if ($self->{default_node} and (!$p{noinit}));
|
139
|
|
|
|
|
|
|
|
140
|
3
|
|
|
|
|
30
|
$self->SUPER::_init(%p);
|
141
|
|
|
|
|
|
|
}
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=head2 METHODS
|
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=cut
|
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
our %_errmsgs = (
|
151
|
|
|
|
|
|
|
0xF0 => "READER - Invalid command parameter(s)",
|
152
|
|
|
|
|
|
|
0xF1 => "READER - Insufficient data",
|
153
|
|
|
|
|
|
|
0xF2 => "READER - Command not supported",
|
154
|
|
|
|
|
|
|
0xF3 => "READER - Antenna Fault (not present or shorted)",
|
155
|
|
|
|
|
|
|
0xF4 => "READER - DSP Timeout",
|
156
|
|
|
|
|
|
|
0xF5 => "READER - DSP Error",
|
157
|
|
|
|
|
|
|
0xF6 => "READER - DSP Idle",
|
158
|
|
|
|
|
|
|
0xF7 => "READER - Zero Power",
|
159
|
|
|
|
|
|
|
0xFF => "READER - Undefined error",
|
160
|
|
|
|
|
|
|
);
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# Prototype
|
163
|
|
|
|
|
|
|
sub uniq(&@);
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub _setdefaults
|
166
|
|
|
|
|
|
|
{
|
167
|
36
|
|
|
36
|
|
56
|
my $self = shift;
|
168
|
36
|
|
|
|
|
41
|
my($p)=@_;
|
169
|
36
|
100
|
|
|
|
104
|
$p->{node} = $self->{default_node}
|
170
|
|
|
|
|
|
|
unless defined($p->{node});
|
171
|
36
|
100
|
|
|
|
102
|
$p->{antenna} = $self->{default_antenna}
|
172
|
|
|
|
|
|
|
unless defined($p->{antenna});
|
173
|
36
|
100
|
66
|
|
|
358
|
$p->{antenna} = $antid{$p->{antenna}}
|
174
|
|
|
|
|
|
|
if (defined($p->{antenna}) and $p->{antenna} < 10);
|
175
|
36
|
|
|
|
|
59
|
$p;
|
176
|
|
|
|
|
|
|
}
|
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub _makepacket
|
179
|
|
|
|
|
|
|
{
|
180
|
15
|
|
|
15
|
|
23
|
my $self = shift;
|
181
|
15
|
|
|
|
|
542
|
my(%p)=@_;
|
182
|
15
|
|
|
|
|
46
|
$self->_setdefaults(\%p);
|
183
|
|
|
|
|
|
|
|
184
|
15
|
|
100
|
|
|
67
|
$p{data}||="";
|
185
|
15
|
|
|
|
|
338
|
my $packet = pack("CCCa*",$p{node},length($p{data})+5,$p{cmd},$p{data});
|
186
|
15
|
|
|
|
|
94
|
return pack("Ca*v",1,$packet,crc($packet));
|
187
|
|
|
|
|
|
|
}
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub _parsepacket
|
190
|
|
|
|
|
|
|
{
|
191
|
17
|
|
|
17
|
|
27
|
my $self = shift;
|
192
|
17
|
|
|
|
|
26
|
my($packet)=@_;
|
193
|
17
|
|
|
|
|
23
|
my %dat;
|
194
|
|
|
|
|
|
|
my $sof;
|
195
|
|
|
|
|
|
|
|
196
|
17
|
|
|
|
|
25
|
my $dl = length($packet)-7;
|
197
|
17
|
|
|
|
|
188
|
($sof,@dat{qw(node len cmd status data crc)}) = unpack("CCCCCa${dl}v",$packet);
|
198
|
17
|
50
|
|
|
|
50
|
unless ($sof==1)
|
199
|
|
|
|
|
|
|
{
|
200
|
0
|
|
|
|
|
0
|
return $self->error("No start of frame byte in packet!");
|
201
|
|
|
|
|
|
|
}
|
202
|
17
|
50
|
|
|
|
77
|
unless (crc(substr($packet,1,-2)) == $dat{crc})
|
203
|
|
|
|
|
|
|
{
|
204
|
0
|
|
|
|
|
0
|
return $self->error("Bad CRC in packet!\n");
|
205
|
|
|
|
|
|
|
}
|
206
|
17
|
50
|
33
|
|
|
182
|
if ( ($dat{status} & 0x80)==0x80 or ($dat{status} & 0xC0)==0xC0)
|
207
|
|
|
|
|
|
|
{
|
208
|
0
|
|
|
|
|
0
|
my $ec = unpack("C",$dat{data});
|
209
|
0
|
|
|
|
|
0
|
return $self->error($_errmsgs{$ec},$ec);
|
210
|
|
|
|
|
|
|
}
|
211
|
17
|
|
|
|
|
98
|
return \%dat;
|
212
|
|
|
|
|
|
|
}
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
sub _getpacket
|
215
|
|
|
|
|
|
|
{
|
216
|
17
|
|
|
17
|
|
26
|
my($self)=@_;
|
217
|
17
|
50
|
|
|
|
78
|
my $data = $self->_readbytes(3)
|
218
|
|
|
|
|
|
|
or die "Couldn't read data: $!\n";
|
219
|
17
|
50
|
|
|
|
1280
|
length($data) == 3
|
220
|
|
|
|
|
|
|
or die "Data short read!\n";
|
221
|
17
|
|
|
|
|
67
|
my($sof,$addr,$len)=unpack("CCC",$data);
|
222
|
|
|
|
|
|
|
|
223
|
17
|
50
|
|
|
|
57
|
my $moredata = $self->_readbytes($len-2)
|
224
|
|
|
|
|
|
|
or die "Couldn't read data: $!\n";
|
225
|
17
|
50
|
|
|
|
509
|
length($moredata) == ($len-2)
|
226
|
|
|
|
|
|
|
or die "Data short read!\n";
|
227
|
|
|
|
|
|
|
|
228
|
17
|
50
|
|
|
|
60
|
$self->debug(" RECV: ",hexdump($data.$moredata),"\n")
|
229
|
|
|
|
|
|
|
if ($self->{_debug});
|
230
|
17
|
|
|
|
|
64
|
return $data.$moredata;
|
231
|
|
|
|
|
|
|
}
|
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub _sendpacket
|
234
|
|
|
|
|
|
|
{
|
235
|
15
|
|
|
15
|
|
27
|
my $self = shift;
|
236
|
15
|
|
|
|
|
23
|
my($data)=@_;
|
237
|
|
|
|
|
|
|
|
238
|
15
|
50
|
|
|
|
41
|
$self->debug(" SEND: ",hexdump($data),"\n")
|
239
|
|
|
|
|
|
|
if ($self->{_debug});
|
240
|
15
|
50
|
|
|
|
109
|
$self->_writebytes($data)
|
241
|
|
|
|
|
|
|
or die "Couldn't write to COM port: $^E";
|
242
|
|
|
|
|
|
|
}
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub _do_something
|
245
|
|
|
|
|
|
|
{
|
246
|
15
|
|
|
15
|
|
26
|
my $self = shift;
|
247
|
15
|
|
|
|
|
66
|
my($cmd_sub,$resp_sub,%p)=@_;
|
248
|
15
|
|
|
|
|
21
|
my @ret ;
|
249
|
|
|
|
|
|
|
|
250
|
15
|
50
|
|
|
|
64
|
my $cmd = $cmd_sub->($self,%p)
|
251
|
|
|
|
|
|
|
or return undef;
|
252
|
15
|
50
|
|
|
|
212
|
$self->_sendpacket($cmd)
|
253
|
|
|
|
|
|
|
or die "Couldn't write command: $!\n";
|
254
|
|
|
|
|
|
|
|
255
|
15
|
|
|
|
|
4488
|
while(1)
|
256
|
|
|
|
|
|
|
{
|
257
|
17
|
50
|
|
|
|
138
|
my $resp = $self->_getpacket()
|
258
|
|
|
|
|
|
|
or die "Couldn't read response: $!\n";
|
259
|
17
|
50
|
|
|
|
67
|
my $pr = $resp_sub->($self,$resp)
|
260
|
|
|
|
|
|
|
or return undef;
|
261
|
17
|
|
|
|
|
27
|
push(@ret,$pr);
|
262
|
17
|
100
|
|
|
|
57
|
last unless ($pr->{status} & 0x01);
|
263
|
|
|
|
|
|
|
}
|
264
|
15
|
100
|
|
|
|
179
|
return wantarray?@ret:$ret[0];
|
265
|
|
|
|
|
|
|
}
|
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=head3 get
|
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Get various properties of the reader or the internal state of the
|
270
|
|
|
|
|
|
|
object. The syntax is described in L
|
271
|
|
|
|
|
|
|
method|RFID::Reader/get> documentation. See L
|
272
|
|
|
|
|
|
|
and L for the properties
|
273
|
|
|
|
|
|
|
that can be retreived.
|
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=cut
|
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub get
|
278
|
|
|
|
|
|
|
{
|
279
|
4
|
|
|
4
|
1
|
8197
|
my $self = shift;
|
280
|
4
|
|
|
|
|
14
|
my %get;
|
281
|
|
|
|
|
|
|
my %ret;
|
282
|
0
|
|
|
|
|
0
|
my %paramblocks;
|
283
|
0
|
|
|
|
|
0
|
my $readerstatus;
|
284
|
|
|
|
|
|
|
|
285
|
4
|
|
|
|
|
17
|
foreach my $g (@_)
|
286
|
|
|
|
|
|
|
{
|
287
|
6
|
100
|
33
|
|
|
64
|
if ($paramblock_rename{$g} or ($g =~ /^(\w+)_Antenna(\d+)$/ and $paramblock_rename{$1}))
|
|
|
50
|
66
|
|
|
|
|
|
|
50
|
|
|
|
|
|
288
|
|
|
|
|
|
|
{
|
289
|
4
|
|
|
|
|
13
|
my($ant,$asv);
|
290
|
4
|
50
|
|
|
|
31
|
if ($paramblock_rename{$g})
|
291
|
|
|
|
|
|
|
{
|
292
|
4
|
|
|
|
|
15
|
$ant = $self->{default_antenna};
|
293
|
4
|
|
|
|
|
7
|
$asv = $paramblock_rename{$g};
|
294
|
|
|
|
|
|
|
}
|
295
|
|
|
|
|
|
|
else
|
296
|
|
|
|
|
|
|
{
|
297
|
0
|
|
|
|
|
0
|
$ant = $2;
|
298
|
0
|
|
|
|
|
0
|
$asv = $paramblock_rename{$1};
|
299
|
|
|
|
|
|
|
}
|
300
|
4
|
100
|
|
|
|
23
|
if (!$paramblocks{$ant})
|
301
|
|
|
|
|
|
|
{
|
302
|
2
|
50
|
|
|
|
34
|
$paramblocks{$ant} = $self->getparamblock(antenna => $antid{$ant})
|
303
|
|
|
|
|
|
|
or die "Couldn't getparamblock for antenna $ant!";
|
304
|
|
|
|
|
|
|
}
|
305
|
4
|
50
|
|
|
|
9
|
if ($g eq 'Mask')
|
306
|
|
|
|
|
|
|
{
|
307
|
0
|
|
|
|
|
0
|
my $masktype = $paramblocks{$ant}{filter_type};
|
308
|
0
|
0
|
|
|
|
0
|
if ($masktype == 0)
|
309
|
|
|
|
|
|
|
{
|
310
|
0
|
|
|
|
|
0
|
$ret{$g}='';
|
311
|
|
|
|
|
|
|
}
|
312
|
|
|
|
|
|
|
else
|
313
|
|
|
|
|
|
|
{
|
314
|
0
|
|
|
|
|
0
|
my $mask = bin2hex_big_endian($paramblocks{$ant}{filter_bits});
|
315
|
0
|
|
|
|
|
0
|
$mask =~ s/\s//g;
|
316
|
0
|
0
|
|
|
|
0
|
if ($masktype == 1)
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
317
|
|
|
|
|
|
|
{
|
318
|
0
|
|
|
|
|
0
|
$ret{$g} = substr($mask,0,2)."/8";
|
319
|
|
|
|
|
|
|
}
|
320
|
|
|
|
|
|
|
elsif ($masktype == 2)
|
321
|
|
|
|
|
|
|
{
|
322
|
0
|
|
|
|
|
0
|
$ret{$g} = substr($mask,0,10)."/40";
|
323
|
|
|
|
|
|
|
}
|
324
|
|
|
|
|
|
|
elsif ($masktype == 3)
|
325
|
|
|
|
|
|
|
{
|
326
|
0
|
|
|
|
|
0
|
$ret{g} = substr($mask,0,16)."/64";
|
327
|
|
|
|
|
|
|
}
|
328
|
|
|
|
|
|
|
else
|
329
|
|
|
|
|
|
|
{
|
330
|
|
|
|
|
|
|
# Error
|
331
|
0
|
|
|
|
|
0
|
$ret{g} = undef;
|
332
|
|
|
|
|
|
|
}
|
333
|
|
|
|
|
|
|
}
|
334
|
|
|
|
|
|
|
}
|
335
|
|
|
|
|
|
|
else
|
336
|
|
|
|
|
|
|
{
|
337
|
4
|
|
|
|
|
15
|
$ret{$g} = $paramblocks{$ant}{$asv};
|
338
|
|
|
|
|
|
|
}
|
339
|
|
|
|
|
|
|
}
|
340
|
|
|
|
|
|
|
elsif ($g eq 'AntennaSequence')
|
341
|
|
|
|
|
|
|
{
|
342
|
0
|
|
|
|
|
0
|
$ret{$g} = [$self->{_antenna_sequence}];
|
343
|
|
|
|
|
|
|
}
|
344
|
|
|
|
|
|
|
elsif ($readerstatus_rename{$g})
|
345
|
|
|
|
|
|
|
{
|
346
|
2
|
50
|
|
|
|
9
|
if (!$readerstatus)
|
347
|
|
|
|
|
|
|
{
|
348
|
2
|
|
|
|
|
3
|
my %p;
|
349
|
2
|
|
|
|
|
7
|
$self->_setdefaults(\%p);
|
350
|
2
|
50
|
|
|
|
29
|
$readerstatus = $self->getreaderstatus(%p)
|
351
|
|
|
|
|
|
|
or die "Couldn't get reader status: $self->{error}";
|
352
|
|
|
|
|
|
|
}
|
353
|
2
|
|
|
|
|
11
|
$ret{$g}=$readerstatus->{$readerstatus_rename{$g}};
|
354
|
|
|
|
|
|
|
}
|
355
|
|
|
|
|
|
|
else
|
356
|
|
|
|
|
|
|
{
|
357
|
0
|
|
|
|
|
0
|
croak "Unknown setting '$g'";
|
358
|
|
|
|
|
|
|
}
|
359
|
|
|
|
|
|
|
}
|
360
|
4
|
100
|
|
|
|
19
|
if (wantarray)
|
361
|
|
|
|
|
|
|
{
|
362
|
2
|
|
|
|
|
22
|
return %ret;
|
363
|
|
|
|
|
|
|
}
|
364
|
|
|
|
|
|
|
else
|
365
|
|
|
|
|
|
|
{
|
366
|
|
|
|
|
|
|
# Return last value
|
367
|
2
|
|
|
|
|
20
|
return $ret{$_[$#_]};
|
368
|
|
|
|
|
|
|
}
|
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
}
|
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub getparamblock
|
373
|
|
|
|
|
|
|
{
|
374
|
4
|
|
|
4
|
0
|
8
|
my $self = shift;
|
375
|
4
|
|
|
|
|
15
|
my(%p)=@_;
|
376
|
4
|
|
|
|
|
14
|
$self->_setdefaults(\%p);
|
377
|
4
|
|
|
|
|
17
|
$self->_do_something(\&_cmd_getparamblock,\&_resp_getparamblock,@_);
|
378
|
|
|
|
|
|
|
}
|
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub _cmd_getparamblock
|
382
|
|
|
|
|
|
|
{
|
383
|
4
|
|
|
4
|
|
8
|
my $self = shift;
|
384
|
4
|
|
|
|
|
97
|
my(%p)=@_;
|
385
|
4
|
|
|
|
|
90
|
$self->_setdefaults(\%p);
|
386
|
4
|
|
|
|
|
30
|
$self->_makepacket(%p,
|
387
|
|
|
|
|
|
|
cmd => 0x24,
|
388
|
|
|
|
|
|
|
data => pack("C",$p{antenna}),
|
389
|
|
|
|
|
|
|
);
|
390
|
|
|
|
|
|
|
}
|
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub _resp_getparamblock
|
393
|
|
|
|
|
|
|
{
|
394
|
4
|
|
|
4
|
|
6
|
my $self = shift;
|
395
|
4
|
50
|
|
|
|
28
|
my $pp = $self->_parsepacket(@_)
|
396
|
|
|
|
|
|
|
or return undef;
|
397
|
4
|
|
|
|
|
52
|
(@$pp{qw(power_level environment combine_antenna_bits protocol_speed filter_type tagtype reserved_bits filter_bits reserved_bits)}) =
|
398
|
|
|
|
|
|
|
unpack("CCa1CCCa2a8a*",$pp->{data});
|
399
|
4
|
|
|
|
|
22
|
$pp->{combine_antenna}=[];
|
400
|
4
|
|
|
|
|
8
|
my $ca = ord $pp->{combine_antenna_bits};
|
401
|
4
|
|
|
|
|
11
|
foreach my $i (0..3)
|
402
|
|
|
|
|
|
|
{
|
403
|
16
|
|
|
|
|
36
|
my @antarr = (MATRICS_ANT_1, MATRICS_ANT_2, MATRICS_ANT_3, MATRICS_ANT_4);
|
404
|
16
|
50
|
|
|
|
52
|
if ($ca & (1 << $i))
|
405
|
|
|
|
|
|
|
{
|
406
|
0
|
|
|
|
|
0
|
push(@{$pp->{combine_antenna}},$antarr[$i]);
|
|
0
|
|
|
|
|
0
|
|
407
|
|
|
|
|
|
|
}
|
408
|
|
|
|
|
|
|
}
|
409
|
4
|
|
|
|
|
13
|
$pp;
|
410
|
|
|
|
|
|
|
}
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=head3 set
|
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
Set one or more properties associated with a reader. Depending on
|
416
|
|
|
|
|
|
|
implementation, this may send one or more commands to the reader, set
|
417
|
|
|
|
|
|
|
an internal flag, or take some other action.
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
This method takes a hash with the properties to be set as keys, and
|
420
|
|
|
|
|
|
|
their new values as values. It returns a list of errors that occured;
|
421
|
|
|
|
|
|
|
if no errors occured, it will return an empty list. In a scalar
|
422
|
|
|
|
|
|
|
context, that evaluates to the number of errors that occured, so you
|
423
|
|
|
|
|
|
|
can test for errors like this:
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
my @errs = $reader->set(SomeVariable => "New Value") == 0
|
426
|
|
|
|
|
|
|
or die "Couldn't set SomeVariable: @errs";
|
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
See L for the properties that can be set.
|
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=cut
|
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub set
|
433
|
|
|
|
|
|
|
{
|
434
|
5
|
|
|
5
|
1
|
10
|
my $self = shift;
|
435
|
5
|
|
|
|
|
22
|
my(%p)=@_;
|
436
|
5
|
|
|
|
|
10
|
my %antset;
|
437
|
|
|
|
|
|
|
my %ant_specific;
|
438
|
0
|
|
|
|
|
0
|
my %unknown;
|
439
|
0
|
|
|
|
|
0
|
my @errs;
|
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# First pass through settings.
|
442
|
|
|
|
|
|
|
# Settings that can be grouped into a single command are collected,
|
443
|
|
|
|
|
|
|
# and set efficiently in a second pass.
|
444
|
5
|
|
|
|
|
25
|
while(my($k,$v)=each(%p))
|
445
|
|
|
|
|
|
|
{
|
446
|
12
|
100
|
33
|
|
|
109
|
if ($paramblock_rename{$k} or ($k =~ /^(\w+)_Antenna(\d+)$/ and $paramblock_rename{$1}))
|
|
|
50
|
66
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
447
|
|
|
|
|
|
|
{
|
448
|
4
|
|
|
|
|
6
|
my($ant,$param);
|
449
|
4
|
50
|
|
|
|
13
|
if ($paramblock_rename{$k})
|
450
|
|
|
|
|
|
|
{
|
451
|
4
|
|
|
|
|
10
|
($ant,$param)=('ALL',$paramblock_rename{$k});
|
452
|
|
|
|
|
|
|
}
|
453
|
|
|
|
|
|
|
else
|
454
|
|
|
|
|
|
|
{
|
455
|
0
|
|
|
|
|
0
|
($ant,$param)=($2,$paramblock_rename{$1});
|
456
|
|
|
|
|
|
|
}
|
457
|
4
|
50
|
|
|
|
12
|
if ($param eq 'Mask')
|
458
|
|
|
|
|
|
|
{
|
459
|
0
|
0
|
|
|
|
0
|
if ($v eq '')
|
460
|
|
|
|
|
|
|
{
|
461
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_type}=0;
|
462
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_bits}="\0"x8;
|
463
|
|
|
|
|
|
|
}
|
464
|
|
|
|
|
|
|
else
|
465
|
|
|
|
|
|
|
{
|
466
|
0
|
|
|
|
|
0
|
my($mask,$len,$start)=split(/\//,$v);
|
467
|
0
|
0
|
|
|
|
0
|
if ($start)
|
468
|
|
|
|
|
|
|
{
|
469
|
0
|
|
|
|
|
0
|
push(@errs,"Matrics reader doesn't support mask start bit");
|
470
|
0
|
|
|
|
|
0
|
next;
|
471
|
|
|
|
|
|
|
}
|
472
|
0
|
0
|
|
|
|
0
|
if (!defined($len))
|
473
|
|
|
|
|
|
|
{
|
474
|
0
|
|
|
|
|
0
|
$len = length($mask) * 4;
|
475
|
|
|
|
|
|
|
}
|
476
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_bits} = hex2bin_big_endian($mask);
|
477
|
0
|
0
|
|
|
|
0
|
if ($len == 0)
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
478
|
|
|
|
|
|
|
{
|
479
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_type} = 0;
|
480
|
|
|
|
|
|
|
}
|
481
|
|
|
|
|
|
|
elsif ($len == 8)
|
482
|
|
|
|
|
|
|
{
|
483
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_type} = 1;
|
484
|
|
|
|
|
|
|
}
|
485
|
|
|
|
|
|
|
elsif ($len == 40)
|
486
|
|
|
|
|
|
|
{
|
487
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_type} = 2;
|
488
|
|
|
|
|
|
|
}
|
489
|
|
|
|
|
|
|
elsif ($len == 64)
|
490
|
|
|
|
|
|
|
{
|
491
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_type} = 3;
|
492
|
|
|
|
|
|
|
}
|
493
|
|
|
|
|
|
|
else
|
494
|
|
|
|
|
|
|
{
|
495
|
0
|
|
|
|
|
0
|
push(@errs,"Matrics reader only supports mask len of 0, 8, 40, and 64.");
|
496
|
0
|
|
|
|
|
0
|
next;
|
497
|
|
|
|
|
|
|
}
|
498
|
0
|
|
|
|
|
0
|
$antset{$ant}{filter_bits} = substr($antset{$ant}{filter_bits},0,$len/8) . "\0" x (8-($len/8));
|
499
|
|
|
|
|
|
|
}
|
500
|
|
|
|
|
|
|
}
|
501
|
4
|
|
|
|
|
52
|
$antset{$ant}{$param} = $v;
|
502
|
|
|
|
|
|
|
}
|
503
|
|
|
|
|
|
|
elsif ($k eq 'AntennaSequence')
|
504
|
|
|
|
|
|
|
{
|
505
|
0
|
|
|
|
|
0
|
$self->{_antenna_sequence} = $v;
|
506
|
|
|
|
|
|
|
}
|
507
|
|
|
|
|
|
|
elsif ($k eq 'node')
|
508
|
|
|
|
|
|
|
{
|
509
|
0
|
|
|
|
|
0
|
push(@errs, "Can't set node (yet)!");
|
510
|
0
|
|
|
|
|
0
|
next;
|
511
|
|
|
|
|
|
|
}
|
512
|
|
|
|
|
|
|
elsif ($k eq 'baudrate')
|
513
|
|
|
|
|
|
|
{
|
514
|
0
|
|
|
|
|
0
|
push(@errs,"Can't set baudrate (yet)!");
|
515
|
0
|
|
|
|
|
0
|
next;
|
516
|
|
|
|
|
|
|
}
|
517
|
|
|
|
|
|
|
else
|
518
|
|
|
|
|
|
|
{
|
519
|
8
|
|
|
|
|
34
|
$unknown{$k}=$v;
|
520
|
|
|
|
|
|
|
}
|
521
|
|
|
|
|
|
|
}
|
522
|
5
|
100
|
|
|
|
25
|
if (keys %unknown)
|
523
|
|
|
|
|
|
|
{
|
524
|
3
|
|
|
|
|
51
|
push(@errs,$self->SUPER::set(%unknown));
|
525
|
|
|
|
|
|
|
}
|
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# Copy options for all antennas into each individual antenna
|
528
|
5
|
|
|
|
|
113
|
while (my($k,$v)=each(%{$antset{ALL}}))
|
|
9
|
|
|
|
|
62
|
|
529
|
|
|
|
|
|
|
{
|
530
|
4
|
|
|
|
|
7
|
foreach my $a (@{$self->{_antenna_sequence}})
|
|
4
|
|
|
|
|
17
|
|
531
|
|
|
|
|
|
|
{
|
532
|
4
|
|
|
|
|
18
|
$antset{$a}{$k} = $v;
|
533
|
|
|
|
|
|
|
}
|
534
|
|
|
|
|
|
|
}
|
535
|
|
|
|
|
|
|
|
536
|
5
|
|
|
|
|
22
|
foreach my $ant (1..4)
|
537
|
|
|
|
|
|
|
{
|
538
|
20
|
100
|
|
|
|
79
|
if ($antset{$ant})
|
539
|
|
|
|
|
|
|
{
|
540
|
2
|
|
|
|
|
7
|
$antset{$ant}{antenna} = $antid{$ant};
|
541
|
2
|
|
|
|
|
8
|
$self->_setdefaults($antset{$ant});
|
542
|
2
|
50
|
|
|
|
12
|
$self->changeparamblock(%{$antset{$ant}})
|
|
2
|
|
|
|
|
46
|
|
543
|
|
|
|
|
|
|
or push(@errs, "Error changing params: $self->{error}");
|
544
|
|
|
|
|
|
|
}
|
545
|
|
|
|
|
|
|
}
|
546
|
5
|
|
|
|
|
36
|
@errs;
|
547
|
|
|
|
|
|
|
}
|
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
sub setparamblock
|
551
|
|
|
|
|
|
|
{
|
552
|
2
|
|
|
2
|
0
|
6
|
my $self = shift;
|
553
|
2
|
|
|
|
|
25
|
my(%p)=@_;
|
554
|
2
|
|
|
|
|
9
|
$self->_setdefaults(\%p);
|
555
|
|
|
|
|
|
|
|
556
|
2
|
|
|
|
|
16
|
$self->_do_something(\&_cmd_setparamblock,\&_resp_setparamblock,@_);
|
557
|
|
|
|
|
|
|
}
|
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub _cmd_setparamblock
|
560
|
|
|
|
|
|
|
{
|
561
|
2
|
|
|
2
|
|
3
|
my $self = shift;
|
562
|
2
|
|
|
|
|
22
|
my(%p)=@_;
|
563
|
|
|
|
|
|
|
|
564
|
2
|
50
|
50
|
|
|
30
|
$self->_makepacket(%p,
|
|
|
50
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
565
|
|
|
|
|
|
|
cmd => 0x23,
|
566
|
|
|
|
|
|
|
data => pack("CCCCCCCCCCx2a8x16",
|
567
|
|
|
|
|
|
|
$self->_make_confwhich_ant(\%p), # Returns 4 bytes
|
568
|
|
|
|
|
|
|
defined($p{power_level})?$p{power_level}:0xff,
|
569
|
|
|
|
|
|
|
defined($p{environment})?$p{environment}:0x00,
|
570
|
|
|
|
|
|
|
$self->_make_combine_antenna_bits(\%p),
|
571
|
|
|
|
|
|
|
$p{protocol_speed}||0,
|
572
|
|
|
|
|
|
|
$p{filter_type}||0,
|
573
|
|
|
|
|
|
|
$p{tagtype}||0,
|
574
|
|
|
|
|
|
|
$p{filter_bits}||"\0"x8,
|
575
|
|
|
|
|
|
|
)
|
576
|
|
|
|
|
|
|
);
|
577
|
|
|
|
|
|
|
}
|
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
sub _resp_setparamblock
|
580
|
|
|
|
|
|
|
{
|
581
|
2
|
|
|
2
|
|
5
|
my $self = shift;
|
582
|
2
|
50
|
|
|
|
6
|
my $pp = $self->_parsepacket(@_)
|
583
|
|
|
|
|
|
|
or return undef;
|
584
|
|
|
|
|
|
|
}
|
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
sub changeparamblock
|
587
|
|
|
|
|
|
|
{
|
588
|
2
|
|
|
2
|
0
|
4
|
my $self = shift;
|
589
|
2
|
|
|
|
|
18
|
my(%p)=@_;
|
590
|
2
|
|
|
|
|
7
|
$self->_setdefaults(\%p);
|
591
|
|
|
|
|
|
|
|
592
|
2
|
50
|
|
|
|
9
|
croak "changeparamblock: The required parameter 'antenna' is missing.\n"
|
593
|
|
|
|
|
|
|
unless ($p{antenna});
|
594
|
2
|
50
|
|
|
|
9
|
my $curparam = $self->getparamblock(@_)
|
595
|
|
|
|
|
|
|
or return undef;
|
596
|
2
|
50
|
|
|
|
10
|
if ($p{combine_antennas})
|
597
|
|
|
|
|
|
|
{
|
598
|
0
|
|
|
|
|
0
|
delete $curparam->{combine_antenna_bits};
|
599
|
|
|
|
|
|
|
}
|
600
|
2
|
|
|
|
|
25
|
return $self->setparamblock(%$curparam, @_);
|
601
|
|
|
|
|
|
|
}
|
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=head3 readtags
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
Read all of the tags in the reader's field, honoring any settings
|
606
|
|
|
|
|
|
|
affecting the reading and filtering of tags. This returns a (possibly
|
607
|
|
|
|
|
|
|
empty) list of tags, which will be of type
|
608
|
|
|
|
|
|
|
L or
|
609
|
|
|
|
|
|
|
L. See L
|
610
|
|
|
|
|
|
|
readtags method documentation|RFID::Reader/readtags> for more
|
611
|
|
|
|
|
|
|
information.
|
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=cut
|
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
sub readtags
|
616
|
|
|
|
|
|
|
{
|
617
|
2
|
|
|
2
|
1
|
6898
|
my $self = shift;
|
618
|
2
|
|
|
|
|
4
|
my @tags;
|
619
|
|
|
|
|
|
|
|
620
|
2
|
|
|
|
|
4
|
foreach my $ant (@{$self->{_antenna_sequence}})
|
|
2
|
|
|
|
|
18
|
|
621
|
|
|
|
|
|
|
{
|
622
|
2
|
|
|
|
|
16
|
my $r = $self->readfullfield(antenna => $ant);
|
623
|
2
|
50
|
|
|
|
10
|
push(@tags,@{$r->{tags}})
|
|
2
|
|
|
|
|
14
|
|
624
|
|
|
|
|
|
|
if ($r->{tags});
|
625
|
|
|
|
|
|
|
}
|
626
|
2
|
|
|
|
|
52
|
return $self->filter_tags(@tags);
|
627
|
|
|
|
|
|
|
}
|
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
sub readfullfield
|
630
|
|
|
|
|
|
|
{
|
631
|
2
|
|
|
2
|
0
|
5
|
my $self = shift;
|
632
|
2
|
50
|
|
|
|
15
|
my @resp = $self->_do_something(\&_cmd_readfullfield,\&_resp_readfullfield,@_)
|
633
|
|
|
|
|
|
|
or return undef;
|
634
|
2
|
|
|
|
|
5
|
my $ret = shift(@resp);
|
635
|
2
|
|
|
|
|
4
|
foreach my $r (@resp)
|
636
|
|
|
|
|
|
|
{
|
637
|
2
|
|
|
|
|
5
|
$ret->{numtags} += $r->{numtags};
|
638
|
2
|
|
|
|
|
13
|
push(@{$ret->{tags}}, @{$r->{tags}});
|
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
19
|
|
639
|
|
|
|
|
|
|
}
|
640
|
2
|
|
|
|
|
11
|
$ret;
|
641
|
|
|
|
|
|
|
}
|
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
sub _cmd_readfullfield
|
644
|
|
|
|
|
|
|
{
|
645
|
2
|
|
|
2
|
|
5
|
my $self = shift;
|
646
|
2
|
|
|
|
|
7
|
my(%p)=@_;
|
647
|
2
|
|
|
|
|
10
|
$self->_setdefaults(\%p);
|
648
|
|
|
|
|
|
|
|
649
|
2
|
|
|
|
|
16
|
$self->_makepacket(%p,
|
650
|
|
|
|
|
|
|
cmd => 0x22,
|
651
|
|
|
|
|
|
|
data => pack("C",$p{antenna}),
|
652
|
|
|
|
|
|
|
);
|
653
|
|
|
|
|
|
|
}
|
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
sub _resp_readfullfield
|
656
|
|
|
|
|
|
|
{
|
657
|
4
|
|
|
4
|
|
7
|
my $self = shift;
|
658
|
4
|
50
|
|
|
|
14
|
my $pp =$self->_parsepacket(@_)
|
659
|
|
|
|
|
|
|
or return undef;
|
660
|
4
|
|
|
|
|
9
|
my $dc = $pp->{data};
|
661
|
4
|
|
|
|
|
35
|
(@$pp{qw(antenna numtags)}) = unpack("CC",substr($dc,0,2,""));
|
662
|
4
|
|
|
|
|
83
|
$pp->{tags} = [$self->_parsetags($pp->{numtags},$dc,Antenna => $antname{$pp->{antenna}})];
|
663
|
4
|
|
|
|
|
14
|
$pp;
|
664
|
|
|
|
|
|
|
}
|
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
sub readfullfield_unique
|
667
|
|
|
|
|
|
|
{
|
668
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
669
|
0
|
|
|
|
|
0
|
my $pp = $self->readfullfield(@_);
|
670
|
|
|
|
|
|
|
|
671
|
0
|
|
|
0
|
|
0
|
@{$pp->{utags}} = uniq { $a->tagcmp($b) }
|
|
0
|
|
|
|
|
0
|
|
672
|
0
|
|
|
|
|
0
|
sort { $a->tagcmp($b) }
|
|
0
|
|
|
|
|
0
|
|
673
|
0
|
|
|
|
|
0
|
@{$pp->{tags}};
|
674
|
0
|
|
|
|
|
0
|
$pp->{unumtags}=scalar(@{$pp->{utags}});
|
|
0
|
|
|
|
|
0
|
|
675
|
0
|
|
|
|
|
0
|
$pp;
|
676
|
|
|
|
|
|
|
}
|
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
sub start_constant_read
|
679
|
|
|
|
|
|
|
{
|
680
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
681
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
682
|
0
|
|
|
|
|
0
|
$self->_setdefaults(\%p);
|
683
|
|
|
|
|
|
|
|
684
|
0
|
|
|
|
|
0
|
my $cmd = $self->_cmd_start_constant_read(%p);
|
685
|
0
|
0
|
|
|
|
0
|
$self->_sendpacket($cmd)
|
686
|
|
|
|
|
|
|
or die "Couldn't read command: $!\n";
|
687
|
0
|
|
|
|
|
0
|
$self->{_constant_read}{$p{node}}=1;
|
688
|
|
|
|
|
|
|
}
|
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
sub _cmd_start_constant_read
|
691
|
|
|
|
|
|
|
{
|
692
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
693
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
694
|
|
|
|
|
|
|
|
695
|
0
|
|
|
|
|
0
|
$self->_setdefaults(\%p);
|
696
|
|
|
|
|
|
|
# $antflag{$_}=1
|
697
|
|
|
|
|
|
|
# foreach grep { defined } ($p{antenna1}||$p{antenna}||MATRICS_ANT_1,
|
698
|
|
|
|
|
|
|
# @$p{qw(antenna2 antenna3 antenna4)});
|
699
|
0
|
0
|
0
|
|
|
0
|
$self->_makepacket(%p,
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
700
|
|
|
|
|
|
|
cmd => 0x25,
|
701
|
|
|
|
|
|
|
data => pack("CCCCCCCCCCCCa8",
|
702
|
|
|
|
|
|
|
$p{antenna1}||$p{antenna}||0,
|
703
|
|
|
|
|
|
|
$p{antenna2}||0,
|
704
|
|
|
|
|
|
|
$p{antenna3}||0,
|
705
|
|
|
|
|
|
|
$p{antenna4}||0,
|
706
|
|
|
|
|
|
|
$p{antenna1_power}||0xff,
|
707
|
|
|
|
|
|
|
$p{antenna2_power}||$p{antenna2}?0xff:0,
|
708
|
|
|
|
|
|
|
$p{antenna3_power}||$p{antenna3}?0xff:0,
|
709
|
|
|
|
|
|
|
$p{antenna4_power}||$p{antenna4}?0xff:0,
|
710
|
|
|
|
|
|
|
$p{dwell_time}||150,
|
711
|
|
|
|
|
|
|
$p{channel}||8,
|
712
|
|
|
|
|
|
|
$p{maskbits}||0,$p{masktype}||0,
|
713
|
|
|
|
|
|
|
$p{mask}||"\0\0\0\0\0\0\0\0",
|
714
|
|
|
|
|
|
|
),
|
715
|
|
|
|
|
|
|
);
|
716
|
|
|
|
|
|
|
}
|
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub _epc_parsetags
|
719
|
|
|
|
|
|
|
{
|
720
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
721
|
0
|
|
|
|
|
0
|
my($count,$dc,%tagprops)=@_;
|
722
|
0
|
|
|
|
|
0
|
my @tags;
|
723
|
|
|
|
|
|
|
|
724
|
0
|
|
|
|
|
0
|
foreach my $i (1..$count)
|
725
|
|
|
|
|
|
|
{
|
726
|
0
|
|
|
|
|
0
|
my $type = ord(substr($dc,0,1));
|
727
|
0
|
|
|
|
|
0
|
my $tag;
|
728
|
0
|
0
|
|
|
|
0
|
if ($type == 0x0C)
|
729
|
|
|
|
|
|
|
{
|
730
|
|
|
|
|
|
|
# Proprietary Matrics Tag
|
731
|
0
|
|
|
|
|
0
|
$tag = RFID::Matrics::Tag->new(id => bin2hex_little_endian(unpack("a8",substr($dc,1,8))),
|
732
|
|
|
|
|
|
|
%tagprops);
|
733
|
|
|
|
|
|
|
}
|
734
|
|
|
|
|
|
|
else
|
735
|
|
|
|
|
|
|
{
|
736
|
|
|
|
|
|
|
# EPC tag
|
737
|
0
|
|
|
|
|
0
|
$tag = RFID::EPC::Tag->new(id => bin2hex_little_endian(unpack("a12",substr($dc,0,13))),
|
738
|
|
|
|
|
|
|
%tagprops);
|
739
|
|
|
|
|
|
|
}
|
740
|
0
|
|
|
|
|
0
|
push(@tags,$tag);
|
741
|
|
|
|
|
|
|
}
|
742
|
0
|
|
|
|
|
0
|
@tags;
|
743
|
|
|
|
|
|
|
}
|
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
sub constant_read
|
746
|
|
|
|
|
|
|
{
|
747
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
748
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
749
|
|
|
|
|
|
|
|
750
|
0
|
|
|
|
|
0
|
$self->_setdefaults(\%p);
|
751
|
|
|
|
|
|
|
|
752
|
0
|
0
|
|
|
|
0
|
croak "Please call start_constant_read before constant_read\n"
|
753
|
|
|
|
|
|
|
unless ($self->{_constant_read}{$p{node}});
|
754
|
|
|
|
|
|
|
|
755
|
0
|
0
|
|
|
|
0
|
my $resp = $self->_getpacket()
|
756
|
|
|
|
|
|
|
or die "Couldn't read response: $!\n";
|
757
|
0
|
|
|
|
|
0
|
my $pr = $self->_resp_constant_read($resp);
|
758
|
0
|
|
|
|
|
0
|
return $pr;
|
759
|
|
|
|
|
|
|
}
|
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
sub _resp_constant_read
|
762
|
|
|
|
|
|
|
{
|
763
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
764
|
|
|
|
|
|
|
|
765
|
0
|
|
|
|
|
0
|
my $pp = $self->_parsepacket(@_);
|
766
|
0
|
0
|
|
|
|
0
|
if (!$pp)
|
767
|
|
|
|
|
|
|
{
|
768
|
0
|
|
|
|
|
0
|
return { numtags => 0,
|
769
|
|
|
|
|
|
|
tags => [],
|
770
|
|
|
|
|
|
|
error => $self->{error},
|
771
|
|
|
|
|
|
|
errcode => $self->{errcode},
|
772
|
|
|
|
|
|
|
};
|
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
}
|
775
|
|
|
|
|
|
|
|
776
|
0
|
0
|
|
|
|
0
|
if ($pp->{error})
|
777
|
|
|
|
|
|
|
{
|
778
|
0
|
|
|
|
|
0
|
$pp->{numtags} = 0;
|
779
|
0
|
|
|
|
|
0
|
$pp->{tags} = [];
|
780
|
0
|
|
|
|
|
0
|
return $pp;
|
781
|
|
|
|
|
|
|
}
|
782
|
0
|
|
|
|
|
0
|
my $dc = $pp->{data};
|
783
|
0
|
|
|
|
|
0
|
@$pp{qw(antenna numtags)} = unpack("CC",substr($dc,0,2,""));
|
784
|
0
|
|
|
|
|
0
|
$pp->{tags} = [$self->_parsetags($pp->{numtags},$dc)];
|
785
|
0
|
|
|
|
|
0
|
return $pp;
|
786
|
|
|
|
|
|
|
}
|
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
sub _parsetags
|
789
|
|
|
|
|
|
|
{
|
790
|
4
|
|
|
4
|
|
7
|
my $self = shift;
|
791
|
4
|
|
|
|
|
14
|
my($count,$dc,%tagprops)=@_;
|
792
|
4
|
|
|
|
|
14
|
my @tags;
|
793
|
|
|
|
|
|
|
|
794
|
4
|
|
|
|
|
20
|
foreach my $i (1..$count)
|
795
|
|
|
|
|
|
|
{
|
796
|
10
|
|
|
|
|
30
|
my $type_len_bits = unpack("C",substr($dc,0,1,""));
|
797
|
10
|
50
|
|
|
|
35
|
my $len = ($type_len_bits & 0x10) ? 12 : 8;
|
798
|
10
|
|
|
|
|
14
|
my $type = ($type_len_bits & 0x0f);
|
799
|
10
|
|
|
|
|
28
|
my $id_bits = unpack("a*",substr($dc,0,$len,""));
|
800
|
10
|
|
|
|
|
12
|
my $tag;
|
801
|
10
|
100
|
|
|
|
22
|
if ($type == 0)
|
802
|
|
|
|
|
|
|
{
|
803
|
|
|
|
|
|
|
# EPC tag
|
804
|
2
|
|
|
|
|
10
|
$tag = RFID::EPC::Tag->new(id => bin2hex_little_endian($id_bits),
|
805
|
|
|
|
|
|
|
%tagprops);
|
806
|
|
|
|
|
|
|
}
|
807
|
|
|
|
|
|
|
else
|
808
|
|
|
|
|
|
|
{
|
809
|
|
|
|
|
|
|
# Proprietary Matrics Tag
|
810
|
8
|
|
|
|
|
26
|
$tag = RFID::Matrics::Tag->new(id => bin2hex_little_endian($id_bits),
|
811
|
|
|
|
|
|
|
%tagprops);
|
812
|
|
|
|
|
|
|
}
|
813
|
10
|
|
|
|
|
136
|
push(@tags,$tag);
|
814
|
|
|
|
|
|
|
}
|
815
|
4
|
|
|
|
|
44
|
@tags;
|
816
|
|
|
|
|
|
|
}
|
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
sub stop_constant_read
|
819
|
|
|
|
|
|
|
{
|
820
|
3
|
|
|
3
|
0
|
5
|
my $self = shift;
|
821
|
3
|
|
|
|
|
18
|
my(%p)=@_;
|
822
|
|
|
|
|
|
|
|
823
|
3
|
|
|
|
|
39
|
$self->_setdefaults(\%p);
|
824
|
3
|
|
|
|
|
19
|
delete $self->{_constant_read}{$p{node}};
|
825
|
3
|
|
|
|
|
58
|
$self->_do_something(\&_cmd_stop_constant_read,\&_resp_stop_constant_read,@_);
|
826
|
|
|
|
|
|
|
}
|
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
sub _cmd_stop_constant_read
|
830
|
|
|
|
|
|
|
{
|
831
|
3
|
|
|
3
|
|
5
|
my $self = shift;
|
832
|
3
|
|
|
|
|
7
|
my(%p)=@_;
|
833
|
|
|
|
|
|
|
|
834
|
3
|
|
|
|
|
44
|
$self->_makepacket(%p,
|
835
|
|
|
|
|
|
|
cmd => 0x26,
|
836
|
|
|
|
|
|
|
data => "",
|
837
|
|
|
|
|
|
|
);
|
838
|
|
|
|
|
|
|
}
|
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
sub _resp_stop_constant_read
|
841
|
|
|
|
|
|
|
{
|
842
|
3
|
|
|
3
|
|
7
|
my $self = shift;
|
843
|
3
|
50
|
|
|
|
36
|
my $pp = $self->_parsepacket(@_)
|
844
|
|
|
|
|
|
|
or return undef;
|
845
|
3
|
|
|
|
|
12
|
return $pp;
|
846
|
|
|
|
|
|
|
}
|
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
sub stop_all_constant_read
|
849
|
|
|
|
|
|
|
{
|
850
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
851
|
|
|
|
|
|
|
|
852
|
0
|
0
|
0
|
|
|
0
|
if ($self->{_constant_read} && $self->_connected)
|
853
|
|
|
|
|
|
|
{
|
854
|
0
|
|
|
|
|
0
|
foreach my $node (keys %{$self->{_constant_read}})
|
|
0
|
|
|
|
|
0
|
|
855
|
|
|
|
|
|
|
{
|
856
|
0
|
|
|
|
|
0
|
$self->stop_constant_read(node => $node);
|
857
|
|
|
|
|
|
|
}
|
858
|
|
|
|
|
|
|
}
|
859
|
0
|
|
|
|
|
0
|
1;
|
860
|
|
|
|
|
|
|
}
|
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
sub _make_confwhich_ant
|
863
|
|
|
|
|
|
|
{
|
864
|
2
|
|
|
2
|
|
5
|
my $self = shift;
|
865
|
2
|
|
|
|
|
10
|
my($p)=@_;
|
866
|
2
|
|
|
|
|
4
|
my %antflag;
|
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
|
869
|
2
|
|
|
|
|
7
|
foreach my $a (grep { defined($p->{"antenna".$_}) }
|
|
8
|
|
|
|
|
31
|
|
870
|
|
|
|
|
|
|
(1..4))
|
871
|
|
|
|
|
|
|
{
|
872
|
0
|
|
|
|
|
0
|
$antflag{$antid{$a}} = 1;
|
873
|
|
|
|
|
|
|
}
|
874
|
2
|
50
|
|
|
|
10
|
if (!(keys %antflag))
|
875
|
|
|
|
|
|
|
{
|
876
|
2
|
|
50
|
|
|
16
|
$antflag{$p->{antenna}||MATRICS_ANT_1} = 1;
|
877
|
|
|
|
|
|
|
}
|
878
|
2
|
50
|
|
|
|
43
|
return ($antflag{MATRICS_ANT_1()}?1:0,
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
879
|
|
|
|
|
|
|
$antflag{MATRICS_ANT_2()}?1:0,
|
880
|
|
|
|
|
|
|
$antflag{MATRICS_ANT_3()}?1:0,
|
881
|
|
|
|
|
|
|
$antflag{MATRICS_ANT_4()}?1:0,
|
882
|
|
|
|
|
|
|
);
|
883
|
|
|
|
|
|
|
}
|
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
sub _make_combine_antenna_bits
|
886
|
|
|
|
|
|
|
{
|
887
|
2
|
|
|
2
|
|
5
|
my $self = shift;
|
888
|
2
|
|
|
|
|
8
|
my($p)=@_;
|
889
|
2
|
|
|
|
|
8
|
my %antbit = (
|
890
|
|
|
|
|
|
|
MATRICS_ANT_1() => 1,
|
891
|
|
|
|
|
|
|
MATRICS_ANT_2() => 2,
|
892
|
|
|
|
|
|
|
MATRICS_ANT_3() => 4,
|
893
|
|
|
|
|
|
|
MATRICS_ANT_4() => 8,
|
894
|
|
|
|
|
|
|
);
|
895
|
|
|
|
|
|
|
|
896
|
2
|
50
|
|
|
|
8
|
if (!$p->{combine_antenna_bits})
|
897
|
|
|
|
|
|
|
{
|
898
|
0
|
|
|
|
|
0
|
my $cab = 0;
|
899
|
0
|
0
|
|
|
|
0
|
if ($p->{combine_antennas})
|
900
|
|
|
|
|
|
|
{
|
901
|
0
|
|
|
|
|
0
|
$cab |= $antbit{$_}
|
902
|
0
|
|
|
|
|
0
|
foreach (@{$p->{combine_antennas}});
|
903
|
|
|
|
|
|
|
}
|
904
|
0
|
|
|
|
|
0
|
$p->{combine_antenna_bits}=chr($cab);
|
905
|
|
|
|
|
|
|
}
|
906
|
2
|
|
|
|
|
59
|
return ord($p->{combine_antenna_bits});
|
907
|
|
|
|
|
|
|
}
|
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
sub epc_readfullfield
|
910
|
|
|
|
|
|
|
{
|
911
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
912
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
913
|
0
|
|
|
|
|
0
|
$self->_setdefaults(\%p);
|
914
|
|
|
|
|
|
|
|
915
|
0
|
0
|
|
|
|
0
|
my @resp = $self->_do_something(\&_cmd_epc_readfullfield,
|
916
|
|
|
|
|
|
|
\&_resp_epc_readfullfield,
|
917
|
|
|
|
|
|
|
%p)
|
918
|
|
|
|
|
|
|
or return undef;
|
919
|
|
|
|
|
|
|
|
920
|
0
|
|
|
|
|
0
|
my $ret = shift(@resp);
|
921
|
0
|
|
|
|
|
0
|
foreach my $r (@resp)
|
922
|
|
|
|
|
|
|
{
|
923
|
0
|
|
|
|
|
0
|
$ret->{numtags} += $r->{numtags};
|
924
|
0
|
|
|
|
|
0
|
push(@{$ret->{tags}},@{$r->{tags}});
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
925
|
|
|
|
|
|
|
}
|
926
|
0
|
|
|
|
|
0
|
$ret;
|
927
|
|
|
|
|
|
|
}
|
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
sub _cmd_epc_readfullfield
|
930
|
|
|
|
|
|
|
{
|
931
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
932
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
933
|
|
|
|
|
|
|
|
934
|
0
|
|
|
|
|
0
|
$self->_makepacket(%p,
|
935
|
|
|
|
|
|
|
cmd => 0x10,
|
936
|
|
|
|
|
|
|
data => pack("C",$p{antenna}),
|
937
|
|
|
|
|
|
|
);
|
938
|
|
|
|
|
|
|
}
|
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
sub _resp_epc_readfullfield
|
941
|
|
|
|
|
|
|
{
|
942
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
943
|
0
|
0
|
|
|
|
0
|
my $pp = $self->_parsepacket(@_)
|
944
|
|
|
|
|
|
|
or return undef;
|
945
|
0
|
|
|
|
|
0
|
my $dc = $pp->{data};
|
946
|
0
|
|
|
|
|
0
|
(@$pp{qw(antenna numtags)}) = unpack("CC",substr($dc,0,2,""));
|
947
|
0
|
|
|
|
|
0
|
$pp->{tags} = [$self->_epc_parsetags($pp->{numtags},$dc)];
|
948
|
0
|
|
|
|
|
0
|
$pp;
|
949
|
|
|
|
|
|
|
}
|
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
sub epc_readfullfield_unique
|
952
|
|
|
|
|
|
|
{
|
953
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
954
|
0
|
|
|
|
|
0
|
my $ret = $self->epc_readfullfield;
|
955
|
|
|
|
|
|
|
|
956
|
0
|
|
|
0
|
|
0
|
@{$ret->{utags}} = uniq { $a->tagcmp($b) }
|
|
0
|
|
|
|
|
0
|
|
957
|
0
|
|
|
|
|
0
|
sort { $a->tagcmp($b) }
|
|
0
|
|
|
|
|
0
|
|
958
|
0
|
|
|
|
|
0
|
@{$ret->{tags}};
|
959
|
0
|
|
|
|
|
0
|
$ret->{unumtags}=scalar(@{$ret->{utags}});
|
|
0
|
|
|
|
|
0
|
|
960
|
|
|
|
|
|
|
|
961
|
0
|
|
|
|
|
0
|
$ret;
|
962
|
|
|
|
|
|
|
}
|
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
sub epc_getparamblock
|
965
|
|
|
|
|
|
|
{
|
966
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
967
|
0
|
|
|
|
|
0
|
$self->_do_something(\&_cmd_epc_getparamblock,\&_resp_epc_getparamblock,@_);
|
968
|
|
|
|
|
|
|
}
|
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
sub _cmd_epc_getparamblock
|
971
|
|
|
|
|
|
|
{
|
972
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
973
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
974
|
|
|
|
|
|
|
|
975
|
0
|
|
|
|
|
0
|
$self->_makepacket(%p,
|
976
|
|
|
|
|
|
|
cmd => 0x16,
|
977
|
|
|
|
|
|
|
data => pack("C",$p{antenna}),
|
978
|
|
|
|
|
|
|
);
|
979
|
|
|
|
|
|
|
}
|
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
sub _resp_epc_getparamblock
|
982
|
|
|
|
|
|
|
{
|
983
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
984
|
0
|
0
|
|
|
|
0
|
my $pp = $self->_parsepacket(@_)
|
985
|
|
|
|
|
|
|
or return undef;
|
986
|
|
|
|
|
|
|
|
987
|
0
|
|
|
|
|
0
|
@$pp{qw(power_level environment combine_antenna_bits protocol_speed filter_type reserved1_bits filter_bits reserved2_bits)} =
|
988
|
|
|
|
|
|
|
unpack("CCCCCa3a8a16",$pp->{data});
|
989
|
0
|
|
|
|
|
0
|
$pp;
|
990
|
|
|
|
|
|
|
}
|
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
sub epc_setparamblock
|
993
|
|
|
|
|
|
|
{
|
994
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
995
|
0
|
|
|
|
|
0
|
$self->_do_something(\&_cmd_epc_setparamblock,\&_resp_epc_setparamblock,@_);
|
996
|
|
|
|
|
|
|
}
|
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
sub _cmd_epc_setparamblock
|
999
|
|
|
|
|
|
|
{
|
1000
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
1001
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
|
1004
|
0
|
0
|
0
|
|
|
0
|
$self->_makepacket(%p,
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
1005
|
|
|
|
|
|
|
cmd => 0x15,
|
1006
|
|
|
|
|
|
|
data => pack("CCCCCCCCCa3a8a16",
|
1007
|
|
|
|
|
|
|
$self->_make_confwhich_ant(\%p), # Returns 4 bytes
|
1008
|
|
|
|
|
|
|
defined($p{power_level})?$p{power_level}:0xff,
|
1009
|
|
|
|
|
|
|
$p{environment}||0x00,
|
1010
|
|
|
|
|
|
|
$self->_make_combine_antenna_bits(\%p),
|
1011
|
|
|
|
|
|
|
$p{protocol_speeed}||0x00,
|
1012
|
|
|
|
|
|
|
$p{filter_type}||0x00,
|
1013
|
|
|
|
|
|
|
$p{reserved1_bits}||("\0"x3),
|
1014
|
|
|
|
|
|
|
$p{filter_bits}||("\0"x8),
|
1015
|
|
|
|
|
|
|
$p{reserved2_bits}||("\0"x16),
|
1016
|
|
|
|
|
|
|
)
|
1017
|
|
|
|
|
|
|
);
|
1018
|
|
|
|
|
|
|
}
|
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
sub _resp_epc_setparamblock
|
1021
|
|
|
|
|
|
|
{
|
1022
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
1023
|
|
|
|
|
|
|
|
1024
|
0
|
0
|
|
|
|
0
|
my $pp = $self->_parsepacket(@_)
|
1025
|
|
|
|
|
|
|
or return undef;
|
1026
|
|
|
|
|
|
|
}
|
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
sub epc_changeparamblock
|
1030
|
|
|
|
|
|
|
{
|
1031
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
1032
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
1033
|
|
|
|
|
|
|
|
1034
|
0
|
0
|
|
|
|
0
|
croak "changeparam: The required parameter 'antenna' is missing.\n"
|
1035
|
|
|
|
|
|
|
unless ($p{antenna});
|
1036
|
0
|
0
|
|
|
|
0
|
my $curparam = $self->epc_getparamblock(@_)
|
1037
|
|
|
|
|
|
|
or return undef;
|
1038
|
0
|
|
|
|
|
0
|
return $self->epc_setparamblock(%$curparam, @_);
|
1039
|
|
|
|
|
|
|
}
|
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
sub setnodeaddress
|
1042
|
|
|
|
|
|
|
{
|
1043
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
1044
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
1045
|
|
|
|
|
|
|
|
1046
|
0
|
|
0
|
|
|
0
|
my $node = $p{oldnode}||0xFF;
|
1047
|
0
|
0
|
0
|
|
|
0
|
if ($p{oldnode}==0xFF or !$p{oldnode})
|
1048
|
|
|
|
|
|
|
{
|
1049
|
|
|
|
|
|
|
# No response to broadcast commands, just send it.
|
1050
|
0
|
0
|
|
|
|
0
|
my $cmd = _cmd_setnodeaddress($self, @_)
|
1051
|
|
|
|
|
|
|
or return undef;
|
1052
|
0
|
0
|
|
|
|
0
|
$self->_sendpacket($cmd)
|
1053
|
|
|
|
|
|
|
or die "Couldn't write command: $!\n";
|
1054
|
0
|
|
|
|
|
0
|
return { noresponse => 1 };
|
1055
|
|
|
|
|
|
|
}
|
1056
|
|
|
|
|
|
|
else
|
1057
|
|
|
|
|
|
|
{
|
1058
|
0
|
|
|
|
|
0
|
$self->_do_something(\&_cmd_setnodeaddress,\&_resp_setnodeaddress,@_);
|
1059
|
|
|
|
|
|
|
}
|
1060
|
|
|
|
|
|
|
}
|
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
sub _cmd_setnodeaddress
|
1063
|
|
|
|
|
|
|
{
|
1064
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
1065
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
1066
|
0
|
|
|
|
|
0
|
$self->_setdefaults(\%p);
|
1067
|
|
|
|
|
|
|
|
1068
|
0
|
0
|
|
|
|
0
|
if (!$p{serialnum_bits})
|
1069
|
|
|
|
|
|
|
{
|
1070
|
0
|
0
|
|
|
|
0
|
defined($p{serialnum}) or return $self->error("Missing required parameter serialnum or serialnum_bits");
|
1071
|
0
|
|
|
|
|
0
|
$p{serialnum_bits} = hex2bin_little_endian($p{serialnum});
|
1072
|
|
|
|
|
|
|
}
|
1073
|
0
|
0
|
0
|
|
|
0
|
$p{newnode} or $p{node} or return $self->error("Missing required parameter newnode or node");
|
1074
|
|
|
|
|
|
|
|
1075
|
0
|
|
0
|
|
|
0
|
$self->_makepacket(%p,
|
|
|
|
0
|
|
|
|
|
1076
|
|
|
|
|
|
|
node => $p{oldnode}||0xFF,
|
1077
|
|
|
|
|
|
|
cmd => 0x12,
|
1078
|
|
|
|
|
|
|
data => pack("Ca8",
|
1079
|
|
|
|
|
|
|
$p{newnode}||$p{node},
|
1080
|
|
|
|
|
|
|
$p{serialnum_bits},
|
1081
|
|
|
|
|
|
|
),
|
1082
|
|
|
|
|
|
|
);
|
1083
|
|
|
|
|
|
|
}
|
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
sub _resp_setnodeaddress
|
1086
|
|
|
|
|
|
|
{
|
1087
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
1088
|
0
|
0
|
|
|
|
0
|
my $pp = $self->_parsepacket(@_)
|
1089
|
|
|
|
|
|
|
or return undef;
|
1090
|
|
|
|
|
|
|
}
|
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
sub getreaderstatus
|
1093
|
|
|
|
|
|
|
{
|
1094
|
2
|
|
|
2
|
0
|
6
|
my $self = shift;
|
1095
|
2
|
|
|
|
|
10
|
$self->_do_something(\&_cmd_getreaderstatus,\&_resp_getreaderstatus,@_);
|
1096
|
|
|
|
|
|
|
}
|
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
sub _cmd_getreaderstatus
|
1099
|
|
|
|
|
|
|
{
|
1100
|
2
|
|
|
2
|
|
11
|
my $self = shift;
|
1101
|
2
|
|
|
|
|
6
|
my(%p)=@_;
|
1102
|
|
|
|
|
|
|
|
1103
|
2
|
|
|
|
|
12
|
$self->_makepacket(%p,
|
1104
|
|
|
|
|
|
|
cmd => 0x14,
|
1105
|
|
|
|
|
|
|
);
|
1106
|
|
|
|
|
|
|
}
|
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
sub _resp_getreaderstatus
|
1109
|
|
|
|
|
|
|
{
|
1110
|
2
|
|
|
2
|
|
6
|
my $self = shift;
|
1111
|
2
|
50
|
|
|
|
8
|
my $pp = $self->_parsepacket(@_)
|
1112
|
|
|
|
|
|
|
or return undef;
|
1113
|
|
|
|
|
|
|
|
1114
|
2
|
|
|
|
|
42
|
@$pp{qw(serialnum_bits version_major version_minor version_eng
|
1115
|
|
|
|
|
|
|
reset_flag combine_antenna_bits antenna_status_bits
|
1116
|
|
|
|
|
|
|
last_error reserved)} =
|
1117
|
|
|
|
|
|
|
unpack("a8CCCCa4a4Ca11",$pp->{data});
|
1118
|
2
|
|
|
|
|
17
|
$pp->{version}=join(".",@$pp{qw(version_major version_minor version_eng)});
|
1119
|
2
|
|
|
|
|
11
|
$pp->{serialnum} = bin2hex_little_endian($pp->{serialnum_bits});
|
1120
|
2
|
|
|
|
|
9
|
$pp;
|
1121
|
|
|
|
|
|
|
}
|
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
sub getnodeaddress
|
1124
|
|
|
|
|
|
|
{
|
1125
|
2
|
|
|
2
|
0
|
8
|
my $self = shift;
|
1126
|
2
|
|
|
|
|
20
|
$self->_do_something(\&_cmd_getnodeaddress,\&_resp_getnodeaddress,@_);
|
1127
|
|
|
|
|
|
|
}
|
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
sub _cmd_getnodeaddress
|
1131
|
|
|
|
|
|
|
{
|
1132
|
2
|
|
|
2
|
|
5
|
my $self = shift;
|
1133
|
2
|
|
|
|
|
7
|
my(%p)=@_;
|
1134
|
|
|
|
|
|
|
|
1135
|
2
|
50
|
|
|
|
9
|
if (!$p{serialnum_bits})
|
1136
|
|
|
|
|
|
|
{
|
1137
|
2
|
50
|
|
|
|
11
|
defined($p{serialnum}) or return $self->error("Missing required parameter serialnum or serialnum_bits");
|
1138
|
2
|
|
|
|
|
29
|
$p{serialnum_bits} = hex2bin_little_endian($p{serialnum});
|
1139
|
|
|
|
|
|
|
}
|
1140
|
2
|
|
|
|
|
17
|
$self->_makepacket(%p,
|
1141
|
|
|
|
|
|
|
node => 0xff,
|
1142
|
|
|
|
|
|
|
cmd => 0x19,
|
1143
|
|
|
|
|
|
|
data => pack("a8",
|
1144
|
|
|
|
|
|
|
$p{serialnum_bits},
|
1145
|
|
|
|
|
|
|
),
|
1146
|
|
|
|
|
|
|
);
|
1147
|
|
|
|
|
|
|
}
|
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
sub _resp_getnodeaddress
|
1150
|
|
|
|
|
|
|
{
|
1151
|
2
|
|
|
2
|
|
4
|
my $self = shift;
|
1152
|
2
|
50
|
|
|
|
8
|
my $pp = $self->_parsepacket(@_)
|
1153
|
|
|
|
|
|
|
or return undef;
|
1154
|
|
|
|
|
|
|
}
|
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
# NOT FINISHED
|
1157
|
|
|
|
|
|
|
our %baudnum = (
|
1158
|
|
|
|
|
|
|
230400 => 0,
|
1159
|
|
|
|
|
|
|
115200 => 1,
|
1160
|
|
|
|
|
|
|
57600 => 2,
|
1161
|
|
|
|
|
|
|
38400 => 3,
|
1162
|
|
|
|
|
|
|
19200 => 4,
|
1163
|
|
|
|
|
|
|
9600 => 5,
|
1164
|
|
|
|
|
|
|
);
|
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
sub _cmd_setbaudrate
|
1168
|
|
|
|
|
|
|
{
|
1169
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
1170
|
0
|
|
|
|
|
0
|
my(%p)=@_;
|
1171
|
|
|
|
|
|
|
|
1172
|
0
|
0
|
|
|
|
0
|
if (!$p{baudrate_bits})
|
1173
|
|
|
|
|
|
|
{
|
1174
|
0
|
0
|
|
|
|
0
|
defined($p{baudrate}) or return $self->error("Missing required parameter baudrate");
|
1175
|
0
|
0
|
|
|
|
0
|
defined($p{baudrate_bits}=$baudnum{$p{baudrate}})
|
1176
|
|
|
|
|
|
|
or return $self->error("Invalid baud rate.");
|
1177
|
|
|
|
|
|
|
}
|
1178
|
0
|
|
|
|
|
0
|
$self->_makepacket(%p,
|
1179
|
|
|
|
|
|
|
cmd => 0x1D,
|
1180
|
|
|
|
|
|
|
data => pack("C",$p{baudrate_bits}),
|
1181
|
|
|
|
|
|
|
);
|
1182
|
|
|
|
|
|
|
|
1183
|
|
|
|
|
|
|
}
|
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
sub _resp_setbaudrate
|
1186
|
|
|
|
|
|
|
{
|
1187
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
1188
|
0
|
0
|
|
|
|
0
|
my $pp = $self->_parsepacket(@_)
|
1189
|
|
|
|
|
|
|
or return undef;
|
1190
|
|
|
|
|
|
|
}
|
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
sub setbaudrate
|
1193
|
|
|
|
|
|
|
{
|
1194
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
1195
|
|
|
|
|
|
|
|
1196
|
0
|
|
|
|
|
0
|
$self->_do_something(\&_cmd_setbaudrate,\&_resp_setbaudrate,@_);
|
1197
|
|
|
|
|
|
|
}
|
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
=head3 finish
|
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
Perform any cleanup tasks for the reader. In particular, shut off any
|
1203
|
|
|
|
|
|
|
constant reads that are currently running.
|
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
=cut
|
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
sub finish
|
1208
|
|
|
|
|
|
|
{
|
1209
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
1210
|
0
|
0
|
|
|
|
0
|
$self->stop_all_constant_read()
|
1211
|
|
|
|
|
|
|
or warn "Couldn't stop all constant readers: $!\n";
|
1212
|
|
|
|
|
|
|
}
|
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
sub error
|
1215
|
|
|
|
|
|
|
{
|
1216
|
0
|
|
|
0
|
0
|
0
|
my $self = shift;
|
1217
|
0
|
|
|
|
|
0
|
my($em,$ec)=@_;
|
1218
|
|
|
|
|
|
|
|
1219
|
0
|
|
|
|
|
0
|
$self->{error}=$em;
|
1220
|
0
|
0
|
|
|
|
0
|
$self->{errcode}=defined($ec)?$ec:1;
|
1221
|
0
|
|
|
|
|
0
|
$self->debug("Error: $em\n");
|
1222
|
0
|
|
|
|
|
0
|
return undef;
|
1223
|
|
|
|
|
|
|
}
|
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
# Convert a hex string to binary, LSB first
|
1226
|
|
|
|
|
|
|
sub hex2bin_little_endian
|
1227
|
|
|
|
|
|
|
{
|
1228
|
2
|
|
|
2
|
0
|
5
|
my $hex = $_[0];
|
1229
|
2
|
|
|
|
|
7
|
$hex =~ tr/0-9a-fA-F//cd;
|
1230
|
2
|
|
|
|
|
30
|
pack("C*",map { hex } reverse unpack("a2"x(length($hex)/2),$hex));
|
|
16
|
|
|
|
|
40
|
|
1231
|
|
|
|
|
|
|
}
|
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
sub hex2bin_big_endian
|
1234
|
|
|
|
|
|
|
{
|
1235
|
0
|
|
|
0
|
0
|
0
|
my $hex = $_[0];
|
1236
|
0
|
|
|
|
|
0
|
$hex =~ tr/0-9a-fA-F//cd;
|
1237
|
0
|
|
|
|
|
0
|
pack("C*",map { hex } unpack("a2"x(length($hex)/2),$hex));
|
|
0
|
|
|
|
|
0
|
|
1238
|
|
|
|
|
|
|
}
|
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
sub bin2hex_little_endian
|
1241
|
|
|
|
|
|
|
{
|
1242
|
12
|
|
|
12
|
0
|
100
|
unpack("H*",pack("C*",reverse(unpack("C*",$_[0]))));
|
1243
|
|
|
|
|
|
|
}
|
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
sub bin2hex_big_endian
|
1246
|
|
|
|
|
|
|
{
|
1247
|
0
|
|
|
0
|
0
|
|
unpack("H*",$_[0]);
|
1248
|
|
|
|
|
|
|
# my @a = split(//,$_[0]);
|
1249
|
|
|
|
|
|
|
# sprintf "%02x" x scalar(@a), map {ord} @a;
|
1250
|
|
|
|
|
|
|
}
|
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
=head2 Properties
|
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
=head3 Antenna
|
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
The default antenna for get operations; see also
|
1257
|
|
|
|
|
|
|
L. This defaults to 1 if it is not
|
1258
|
|
|
|
|
|
|
set.
|
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
=head3 AntennaSequence
|
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
An arrayref of the antennas that should be queried, and in what order.
|
1263
|
|
|
|
|
|
|
The antenna names for a 4-port Matrics reader are simply 1, 2, 3, and
|
1264
|
|
|
|
|
|
|
4. For example:
|
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
$reader->set(AntennaSequence => [1,2,3,4]);
|
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
The default AntennaSequence is the L.
|
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
=head3 Debug
|
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
Control the amount debugging information sent to C. A higher
|
1273
|
|
|
|
|
|
|
value for this property will cause more information to be output.
|
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
=head3 UniqueTags
|
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
A boolean value controlling whether duplicate tags should be removed
|
1278
|
|
|
|
|
|
|
from the list returned by L.
|
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
=head3 Environment
|
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
How long an antenna should try to read tags during a
|
1283
|
|
|
|
|
|
|
L command, between 0 and 4. 0 will read for a very
|
1284
|
|
|
|
|
|
|
short time, and is appropriate for environments where tags come and go
|
1285
|
|
|
|
|
|
|
very quickly, and it's OK if you miss a tag somtimes. 4 will read for
|
1286
|
|
|
|
|
|
|
longer, and is appropriate where tags stay relatively static and you
|
1287
|
|
|
|
|
|
|
want the reader to try its best to find all of them.
|
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
When this item is retreived, you get the value for the default
|
1290
|
|
|
|
|
|
|
antenna. When it's set, it's set for all of the antennas in the
|
1291
|
|
|
|
|
|
|
L. To set the level for only one
|
1292
|
|
|
|
|
|
|
antenna, use C>, where C> is the number
|
1293
|
|
|
|
|
|
|
of the antenna you'd like to set.
|
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
=head3 Node
|
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
The Matrics node address associated with this object. It defaults to
|
1298
|
|
|
|
|
|
|
4.
|
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
=head3 PowerLevel
|
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
The amount of power an antenna should use when doing a read, between 0
|
1303
|
|
|
|
|
|
|
and 255. 255 is full-power; the scale of this setting is logarithmic,
|
1304
|
|
|
|
|
|
|
so 208 is about 50% power, and 0x80 is about 25% power.
|
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
When this item is retreived, you get the value for the default
|
1307
|
|
|
|
|
|
|
antenna. When it's set, it's set for all of the antennas in the
|
1308
|
|
|
|
|
|
|
L. To set the level for only one
|
1309
|
|
|
|
|
|
|
antenna, use C>, where C> is the number of
|
1310
|
|
|
|
|
|
|
the antenna you'd like to set.
|
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
=head3 ReaderVersion
|
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
The software version running on this reader, as a string. Cannot be
|
1315
|
|
|
|
|
|
|
L.
|
1316
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
=head3 ReaderSerialNum
|
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
The serial number of this reader, as a string. Cannot be L.
|
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
=head1 SEE ALSO
|
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
L, L,
|
1324
|
|
|
|
|
|
|
L, L,
|
1325
|
|
|
|
|
|
|
L.
|
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
=head1 AUTHOR
|
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
Scott Gifford ,
|
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
Copyright (C) 2004 The Regents of the University of Michigan.
|
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
See the file LICENSE included with the distribution for license
|
1334
|
|
|
|
|
|
|
information.
|
1335
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
=cut
|
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
1;
|