Skip to content

Commit e719730

Browse files
committed
Merge branch 'error-handling'
Improve bad frame error reporting
2 parents 455fa3e + 8725b32 commit e719730

File tree

4 files changed

+97
-38
lines changed

4 files changed

+97
-38
lines changed

bin/xbee-daemon.pl

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
#!/usr/bin/perl -w
1+
#!/usr/bin/perl
22
# vim:sw=4:ts=4:
33
#
44
# Copyright (C) 2010, Nick Andrew <nick@nick-andrew.net>
55
# Licensed under the terms of the GNU General Public License, Version 3
66
#
77
# Distribute all messages received from a USB-connected XBee to TCP-connected clients.
88
#
9-
# Usage: xbee-daemon.pl [-d /dev/ttyUSBx] [-v] listen_addr ...
9+
# Usage: xbee-daemon.pl [-d /dev/ttyUSBx] [--debug] listen_addr ...
1010
#
1111
# -d /dev/ttyUSBx Connect to specified device
12-
# -v Verbose
12+
# --debug Enable per-packet logging (decoded packets)
1313
#
1414
# listen_addr A set of one or more listening addresses.
1515
# Format: host:port
@@ -21,8 +21,9 @@
2121
# All I/O to/from the xbee is logged.
2222

2323
use strict;
24+
use warnings;
2425

25-
use Getopt::Std qw(getopts);
26+
use Getopt::Long qw(GetOptions);
2627
use IO::Select qw();
2728
use Sys::Syslog qw();
2829

@@ -31,10 +32,14 @@
3132
use Selector::SocketFactory qw();
3233
use TullNet::XBee::Device qw();
3334

34-
use vars qw($opt_d $opt_v);
35+
my $opt_d;
36+
my $opt_debug;
3537

3638
$| = 1;
37-
getopts('d:v');
39+
GetOptions(
40+
'debug' => \$opt_debug,
41+
'd=s' => \$opt_d,
42+
);
3843

3944
$opt_d || die "Need option -d /dev/ttyUSBx";
4045

