File Coverage

blib/lib/UAV/Pilot/ArdupilotProtocol/Packet.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2014 Timm Murray
2             # All rights reserved.
3             #
4             # Redistribution and use in source and binary forms, with or without
5             # modification, are permitted provided that the following conditions are met:
6             #
7             # * Redistributions of source code must retain the above copyright notice,
8             # this list of conditions and the following disclaimer.
9             # * Redistributions in binary form must reproduce the above copyright
10             # notice, this list of conditions and the following disclaimer in the
11             # documentation and/or other materials provided with the distribution.
12             #
13             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23             # POSSIBILITY OF SUCH DAMAGE.
24             package UAV::Pilot::ArdupilotProtocol::Packet;
25 1     1   2153 use v5.14;
  1         3  
  1         42  
26 1     1   622 use Moose::Role;
  0            
  0            
27              
28              
29             use constant _USE_DEFAULT_BUILDARGS => 1;
30             use constant _PACKET_QUEUE_MAP_KEY_SEPERATOR => '|';
31              
32              
33             has 'preamble' => (
34             is => 'rw',
35             isa => 'Int',
36             default => 0x3444,
37             );
38             has 'version' => (
39             is => 'rw',
40             isa => 'Int',
41             default => 0x00,
42             );
43             has 'checksum1' => (
44             is => 'ro',
45             isa => 'Int',
46             writer => '_set_checksum1',
47             );
48             has 'checksum2' => (
49             is => 'ro',
50             isa => 'Int',
51             writer => '_set_checksum2',
52             );
53             has '_is_checksum_clean' => (
54             is => 'rw',
55             isa => 'Bool',
56             default => 0,
57             );
58             requires 'payload_length';
59             requires 'message_id';
60             requires 'payload_fields';
61             requires 'payload_fields_length';
62              
63             with 'UAV::Pilot::Logger';
64              
65              
66             before 'BUILDARGS' => sub {
67             my ($class, $args) = @_;
68             return $args if delete $args->{fresh};
69             return $args unless $class->_USE_DEFAULT_BUILDARGS;
70              
71             my $payload = delete $args->{payload};
72             my @payload = @$payload;
73              
74             my %payload_fields_length = %{ $class->payload_fields_length };
75             foreach my $field (@{ $class->payload_fields }) {
76             $class->_logger->warn(
77             "No entry for '$field' in $class->payload_fields_length"
78             ) unless exists $payload_fields_length{$field};
79             my $length = $payload_fields_length{$field} // 1;
80              
81             my $value = 0;
82             foreach (1 .. $length) {
83             $value <<= 8;
84             $value |= shift @payload;
85             }
86              
87             $args->{$field} = $value;
88             }
89              
90             return $args;
91             };
92              
93              
94             sub write
95             {
96             my ($self, $fh) = @_;
97             $self->make_checksum_clean;
98              
99             my $packet = $self->make_byte_vector;
100             $fh->print( $packet );
101              
102             return 1;
103             }
104              
105             sub make_byte_vector
106             {
107             my ($self) = @_;
108             my $packet = pack 'n C*',
109             $self->preamble,
110             $self->payload_length,
111             $self->message_id,
112             $self->version,
113             $self->get_ordered_payload_value_bytes,
114             $self->checksum1,
115             $self->checksum2;
116             return $packet;
117             }
118              
119             sub get_ordered_payload_values
120             {
121             my ($self) = @_;
122             return map $self->$_, @{ $self->payload_fields };
123             }
124              
125             sub get_ordered_payload_value_bytes
126             {
127             my ($self) = @_;
128             my @bytes;
129             my %payload_fields_length = %{ $self->payload_fields_length };
130              
131             foreach my $field (@{ $self->payload_fields }) {
132             $self->_logger->warn(
133             "No entry for '$field' in $self->payload_fields_length"
134             ) unless exists $payload_fields_length{$field};
135             my $length = $payload_fields_length{$field} // 1;
136              
137             my $raw_value = $self->$field;
138             my @raw_bytes;
139             foreach (1 .. $length) {
140             if( defined $raw_value) {
141             my $value = $raw_value & 0xFF;
142             push @raw_bytes, $value;
143             $raw_value >>= 8;
144             }
145             else {
146             push @raw_bytes, 0;
147             }
148             }
149              
150             push @bytes, reverse @raw_bytes;
151             }
152              
153             return @bytes;
154             }
155              
156             sub _calc_checksum
157             {
158             my ($self) = @_;
159             my @data = (
160             $self->payload_length,
161             $self->message_id,
162             $self->version,
163             $self->get_ordered_payload_value_bytes,
164             );
165              
166             my ($check1, $check2) = UAV::Pilot->checksum_fletcher8( @data );
167             $self->_set_checksum1( $check1 );
168             $self->_set_checksum2( $check2 );
169             return 1;
170             }
171              
172             sub make_checksum_clean
173             {
174             my ($self) = @_;
175             return 1 if $self->_is_checksum_clean;
176             $self->_calc_checksum;
177             $self->_is_checksum_clean( 1 );
178             return 1;
179             }
180              
181             sub make_packet_queue_map_key
182             {
183             my ($self) = @_;
184             # NOTE: any changes here must be reflected in
185             # Packet::Ack::make_ack_packet_queue_key()
186             my $key = join( $self->_PACKET_QUEUE_MAP_KEY_SEPERATOR,
187             $self->message_id,
188             $self->checksum1,
189             $self->checksum2,
190             );
191             return $key;
192             }
193              
194              
195             sub _make_checksum_unclean
196             {
197             my ($self) = @_;
198             $self->_is_checksum_clean( 0 );
199             return 1;
200             }
201              
202              
203             1;
204             __END__