File Coverage

blib/lib/Device/Modbus/Response.pm
Criterion Covered Total %
statement 56 56 100.0
branch 44 46 95.6
condition 41 45 91.1
subroutine 7 7 100.0
pod 0 2 0.0
total 148 156 94.8


line stmt bran cond sub pod time code
1             package Device::Modbus::Response;
2              
3 10     10   30596 use parent 'Device::Modbus';
  10         701  
  10         44  
4 10     10   1307 use Device::Modbus::Exception;
  10         11  
  10         152  
5 10     10   30 use Carp;
  10         12  
  10         431  
6 10     10   41 use strict;
  10         10  
  10         148  
7 10     10   36 use warnings;
  10         24  
  10         5279  
8              
9             my %parameters_for = (
10             'Read Coils'
11             => [qw(code bytes values)],
12             'Read Discrete Inputs'
13             => [qw(code bytes values)],
14             'Read Holding Registers'
15             => [qw(code bytes values)],
16             'Read Input Registers'
17             => [qw(code bytes values)],
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)],
24             'Write Multiple Registers'
25             => [qw(code address quantity)],
26             'Read/Write Multiple Registers'
27             => [qw(code bytes values)],
28             );
29              
30             my %format_for = (
31             0x01 => 'CCC*',
32             0x02 => 'CCC*',
33             0x03 => 'CCn*',
34             0x04 => 'CCn*',
35             0x05 => 'Cnn',
36             0x06 => 'Cnn',
37             0x0F => 'Cnn',
38             0x10 => 'Cnn',
39             0x17 => 'CCn*',
40             );
41              
42              
43             sub new {
44 44     44 0 10865 my ($class, %args) = @_;
45              
46             # Must receive either a function name or a function code
47             croak 'A function name or code is required when creating a response'
48 44 100 66     628 unless $args{function} || $args{code};
49              
50 43 100       80 if ($args{function}) {
51             croak "Function $args{function} is not supported"
52 17 100       142 unless exists $Device::Modbus::code_for{$args{function}};
53 16         35 $args{code} = $Device::Modbus::code_for{$args{function}};
54             }
55             else {
56             croak "Code $args{code} is not supported"
57 26 100       190 unless exists $Device::Modbus::function_for{$args{code}};
58 25         60 $args{function} = $Device::Modbus::function_for{$args{code}};
59             }
60              
61              
62             # Validate we have all the needed parameters
63 41         32 foreach (@{$parameters_for{$args{function}}}) {
  41         93  
64             # This is calculated
65 123 100       181 next if $_ eq 'bytes';
66              
67             # But the rest are required
68             croak "Response for function $args{function} requires '$_'"
69 103 100       333 unless exists $args{$_};
70             }
71              
72             # Validate parameters
73 39 100 100     292 if ($args{code} == 0x01 || $args{code} == 0x02) {
    100 100        
    100 100        
    100          
    100          
    50          
74 9 100 100     22 unless (@{$args{values}} > 0 && @{$args{values}} <= 0x7D0) {
  9         24  
  8         28  
75             die Device::Modbus::Exception->new(
76 2         12 code => $args{code} + 0x80,
77             exception_code => 3
78             );
79             }
80             }
81             elsif ($args{code} == 0x03 || $args{code} == 0x04 || $args{code} == 0x17) {
82 10 100 100     10 unless (@{$args{values}} > 0 && @{$args{values}} <= 0x7D) {
  10         28  
  9         26  
83             die Device::Modbus::Exception->new(
84 3         9 code => $args{code} + 0x80,
85             exception_code => 3
86             );
87             }
88             }
89             elsif ($args{code} == 0x05) {
90 6 100       18 unless (defined $args{value}) {
91             die Device::Modbus::Exception->new(
92 1         5 code => $args{code} + 0x80,
93             exception_code => 3
94             );
95             }
96             }
97             elsif ($args{code} == 0x06) {
98 5 100 100     25 unless ($args{value} >= 0 && $args{value} <= 0xFFFF) {
99             die Device::Modbus::Exception->new(
100 2         7 code => $args{code} + 0x80,
101             exception_code => 3
102             );
103             }
104             }
105             elsif ($args{code} == 0x0F) {
106 4 100 100     20 unless ($args{quantity} > 0 && $args{quantity} <= 0x7B0) {
107             die Device::Modbus::Exception->new(
108 2         8 code => $args{code} + 0x80,
109             exception_code => 3
110             );
111             }
112             }
113             elsif ($args{code} == 0x10) {
114 5 100 100     25 unless ($args{quantity} >= 1 && $args{quantity} <= 0x7B) {
115             die Device::Modbus::Exception->new(
116 2         6 code => $args{code} + 0x80,
117             exception_code => 3
118             );
119             }
120             }
121              
122 27         64 return bless \%args, $class;
123             }
124              
125             sub pdu {
126 9     9 0 6725 my $self = shift;
127              
128 9 100 100     89 if ($self->{code} == 0x01 || $self->{code} == 0x02) {
    100 100        
    100 66        
    50 100        
      66        
129 2         8 my $values = $self->flatten_bit_values($self->{values});
130 2         9 return pack('CC', $self->{code}, scalar(@$values))
131             . join '', @$values;
132             }
133             elsif ($self->{code} == 0x03 || $self->{code} == 0x04 || $self->{code} == 0x17) {
134 2         1 my $bytes = 2 * scalar @{$self->{values}};
  2         5  
135             return pack $format_for{$self->{code}},
136 2         5 $self->{code}, $bytes, @{$self->{values}};
  2         9  
137             }
138             elsif ($self->{code} == 0x05 || $self->{code} == 0x06) {
139 3         4 my $value = $self->{value};
140 3 100 66     9 $value = 0xFF00 if $self->{code} == 0x05 && $self->{value};
141             return pack $format_for{$self->{code}},
142 3         13 $self->{code}, $self->{address}, $value;
143             }
144             elsif ($self->{code} == 0x0F || $self->{code} == 0x10) {
145             return pack $format_for{$self->{code}},
146 2         9 $self->{code}, $self->{address}, $self->{quantity};
147             }
148             }
149              
150             1;