File Coverage

blib/lib/Device/RFXCOM/RX.pm
Criterion Covered Total %
statement 116 118 98.3
branch 37 42 88.1
condition 10 15 66.6
subroutine 19 19 100.0
pod 3 3 100.0
total 185 197 93.9


line stmt bran cond sub pod time code
1 4     4   146967 use strict;
  4         9  
  4         157  
2 4     4   22 use warnings;
  4         8  
  4         221  
3             package Device::RFXCOM::RX;
4             $Device::RFXCOM::RX::VERSION = '1.142010';
5             # ABSTRACT: Module to support RFXCOM RF receiver
6              
7              
8 4     4   94 use 5.006;
  4         26  
  4         216  
9             use constant {
10 4         325 DEBUG => $ENV{DEVICE_RFXCOM_RX_DEBUG},
11             TESTING => $ENV{DEVICE_RFXCOM_RX_TESTING},
12 4     4   20 };
  4         6  
13 4     4   20 use base 'Device::RFXCOM::Base';
  4         14  
  4         2524  
14 4     4   27 use Carp qw/croak/;
  4         8  
  4         203  
15 4     4   22 use IO::Handle;
  4         6  
  4         132  
16 4     4   18 use IO::Select;
  4         9  
  4         123  
17 4     4   2750 use Device::RFXCOM::Response;
  4         9  
  4         133  
18             use Module::Pluggable
19 4         31 search_path => 'Device::RFXCOM::Decoder',
20 4     4   3358 instantiate => 'new';
  4         43067  
