File Coverage

blib/lib/Device/Modbus/TCP.pm
Criterion Covered Total %
statement 76 86 88.3
branch 12 20 60.0
condition 5 12 41.6
subroutine 17 20 85.0
pod 0 7 0.0
total 110 145 75.8


line stmt bran cond sub pod time code
1             package Device::Modbus::TCP;
2              
3 7     7   4445 use Device::Modbus::TCP::ADU;
  7         7  
  7         140  
4 7     7   2786 use IO::Socket::INET;
  7         109172  
  7         28  
5 7     7   2520 use Errno qw(:POSIX);
  7         7  
  7         2030  
6 7     7   3101 use Time::HiRes qw(time);
  7         6524  
  7         21  
7 7     7   3612 use Try::Tiny;
  7         6552  
  7         294  
8 7     7   35 use Role::Tiny;
  7         7  
  7         35  
9 7     7   1603 use Carp;
  7         7  
  7         336  
10 7     7   28 use strict;
  7         7  
  7         105  
11 7     7   21 use warnings;
  7         7  
  7         3633  
12              
13             our $VERSION = '0.22';
14              
15             ####
16              
17             sub read_port {
18 11     11 0 539 my ($self, $bytes) = @_;
19              
20 11 100       45 return unless $bytes;
21              
22 8         19 my $sock = $self->socket;
23 8 50       27 croak "Disconnected" unless $sock->connected;
24              
25 8     0   117 local $SIG{'ALRM'} = sub { croak "Connection timed out\n" };
  0         0  
26              
27 8         9 my $msg;
28             RECEIVE : {
29 8         9 alarm $self->{timeout};
  42570         49998  
30 42570         56713 my $rc = $self->socket->recv($msg, $bytes);
31 42570         257961 alarm 0;
32 42570 100 33     69746 if (exists $!{EINTR} && $!{EINTR} || length($msg) == 0) {
      66        
33 42562         417913 redo RECEIVE;
34             }
35 7 50       150 if (!defined $rc) {
36 0         0 croak "Communication error while reading request: $!";
37             }
38             }
39              
40             # say STDERR "Bytes: " . length($msg) . " MSG: " . unpack 'H*', $msg;
41 7         12 $self->{buffer} = $msg;
42 7         40 return $msg;
43             }
44              
45             sub write_port {
46 2     2 0 13 my ($self, $adu) = @_;
47              
48 2     0   49 local $SIG{'ALRM'} = sub { die "Connection timed out\n" };
  0         0  
49 2         5 my $attempts = 0;
50 2         8 my $rc;
51             SEND: {
52 2         2 my $sock = $self->socket;
  2         9  
53             try {
54 2     2   78 alarm $self->{timeout};
55 2         9 $rc = $sock->send($adu->binary_message);
56 2         2299 alarm 0;
57 2 50       10 if (!defined $rc) {
58 0         0 die "Communication error while sending request: $!";
59             }
60             }
61             catch {
62 0 0   0   0 if ($_ =~ /timed out/) {
63 0         0 $sock->close;
64 0         0 $self->_build_socket;
65 0         0 $attempts++;
66             }
67             else {
68 0         0 croak $_;
69             }
70 2         34 };
71 2 50 33     61 last SEND if $attempts >= 5 || $rc == length($adu->binary_message);
72 0         0 redo SEND;
73             }
74 2         21 return $rc;
75             }
76              
77             sub disconnect {
78 1     1 0 870 my $self = shift;
79 1         4 $self->socket->close;
80             }
81              
82             sub parse_buffer {
83 8     8 0 80 my ($self, $bytes, $pattern) = @_;
84 8         29 $self->read_port($bytes);
85             croak "Time out error" unless
86 7 50 33     42 defined $self->{buffer} && length($self->{buffer}) >= $bytes;
87 7         31 return unpack $pattern, substr $self->{buffer},0,$bytes,'';
88             }
89              
90             sub new_adu {
91 5     5 0 1000816 my ($self, $msg) = @_;
92 5         67 my $adu = Device::Modbus::TCP::ADU->new;
93 5 100       39 if (defined $msg) {
94 1         8 $adu->message($msg);
95 1 50       43 $adu->unit($msg->{unit}) if defined $msg->{unit};
96 1         14 $adu->id( $self->next_trn_id );
97             }
98 5         9 return $adu;
99             }
100              
101             ### Parsing a message
102              
103             sub parse_header {
104 3     3 0 10 my ($self, $adu) = @_;
105 3         16 my ($id, $proto, $length, $unit) = $self->parse_buffer(7, 'nnnC');
106            
107 2         18 $adu->id($id);
108 2         7 $adu->length($length);
109 2         5 $adu->unit($unit);
110              
111 2         15 return $adu;
112             }
113              
114             sub parse_footer {
115 2     2 0 188 my ($self, $adu) = @_;
116 2         4 return $adu;
117             }
118              
119             1;
120              
121             __END__