|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Device::Modbus::Server;  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
3
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
7742
 | 
 use Device::Modbus;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
96
 | 
    | 
| 
4
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
1572
 | 
 use Device::Modbus::Request;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
111
 | 
    | 
| 
5
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
1502
 | 
 use Device::Modbus::Response;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
95
 | 
    | 
| 
6
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
18
 | 
 use Device::Modbus::Exception;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
85
 | 
    | 
| 
7
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
1070
 | 
 use Device::Modbus::Unit;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
82
 | 
    | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
9
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
2507
 | 
 use Try::Tiny;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4239
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
188
 | 
    | 
| 
10
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
19
 | 
 use Carp;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
150
 | 
    | 
| 
11
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
15
 | 
 use strict;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
    | 
| 
12
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
13
 | 
 use warnings;  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4962
 | 
    | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub proto {  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return {  | 
| 
16
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
566
 | 
         units     => {},  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         log_level => 2,  | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         timeout   => 5,  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ### Unit management  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub units {  | 
| 
25
 | 
34
 | 
 
 | 
 
 | 
  
34
  
 | 
  
0
  
 | 
291
 | 
     my $self = shift;  | 
| 
26
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
73
 | 
     return $self->{units};  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub add_server_unit {  | 
| 
30
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
1390
 | 
     my ($self, $unit, $id) = @_;  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
32
 | 
5
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
58
 | 
     if (ref $unit && $unit->isa('Device::Modbus::Unit')) {  | 
| 
33
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
         $unit->init_unit;  | 
| 
34
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         $self->units->{$unit->id} = $unit;  | 
| 
35
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
         return $unit;  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
38
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
263
 | 
         croak "Units must be subclasses of Device::Modbus::Unit";  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub get_server_unit {  | 
| 
43
 | 
16
 | 
 
 | 
 
 | 
  
16
  
 | 
  
0
  
 | 
37
 | 
     my ($self, $unit_id) = @_;  | 
| 
44
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
     return $self->units->{$unit_id};  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # To be overrided in subclasses  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub init_server {  | 
| 
49
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
  
0
  
 | 
110
 | 
     croak "Server must be initialized\n";  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ### Request parsing  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub receive_request {  | 
| 
56
 | 
37
 | 
 
 | 
 
 | 
  
37
  
 | 
  
0
  
 | 
23238
 | 
     my $self = shift;  | 
| 
57
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
     $self->read_port;  | 
| 
58
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
173
 | 
     my $adu = $self->new_adu();  | 
| 
59
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
     $self->parse_header($adu);  | 
| 
60
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
91
 | 
     $self->parse_pdu($adu);  | 
| 
61
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
     $self->parse_footer($adu);  | 
| 
62
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
89
 | 
     return $adu;  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub parse_pdu {  | 
| 
66
 | 
37
 | 
 
 | 
 
 | 
  
37
  
 | 
  
0
  
 | 
35
 | 
     my ($self, $adu) = @_;  | 
| 
67
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
     my $request;  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
69
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
83
 | 
     my $code = $self->parse_buffer(1,'C');  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
71
 | 
37
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
511
 | 
     if ($code == 0x01 || $code == 0x02 || $code == 0x03 || $code == 0x04) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Read coils, discrete inputs, holding registers, input registers  | 
| 
73
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
         my ($address, $quantity) = $self->parse_buffer(4,'nn');  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
75
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
104
 | 
         $request = Device::Modbus::Request->new(  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             code       => $code,  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             address    => $address,  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             quantity   => $quantity  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     elsif ($code == 0x05 || $code == 0x06) {  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Write single coil and single register  | 
| 
83
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
         my ($address, $value) = $self->parse_buffer(4, 'nn');  | 
| 
84
 | 
5
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
44
 | 
         if ($code == 0x05 && $value != 0xFF00 && $value != 0) {  | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
85
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
             $request = Device::Modbus::Exception->new(  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code           => $code + 0x80,  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 exception_code => 3  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         else {                 | 
| 
91
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
             $request = Device::Modbus::Request->new(  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code       => $code,  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 address    => $address,  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 value      => $value  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     elsif ($code == 0x0F) {  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Write multiple coils  | 
| 
100
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
         my ($address, $qty, $bytes) = $self->parse_buffer(5, 'nnC');  | 
| 
101
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
67
 | 
         my $bytes_qty = $qty % 8 ? int($qty/8) + 1 : $qty/8;  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
103
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
13
 | 
         if ($bytes == $bytes_qty) {  | 
| 
104
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
             my (@values) = $self->parse_buffer($bytes, 'C*');  | 
| 
105
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
69
 | 
             @values      = Device::Modbus->explode_bit_values(@values);  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
107
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
135
 | 
             $request = Device::Modbus::Request->new(  | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code       => $code,  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 address    => $address,  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 quantity   => $qty,  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 bytes      => $bytes,  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 values     => \@values  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         else {  | 
| 
116
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             $request = Device::Modbus::Exception->new(  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code           => $code + 0x80,  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 exception_code => 3  | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     elsif ($code == 0x10) {  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Write multiple registers  | 
| 
124
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
         my ($address, $qty, $bytes) = $self->parse_buffer(5, 'nnC');  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
126
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
49
 | 
         if ($bytes == 2 * $qty) {  | 
| 
127
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
             my (@values) = $self->parse_buffer($bytes, 'n*');  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
129
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
             $request = Device::Modbus::Request->new(  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code       => $code,  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 address    => $address,  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 quantity   => $qty,  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 bytes      => $bytes,  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 values     => \@values  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         else {  | 
| 
138
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             $request = Device::Modbus::Exception->new(  | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code           => $code + 0x80,  | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 exception_code => 3  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     elsif ($code == 0x17) {  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Read/Write multiple registers  | 
| 
146
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
         my ($read_addr, $read_qty, $write_addr, $write_qty, $bytes)  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             = $self->parse_buffer(9, 'nnnnC');  | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
149
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
29
 | 
         if ($bytes == 2 * $write_qty) {  | 
| 
150
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
             my (@values) = $self->parse_buffer($bytes, 'n*');  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
152
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
             $request = Device::Modbus::Request->new(  | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code           => $code,  | 
| 
154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 read_address   => $read_addr,  | 
| 
155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 read_quantity  => $read_qty,  | 
| 
156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 write_address  => $write_addr,  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 write_quantity => $write_qty,  | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 bytes          => $bytes,  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 values         => \@values  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         else {  | 
| 
163
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             $request = Device::Modbus::Exception->new(  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 code           => $code + 0x80,  | 
| 
165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 exception_code => 3  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Unimplemented function  | 
| 
171
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
         $request = Device::Modbus::Exception->new(  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             code           => $code + 0x80,  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             exception_code => 1,  | 
| 
174
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
177
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
200
 | 
     $adu->message($request);  | 
| 
178
 | 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
     return $request;          | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ### Server code  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Read Coils'                    => 0x01,  | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Read Discrete Inputs'          => 0x02,  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Read Holding Registers'        => 0x03,  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Read Input Registers'          => 0x04,  | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Write Single Coil'             => 0x05,  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Write Single Register'         => 0x06,  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Write Multiple Coils'          => 0x0F,  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Write Multiple Registers'      => 0x10,  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    'Read/Write Multiple Registers' => 0x17,  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #my %area_and_mode_for = (  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %can_read_zone = (  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x01 => ['discrete_coils',    'read' ],  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x02 => ['discrete_inputs',   'read' ],  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x03 => ['holding_registers', 'read' ],  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x04 => ['input_registers',   'read' ],  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x17 => ['holding_registers', 'read' ],  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %can_write_zone = (  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x05 => ['discrete_coils',    'write' ],  | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x06 => ['holding_registers', 'write' ],  | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x0F => ['discrete_coils',    'write' ],  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x10 => ['holding_registers', 'write' ],  | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0x17 => ['holding_registers', 'write' ],  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub modbus_server {  | 
| 
211
 | 
15
 | 
 
 | 
 
 | 
  
15
  
 | 
  
0
  
 | 
49
 | 
     my ($server, $adu) = @_;  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ### Make sure the requested unit exists in this server  | 
| 
214
 | 
15
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
23
 | 
     unless (exists $server->units->{$adu->unit}) {  | 
| 
215
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         return $server->request_for_others($adu);  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ### Process write requests first  | 
| 
219
 | 
15
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
31
 | 
     if (exists $can_write_zone{ $adu->code }) {  | 
| 
220
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         my ($zone, $mode) = @{$can_write_zone{$adu->code}};  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
221
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
         my $resp = $server->process_write_requests($adu, $zone, $mode);  | 
| 
222
 | 
7
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
21
 | 
         return $resp if $resp;  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ### Process read requests last  | 
| 
226
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
     my ($zone, $mode) = @{$can_read_zone{$adu->code}};  | 
| 
 
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
227
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
     my $resp = $server->process_read_requests($adu, $zone, $mode);  | 
| 
228
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
     return $resp;  | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub process_write_requests {  | 
| 
232
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
  
0
  
 | 
9
 | 
     my ($server, $adu, $zone, $mode) = @_;  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
234
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
     my $unit = $server->get_server_unit($adu->unit);  | 
| 
235
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
     my $code = $adu->code;  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
237
 | 
7
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
13
 | 
     my $address = $adu->message->{address} // $adu->message->{write_address};  | 
| 
238
 | 
7
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
11
 | 
     my $values  = $adu->message->{values} // [ $adu->message->{value} ];  | 
| 
239
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     my $quantity = @$values;  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Find the requested address within unit's addresses  | 
| 
242
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
     $server->log(4, "Routing 'write' zone: <$zone> address: <$address> qty: <$quantity>");  | 
| 
243
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
     my $match = $unit->route($zone, $mode, $address, $quantity);  | 
| 
244
 | 
7
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
25
 | 
     $server->log(4, 'Match was' . (ref $match ? ' ' : ' not ') . 'successful');  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return Device::Modbus::Exception->new(  | 
| 
247
 | 
7
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
28
 | 
         function       => $Device::Modbus::function_for{$code},  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         exception_code => $match,  | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         unit           => $adu->unit  | 
| 
250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ) unless ref $match;  | 
| 
251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Execute the requested route with the given parameters  | 
| 
254
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
     my $response;  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     try {  | 
| 
256
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
102
 | 
         $match->routine->($unit, $server, $adu->message, $address, $quantity, $values);  | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     catch {  | 
| 
259
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
14
 | 
         $server->log(4,  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             "Action failed for 'write' zone: <$zone> address: <$address> quantity: <$quantity> error: $_ ");  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $response = Device::Modbus::Exception->new(  | 
| 
263
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
             function       => $Device::Modbus::function_for{$code},  | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             exception_code => 4,  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             unit           => $adu->unit  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
267
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
29
 | 
     };  | 
| 
268
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
3088
 | 
     return $response if defined $response;  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Build the response  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Write single values  | 
| 
272
 | 
3
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
23
 | 
     if ($code == 0x05 || $code == 0x06) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
273
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
         $response = Device::Modbus::Response->new(  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             code    => $code,  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             address => $address,  | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             value   => $values->[0]  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Write multiple values  | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     elsif ($code == 0x0F || $code == 0x10) {  | 
| 
281
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
         $response = Device::Modbus::Response->new(  | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             code     => $code,  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             address  => $address,  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             quantity => $quantity  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     elsif ($code == 0x17) {  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # 0x17 must perform a read operation afterwards  | 
| 
289
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1
 | 
         $response = '';  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
292
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     return $response;  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub process_read_requests {  | 
| 
296
 | 
9
 | 
 
 | 
 
 | 
  
9
  
 | 
  
0
  
 | 
11
 | 
     my ($server, $adu, $zone, $mode) = @_;  | 
| 
297
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
298
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
     my $unit = $server->get_server_unit($adu->unit);  | 
| 
299
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
     my $code = $adu->code;  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
301
 | 
9
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
14
 | 
     my $address  = $adu->message->{address} // $adu->message->{write_address};  | 
| 
302
 | 
9
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
15
 | 
     my $quantity = $adu->message->{quantity} // $adu->message->{read_quantity};  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
304
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
     $server->log(4, "Routing 'read' zone: <$zone> address: <$address> quantity: <$quantity>");  | 
| 
305
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
     my $match = $unit->route($zone, 'read', $address, $quantity);  | 
| 
306
 | 
9
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
33
 | 
     $server->log(4,  | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'Match was' . (ref $match ? ' ' : ' not ') . 'successful');  | 
| 
308
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return Device::Modbus::Exception->new(  | 
| 
310
 | 
9
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
30
 | 
         function       => $Device::Modbus::function_for{$code},  | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         exception_code => $match,  | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         unit           => $adu->unit  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ) unless ref $match;  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
315
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
     my @vals;  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $response;  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     try {  | 
| 
318
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
73
 | 
         @vals = $match->routine->($unit, $server, $adu->message, $address, $quantity);  | 
| 
319
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2253
 | 
         croak 'Quantity of returned values differs from request'  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             unless scalar @vals == $quantity;  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     catch {  | 
| 
323
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
56
 | 
         $server->log(4,  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             "Action failed for 'read' zone: <$zone> address: <$address> quantity: <$quantity> -- $_");  | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $response = Device::Modbus::Exception->new(  | 
| 
327
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             function       => $Device::Modbus::function_for{$code},  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             exception_code => 4,  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             unit           => $adu->unit  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
331
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
     };  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
333
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     unless (defined $response) {  | 
| 
334
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
         $response = Device::Modbus::Response->new(  | 
| 
335
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             code   => $code,  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             values => \@vals  | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
340
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     return $response;  | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |