File Coverage

blib/lib/Device/Modbus/Response.pm
Criterion Covered Total %
statement 68 68 100.0
branch 26 26 100.0
condition 19 21 90.4
subroutine 8 8 100.0
pod 0 2 0.0
total 121 125 96.8


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