File Coverage

blib/lib/Device/Modbus/RTU.pm
Criterion Covered Total %
statement 66 66 100.0
branch 12 14 85.7
condition 12 13 92.3
subroutine 14 14 100.0
pod 0 8 0.0
total 104 115 90.4


line stmt bran cond sub pod time code
1             package Device::Modbus::RTU;
2              
3 3     3   1680 use Device::Modbus::RTU::ADU;
  3         4  
  3         87  
4 3     3   767 use Device::SerialPort;
  3         1238  
  3         74  
5 3     3   16 use Carp;
  3         4  
  3         143  
6 3     3   10 use strict;
  3         5  
  3         45  
7 3     3   9 use warnings;
  3         4  
  3         98  
8              
9             our $VERSION = '0.021';
10              
11 3     3   9 use Role::Tiny;
  3         4  
  3         13  
12              
13             sub open_port {
14 5     5 0 7 my $self = shift;
15              
16             # Validate parameters
17             croak "Attribute 'port' is required for a Modbus RTU client"
18 5 100       178 unless exists $self->{port};
19              
20             # Defaults related with the serial port
21 4   100     33 $self->{baudrate} //= 9600;
22 4   100     19 $self->{databits} //= 8;
23 4   100     13 $self->{parity} //= 'even';
24 4   100     20 $self->{stopbits} //= 1;
25 4   100     11 $self->{timeout} //= 10; # seconds
26              
27             # Serial Port object
28 4         19 my $serial = Device::SerialPort->new($self->{port});
29 4 50       34 croak "Unable to open serial port " . $self->{port} unless $serial;
30              
31 4         31 $serial->baudrate ($self->{baudrate});
32 4         59 $serial->databits ($self->{databits});
33 4         40 $serial->parity ($self->{parity});
34 4         48 $serial->stopbits ($self->{stopbits});
35 4         138 $serial->handshake('none');
36              
37             # char_time and read_char_time are given in milliseconds
38             $self->{char_time} =
39 4         36 1000*($self->{databits}+$self->{stopbits}+1)/ $self->{baudrate};
40              
41 4         25 $serial->read_char_time($self->{char_time});
42 4 100       41 if ($self->{baudrate} < 19200) {
43 3         21 $serial->read_const_time(3.5 * $self->{char_time});
44             }
45             else {
46 1         5 $serial->read_const_time(1.75);
47             }
48              
49 4 50       41 $serial->write_settings || croak "Unable to open port: $!";
50 4         40 $serial->purge_all;
51 4         24 $self->{port} = $serial;
52 4         7 return $serial;
53             }
54              
55             sub read_port {
56 7875     7875 0 21339 my $self = shift;
57 7875         14991 my ($bytes, $read) = $self->{port}->read(255);
58             # say STDERR "> " . join '-', unpack 'C*', $read;
59 7875         35025 $self->{buffer} = $read;
60 7875         10180 return $read;
61             }
62              
63             sub write_port {
64 2     2 0 6 my ($self, $adu) = @_;
65 2         7 $self->{port}->write($adu->binary_message);
66             }
67              
68             sub disconnect {
69 2     2 0 4 my $self = shift;
70 2         23 $self->{port}->close;
71             }
72              
73             #### Modbus RTU Operations
74              
75             sub parse_buffer {
76 7882     7882 0 7351 my ($self, $bytes, $pattern) = @_;
77             croak "Timeout error" unless
78 7882 100 66     524932 defined $self->{buffer} && length($self->{buffer}) >= $bytes;
79 9         35 return unpack $pattern, substr $self->{buffer},0,$bytes,'';
80             }
81              
82             sub new_adu {
83 7880     7880 0 16505 my ($self, $msg) = @_;
84 7880         14232 my $adu = Device::Modbus::RTU::ADU->new;
85 7880 100       31635 if (defined $msg) {
86 5         23 $adu->message($msg);
87 5 100       58 $adu->unit($msg->{unit}) if defined $msg->{unit};
88             }
89 7879         10449 return $adu;
90             }
91              
92             ### Parsing a message
93              
94             sub parse_header {
95 7875     7875 0 14704 my ($self, $adu) = @_;
96 7875         9099 my $unit = $self->parse_buffer(1, 'C');
97 2         8 $adu->unit($unit);
98 2         20 return $adu;
99             }
100              
101             sub parse_footer {
102 2     2 0 155 my ($self, $adu) = @_;
103 2         5 my $crc = $self->parse_buffer(2, 'v');
104 2         9 $adu->crc($crc);
105 2         3 return $adu;
106             }
107              
108             1;
109              
110             __END__