File Coverage

blib/lib/Ekahau.pm
Criterion Covered Total %
statement 3 3 100.0
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 4 4 100.0


line stmt bran cond sub pod time code
1             package Ekahau;
2 5     5   26616 use base 'Ekahau::Base'; our $VERSION = $Ekahau::Base::VERSION;
  5         14  
  5         15662  
3              
4             # Written by Scott Gifford
5             # Copyright (C) 2004 The Regents of the University of Michigan.
6             # See the file LICENSE included with the distribution for license
7             # information.
8              
9             use warnings;
10             use strict;
11              
12             =head1 NAME
13              
14             Ekahau - Synchronous interface to Ekahau location sensing system
15              
16             =head1 SYNOPSIS
17              
18             The C class provides a straightforward synchronous interface
19             to the Ekahau location sensing system's YAX protocol. The YAX
20             protocol itself is asynchronous, so this module tries its best to hide
21             that from you.
22              
23             This class inherits from L, and you can use methods from
24             that class. An alternative, event-driven interface is provided by
25             L.
26              
27             =head1 DESCRIPTION
28              
29             This class implements methods for querying the Ekahau Positioning
30             Engine, and processing the responses. Each C object
31             represents a connection to the Ekahau server. Some methods send
32             queries to the server, others receive responses, and still others do
33             both. This class tries to hide the asynchronousness of the YAX
34             protocol from you.
35              
36             The basic mechanism allows you to send requests, and wait for
37             responses. If other responses unrelated to the one you're waiting for
38             come in, they will be queued and sent to you when you ask for them.
39             Some methods will first send a request then wait for the response,
40             handling all of the details internally.
41              
42             =head2 Constructor
43              
44             This classes uses L as its constructor.
45              
46             =head2 Methods
47              
48             =head3 getresponse ( [ $tags ], [ $cmds ] )
49              
50             Get the next L matching the given tags and commands.
51             If you specify neither tags nor commands, the next response will be
52             returned. If you specify just one of the two, responses will only be
53             matched against the one specified. If you specify both, responses
54             must match one of the tags, and one of the commands.
55              
56             Both C<$tags> and C<$cmds> should be list references.
57              
58             Examples:
59              
60             $obj->getresponse(); # Next response
61             $obj->getresponse(['LOC','AREA']); # Next response with LOC or AREA tag
62             $obj->getresponse([],['DEVICE_LIST']); # Next DEVICE_LIST response
63              
64             =cut
65              
66             sub getresponse
67             {
68             my $self = shift;
69             my ($tags,$cmds)=@_;
70              
71             my $tagmap = (($tags and @$tags) ? { map { $_ => 1 } @$tags } : undef);
72             my $cmdmap = (($cmds and @$cmds) ? { map { $_ => 1 } @$cmds } : undef);
73              
74             # See if we have something on the queue already
75             if ($self->{_q})
76             {
77             foreach my $i (0..$#{$self->{_q}})
78             {
79             if (_respmatch($self->{_q}[$i],$tagmap,$cmdmap))
80             {
81             return splice(@{$self->{_q}},$i,1);
82             }
83             }
84             }
85              
86             # Wait until we get something, or the timeout expires.
87             my $started = time;
88             while(1)
89             {
90             while (my $resp = $self->getpending)
91             {
92             return $resp if (_respmatch($resp,$tagmap,$cmdmap));
93             push @{$self->{_q}},$resp;
94             }
95             # See if we timed out.
96             $self->can_read($self->{_timeout}?($self->{_timeout}-(time-$started)):0)
97             or return undef;
98            
99             $self->readsome()
100             or return undef;
101             }
102             }
103              
104             =head3 gettaggedresponse ( @tags )
105              
106             Get the next L matching any of the given tags. This
107             is exactly equivalent to:
108              
109             $self->getresponse([@_]);
110              
111             =cut
112              
113             sub gettaggedresponse
114             {
115             my $self = shift;
116             $self->getresponse([@_]);
117             }
118              
119             sub _respmatch
120             {
121             my($resp,$tagmap,$cmdmap) = @_;
122             return ((!$tagmap and !$cmdmap)
123             or (!$cmdmap and $resp->{tag} and $tagmap->{$resp->{tag}})
124             or (!$tagmap and $resp->{cmd} and $cmdmap->{$resp->{cmd}})
125             or ($resp->{cmd} and $resp->{tag} and $tagmap->{$resp->{tag}} and $cmdmap->{$resp->{cmd}}));
126              
127             }
128              
129             =head3 get_device_list ( )
130              
131             Get a list of devices currently detected on the Ekahau system. The
132             response is returned as a reference to a list of numeric IDs. On
133             error, this method will return C.
134              
135             =cut
136              
137             sub get_device_list
138             {
139             my $self = shift;
140              
141             my $tag = $self->request_device_list
142             or return undef;
143             my $resp = $self->gettaggedresponse($tag)
144             or return undef;
145            
146             if ($resp->error)
147             {
148             return $self->reterr("GET_DEVICE_LIST failed: ".$resp->error_description);
149             }
150             elsif ($resp->type ne 'DeviceList')
151             {
152             return $self->reterr("GET_DEVICE_LIST failed: unexpected response!");
153             }
154              
155             return [$resp->devices];
156             }
157              
158             =head3 get_device_properties ( $device_id )
159              
160             Get the properties for the device with the given ID. Returns an
161             L object.
162              
163             =cut
164              
165             sub get_device_properties
166             {
167             my $self = shift;
168              
169             my $tag = $self->request_device_properties(@_)
170             or return undef;
171             my $resp = $self->gettaggedresponse($tag)
172             or return undef;
173              
174             if ($resp->error)
175             {
176             return $self->reterr("GET_DEVICE_PROPERTIES failed: ".$resp->error_description);
177             }
178             elsif ($resp->type ne 'DeviceProperties')
179             {
180             return $self->reterr("GET_DEVICE_PROPERTIES failed: unexpected response of type ".$resp->type."!");
181             }
182            
183             $resp;
184             }
185              
186             =head3 get_location_context ( $area_id )
187              
188             Get the properties of the area with the given ID. Returns an
189             L object.
190              
191             =cut
192              
193             sub get_location_context
194             {
195             my $self = shift;
196              
197             my $tag = $self->request_location_context(@_)
198             or return undef;
199             my $resp = $self->gettaggedresponse($tag)
200             or return undef;
201              
202             if ($resp->error)
203             {
204             $self->{err} = "GET_CONTEXT failed: ".$resp->error_description;
205             return undef;
206             }
207             elsif ($resp->type ne 'LocationContext')
208             {
209             $self->{err} = "GET_CONTEXT failed: unexpected response!";
210             return undef;
211             }
212            
213             $resp;
214             }
215              
216             =head3 get_map_image ( $area_id )
217              
218             Get a map of the area with the given ID. Returns an
219             L object.
220              
221             =cut
222              
223             sub get_map_image
224             {
225             my $self = shift;
226              
227             my $tag = $self->request_map_image(@_)
228             or return undef;
229             my $resp = $self->gettaggedresponse($tag)
230             or return undef;
231              
232             if ($resp->error)
233             {
234             return $self->reterr("GET_MAP failed: ".$resp->error_description);
235             }
236             elsif ($resp->type ne 'MapImage')
237             {
238             return $self->reterr("GET_MAP failed: unexpected response!");
239             }
240            
241             $resp;
242             }
243              
244             =head3 get_all_areas ( )
245              
246             Returns a list of all logical areas, as an
247             L object.
248              
249             =cut
250              
251             sub get_all_areas
252             {
253             my $self = shift;
254              
255             my $tag = $self->request_all_areas(@_)
256             or return undef;
257             my $resp = $self->gettaggedresponse($tag)
258             or return undef;
259              
260             if ($resp->error)
261             {
262             return $self->reterr("GET_LOGICAL_AREAS failed: ".$resp->error_description);
263             }
264             elsif ($resp->type ne 'AreaList')
265             {
266             return $self->reterr("GET_LOGICAL_AREAS failed: unexpected response!");
267             }
268            
269             $resp;
270             }
271              
272              
273             =head3 start_location_track ( [ $properties ], $device)
274              
275             Start a continuous query tracking the location of the given device.
276             An optional hash reference containing properties for the query can be
277             given; see L for details.
278              
279             You can get results with L or L.
280              
281             =cut
282              
283             sub start_location_track
284             {
285             my $self = shift;
286             my %p = ref $_[0] ? %{ (shift) } : ();
287             $p{Tag} = 'LOC';
288             $self->SUPER::start_location_track(\%p,@_);
289             }
290              
291             =head3 stop_location_track ($device)
292              
293             Stop a continuous query tracking the location of the given device.
294              
295             =cut
296              
297             sub stop_location_track
298             {
299             my $self = shift;
300            
301             my $tag = $self->request_stop_location_track(@_)
302             or return undef;
303             my $resp = $self->gettaggedresponse($tag)
304             or return undef;
305              
306             if ($resp->error)
307             {
308             return $self->reterr("STOP_LOCATION_TRACK failed: ".$resp->error_description);
309             }
310             elsif ($resp->type ne 'StopLocationTrackOK')
311             {
312             return $self->reterr("STOP_LOCATION_TRACK failed: unexpected response '".$resp->type."!");
313             }
314              
315             $resp;
316             }
317              
318              
319             =head3 next_location ( )
320              
321             Return the next location for any of the continuous queries started by
322             calls to L. The returned value will be an
323             L object, or C on error.
324              
325             =cut
326              
327             sub next_location
328             {
329             my $self = shift;
330              
331             my $r = $self->gettaggedresponse('LOC')
332             or return undef;
333            
334             if ($r->{cmd} ne 'LOCATION_ESTIMATE')
335             {
336             return $self->reterr("LOCATION_ESTIMATE failed: $r->{cmd} @{$r->{args}}");
337             }
338             return $r;
339             }
340              
341             =head3 start_area_track ( [ $properties ], $device_id )
342              
343             Start a continuous query tracking the area where the given device is
344             located. An optional hash reference containing properties for the
345             query can be given; see L for details.
346              
347             You can get results with L or L.
348              
349             =cut
350              
351             sub start_area_track
352             {
353             my $self = shift;
354             my %p = ref $_[0] ? %{ (shift) } : ();
355             $p{Tag} = 'AREA';
356              
357             $self->SUPER::start_area_track(\%p,@_);
358             }
359              
360             =head3 stop_area_track ($device)
361              
362             Stop a continuous query tracking the area of the given device.
363              
364             =cut
365              
366             sub stop_area_track
367             {
368             my $self = shift;
369            
370             my $tag = $self->request_stop_area_track(@_)
371             or return undef;
372             my $resp = $self->gettaggedresponse($tag)
373             or return undef;
374              
375             if ($resp->error)
376             {
377             return $self->reterr("STOP_AREA_TRACK failed: ".$resp->error_description);
378             }
379             elsif ($resp->type ne 'StopAreaTrackOK')
380             {
381             return $self->reterr("STOP_AREA_TRACK failed: unexpected response '".$resp->type."'!");
382             }
383              
384             $resp;
385             }
386              
387              
388             =head3 next_area ( )
389              
390             Return the next area for any of the continuous queries started by
391             calls to L. The returned value will be an
392             L object, or C on error.
393              
394             =cut
395              
396             sub next_area
397             {
398             my $self = shift;
399             my $r = $self->gettaggedresponse('AREA');
400            
401             if ($r->{cmd} ne 'AREA_ESTIMATE')
402             {
403             return $self->reterr("AREA_ESTIMATE failed: $r->{cmd} @{$r->{args}}");
404             }
405             return $r;
406             }
407              
408             =head3 start_track ( [ $properties ], $device_id )
409              
410             Start tracking both the location and the area of the given device, by
411             calling the L and L methods.
412              
413             An optional hash reference containing properties for the query can be
414             given. These properties will be passed on to both queries; if you
415             need different properties for the two queries call the methods
416             directly.
417              
418             Results can be obtained by calling the L method.
419              
420             =cut
421              
422             sub start_track
423             {
424             my $self = shift;
425              
426             # Use temp variables so we can return the correct result,
427             # but still make sure we start both.
428             my $e1 = $self->start_location_track(@_);
429             my $e2 = $self->start_area_track(@_);
430              
431             return $e1 and $e2;
432             }
433              
434             =head3 stop_track ( $device_id )
435              
436             Stop tracking both the location and the area of the given device, by
437             calling the L and L methods.
438              
439             =cut
440              
441             sub stop_track
442             {
443             my $self = shift;
444              
445             # Use temp variables so we can return the correct result,
446             # but still make sure we start both.
447             my $e1 = $self->stop_location_track(@_);
448             my $e2 = $self->stop_area_track(@_);
449              
450             return $e1 and $e2;
451             }
452              
453              
454             =head3 next_track ( )
455              
456             Return the next location or area for any of the continuous queries
457             started by calls to L, L, or
458             L. The returned value will be an
459             L object, or C on error.
460              
461             =cut
462              
463             sub next_track
464             {
465             my $self = shift;
466             my(%p)=@_;
467              
468             return $self->gettaggedresponse('LOC','AREA');
469             }
470              
471              
472             1;
473              
474             =head1 AUTHOR
475              
476             Scott Gifford Egifford@umich.eduE, Esgifford@suspectclass.comE
477              
478             Copyright (C) 2005 The Regents of the University of Michigan.
479              
480             See the file LICENSE included with the distribution for license
481             information.
482              
483              
484             =head1 SEE ALSO
485              
486             L, L.
487              
488             =cut