File Coverage

blib/lib/Device/RFXCOM/RX.pm
Criterion Covered Total %
statement 115 117 98.2
branch 37 42 88.1
condition 9 15 60.0
subroutine 19 19 100.0
pod 3 3 100.0
total 183 196 93.3


line stmt bran cond sub pod time code
1 4     4   133151 use strict;
  4         10  
  4         180  
2 4     4   26 use warnings;
  4         12  
  4         294  
3             package Device::RFXCOM::RX;
4             $Device::RFXCOM::RX::VERSION = '1.163170';
5             # ABSTRACT: Module to support RFXCOM RF receiver
6              
7              
8 4     4   141 use 5.006;
  4         34  
9             use constant {
10             DEBUG => $ENV{DEVICE_RFXCOM_RX_DEBUG},
11             TESTING => $ENV{DEVICE_RFXCOM_RX_TESTING},
12 4     4   29 };
  4         8  
  4         430  
13 4     4   22 use base 'Device::RFXCOM::Base';
  4         7  
  4         2290  
14 4     4   34 use Carp qw/croak/;
  4         8  
  4         291  
15 4     4   24 use IO::Handle;
  4         9  
  4         195  
16 4     4   21 use IO::Select;
  4         8  
  4         147  
17 4     4   2636 use Device::RFXCOM::Response;
  4         9  
  4         142  
18             use Module::Pluggable
19 4         32 search_path => 'Device::RFXCOM::Decoder',
20 4     4   2639 instantiate => 'new';
  4         52489  
21              
22              
23             sub new {
24 8     8 1 3992 my $pkg = shift;
25 8         73 $pkg->SUPER::_new(device => '/dev/rfxcom-rx', @_);
26             }
27              
28             sub _init {
29 2     2   5 my ($self, $cb) = @_;
30 2         25 $self->_write(hex => 'F020', desc => 'version check');
31 2         10 $self->_write(hex => 'F02A', desc => 'enable all possible receiving modes');
32             $self->_write(hex => 'F041', desc => 'variable length with visonic',
33 2   33     15 callback => $cb || $self->{init_callback});
34 2         5 $self->{init} = 1;
35             }
36              
37              
38             sub read {
39 8     8 1 591490 my ($self, $timeout) = @_;
40 8         45 my $res = $self->read_one(\$self->{_buf});
41 8 100       41 return $res if (defined $res);
42 5 100       36 $self->_discard_buffer_check() if ($self->{_buf} ne '');
43 5         25 $self->_discard_dup_cache_check();
44 5         21 my $fh = $self->filehandle;
45 5         36 my $sel = IO::Select->new($fh);
46 6         302 REDO:
47             my $start = $self->_time_now;
48 6 100       24 $sel->can_read($timeout) or return;
49 5         277 my $bytes = sysread $fh, $self->{_buf}, 2048, length $self->{_buf};
50 5         28 $self->{_last_read} = $self->_time_now;
51 5 100       26 $timeout -= $self->{_last_read} - $start if (defined $timeout);
52 5 50       37 unless ($bytes) {
53 0 0       0 croak defined $bytes ? 'closed' : 'error: '.$!;
54             }
55 5         21 $res = $self->read_one(\$self->{_buf});
56 5 100       27 $self->_write_now() if (defined $res);
57 5 100       26 goto REDO unless ($res);
58 4         111 return $res;
59             }
60              
61              
62              
63             sub read_one {
64 98     98 1 2078812 my ($self, $rbuf) = @_;
65 98 100       333 return unless ($$rbuf);
66              
67 95         111 print STDERR "rbuf=", (unpack "H*", $$rbuf), "\n" if DEBUG;
68 95         256 my $header_byte = unpack "C", $$rbuf;
69 95         401 my %result =
70             (
71             header_byte => $header_byte,
72             type => 'unknown',
73             );
74 95         274 $result{master} = !($header_byte&0x80);
75 95         137 my $bits = $header_byte & 0x7f;
76 95         139 my $msg = '';
77 95         104 my @bytes;
78 95 100 100     617 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         4 $msg = $$rbuf;
82 1         2 substr $msg, 0, 1, '';
83 1         2 $$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         4 print STDERR "got mode response\n" if DEBUG;
91 2         38 substr $$rbuf, 0, 1, '';
92 2         5 $result{type} = 'mode';
93              
94             } elsif ($bits == 0) {
95              
96 1         2 print STDERR "got empty message\n" if DEBUG;
97 1         4 substr $$rbuf, 0, 1, '';
98 1         2 $result{type} = 'empty';
99              
100             } else {
101              
102 91         220 my $length = $bits / 8;
103              
104 91         106 print STDERR "bits=$bits length=$length\n" if DEBUG;
105 91 50       280 return if (length $$rbuf < 1 + $length);
106              
107 91 100       258 if ($length != int $length) {
108 28         52 $length = 1 + int $length;
109             }
110              
111 91         252 $msg = substr $$rbuf, 0, 1 + $length, ''; # message from buffer
112 91         163 substr $msg, 0, 1, '';
113 91         465 @bytes = unpack 'C*', $msg;
114              
115 91         365 $result{key} = $bits.'!'.$msg;
116 91         275 my $entry = $self->_cache_get(\%result);
117 91 100       187 if ($entry) {
118 2         4 print STDERR "using cache entry\n" if DEBUG;
119 2         4 @result{qw/messages type/} = @{$entry->{result}}{qw/messages type/};
  2         12  
120 2         10 $self->_cache_set(\%result);
121             } else {
122 89         109 foreach my $decoder (@{$self->{plugins}}) {
  89         262  
123 702 100       2792 my $matched = $decoder->decode($self, $msg, \@bytes, $bits, \%result)
124             or next;
125 61         666 ($result{type} = lc ref $decoder) =~ s/.*:://;
126 61         139 last;
127             }
128 89         286 $self->_cache_set(\%result);
129             }
130             }
131              
132 95         332 @result{qw/data bytes/} = ($msg, \@bytes);
133 95         706 return Device::RFXCOM::Response->new(%result);
134             }
135              
136             sub _cache_get {
137 96     96   141 my ($self, $result) = @_;
138 96         322 $self->{_cache}->{$result->{key}};
139             }
140              
141             sub _cache_set {
142 93     93   148 my ($self, $result) = @_;
143 93 100       301 return if ($result->{dont_cache});
144 90         189 my $entry = $self->{_cache}->{$result->{key}};
145 90 100       274 if ($entry) {
146 4 100       31 $result->{duplicate} = 1 if ($self->_cache_is_duplicate($entry));
147 4         20 $entry->{t} = $self->_time_now;
148 4         19 return $entry;
149             }
150             $self->{_cache}->{$result->{key}} =
151             {
152 86         308 result => $result,
153             t => $self->_time_now,
154             };
155             }
156              
157             sub _cache_is_duplicate {
158 5     5   13 my ($self, $entry) = @_;
159 5         29 ($self->_time_now - $entry->{t}) < $self->{dup_timeout};
160             }
161              
162             sub _discard_buffer_check {
163 1     1   4 my $self = shift;
164 1 50 33     96 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   18 my $self = shift;
172 5 100       28 if ($self->{_last_read} < ($self->_time_now - $self->{dup_timeout})) {
173 2         8 $self->{_cache} = {};
174             }
175             }
176              
177             1;
178              
179             __END__