| File: | blib/lib/Bit/MorseSignals/Receiver.pm |
| Coverage: | 100.0% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Bit::MorseSignals::Receiver; | ||||||
| 2 | |||||||
| 3 | 10 10 10 | 45 19 44 | use strict; | ||||
| 4 | 10 10 10 | 56 21 51 | use warnings; | ||||
| 5 | |||||||
| 6 | 10 10 10 | 60 15 69 | use Carp qw/croak/; | ||||
| 7 | 10 10 10 | 67 16 86 | use Encode qw/decode_utf8/; | ||||
| 8 | 10 10 10 | 61 16 65 | use Storable qw/thaw/; | ||||
| 9 | |||||||
| 10 | 10 10 10 | 65 16 56 | use Bit::MorseSignals qw/:consts/; | ||||
| 11 | |||||||
| 12 - 20 | =head1 NAME Bit::MorseSignals::Receiver - Base class for Bit::MorseSignals receivers. =head1 VERSION Version 0.06 =cut | ||||||
| 21 | |||||||
| 22 | our $VERSION = '0.06'; | ||||||
| 23 | |||||||
| 24 - 40 | =head1 SYNOPSIS
use Bit::MorseSignals::Receiver;
my $pants = new Bit::MorseSignals::Receiver done => sub { print "received $_[1]!\n" };
while (...) {
my $bit = comes_from_somewhere_lets_say_signals();
$pants->push($bit);
}
=head1 DESCRIPTION
Base class for L<Bit::MorseSignals> receivers. Please refer to this module for more general information about the protocol.
Given a sequence of bits coming from the L<Bit::MorseSignals> protocol, the receiver object detects when a packet has been completed and then reconstructs the original message depending of the datatype specified in the header.
=cut | ||||||
| 41 | |||||||
| 42 | sub _check_self { | ||||||
| 43 | 5692 | 63302 | croak 'First argument isn\'t a valid ' . __PACKAGE__ . ' object' | ||||
| 44 | unless ref $_[0] and $_[0]->isa(__PACKAGE__); | ||||||
| 45 | } | ||||||
| 46 | |||||||
| 47 - 53 | =head1 METHODS =head2 C<< new < done => $cb > >> L<Bit::MorseSignals::Receiver> object constructor. With the C<'done'> option, you can specify a callback that will be triggered every time a message is completed, and in which C<$_[0]> will be the receiver object and C<$_[1]> the message received. =cut | ||||||
| 54 | |||||||
| 55 | sub new { | ||||||
| 56 | 10 | 1 | 112 | my $class = shift; | |||
| 57 | 10 | 104 | return unless $class = ref $class || $class; | ||||
| 58 | 9 | 61 | croak 'Optional arguments must be passed as key => value pairs' if @_ % 2; | ||||
| 59 | 8 | 31 | my %opts = @_; | ||||
| 60 | 8 | 45 | my $self = { | ||||
| 61 | msg => undef, | ||||||
| 62 | done => $opts{done}, | ||||||
| 63 | }; | ||||||
| 64 | 8 | 56 | bless $self, $class; | ||||
| 65 | 8 | 30 | $self->reset; | ||||
| 66 | 8 | 32 | return $self; | ||||
| 67 | } | ||||||
| 68 | |||||||
| 69 - 73 | =head2 C<push $bit> Tells the receiver that you have received the bit C<$bit>. Returns true while the message isn't completed, and C<undef> as soon as it is. =cut | ||||||
| 74 | |||||||
| 75 | sub push { | ||||||
| 76 | 5648 | 1 | 19495 | my ($self, $bit) = @_; | |||
| 77 | 5648 | 17812 | _check_self($self); | ||||
| 78 | 5646 | 22906 | if (!defined $bit) { | ||||
| 79 | 5623 | 13340 | $bit = $_; | ||||
| 80 | 5623 | 21387 | return unless defined $bit; | ||||
| 81 | } | ||||||
| 82 | 5645 | 19805 | $bit = $bit ? 1 : 0; | ||||
| 83 | |||||||
| 84 | 5645 | 25610 | if ($self->{state} == 3) { # data | ||||
| 85 | |||||||
| 86 | 5397 | 25932 | vec($self->{buf}, $self->{len}, 1) = $bit; | ||||
| 87 | 5397 | 15203 | ++$self->{len}; | ||||
| 88 | 5397 | 29758 | if ($self->{len} >= $self->{sig_len}) { | ||||
| 89 | 5249 | 12417 | my $res = 1; | ||||
| 90 | 5249 | 23556 | for (1 .. $self->{sig_len}) { | ||||
| 91 | 9731 | 87631 | if (vec($self->{buf}, $self->{len} - $_, 1) != vec($self->{sig}, $_-1, 1)) { | ||||
| 92 | 5224 | 11998 | $res = 0; | ||||
| 93 | 5224 | 10676 | last; | ||||
| 94 | } | ||||||
| 95 | } | ||||||
| 96 | 5249 | 20475 | if ($res) { | ||||
| 97 | 25 | 178 | my $base = int $self->{sig_len} / 8 + $self->{sig_len} % 8 != 0; | ||||
| 98 | 25 | 119 | substr $self->{buf}, -$base, $base, ''; | ||||
| 99 | 25 9 | 164 44 | my @demanglers = (sub { $_[0] }, \&decode_utf8, \&thaw ); | ||||
| 100 | # BM_DATA_{PLAIN, UTF8, STORABLE} | ||||||
| 101 | $self->{msg} = defined $demanglers[$self->{type}] | ||||||
| 102 | 25 | 133 | ? do { | ||||
| 103 | 24 2 | 148 3 | local $SIG{__DIE__} = sub { warn @_ }; | ||||
| 104 | 24 | 153 | $demanglers[$self->{type}]->($self->{buf}) | ||||
| 105 | } | ||||||
| 106 | : $self->{buf}; | ||||||
| 107 | 24 | 386 | $self->reset; | ||||
| 108 | 24 | 191 | $self->{done}->($self, $self->{msg}) if $self->{done}; | ||||
| 109 | 24 | 206 | return; | ||||
| 110 | } | ||||||
| 111 | } | ||||||
| 112 | |||||||
| 113 | } elsif ($self->{state} == 2) { # header | ||||||
| 114 | |||||||
| 115 | 75 | 385 | vec($self->{buf}, $self->{len}++, 1) = $bit; | ||||
| 116 | 75 | 363 | if ($self->{len} >= 3) { | ||||
| 117 | 25 | 168 | my $type = 2 * vec($self->{buf}, 1, 1) | ||||
| 118 | + vec($self->{buf}, 0, 1); | ||||||
| 119 | 25 | 125 | $type = BM_DATA_PLAIN if vec($self->{buf}, 2, 1); | ||||
| 120 | 25 25 | 132 112 | @{$self}{qw/state type buf len/} = (3, $type, '', 0); | ||||
| 121 | } | ||||||
| 122 | |||||||
| 123 | } elsif ($self->{state} == 1) { # end of signature | ||||||
| 124 | |||||||
| 125 | 148 | 693 | if ($self->{sig_bit} != $bit) { | ||||
| 126 | 25 | 85 | $self->{state} = 2; | ||||
| 127 | } | ||||||
| 128 | 148 | 818 | vec($self->{sig}, $self->{sig_len}++, 1) = $bit; | ||||
| 129 | |||||||
| 130 | } else { # first bit | ||||||
| 131 | |||||||
| 132 | 25 25 | 149 121 | @{$self}{qw/state sig sig_bit sig_len buf len/} | ||||
| 133 | = (1, '', $bit, 1, '', 0 ); | ||||||
| 134 | 25 | 126 | vec($self->{sig}, 0, 1) = $bit; | ||||
| 135 | |||||||
| 136 | } | ||||||
| 137 | |||||||
| 138 | 5620 | 31058 | return $self; | ||||
| 139 | } | ||||||
| 140 | |||||||
| 141 - 145 | =head2 C<reset> Resets the current receiver state, obliterating any current message being received. =cut | ||||||
| 146 | |||||||
| 147 | sub reset { | ||||||
| 148 | 35 | 1 | 119 | my ($self) = @_; | |||
| 149 | 35 | 118 | _check_self($self); | ||||
| 150 | 33 | 110 | $self->{state} = 0; | ||||
| 151 | 33 33 | 151 139 | @{$self}{qw/sig sig_bit sig_len type buf len/} = (); | ||||
| 152 | 33 | 90 | return $self; | ||||
| 153 | } | ||||||
| 154 | |||||||
| 155 - 159 | =head2 C<busy> True when the receiver is in the middle of assembling a message. =cut | ||||||
| 160 | |||||||
| 161 | sub busy { | ||||||
| 162 | 5 | 1 | 20 | my ($self) = @_; | |||
| 163 | 5 | 18 | _check_self($self); | ||||
| 164 | 3 | 20 | return $self->{state} > 0; | ||||
| 165 | } | ||||||
| 166 | |||||||
| 167 - 171 | =head2 C<msg> The last message completed, or C<undef> when no message has been assembled yet. =cut | ||||||
| 172 | |||||||
| 173 | sub msg { | ||||||
| 174 | 4 | 1 | 21 | my ($self) = @_; | |||
| 175 | 4 | 12 | _check_self($self); | ||||
| 176 | 2 | 12 | return $self->{msg}; | ||||
| 177 | } | ||||||
| 178 | |||||||
| 179 - 216 | =head1 EXPORT
An object module shouldn't export any function, and so does this one.
=head1 DEPENDENCIES
L<Carp> (standard since perl 5), L<Encode> (since perl 5.007003), L<Storable> (idem).
=head1 SEE ALSO
L<Bit::MorseSignals>, L<Bit::MorseSignals::Emitter>.
=head1 AUTHOR
Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
You can contact me by mail or on #perl @ FreeNode (vincent or Prof_Vince).
=head1 BUGS
Please report any bugs or feature requests to C<bug-bit-morsesignals-receiver at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Bit-MorseSignals>. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Bit::MorseSignals::Receiver
Tests code coverage report is available at L<http://www.profvince.com/perl/cover/Bit-MorseSignals>.
=head1 COPYRIGHT & LICENSE
Copyright 2008 Vincent Pit, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut | ||||||
| 217 | |||||||
| 218 | 1; # End of Bit::MorseSignals::Receiver | ||||||