File Coverage

blib/lib/Device/Modbus/TCP.pm
Criterion Covered Total %
statement 79 89 88.7
branch 12 20 60.0
condition 4 9 44.4
subroutine 17 20 85.0
pod 0 7 0.0
total 112 145 77.2


line stmt bran cond sub pod time code
1             package Device::Modbus::TCP;
2              
3 17     17   21949 use Device::Modbus::TCP::ADU;
  17         68  
  17         621  
4 17     17   10234 use IO::Socket::INET;
  17         383562  
  17         106  
5 17     17   8157 use Errno qw(:POSIX);
  17         58  
  17         5977  
6 17     17   5248 use Time::HiRes qw(time);
  17         12524  
  17         120  
7 17     17   10519 use Try::Tiny;
  17         50653  
  17         1265  
8 17     17   165 use Role::Tiny;
  17         34  
  17         174  
9 17     17   11045 use Carp;
  17         48  
  17         1348  
10 17     17   120 use strict;
  17         41  
  17         814  
11 17     17   106 use warnings;
  17         31  
  17         16660  
12              
13             our $VERSION = '0.025';
14              
15             ####
16              
17             sub read_port {
18 26     26 0 4232 my ($self, $bytes) = @_;
19              
20 26 100       178 return unless $bytes;
21              
22 19         86 my $sock = $self->socket;
23 19 50       112 croak "Disconnected" unless $sock->connected;
24              
25 19     0   505 local $SIG{'ALRM'} = sub { croak "Connection timed out\n" };
  0         0  
26 19         101 alarm $self->{timeout};
27              
28 19         59 my $msg = '';
29 19         39 do {
30 25983         40070 my $read;
31 25983         63770 my $rc = $self->socket->recv($read, $bytes - length($msg));
32 25982         344892 $msg .= $read;
33 25982 100 66     77642 if ($!{EINTR} || length($msg) == 0) {
34             # Shutdowns socket in case of timeout
35 2         64 $self->socket->shutdown(2);
36 2         436 last;
37             }
38 25980 50       316690 if (!defined $rc) {
39 0         0 croak "Communication error while receiving data: $!";
40             }
41             }
42             while (length($msg) < $bytes);
43 16         56 alarm 0;
44              
45             # say STDERR "Bytes: " . length($msg) . " MSG: " . unpack 'H*', $msg;
46 16         44 $self->{buffer} = $msg;
47 16         127 return $msg;
48             }
49              
50             sub write_port {
51 3     3 0 22 my ($self, $adu) = @_;
52              
53 3     0   95 local $SIG{'ALRM'} = sub { die "Connection timed out\n" };
  0         0  
54 3         17 my $attempts = 0;
55 3         14 my $rc;
56             SEND: {
57 3         10 my $sock = $self->socket;
  3         23  
58             try {
59 3     3   273 alarm $self->{timeout};
60 3         21 $rc = $sock->send($adu->binary_message);
61 3         12614 alarm 0;
62 3 50       32 if (!defined $rc) {
63 0         0 die "Communication error while sending request: $!";
64             }
65             }
66             catch {
67 0 0   0   0 if ($_ =~ /timed out/) {
68 0         0 $sock->close;
69 0         0 $self->_build_socket;
70 0         0 $attempts++;
71             }
72             else {
73 0         0 croak $_;
74             }
75 3         65 };
76 3 50 33     131 last SEND if $attempts >= 5 || $rc == length($adu->binary_message);
77 0         0 redo SEND;
78             }
79 3         55 return $rc;
80             }
81              
82             sub disconnect {
83 2     2 0 2948 my $self = shift;
84 2         12 $self->socket->close;
85             }
86              
87             sub parse_buffer {
88 19     19 0 260 my ($self, $bytes, $pattern) = @_;
89 19         92 $self->read_port($bytes);
90             croak "Time out error" unless
91 16 50 33     138 defined $self->{buffer} && length($self->{buffer}) >= $bytes;
92 16         137 return unpack $pattern, substr $self->{buffer},0,$bytes,'';
93             }
94              
95             sub new_adu {
96 12     12 0 1004423 my ($self, $msg) = @_;
97 12         356 my $adu = Device::Modbus::TCP::ADU->new;
98 12 100       220 if (defined $msg) {
99 3         49 $adu->message($msg);
100 3 50       130 $adu->unit($msg->{unit}) if defined $msg->{unit};
101 3         76 $adu->id( $self->next_trn_id );
102             }
103 12         55 return $adu;
104             }
105              
106             ### Parsing a message
107              
108             sub parse_header {
109 7     7 0 64 my ($self, $adu) = @_;
110 7         58 my ($id, $proto, $length, $unit) = $self->parse_buffer(7, 'nnnC');
111            
112 5         66 $adu->id($id);
113 5         36 $adu->length($length);
114 5         28 $adu->unit($unit);
115              
116 5         86 return $adu;
117             }
118              
119             sub parse_footer {
120 4     4 0 650 my ($self, $adu) = @_;
121 4         15 return $adu;
122             }
123              
124             1;
125              
126             __END__