File Coverage

blib/lib/Protocol/Modbus/Response.pm
Criterion Covered Total %
statement 18 89 20.2
branch 0 22 0.0
condition 0 18 0.0
subroutine 6 16 37.5
pod 0 10 0.0
total 24 155 15.4


line stmt bran cond sub pod time code
1             package Protocol::Modbus::Response;
2              
3 5     5   25 use strict;
  5         10  
  5         150  
4 5     5   22 use warnings;
  5         8  
  5         174  
5 5     5   24 use overload '""' => \&stringify;
  5         8  
  5         35  
6 5     5   293 use overload 'eq' => \=
  5         10  
  5         31  
7              
8 5     5   3108 use Protocol::Modbus::Exception;
  5         14  
  5         120  
9 5     5   27 use Carp;
  5         8  
  5         5932  
10              
11             our @in = ();
12             our @coils = ();
13              
14             sub equals {
15 0     0 0   my ($x, $y) = @_;
16 0           $x->stringify() eq $y->stringify(); # or "$x" == "$y"
17             }
18              
19             #
20             # `frame' is required when calling constructor
21             #
22             sub new {
23 0     0 0   my ($obj, %args) = @_;
24 0   0       my $class = ref($obj) || $obj;
25              
26 0   0       $args{pdu} ||= $args{frame};
27              
28 0           my $self = {_options => {%args},};
29              
30 0           bless $self, $class;
31             }
32              
33             sub stringify {
34 0     0 0   my $self = $_[0];
35 0           my $cRes = 'Modbus generic response';
36 0 0         if ($self->{_function}) {
37 0           $cRes = 'Modbus response (func=%s, address=%s, value=%s)';
38 0           $cRes = sprintf($cRes, $self->{_function}, $self->{_address}, $self->{_value});
39             }
40 0           return ($cRes);
41             }
42              
43             # Frame is the entire packet stream received from transport
44             sub frame {
45 0     0 0   my $self = shift;
46 0 0         if (@_) {
47 0           $self->{_options}->{frame} = $_[0];
48             }
49 0           return ($self->{_options}->{frame});
50             }
51              
52             # PDU is the "Pure" Modbus packet without transport headers
53             sub pdu {
54 0     0 0   my $self = shift;
55 0 0         if (@_) {
56 0           $self->{_options}->{pdu} = $_[0];
57             }
58 0           return ($self->{_options}->{pdu});
59             }
60              
61             sub process {
62 0     0 0   my ($self, $pdu) = @_;
63              
64             # If binary packets not supplied, take them from constructor options ('frame')
65 0   0       $pdu ||= $self->pdu();
66              
67             #warn('Parsing binary data [', unpack('H*', $pdu), ']');
68              
69 0           my $excep = 0; # Modbus exception flag
70 0           my $error = 0; # Error in parsing response
71 0           my $count = 0; # How many bytes in response
72 0           my @bytes = (); # Hold response bytes
73              
74             # Get function code (only first char)
75 0           my $func = ord substr($pdu, 0, 1);
76              
77             # Check if there was an exception (msb on)
78 0 0         if ($func & 0x80) {
79              
80             # Yes, exception for function $func - 0x80
81 0           $func -= 0x80;
82 0           $excep = ord substr($pdu, 1, 1);
83             }
84              
85             # There was an exception response. Throw exception!
86 0 0         if ($excep > 0) {
87 0           warn('Throw exception func=', $func, ' code=', $excep);
88 0           return (throw Protocol::Modbus::Exception(function => $func, code => $excep));
89             }
90              
91             #
92             # Normal response
93             # Decode bytes that arrived
94             #
95 0 0 0       if ($func == &Protocol::Modbus::FUNC_READ_COILS) {
    0          
    0          
    0          
96 0           $count = ord substr($pdu, 1, 1);
97 0           @bytes = split //, substr($pdu, 2);
98 0           @coils = ();
99 0           for (@bytes) {
100 0           $_ = unpack('B*', $_);
101 0           $_ = reverse;
102 0           push @coils, split //;
103             }
104 0           $self->{_coils} = \@coils;
105             }
106             elsif ($func == &Protocol::Modbus::FUNC_READ_INPUTS) {
107 0           $count = ord substr($pdu, 1, 1);
108 0           @bytes = split //, substr($pdu, 2);
109 0           @in = ();
110 0           for (@bytes) {
111 0           $_ = unpack('B*', $_);
112 0           $_ = reverse;
113 0           push @in, split //;
114             }
115 0           $self->{_inputs} = \@in;
116             }
117             elsif ($func == &Protocol::Modbus::FUNC_WRITE_COIL
118             || $func == &Protocol::Modbus::FUNC_WRITE_REGISTER)
119             {
120 0           $self->{_function} = $func;
121 0           $self->{_address} = unpack 'n', substr($pdu, 1, 2);
122 0           $self->{_value} = unpack 'n', substr($pdu, 3, 2);
123             }
124             elsif ($func == &Protocol::Modbus::FUNC_READ_HOLD_REGISTERS) {
125 0           $count = ord substr($pdu, 1, 1);
126 0           @bytes = split //, substr($pdu, 2);
127 0           @in = ();
128 0           for (@bytes) {
129 0           push @in, unpack('H*', $_);
130             }
131 0           $self->{_registers} = \@in;
132             }
133 0           return ($self);
134             }
135              
136             sub coils {
137 0     0 0   $_[0]->{_coils};
138             }
139              
140             sub inputs {
141 0     0 0   $_[0]->{_inputs};
142             }
143              
144             sub registers {
145 0     0 0   $_[0]->{_registers};
146             }
147              
148             # Given function code, return response structure
149             sub structure {
150 0     0 0   my ($self, $func) = @_;
151 0           my @tokens = ();
152              
153 0 0 0       if ( $func == &Protocol::Modbus::FUNC_READ_COILS
    0 0        
154             || $func == &Protocol::Modbus::FUNC_READ_INPUTS)
155             {
156 0           @tokens = (&Protocol::Modbus::PARAM_COUNT, &Protocol::Modbus::PARAM_STATUS_LIST,);
157             }
158             elsif ($func == &Protocol::Modbus::FUNC_READ_HOLD_REGISTERS
159             || $func == &Protocol::Modbus::FUNC_READ_INPUT_REGISTERS)
160             {
161 0           @tokens =
162             (&Protocol::Modbus::PARAM_COUNT, &Protocol::Modbus::PARAM_REGISTER_LIST,);
163             }
164             else {
165 0           croak('UNIMPLEMENTED RESPONSE');
166             }
167              
168 0           return (@tokens);
169             }
170              
171             1;
172