@@ -77,6 +82,10 @@ sub main {
7782

7883
$daemon_obj->addServer($tty_obj);
7984

85+
if ($opt_debug) {
86+
$daemon_obj->debug($opt_debug);
87+
}
88+
8089
foreach my $local_addr (@ARGV) {
8190
my $listener = Selector::SocketFactory->new(
8291
LocalAddr => $local_addr,

lib/Controller/Daemon.pm

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/perl -w
22
# vim:sw=4:ts=4:
33
#
4-
# Copyright (C) 2010, Nick Andrew <nick@nick-andrew.net>
4+
# Copyright (C) 2010-2017, Nick Andrew <nick@nick-andrew.net>
55
# Licensed under the terms of the GNU General Public License, Version 3
66
#
77
# XBee controller daemon
@@ -25,6 +25,7 @@ are accepted and become new clients.
2525
package Controller::Daemon;
2626

2727
use strict;
28+
use warnings;
2829

2930
use JSON qw();
3031
use Time::HiRes qw();
@@ -41,6 +42,7 @@ sub new {
4142
my $self = {
4243
clients => 0,
4344
client_sockets => { },
45+
debug => 0,
4446
selector => undef,
4547
server => undef,
4648
};

lib/TullNet/XBee/API/Frame.pm

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/perl -w
22
# vim:sw=4:ts=4:
33
#
4-
# Copyright (C) 2010, Nick Andrew <nick@nick-andrew.net>
4+
# Copyright (C) 2010-2017, Nick Andrew <nick@nick-andrew.net>
55
# Licensed under the terms of the GNU General Public License, Version 3
66

77
=head1 NAME
@@ -21,15 +21,16 @@ The frame structure is:
2121
data (N bytes)
2222
checksum (1 byte)
2323
24+
Packets longer than 255 bytes (by default) are ignored.
25+
2426
=head2 METHODS
2527
2628
=cut
2729

2830
package TullNet::XBee::API::Frame;
2931

3032
use strict;
31-
32-
my $DEBUG = 1;
33+
use warnings;
3334

3435

3536
=head2 I<new()>
@@ -42,8 +43,11 @@ sub new {
4243
my ($class) = @_;
4344

4445
my $self = {
46+
debug => 0,
4547
data => undef,
48+
leading_junk => '',
4649
l_msb => undef,
50+
packet_max_length => 255,
4751
to_read => undef,
4852
cksum => undef,
4953
state => 0,
@@ -67,27 +71,41 @@ If there's an error in the frame, call $self->checksumError().
6771
sub addData {
6872
my ($self, $buf) = @_;
6973

70-
my $start = chr(0x7e);
7174
my $state = $self->{'state'};
7275

7376
foreach my $c (split(//, $buf)) {
7477

7578
if ($state == 0) {
76-
if ($c eq $start) {
79+
if ($c eq chr(0x7e)) {
7780
$self->{'data'} = undef;
7881
$self->{'done'} = 0;
82+
$self->{'cksum'} = 0;
7983
$state = 1;
84+
if ($self->{'leading_junk'} ne '') {
85+
$self->printHex("Skipping junk pre frame start:", $self->{'leading_junk'});
86+
}
87+
$self->{'leading_junk'} = '';
88+
} else {
89+
$self->{'leading_junk'} .= $c;
8090
}
8191
}
8292
elsif ($state == 1) {
8393
$self->{'l_msb'} = ord($c);
8494
$state = 2;
8595
}
8696
elsif ($state == 2) {
87-
my $l_lsb = ord($c);
88-
$self->{'to_read'} = ($self->{'l_msb'} << 8) + $l_lsb;
89-
$self->{'cksum'} = 0;
90-
$state = 3;
97+
$self->{'l_lsb'} = ord($c);
98+
my $length = ($self->{'l_msb'} << 8) + $self->{'l_lsb'};
99+
if ($length > $self->{'packet_max_length'}) {
100+
# Don't allow arbitrarily long packets
101+
$self->error("Long packet (length %d) ignored, max is %d",
102+
$length, $self->{'packet_max_length'});
103+
$state = 0;
104+
} else {
105+
$self->{'length'} = $length;
106+
$self->{'to_read'} = $length;
107+
$state = 3;
108+
}
91109
}
92110
elsif ($state == 3) {
93111
$self->{'data'} .= $c;
@@ -98,7 +116,9 @@ sub addData {
98116
}
99117
}
100118
elsif ($state == 4) {
101-
$self->{'cksum'} += ord($c);
119+
my $cksum_byte = ord($c);
120+
$self->{'cksum_byte'} = $cksum_byte;
121+
$self->{'cksum'} += $cksum_byte;
102122

103123
if (($self->{'cksum'} & 0xff) != 0xff) {
104124
$self->checksumError();
@@ -116,7 +136,7 @@ sub addData {
116136
}
117137
}
118138

119-
# Remember state for next time
139+
# Remember state within frame for next time
120140
$self->{'state'} = $state;
121141

122142
return 1;
@@ -125,7 +145,7 @@ sub addData {
125145

126146
=head2 I<checksumError()>
127147
128-
Called when an illegal frame has been detected.
148+
Called when an invalid frame has been detected.
129149
130150
Override this in subclasses.
131151
@@ -134,8 +154,14 @@ Override this in subclasses.
134154
sub checksumError {
135155
my ($self) = @_;
136156

137-
printf STDERR ("Checksum error: got %02x, expected 0xff\n", $self->{'cksum'});
138-
$self->printHex("Bad frame:", $self->{'data'});
157+
my $err = sprintf("Frame Checksum error: start=7e l_msb=%02x l_lsb=%02x (length %d), cksum_byte=%02x, cksum=%02x (expected 0xff), data:",
158+
$self->{'l_msb'},
159+
$self->{'l_lsb'},
160+
$self->{'length'},
161+
$self->{'cksum_byte'},
162+
$self->{'cksum'},
163+
);
164+
$self->printHex($err, $self->{'data'});
139165
}
140166

141167

@@ -175,9 +201,9 @@ sub serialise {
175201

176202
my $len = length($buf);
177203

178-
if ($len > 10000) {
204+
if ($len > $self->{'packet_max_length'}) {
179205
# Too long
180-
$@ = 'Packet too long';
206+
$@ = sprintf('Packet too long: length=%d, maximum=%d', $len, $self->{'packet_max_length'});
181207
return undef;
182208
}
183209

@@ -196,25 +222,54 @@ sub serialise {
196222
}
197223

198224

199-
=head2 I<printHex($title, $string)>
225+
=head2 I<debug($string, args...)>
226+
227+
If debugging is enabled, then printf supplied string and args to STDERR. A newline is appended.
228+
229+
=cut
230+
231+
sub debug {
232+
my $self = shift;
233+
234+
if ($self->{'debug'}) {
235+
printf STDERR (@_);
236+
print STDERR "\n";
237+
}
238+
}
239+
240+
241+
=head2 I<error($string, args...)>
242+
243+
Printf supplied string to STDERR. A newline is appended.
244+
245+
=cut
246+
247+
sub error {
248+
my $self = shift;
249+
250+
printf STDERR (@_);
251+
print STDERR "\n";
252+
}
253+
254+
=head2 I<printHex($title, $buf)>
200255
201-
If debugging is enabled and a string is supplied,
202-
then print to STDOUT the title followed by the string in hex.
256+
If a buffer is supplied, then print to STDERR the title followed
257+
by the buffer contents in hex, then a newline.
203258
204259
=cut
205260

206261
sub printHex {
207-
my ($self, $heading, $s) = @_;
262+
my ($self, $title, $buf) = @_;
208263

209-
if ($DEBUG && defined($s)) {
210-
my $str = $heading;
264+
if (defined($buf)) {
265+
my $str = $title;
211266

212-
my @chars = unpack('C*', $s);
267+
my @chars = unpack('C*', $buf);
213268
foreach my $i (@chars) {
214269
$str .= sprintf(" %02x", $i);
215270
}
216271

217-
print STDERR "$str\n";
272+
print STDERR $str, "\n";
218273
}
219274
}
220275

lib/TullNet/XBee/Device.pm

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,6 @@ sub runHandler {
145145
}
146146
}
147147

148-
sub checksumError {
149-
my ($self, $cksum) = @_;
150-
151-
printf STDERR ("Checksum error: got %02x, expected 0xff\n", $self->{'cksum'});
152-
$self->printHex("Bad frame:", $self->{'data'});
153-
}
154-
155148
# ---------------------------------------------------------------------------
156149
# Called when a frame has been successfully received from the XBee
157150
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)