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) Ashish Gulhati
5             #
6             # $Id: lib/IPC/Serial.pm v1.007 Tue Oct 16 22:52:29 PDT 2018 $
7              
8 2     2   112310 use strict;
  2         13  
  2         59  
9              
10             package IPC::Serial;
11              
12 2     2   39 use 5.008001;
  2         6  
13 2     2   8 use warnings;
  2         3  
  2         40  
14 2     2   8 use strict;
  2         3  
  2         53  
15              
16 2     2   1296 use Device::SerialPort qw(:STAT);
  2         51861  
  2         405  
17 2     2   17 use Digest::MD5 qw(md5_hex);
  2         3  
  2         126  
18              
19 2     2   11 use vars qw( $VERSION $AUTOLOAD );
  2         4  
  2         1778  
20              
21             our ( $VERSION ) = '$Revision: 1.007 $' =~ /\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.007 $
152             $Date: Tue Oct 16 22:52:29 PDT 2018 $
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) Ashish Gulhati.
217              
218             This software package is Open Software; you can use, redistribute,
219             and/or modify it under the terms of the Open Artistic License 2.0.
220              
221             Please see L for the full license
222             terms, and ensure that the license grant applies to you before using
223             or modifying this software. By using or modifying this software, you
224             indicate your agreement with the license terms.