File Coverage

blib/lib/Device/Modbus/Request.pm
Criterion Covered Total %
statement 70 70 100.0
branch 52 54 96.3
condition 67 78 85.9
subroutine 7 7 100.0
pod 1 2 50.0
total 197 211 93.3


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