File Coverage

blib/lib/Device/Modbus/Request.pm
Criterion Covered Total %
statement 85 85 100.0
branch 32 32 100.0
condition 48 60 80.0
subroutine 8 8 100.0
pod 0 2 0.0
total 173 187 92.5


line stmt bran cond sub pod time code
1             package Device::Modbus::Request;
2              
3 9     9   43899 use parent 'Device::Modbus';
  9         921  
  9         59  
4 9     9   4912 use Device::Modbus::Exception;
  9         23  
  9         255  
5 9     9   48 use Carp;
  9         15  
  9         492  
6 9     9   44 use strict;
  9         15  
  9         162  
7 9     9   43 use warnings;
  9         13  
  9         182  
8 9     9   75 use v5.10;
  9         25  
9              
10             my %parameters_for = (
11             'Read Coils'
12             => [qw(code address quantity)],
13             'Read Discrete Inputs'
14             => [qw(code address quantity)],
15             'Read Holding Registers'
16             => [qw(code address quantity)],
17             'Read Input Registers'
18             => [qw(code address quantity)],
19             'Write Single Coil'
20             => [qw(code address value)],
21             'Write Single Register'
22             => [qw(code address value)],
23             'Write Multiple Coils'
24             => [qw(code address quantity bytes values)],
25             'Write Multiple Registers'
26             => [qw(code address quantity bytes values)],
27             'Read/Write Multiple Registers'
28             => [qw(code read_address read_quantity
29             write_address write_quantity bytes values)],
30             );
31              
32              
33             my %format_for = (
34             0x01 => 'Cnn',
35             0x02 => 'Cnn',
36             0x03 => 'Cnn',
37             0x04 => 'Cnn',
38             0x05 => 'Cnn',
39             0x06 => 'Cnn',
40             0x0F => 'CnnCC*',
41             0x10 => 'CnnCn*',
42             0x17 => 'CnnnnCn*',
43             );
44              
45             sub new {
46 80     80 0 12786 my ($class, %args) = @_;
47             croak 'A function name or code is required when creating a request'
48 80 100 66     579 unless $args{function} || $args{code};
49              
50 79 100       179 if ($args{function}) {
51             croak "Function $args{function} is not supported"
52 45 100       280 unless exists $Device::Modbus::code_for{$args{function}};
53 44         129 $args{code} = $Device::Modbus::code_for{$args{function}};
54             }
55             else {
56             croak "Function code $args{code} is not supported"
57 34 100       266 unless exists $Device::Modbus::function_for{$args{code}};
58 33         88 $args{function} = $Device::Modbus::function_for{$args{code}};
59             }
60              
61             # Validate we have all the needed parameters
62 77         119 foreach (@{$parameters_for{$args{function}}}) {
  77         238  
63             # These are calculated
64 308 100       1046 next if $_ ~~ ['bytes', 'write_quantity'];
65 269 100 100     951 next if $_ eq 'quantity' && $args{code} ~~ [0x0F, 0x10];
66              
67             # But the rest are required
68             croak "Function $args{function} requires '$_'"
69 252 100       836 unless exists $args{$_};
70             }
71              
72             # Validate parameters
73 76         164 foreach ($args{code}) {
74 76         166 when ([0x01, 0x02]) {
75 16 100 66     166 unless (defined $args{quantity} && $args{quantity} >= 1 && $args{quantity} <= 0x7D0) {
      100        
76             return Device::Modbus::Exception->new(
77 6         34 code => $args{code} + 0x80,
78             exception_code => 3
79             );
80             }
81             }
82 60         197 when ([0x03, 0x04]) {
83 17 100 66     206 unless (defined $args{quantity} && $args{quantity} >= 1 && $args{quantity} <= 0x7D) {
      100        
84             return Device::Modbus::Exception->new(
85 6         25 code => $args{code} + 0x80,
86             exception_code => 3
87             );
88             }
89             }
90 43         75 when (0x05) {
91 8 100       53 $args{value} = 1 if $args{value};
92 8 100 66     108 unless (defined $args{value} && $args{value} >= 0 && $args{value} <= 0xFFFF) {
      66        
93             return Device::Modbus::Exception->new(
94 1         5 code => $args{code} + 0x80,
95             exception_code => 3
96             );
97             }
98             }
99 35         54 when (0x06) {
100 7 100 66     99 unless (defined $args{value} && $args{value} >= 0 && $args{value} <= 0xFFFF) {
      100        
101             return Device::Modbus::Exception->new(
102 2         16 code => $args{code} + 0x80,
103             exception_code => 3
104             );
105             }
106             }
107 28         41 when (0x0F) {
108 9 100 66     34 unless (defined $args{values} && @{$args{values}} >= 1 && @{$args{values}} <= 0x7B0) {
  9   100     64  
  7         44  
109             return Device::Modbus::Exception->new(
110 4         22 code => $args{code} + 0x80,
111             exception_code => 3
112             );
113             }
114             }
115 19         30 when (0x10) {
116 8 100 66     39 unless (defined $args{values} && @{$args{values}} >= 1 && @{$args{values}} <= 0x7B) {
  8   100     55  
  6         38  
117             return Device::Modbus::Exception->new(
118 4         16 code => $args{code} + 0x80,
119             exception_code => 3
120             );
121             }
122             }
123 11         20 when (0x17) {
124 11 100 33     154 unless (
      66        
      100        
      100        
      100        
125             defined $args{read_quantity}
126             && defined $args{values}
127             && $args{read_quantity} >= 1
128             && $args{read_quantity} <= 0x7D
129 7         41 && @{$args{values}} >= 1
130 5         28 && @{$args{values}} <= 0x79) {
131             return Device::Modbus::Exception->new(
132 7         30 code => $args{code} + 0x80,
133             exception_code => 3
134             );
135             }
136             }
137             }
138              
139 46         197 return bless \%args, $class;
140             }
141              
142             sub pdu {
143 19     19 0 16516 my $self = shift;
144              
145 19         49 foreach ($self->{code}) {
146 19         62 when ([0x01, 0x02, 0x03, 0x04]) {
147             return pack $format_for{$_},
148 8         60 $self->{code}, $self->{address}, $self->{quantity};
149             }
150 11         32 when ([0x05, 0x06]) {
151 5         10 my $value = $self->{value};
152 5 100 66     24 $value = 0xFF00 if $_ == 0x05 && $self->{value};
153             return pack $format_for{$_},
154 5         29 $self->{code}, $self->{address}, $value;
155             }
156 6         13 when (0x0F) {
157 2         19 my $values = $self->flatten_bit_values($self->{values});
158 2         5 my $quantity = scalar @{$self->{values}};
  2         6  
159             my $pdu = pack $format_for{$_},
160             $self->{code}, $self->{address},
161 2         9 $quantity, scalar @$values;
162 2         11 return $pdu . join '', @$values;
163             }
164 4         9 when (0x10) {
165 2         4 my $quantity = scalar @{$self->{values}};
  2         5  
166 2         7 my $bytes = 2*$quantity;
167             return pack $format_for{$_},
168             $self->{code}, $self->{address}, $quantity, $bytes,
169 2         9 @{$self->{values}};
  2         13  
170             }
171 2         4 when (0x17) {
172 2         4 my $quantity = scalar @{$self->{values}};
  2         7  
173 2         5 my $bytes = 2*$quantity;
174             return pack $format_for{$_},
175             $self->{code},
176             $self->{read_address},
177             $self->{read_quantity},
178             $self->{write_address},
179             $quantity,
180             $bytes,
181 2         7 @{$self->{values}};
  2         12  
182             }
183             }
184             }
185              
186             1;
187              
188             __END__