File Coverage

blib/lib/IPC/Serial.pm
Criterion Covered Total %
statement 20 101 19.8
branch 0 40 0.0
condition 0 3 0.0
subroutine 7 14 50.0
pod 4 4 100.0
total 31 162 19.1


line stmt bran cond sub pod time code
1             # -*-cperl-*-
2             #
3             # IPC::Serial - Simple message passing over serial ports
4             # Copyright (c) 2016-2017 Ashish Gulhati
5             #
6             # $Id: lib/IPC/Serial.pm v1.006 Sun Jun 11 12:42:55 PDT 2017 $
7              
8 2     2   28126 use strict;
  2         4  
  2         63  
9              
10             package IPC::Serial;
11              
12 2     2   31 use 5.008001;
  2         6  
13 2     2   10 use warnings;
  2         9  
  2         47  
14 2     2   9 use strict;
  2         4  
  2         47  
15              
16 2     2   1210 use Device::SerialPort qw(:STAT);
  2         54287  
  2         434  
17 2     2   21 use Digest::MD5 qw(md5_hex);
  2         4  
  2         103  
18              
19 2     2   10 use vars qw( $VERSION $AUTOLOAD );
  2         5  
  2         1725  
20              
21             our ( $VERSION ) = '$Revision: 1.006 $' =~ /\s+([\d\.]+)/;
22              
23             sub new {
24 0     0 1   my $class = shift;
25 0           my %args = @_;
26 0 0         return undef unless $args{Port};
27 0 0         return unless my $port = new Device::SerialPort ($args{Port});
28 0           $port->read_char_time(0); # don't wait for each character
29 0           $port->read_const_time(10); # 10 ms per unfulfilled "read" call
30 0           $port->user_msg('ON');
31 0           $port->databits(8);
32 0           $port->baudrate(115200);
33 0           $port->parity("none");
34 0           $port->stopbits(1);
35 0           $port->handshake("none");
36 0           bless { Port => $port }, $class;
37             }
38              
39             sub getmsg {
40 0     0 1   my ($self, $idletimeout, $ack, $check) = @_;
41 0           my ($buffer, $chars, $rcvd) = ('');
42 0           until ($rcvd) {
43 0           $buffer = '';
44 0 0         if (defined $self->{savefragment}) { $buffer .= $self->{savefragment}; delete $self->{savefragment}; }
  0            
  0            
45 0           my $timeout = $idletimeout;
46 0   0       while ($timeout and $buffer !~ /\S\n/) {
47 0           my ($count,$saw) = $self->{Port}->read(1024);
48 0 0         if ($count > 0) {
49 0           $buffer .= $saw;
50 0 0         last if $buffer =~ /\S\n/;
51 0           $timeout = $idletimeout;
52             }
53             else {
54 0           $timeout--;
55             }
56             }
57 0 0         if ($buffer =~ /\n/) {
58 0           my $buf = $buffer;
59 0           $buffer =~ s/^\s*(.+?)\:\n(.*)$/$1/s;
60 0           my ($trailing, $cksum, $msg) = ($2);
61 0 0         $self->{savefragment} .= $trailing if $trailing =~ /\S+/;
62 0 0         if ($buffer =~ /(.+):(.*)/) {
63 0 0         if ($2) { # Checksum attached
    0          
64 0 0         unless ($check) {
65 0 0         if ($ack) { # But wasn't expected. We seem to have missed an ACK
66 0           return 'ERR';
67             }
68             }
69 0           ($msg, $cksum) = ($1, $2);
70 0 0         if (_cksum($msg) eq $cksum) { # Valid checksum, all good
71 0           $self->sendmsg('OK');
72 0           $buffer = $msg;
73 0           $rcvd = 1;
74             }
75             else { # Checksum mismatch
76 0           $self->_diag("GM B:$buffer\nBB:$buf\nM:$msg\nH:$cksum\n");
77 0           $self->sendmsg('ERR');
78             }
79             }
80             elsif ($check) { # No checksum but was expected, Error.
81 0           $self->_diag("GM B:$buffer\nBB:$buf\nM:$msg\nH:$cksum\n");
82 0 0         next if $buffer eq 'OK:'; # Ignore if it's a stray OK we missed
83 0           $self->sendmsg('ERR');
84             }
85             else { # No checksum in sent message, not expected
86 0           $buffer =~ s/\:$//;
87 0           $rcvd = 1;
88             }
89             }
90             else { # No colon in sent message, serial ate it
91 0           $self->_diag("GM B:$buffer\nBB:$buf\nM:$msg\nH:$cksum\n");
92 0           $self->sendmsg('ERR');
93             }
94             }
95             else { # Timed out waiting for response.
96 0 0         $self->{savefragment} = $buffer if $buffer =~ /\S+/;
97 0 0         return $ack ? 'ERR' : undef; # If we were waiting for an ACK, this is an error
98             }
99             }
100 0           $buffer =~ s/(?
  0            
101 0           return $buffer;
102             }
103              
104             sub sendmsg {
105 0     0 1   my ($self,$msg,$cksum) = @_;
106 0           $self->_diag("sendmsg:$msg:\n");
107 0           $msg =~ s/\;/\;\;/g; $msg =~ s/\:/\;/g;
  0            
108 0 0         my $hexhash = $cksum ? _cksum($msg) : '';
109 0           my $ack = ''; my $i = 1;
  0            
110 0           while ($ack ne 'OK') {
111 0           $self->_diag("sendmsg:$i:$msg:\n"); $i++;
  0            
112 0           $self->{Port}->write("$msg:$hexhash:\n\n\n\n");
113 0 0         $ack = $cksum ? $self->getmsg(100, 1, 0) : 'OK';
114             }
115 0           return 1;
116             }
117              
118             sub close {
119 0     0 1   my $self = shift;
120 0           $self->port->close;
121             }
122              
123             sub _cksum {
124 0     0     md5_hex(shift);
125             }
126              
127             sub _diag {
128 0     0     my $self = shift;
129 0           print STDERR @_;
130             }
131              
132             sub AUTOLOAD {
133 0     0     my $self = shift; (my $auto = $AUTOLOAD) =~ s/.*:://;
  0            
134 0 0         return if $auto eq 'DESTROY';
135 0 0         if ($auto =~ /^(port)$/x) {
136 0           return $self->{"\u$auto"};
137             }
138             else {
139 0           die "Could not AUTOLOAD method $auto.";
140             }
141             }
142              
143             1; # End of IPC::Serial
144              
145             =head1 NAME
146              
147             IPC::Serial - Simple message passing over serial ports
148              
149             =head1 VERSION
150              
151             $Revision: 1.006 $
152             $Date: Sun Jun 11 12:42:55 PDT 2017 $
153              
154             =head1 SYNOPSIS
155              
156             Simple message passing over serial ports.
157              
158             use IPC::Serial;
159              
160             my $serial1 = new IPC::Serial (Port => '/dev/cua00');
161             my $serial2 = new IPC::Serial (Port => '/dev/cua01');
162              
163             $serial1->sendmsg("Hello there!");
164             my $msg = $serial2->getmsg;
165              
166             =head1 METHODS
167              
168             =head2 new
169              
170             =head2 getmsg
171              
172             =head2 sendmsg
173              
174             =head2 close
175              
176             =head1 AUTHOR
177              
178             Ashish Gulhati, C<< >>
179              
180             =head1 BUGS
181              
182             Please report any bugs or feature requests to C, or through
183             the web interface at L. I will be notified, and then you'll
184             automatically be notified of progress on your bug as I make changes.
185              
186             =head1 SUPPORT
187              
188             You can find documentation for this module with the perldoc command.
189              
190             perldoc IPC::Serial
191              
192             You can also look for information at:
193              
194             =over 4
195              
196             =item * RT: CPAN's request tracker
197              
198             L
199              
200             =item * AnnoCPAN: Annotated CPAN documentation
201              
202             L
203              
204             =item * CPAN Ratings
205              
206             L
207              
208             =item * Search CPAN
209              
210             L
211              
212             =back
213              
214             =head1 LICENSE AND COPYRIGHT
215              
216             Copyright (c) 2017 Ashish Gulhati.
217              
218             This program is free software; you can redistribute it and/or modify it
219             under the terms of the Artistic License 2.0.
220              
221             See L for the full
222             license terms.