File Coverage

blib/lib/Device/Modbus/RTU.pm
Criterion Covered Total %
statement 73 73 100.0
branch 12 14 85.7
condition 17 19 89.4
subroutine 14 14 100.0
pod 0 8 0.0
total 116 128 90.6


line stmt bran cond sub pod time code
1             package Device::Modbus::RTU;
2              
3 8     8   4300 use Device::Modbus::RTU::ADU;
  8         11  
  8         162  
4 8     8   2101 use Device::SerialPort;
  8         23320  
  8         181  
5 8     8   30 use Carp;
  8         9  
  8         309  
6 8     8   24 use strict;
  8         8  
  8         99  
7 8     8   20 use warnings;
  8         6  
  8         209  
8              
9             our $VERSION = '0.022';
10              
11 8     8   22 use Role::Tiny;
  8         9  
  8         669  
12              
13             sub open_port {
14 8     8 0 10 my $self = shift;
15              
16             # Validate parameters
17             croak "Attribute 'port' is required for a Modbus RTU client"
18 8 100       165 unless exists $self->{port};
19              
20             # Defaults related with the serial port
21 7   100     39 $self->{baudrate} //= 9600;
22 7   100     29 $self->{databits} //= 8;
23 7   100     17 $self->{parity} //= 'even';
24 7   100     27 $self->{stopbits} //= 1;
25 7   100     16 $self->{timeout} //= 10; # seconds
26              
27             # Serial Port object
28 7         25 my $serial = Device::SerialPort->new($self->{port});
29 7 50       60 croak "Unable to open serial port " . $self->{port} unless $serial;
30              
31 7         50 $serial->baudrate ($self->{baudrate});
32 7         92 $serial->databits ($self->{databits});
33 7         61 $serial->parity ($self->{parity});
34 7         61 $serial->stopbits ($self->{stopbits});
35 7         62 $serial->handshake('none');
36              
37             # char_time and read_char_time are given in milliseconds
38             $self->{char_time} =
39 7         52 1000*($self->{databits}+$self->{stopbits}+1)/ $self->{baudrate};
40              
41 7         35 $serial->read_char_time($self->{char_time});
42 7 100       51 if ($self->{baudrate} < 19200) {
43 6         36 $serial->read_const_time(3.5 * $self->{char_time});
44             }
45             else {
46 1         5 $serial->read_const_time(1.75);
47             }
48              
49 7 50       58 $serial->write_settings || croak "Unable to open port: $!";
50 7         59 $serial->purge_all;
51 7         35 $self->{port} = $serial;
52 7         13 return $serial;
53             }
54              
55             sub read_port {
56 10751     10751 0 23935 my $self = shift;
57 10751         7835 my $buffer = '';
58 10751         7046 my $bytes = 0;
59 10751         9639 my $timeout = 1000 * $self->{timeout}; # Turn to milliseconds
60 10751   100     6958 do {
      66        
61 204184         1474683 my $read;
62 204184         237026 ($bytes, $read) = $self->{port}->read(255);
63 204184         653045 $buffer .= $read;
64 204184         427126 $timeout -= $self->{port}->read_const_time + 255 * $self->{char_time};
65             } until ($timeout <= 0 || ($bytes == 0 && length($buffer) > 0));
66             # say STDERR "> " . join '-', unpack 'C*', $buffer;
67 10751         62603 $self->{buffer} = $buffer;
68 10751         11958 return $buffer;
69             }
70              
71             sub write_port {
72 4     4 0 9 my ($self, $adu) = @_;
73 4         14 $self->{port}->write($adu->binary_message);
74             }
75              
76             sub disconnect {
77 5     5 0 9 my $self = shift;
78 5         40 $self->{port}->close;
79             }
80              
81             #### Modbus RTU Operations
82              
83             sub parse_buffer {
84 10766     10766 0 7542 my ($self, $bytes, $pattern) = @_;
85             croak "Timeout error" unless
86 10766 100 66     620821 defined $self->{buffer} && length($self->{buffer}) >= $bytes;
87 20         63 return unpack $pattern, substr $self->{buffer},0,$bytes,'';
88             }
89              
90             sub new_adu {
91 10757     10757 0 20212 my ($self, $msg) = @_;
92 10757         16646 my $adu = Device::Modbus::RTU::ADU->new;
93 10757 100       36257 if (defined $msg) {
94 6         22 $adu->message($msg);
95 6 100       60 $adu->unit($msg->{unit}) if defined $msg->{unit};
96             }
97 10756         12275 return $adu;
98             }
99              
100             ### Parsing a message
101              
102             sub parse_header {
103 10751     10751 0 17308 my ($self, $adu) = @_;
104 10751         11095 my $unit = $self->parse_buffer(1, 'C');
105 5         22 $adu->unit($unit);
106 5         46 return $adu;
107             }
108              
109             sub parse_footer {
110 5     5 0 338 my ($self, $adu) = @_;
111 5         11 my $crc = $self->parse_buffer(2, 'v');
112 5         17 $adu->crc($crc);
113 5         6 return $adu;
114             }
115              
116             1;
117              
118             __END__