21              
22              
23             sub new {
24 8     8 1 5239 my $pkg = shift;
25 8         84 $pkg->SUPER::_new(device => '/dev/rfxcom-rx', @_);
26             }
27              
28             sub _init {
29 2     2   5 my ($self, $cb) = @_;
30 2         21 $self->_write(hex => 'F020', desc => 'version check');
31 2         15 $self->_write(hex => 'F02A', desc => 'enable all possible receiving modes');
32 2   66     22 $self->_write(hex => 'F041', desc => 'variable length with visonic',
33             callback => $cb || $self->{init_callback});
34 2         7 $self->{init} = 1;
35             }
36              
37              
38             sub read {
39 8     8 1 599031 my ($self, $timeout) = @_;
40 8         55 my $res = $self->read_one(\$self->{_buf});
41 8 100       51 return $res if (defined $res);
42 5 100       30 $self->_discard_buffer_check() if ($self->{_buf} ne '');
43 5         25 $self->_discard_dup_cache_check();
44 5         37 my $fh = $self->filehandle;
45 5         125 my $sel = IO::Select->new($fh);
46 6         363 REDO:
47             my $start = $self->_time_now;
48 6 100       30 $sel->can_read($timeout) or return;
49 5         336 my $bytes = sysread $fh, $self->{_buf}, 2048, length $self->{_buf};
50 5         22 $self->{_last_read} = $self->_time_now;
51 5 100       23 $timeout -= $self->{_last_read} - $start if (defined $timeout);
52 5 50       22 unless ($bytes) {
53 0 0       0 croak defined $bytes ? 'closed' : 'error: '.$!;
54             }
55 5         21 $res = $self->read_one(\$self->{_buf});
56 5 100       35 $self->_write_now() if (defined $res);
57 5 100       29 goto REDO unless ($res);
58 4         36 return $res;
59             }
60              
61              
62              
63             sub read_one {
64 98     98 1 2074380 my ($self, $rbuf) = @_;
65 98 100       294 return unless ($$rbuf);
66              
67 95         165 print STDERR "rbuf=", (unpack "H*", $$rbuf), "\n" if DEBUG;
68 95         291 my $header_byte = unpack "C", $$rbuf;
69 95         328 my %result =
70             (
71             header_byte => $header_byte,
72             type => 'unknown',
73             );
74 95         250 $result{master} = !($header_byte&0x80);
75 95         143 my $bits = $header_byte & 0x7f;
76 95         124 my $msg = '';
77 95         105 my @bytes;
78 95 100 100     721 if (exists $self->{_waiting} && $header_byte == 0x4d) {
    100 66        
    100 66        
79              
80 1         2 print STDERR "got version check response\n" if DEBUG;
81 1         3 $msg = $$rbuf;
82 1         1 substr $msg, 0, 1, '';
83 1         3 $$rbuf = '';
84 1         2 $result{type} = 'version';
85 1         4 @bytes = unpack 'C*', $msg;
86              
87             } elsif (exists $self->{_waiting} &&
88             ( $header_byte == 0x2c || $header_byte == 0x41 )) {
89              
90 2         3 print STDERR "got mode response\n" if DEBUG;
91 2         5 substr $$rbuf, 0, 1, '';
92 2         5 $result{type} = 'mode';
93              
94             } elsif ($bits == 0) {
95              
96 1         3 print STDERR "got empty message\n" if DEBUG;
97 1         4 substr $$rbuf, 0, 1, '';
98 1         4 $result{type} = 'empty';
99              
100             } else {
101              
102 91         264 my $length = $bits / 8;
103              
104 91         117 print STDERR "bits=$bits length=$length\n" if DEBUG;
105 91 50       361 return if (length $$rbuf < 1 + $length);
106              
107 91 100       214 if ($length != int $length) {
108 28         38 $length = 1 + int $length;
109             }
110              
111 91         217 $msg = substr $$rbuf, 0, 1 + $length, ''; # message from buffer
112 91         137 substr $msg, 0, 1, '';
113 91         291 @bytes = unpack 'C*', $msg;
114              
115 91         258 $result{key} = $bits.'!'.$msg;
116 91         228 my $entry = $self->_cache_get(\%result);
117 91 100       222 if ($entry) {
118 2         5 print STDERR "using cache entry\n" if DEBUG;
119 2         4 @result{qw/messages type/} = @{$entry->{result}}{qw/messages type/};
  2         13  
120 2         10 $self->_cache_set(\%result);
121             } else {
122 89         200 foreach my $decoder (@{$self->{plugins}}) {
  89         247  
123 702 100       3597 my $matched = $decoder->decode($self, $msg, \@bytes, $bits, \%result)
124             or next;
125 61         899 ($result{type} = lc ref $decoder) =~ s/.*:://;
126 61         124 last;
127             }
128 89         404 $self->_cache_set(\%result);
129             }
130             }
131              
132 95         329 @result{qw/data bytes/} = ($msg, \@bytes);
133 95         748 return Device::RFXCOM::Response->new(%result);
134             }
135              
136             sub _cache_get {
137 96     96   158 my ($self, $result) = @_;
138 96         296 $self->{_cache}->{$result->{key}};
139             }
140              
141             sub _cache_set {
142 93     93   220 my ($self, $result) = @_;
143 93 100       493 return if ($result->{dont_cache});
144 90         204 my $entry = $self->{_cache}->{$result->{key}};
145 90 100       199 if ($entry) {
146 4 100       25 $result->{duplicate} = 1 if ($self->_cache_is_duplicate($entry));
147 4         18 $entry->{t} = $self->_time_now;
148 4         12 return $entry;
149             }
150 86         327 $self->{_cache}->{$result->{key}} =
151             {
152             result => $result,
153             t => $self->_time_now,
154             };
155             }
156              
157             sub _cache_is_duplicate {
158 5     5   12 my ($self, $entry) = @_;
159 5         23 ($self->_time_now - $entry->{t}) < $self->{dup_timeout};
160             }
161              
162             sub _discard_buffer_check {
163 1     1   2 my $self = shift;
164 1 50 33     15 if ($self->{_buf} ne '' &&
165             $self->{_last_read} < ($self->_time_now - $self->{discard_timeout})) {
166 0         0 $self->{_buf} = '';
167             }
168             }
169              
170             sub _discard_dup_cache_check {
171 5     5   12 my $self = shift;
172 5 100       51 if ($self->{_last_read} < ($self->_time_now - $self->{dup_timeout})) {
173 2         11 $self->{_cache} = {};
174             }
175             }
176              
177             1;
178              
179             __END__