File Coverage

blib/lib/Device/Modbus/RTU.pm
Criterion Covered Total %
statement 68 68 100.0
branch 12 14 85.7
condition 12 13 92.3
subroutine 15 15 100.0
pod 0 8 0.0
total 107 118 90.6


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