File Coverage

blib/lib/Device/Modbus/TCP.pm
Criterion Covered Total %
statement 78 88 88.6
branch 13 20 65.0
condition 8 12 66.6
subroutine 17 20 85.0
pod 0 7 0.0
total 116 147 78.9


line stmt bran cond sub pod time code
1             package Device::Modbus::TCP;
2              
3 17     17   13290 use Device::Modbus::TCP::ADU;
  17         37  
  17         378  
4 17     17   6812 use IO::Socket::INET;
  17         326693  
  17         116  
5 17     17   8277 use Errno qw(:POSIX);
  17         44  
  17         5800  
6 17     17   4485 use Time::HiRes qw(time);
  17         9964  
  17         100  
7 17     17   10635 use Try::Tiny;
  17         46804  
  17         1123  
8 17     17   123 use Role::Tiny;
  17         244  
  17         140  
9 17     17   7560 use Carp;
  17         41  
  17         1087  
10 17     17   106 use strict;
  17         34  
  17         289  
11 17     17   82 use warnings;
  17         40  
  17         11628  
12              
13             our $VERSION = '0.026';
14              
15             ####
16              
17             sub read_port {
18 26     26 0 3856 my ($self, $bytes) = @_;
19              
20 26 100       131 return unless $bytes;
21              
22 19         77 my $sock = $self->socket;
23 19 50       114 croak "Disconnected" unless $sock->connected;
24              
25 19     0   402 local $SIG{'ALRM'} = sub { croak "Connection timed out\n" };
  0         0  
26 19         79 alarm $self->{timeout};
27              
28 19         46 my $msg = '';
29 19   100     38 do {
30 13741         167043 my $read;
31 13741         42003 my $rc = $self->socket->recv($read, $bytes - length($msg));
32 13741         242396 $msg .= $read;
33 13741 100 66     64007 if ($!{EINTR} || length($msg) == 0) {
34             # Shutdowns socket in case of timeout
35 2         39 $self->socket->shutdown(2);
36             }
37 13741 50       240734 if (!defined $rc) {
38 0         0 croak "Communication error while receiving data: $!";
39             }
40             }
41             while ($self->socket->connected && length($msg) < $bytes);
42 18         248 alarm 0;
43              
44             # say STDERR "Bytes: " . length($msg) . " MSG: " . unpack 'H*', $msg;
45 18         49 $self->{buffer} = $msg;
46 18         123 return $msg;
47             }
48              
49             sub write_port {
50 3     3 0 18 my ($self, $adu) = @_;
51              
52 3     0   54 local $SIG{'ALRM'} = sub { die "Connection timed out\n" };
  0         0  
53 3         8 my $attempts = 0;
54 3         6 my $rc;
55             SEND: {
56 3         9 my $sock = $self->socket;
  3         16  
57             try {
58 3     3   171 alarm $self->{timeout};
59 3         21 $rc = $sock->send($adu->binary_message);
60 3         2412 alarm 0;
61 3 50       16 if (!defined $rc) {
62 0         0 die "Communication error while sending request: $!";
63             }
64             }
65             catch {
66 0 0   0   0 if ($_ =~ /timed out/) {
67 0         0 $sock->close;
68 0         0 $self->_build_socket;
69 0         0 $attempts++;
70             }
71             else {
72 0         0 croak $_;
73             }
74 3         44 };
75 3 50 33     91 last SEND if $attempts >= 5 || $rc == length($adu->binary_message);
76 0         0 redo SEND;
77             }
78 3         37 return $rc;
79             }
80              
81             sub disconnect {
82 2     2 0 2472 my $self = shift;
83 2         10 $self->socket->close;
84             }
85              
86             sub parse_buffer {
87 19     19 0 233 my ($self, $bytes, $pattern) = @_;
88 19         82 $self->read_port($bytes);
89             croak "Time out error" unless
90 18 100 66     896 defined $self->{buffer} && length($self->{buffer}) >= $bytes;
91 16         118 return unpack $pattern, substr $self->{buffer},0,$bytes,'';
92             }
93              
94             sub new_adu {
95 12     12 0 1003437 my ($self, $msg) = @_;
96 12         270 my $adu = Device::Modbus::TCP::ADU->new;
97 12 100       160 if (defined $msg) {
98 3         33 $adu->message($msg);
99 3 50       84 $adu->unit($msg->{unit}) if defined $msg->{unit};
100 3         57 $adu->id( $self->next_trn_id );
101             }
102 12         45 return $adu;
103             }
104              
105             ### Parsing a message
106              
107             sub parse_header {
108 7     7 0 49 my ($self, $adu) = @_;
109 7         51 my ($id, $proto, $length, $unit) = $self->parse_buffer(7, 'nnnC');
110            
111 5         60 $adu->id($id);
112 5         32 $adu->length($length);
113 5         29 $adu->unit($unit);
114              
115 5         76 return $adu;
116             }
117              
118             sub parse_footer {
119 4     4 0 537 my ($self, $adu) = @_;
120 4         14 return $adu;
121             }
122              
123             1;
124              
125             __END__